From 8b675522a2ba5514a73e0b015ff272f61a2affda Mon Sep 17 00:00:00 2001 From: Ivan Komissarov <ABBAPOH@gmail.com> Date: Sun, 12 Jan 2014 10:59:54 +0400 Subject: [PATCH] Fix reading palette images in DDS handler. Change-Id: I039a5f3376a8ebe7230f30a0df31c4a355f22349 Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> Reviewed-by: Alex Char <prevedtest@gmail.com> Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com> --- src/plugins/imageformats/dds/ddsheader.h | 4 ++ src/plugins/imageformats/dds/qddshandler.cpp | 39 +++++++++++++++++-- tests/auto/dds/tst_qdds.cpp | 1 + tests/shared/images/dds.qrc | 1 + tests/shared/images/dds/P4.dds | Bin 0 -> 2240 bytes tests/shared/images/dds/P8.dds | Bin 16512 -> 5248 bytes 6 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 tests/shared/images/dds/P4.dds diff --git a/src/plugins/imageformats/dds/ddsheader.h b/src/plugins/imageformats/dds/ddsheader.h index 7f6df061..7b2b80ce 100644 --- a/src/plugins/imageformats/dds/ddsheader.h +++ b/src/plugins/imageformats/dds/ddsheader.h @@ -134,6 +134,9 @@ enum Format { FormatA2B10G10R10_XR_BIAS = 119, FormatBinaryBuffer = 199, + FormatP4, + FormatA4P4, + FormatLast = 0x7fffffff }; @@ -143,6 +146,7 @@ struct DDSPixelFormat FlagAlphaPixels = 0x00000001, FlagAlpha = 0x00000002, FlagFourCC = 0x00000004, + FlagPaletteIndexed4 = 0x00000008, FlagPaletteIndexed8 = 0x00000020, FlagRGB = 0x00000040, FlagYUV = 0x00000200, diff --git a/src/plugins/imageformats/dds/qddshandler.cpp b/src/plugins/imageformats/dds/qddshandler.cpp index c85e75d2..13bb9aaf 100644 --- a/src/plugins/imageformats/dds/qddshandler.cpp +++ b/src/plugins/imageformats/dds/qddshandler.cpp @@ -221,7 +221,11 @@ static inline QRgb yuv2rgb(quint8 Y, quint8 U, quint8 V) static Format getFormat(const DDSHeader &dds) { const DDSPixelFormat &format = dds.pixelFormat; - if (format.flags & DDSPixelFormat::FlagFourCC) { + if (format.flags & DDSPixelFormat::FlagPaletteIndexed4) { + return FormatP4; + } else if (format.flags & DDSPixelFormat::FlagPaletteIndexed8) { + return FormatP8; + } else if (format.flags & DDSPixelFormat::FlagFourCC) { for (size_t i = 0; i < knownFourCCsSize; ++i) { if (dds.pixelFormat.fourCC == knownFourCCs[i]) return knownFourCCs[i]; @@ -729,7 +733,7 @@ static QImage readCxV8U8(QDataStream &s, const quint32 width, const quint32 heig return image; } -static QImage readPaletteImage(QDataStream &s, quint32 width, quint32 height) +static QImage readPalette8Image(QDataStream &s, quint32 width, quint32 height) { QImage image(width, height, QImage::Format_Indexed8); for (int i = 0; i < 256; ++i) { @@ -749,6 +753,31 @@ static QImage readPaletteImage(QDataStream &s, quint32 width, quint32 height) return image; } +static QImage readPalette4Image(QDataStream &s, quint32 width, quint32 height) +{ + QImage image(width, height, QImage::Format_Indexed8); + for (int i = 0; i < 16; ++i) { + quint8 r, g, b, a; + s >> r >> g >> b >> a; + image.setColor(i, qRgba(r, g, b, a)); + } + + for (quint32 y = 0; y < height; y++) { + quint8 index; + for (quint32 x = 0; x < width - 1; ) { + s >> index; + image.setPixel(x++, y, (index & 0x0f) >> 0); + image.setPixel(x++, y, (index & 0xf0) >> 4); + } + if (width % 2 == 1) { + s >> index; + image.setPixel(width - 1, y, (index & 0x0f) >> 0); + } + } + + return image; +} + static QImage readARGB16(QDataStream &s, quint32 width, quint32 height) { QImage image(width, height, QImage::Format_ARGB32); @@ -1004,9 +1033,11 @@ static QImage readLayer(QDataStream &s, const DDSHeader &dds, const int format, case FormatA2B10G10R10: return readA2R10G10B10(s, dds, width, height); case FormatP8: - return readPaletteImage(s, width, height); case FormatA8P8: - break; + return readPalette8Image(s, width, height); + case FormatP4: + case FormatA4P4: + return readPalette4Image(s, width, height); case FormatA16B16G16R16: return readARGB16(s, width, height); case FormatV8U8: diff --git a/tests/auto/dds/tst_qdds.cpp b/tests/auto/dds/tst_qdds.cpp index 4464b991..eb258497 100644 --- a/tests/auto/dds/tst_qdds.cpp +++ b/tests/auto/dds/tst_qdds.cpp @@ -108,6 +108,7 @@ void tst_qdds::readImage_data() QTest::newRow("45") << QString("YUY2") << QSize(64, 64); QTest::newRow("46") << QString("RXGB") << QSize(64, 64); QTest::newRow("47") << QString("ATI2") << QSize(64, 64); + QTest::newRow("48") << QString("P4") << QSize(64, 64); } void tst_qdds::readImage() diff --git a/tests/shared/images/dds.qrc b/tests/shared/images/dds.qrc index e19eee95..128a38de 100644 --- a/tests/shared/images/dds.qrc +++ b/tests/shared/images/dds.qrc @@ -28,6 +28,7 @@ <file>dds/L8.dds</file> <file>dds/L16.dds</file> <file>dds/mipmaps.dds</file> + <file>dds/P4.dds</file> <file>dds/P8.dds</file> <file>dds/Q8W8V8U8.dds</file> <file>dds/Q16W16V16U16.dds</file> diff --git a/tests/shared/images/dds/P4.dds b/tests/shared/images/dds/P4.dds new file mode 100644 index 0000000000000000000000000000000000000000..c8c3e904bf3666cd1caae80f8784899769712311 GIT binary patch literal 2240 zcmb7_O=uit7{>=utf0^lV?L;)8OS0#1rKu?s5OBXl0ra|_LVS#ha5&o_9Coo3h7}= z7YBRXy$lqz0~?limEc16kns@HWK)xo7Cd-y59-Ow+R#e<fV}>nXLi%1jfhW4Chz-u zp8xZIzo-zG&aN1S@#5)I#st3fW1OO2|3A*6q+j~{N#&<cnl$vK&r_{_{$0QD)}ns( zgGjG@_p9#weW2gCvZg;=@938%0$u&#f!^%?r5_#~=;vN{w6kzkPXu4;f{$qB?FIV! zJ`a6Av8{a4kWy|R<?S?WB}?t}UpTOBSyr>17EY>9(=-QaTMKFGKcl|{TtGDi1X@q^ zPaux+JoCM@;OCC{TPF;6)Sv5p-#+SpFU>8rbI@(%m0Co9ztuXz{j{KzA8)2<Ln*aR z+&{|OC~r0}k2Iw$e2>>-T*%kpAxBv84kb_Y$=s@kwuLyaVwG=WQ<EgIz25$k^*JhE zC3&TYw~#QGaVkG2YspeedH5&QW{=~5=zQ9RcKRO9s^-<C8qdk`6!uY`*x+YOvnF>H zT2Y3qZKe$>XWRr|DV2h7ygbibO01Dq*W(`Zp&$&y`7CAcklTr6&5{0~X@ZwI<97Nr zGO}{;xO{h|^q4sm!utE$g==(pw7A5wt$2B7rU*^~h{Z47w8!~y$UF`*{+M}S1|dAm z=T^gAoFQ)xpY+_o&P=I<`b<Ebo9FJCJnF9vY|Ts+fk`0@>jm7hY;UY4U6fD5oikH{ z>RF+VorXH>5k!>3dn-a<frue9AWpc6OH2`m;Wyy40E$_2k8ybnOQTTBgLlD8qZ!{$ zAik8Wnzu5pylHW7Mii%d0=w<i@OF~isB^IuD>*H8z{R6|7FcM(L&L6T?&8>+pB7s) z0--e}tlxl#5jm#lmBcD8hm&IKrkE1NN0cy&8(F5j{V~F7KLG#s=0$MvSZn<i9m)uK z9k=o$a=%y%Cad7o03GDI-NdPk)UDW3$}|5ciq|HiJ}(YtOJ?hIhrIzIip(Q85XH;c zqR`n=1Hf1t0`(%|4r}Zh%6LECE!@UQA*xmIYR2m%3P$0XQkH=u;!$*wGBVCs%7IzA zobfx%k-!{}haQ2HfqYk>I&$xvcOr`F8hMi0Gl76PiMc3pz$?rzOKRNb<p3N3USWai zFzaT#jJx((pF3HDE4>GX4_);kh)1qV`i{#TW)Bkxe?-_}UUAvNbt(7hmEs+RLng^T z;hZq~n>)(dz-BdaD^O<r$Z^;tjC?}9al$|GoZ~R321p^p*NLP29h#t7@|XRE-%&IS z9GIrQKpb@$cOnca+llyo>5K#SxPx}g-C-o)u2DVB^?3yYOgZjDQ;%xhy#P$*huo>q zdcaYvyIokp;+&4LLH>HZG2#BnnDz$oieYU0*y(iMrUCnp@Df;^CV-8Nm+8OM`x!_5 zFWoLRhxqq|d)&AWi3&{<r)0N7^pKMTb7B-+e3Tmoxgc8=r>A|a9)k`bM^F1{jtmZ( O??>jpKYI52pZp)5Y-l_H literal 0 HcmV?d00001 diff --git a/tests/shared/images/dds/P8.dds b/tests/shared/images/dds/P8.dds index 936b870318f85c737daf8bbab821394c44e7c449..4bef9ec321f1b02e59b770285a2621c578c83134 100644 GIT binary patch literal 5248 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 Mc;w%n{J;5s05W>2Qvd(} literal 16512 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 W0=+l}-qv3}bZT1+{~!PNa`GQKC)Vu% -- GitLab