From 59157971e8ba0be0ed82594e5c24f37d16ac9981 Mon Sep 17 00:00:00 2001 From: Mirco Miranda Date: Wed, 28 Oct 2026 15:46:16 +0100 Subject: [PATCH] Ensure that the palette does not have more than 256 values --- src/imageformats/chunks.cpp | 4 +-- src/imageformats/psd.cpp | 2 +- src/imageformats/tga.cpp | 2 +- src/imageformats/tim.cpp | 2 +- src/imageformats/util_p.h | 56 +++++++++++++++++++++++++++++++++++++ src/imageformats/xcf.cpp | 2 +- 6 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/imageformats/chunks.cpp b/src/imageformats/chunks.cpp index 90a697a..22a49e4 100644 --- a/src/imageformats/chunks.cpp +++ b/src/imageformats/chunks.cpp @@ -565,13 +565,13 @@ QList CMAPChunk::palette(bool halfbride) const { auto p = innerPalette(); if (!halfbride) { - return p; + return truncTo8BitColorTable(p); } auto tmp = p; for (auto &&v : tmp) { p << qRgb(qRed(v) / 2, qGreen(v) / 2, qBlue(v) / 2); } - return p; + return truncTo8BitColorTable(p); } bool CMAPChunk::innerReadStructure(QIODevice *d) diff --git a/src/imageformats/psd.cpp b/src/imageformats/psd.cpp index 5152437..afa10b9 100644 --- a/src/imageformats/psd.cpp +++ b/src/imageformats/psd.cpp @@ -1146,7 +1146,7 @@ bool PSDHandler::read(QImage *image) } img.fill(qRgb(0, 0, 0)); if (!cmds.palette.isEmpty()) { - img.setColorTable(cmds.palette); + img.setColorTable(truncTo8BitColorTable(cmds.palette)); setTransparencyIndex(img, irs); } diff --git a/src/imageformats/tga.cpp b/src/imageformats/tga.cpp index 03af818..166b0e1 100644 --- a/src/imageformats/tga.cpp +++ b/src/imageformats/tga.cpp @@ -652,7 +652,7 @@ static bool LoadTGA(QIODevice *dev, const TgaHeader &tga, QImage &img) return false; } - img.setColorTable(colorTable); + img.setColorTable(truncTo8BitColorTable(colorTable)); } // Convert image to internal format. diff --git a/src/imageformats/tim.cpp b/src/imageformats/tim.cpp index 353438f..debe053 100644 --- a/src/imageformats/tim.cpp +++ b/src/imageformats/tim.cpp @@ -184,7 +184,7 @@ public: // where there shouldn't be any (so, disabled for now) clut << qRgba((v & 0x1F) * 255 / 31, ((v >> 5) & 0x1F) * 255 / 31, ((v >> 10) & 0x1F) * 255 / 31, 255); } - return clut; + return truncTo8BitColorTable(clut); } bool read(QIODevice *d) diff --git a/src/imageformats/util_p.h b/src/imageformats/util_p.h index 4f556dd..39e5966 100644 --- a/src/imageformats/util_p.h +++ b/src/imageformats/util_p.h @@ -138,6 +138,62 @@ inline QImage imageAlloc(qint32 width, qint32 height, const QImage::Format &form return imageAlloc(QSize(width, height), format, init); } +/*! + * \brief fixIndexedColorTable + * Helper function to return a color table with the wanted size. + * + * A copy of the color table is truncated or padded (with transparent entries) + * until it has exactly \a wantedSize entries, clamped to the range [2, 256]. + * A color table larger than 256 entries could crash on conversion to + * Format_Grayscale8, Format_Mono, Format_MonoLSB or Format_Alpha8, while one + * with fewer entries than the maximum possible pixel index would leave some + * indexes undefined. + * \param colorTable The color table to copy and resize. + * \param wantedSize The wanted number of color table entries, by default 256. + * \return The resized copy of the color table. + */ +inline QList fixIndexedColorTable(const QList &colorTable, qint32 wantedSize = 256) +{ + QList list = colorTable; + + // color table larger than 256 items could crash on conversion to + // Format_Grayscale8, Format_Mono, Format_MonoLSB, Format_Alpha8 + wantedSize = qBound(2, 256, wantedSize); + + if (list.size() == wantedSize) { + return list; + } + + // ensure that it is no larger than required + if (list.size() > wantedSize) { + list.resize(wantedSize); + } + + // ensure that it is no smaller than required + while (list.size() < wantedSize) { + list << QRgb(); + } + + return list; +} + +/*! + * \brief truncTo8BitColorTable + * Helper function to limit a color table to 256 entries. + * + * A copy of the color table is returned, truncated to 256 entries if it has + * more. Color tables with more than 256 entries could crash on conversion to + * Format_Grayscale8, Format_Mono, Format_MonoLSB or Format_Alpha8, and indexes + * beyond 255 cannot be stored in an 8-bit indexed pixel format. Color tables + * with at most 256 entries are returned unchanged. + * \param colorTable The color table to copy. + * \return A copy of the color table limited to 256 entries. + */ +inline QList truncTo8BitColorTable(const QList &colorTable) +{ + return(fixIndexedColorTable(colorTable, colorTable.size())); +} + /*! * \brief checkImageSize * Helper function to make sure the image size does not exceed the limit set in Qt. diff --git a/src/imageformats/xcf.cpp b/src/imageformats/xcf.cpp index 12f06b5..ca561ac 100644 --- a/src/imageformats/xcf.cpp +++ b/src/imageformats/xcf.cpp @@ -1561,7 +1561,7 @@ void XCFImageFormat::setPalette(XCFImage &xcf_image, QImage &image) { Q_ASSERT(xcf_image.num_colors == xcf_image.palette.size()); - image.setColorTable(xcf_image.palette); + image.setColorTable(truncTo8BitColorTable(xcf_image.palette)); } /*!