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
zcmbuD4OkOb*2htyQa4x>8H<9tOX>$I5{1~+2&n<8FVRH6c0pN<#7M}ZKrmE@1Q4Mu
z8Ve{5NW~Zp611-$m|7LBMQo8&0TXKCM+LEJ6&e+-c9pZ|PQY%rc6Z<BJ<nujGBdyP
z|DSX3<UUVAL!-tiot&HoySqAZ@kl;S?qv6Wk8y~Q4+&St;n(|@=LK$Y9cV9hfy*Bw
zVCbUFkZQC-+mBCSM2a5b4qSwR(=uT5xlTCUaS!hH_QC+SvEUyS4*_x=@b`QJ%I5E3
zFgFWy?LFW&Lk!z7S1$m}$oLf8MCEYeelLiRwL#{It6+QdcR1Af2)u$tFzUViP<W{u
zgdd%PX>m%3-E|%q^EU!-eGLqvX26NtkCD3p7445eck?N%`>G4PG7drePtU-4iUg{!
zbwf(SbujfjhoQ0CfL-=AyfP~jVr$NW%k)&Jzw;P|CG3FW3qOK?{8m_c=n7nY-V4F&
z%3!;-3v$g}fYyMQe-w<je*y;oLkQgPImo`e3aRyXVbtbRkW}9RPA-1{=ehZi*VF~;
zOgCYxwHs=_e*!ff55ak=6b6k6g{bXkL4vx{+RL!1^%e~Jb2=>8-3k#O)xxeT-4L<8
z0fJU-fH#x}!EJFdypgg88t?oBhp*m;8|Z1^)MR)y?0u+z&<pELc8EWG8RGYt!1AOQ
zUSTCc;D?{X@q0hRTk8)&zSR!H6}2#0wi`x>wcrwz3oe`-$ZWa`iN|litNbD;zw!X0
zDw^QvwTDpB{si(`?clal18L{5pSOEKdg2NU3C#spb_&$|@C;s`uYype4hp~N0#0f%
z@C%QD4s(oIRS8qySq;O&vLW%11%}N^f(uUoTCwMUdV3kv+IwMXjTyGK*kSdTcY*rb
zUabE<MCnX0Dyk5^y!RABi|avTY=_)0zJtZslWD2DK=i2%_}d#H;{93}9$x`5pPYko
zvmGKzPJ(OTYIt={1{}Wo3=UwQ_uP37C9Pc$ySoKuCFa8>+XHwlVJ9S?x(&_~Vj;A=
z0o-S&!P-U~m&^BIp!<06NYp`ewFx$uzK7StG9hNa6*!xYf#<q?5RZPNt6N}H?hz=w
zV22fTcVN`gZBW<Q18*rbP-wXagGSAU=}VKrZ}S(h75flg+6baUZLso~4P>Y8z$|4A
z_-!}}E;BN~eQ^=wp0~sH8&6<(Tp75&DTj!yXCMlhIk~m4zU2-K!}%%w)C^0EoiHV7
zC(Kx(1g`otOjgvu@W{<DBDES=;w_+SvqR!%*WoDE9ah!^nK=IjxDJQ4`0n^E{431=
z_-hEu-3{lupTe6fGhoQv^>F%T4>08hn7i=^%-wVxR_L2y!<ifKm$I`kZ$k~Z&dGuC
zSx145wJa*B2WMs!%+34+-ZfZ(;`E1zrPy0vu-EZ5fj)>BqJIj`X0g{tIo0c{HGi0(
z2yq1UJ3L3kU@*80?`8Mvm%Sq5GDxC?|C$#$=w-{7FR#U|_P&VA<ud*|{RA3Dbv0p^
zFMsbnlEj-N5GDL)|1u&?LS0*n%5)G5Q;}zgIO3{*SHIvss~`8SUEb;0*%t-NJC{*(
z6FBOs-|t_eLmVF@;mw-aIj}ziDS+s6)M~FB*-QJ+VabpZ`E;u}47HD%q)^v;dJgm?
z;Z2eHMIw$jOD%r+_>m#ws<-8Rp%bY)|Lj9}?o0zsd+NIB{)tdtt!BNPCzY!UzV|NB
zbQkAo2+r`<8R*az_z?T(A89p9tdae8{t#TG7ID=IpNL{Ld73M#EiTq!bQTzN<O<qX
zvsf%KOCy`~uk)|J?;^!ExsKC^>vRQVGw2Mpcpq_^*J!#mKKuLUFFnCI9MmJhUAR)N
zBlDHP_0|>V<>}}xPgm@X?IYc%-ODr@ft5}`QH~dcLD$D%_^ezxS<$UwiA42zdBqWV
z#d#69;RpNK*FCIWChMNiU?uT_M|UXd9Ui{2b)|f=d|R<RqBsJ1c>%{KLM|sb&5Ok>
z#AOp^3gjdxsuZJW`;f;jo(xZeAsiEAV1icp<cKfi@(9%Bay7-ZTrT1w4ofUX?=qRj
z$EQ9-$>;MaYBh<X=;-Lt%LCHV{x>{a9xh**(K<Q2RW1+DW6`a^ZuE5zd8B0%d<6Bu
zN(_qn`RAXfd|g;~IV(CV?Pgk9T6lO#$>Ei9d20sR>vrIdxtl<m=OvR>X;5Au5IFeC
z%F0tS9@W*=Wu-}?Z>EKpq<vFzxG#qWXPrz#a-e(yJ@%w{A}^?&*6>4NAw_j`tbY6N
zs~$b7%gT~SBxoQltt9-LlGEE*hr<~Zr=Wx`0mm)@XAZHikzw)3$22ruxIj@`w@!IG
zbd|3!I*^zoX%b2K#Cd6H6Ss-D+fMJ*2wLfxOK>z#Xo3rTd}3l^8UijXMc#PoRN}5(
zzD+eXMUr`@9TLgJc@rmwPxTQgPM_Y!px;C6Jr+;*PF%H@)J}Llf52FZO757_@%2x;
zifVjG0<<8RSYBS9)?i@xTnX>5ov35)=&6IWpaAX1gz-z)P}G{STa8m9BZGGN`ue&S
zVG7ekiDY*9l{|)eCjC;7X;1f#C6-lHRmq5aL5yJ9;;^t$4=C#3*kt3@0YQ<G>FG5^
z6*V<AvuDpPFP}Kklm5*mjVI<460mxts}>683Oov8Vgxb#u&}V>6m?3QY&0(YEb`g2
z$dl>mr4<#kD=LbP&7PfyZwx(dxRR(7WCE`$9C^eAK7xXRm}%2I!<tNCMaY}A$;o4T
zBGcQ_OVcqbPF7S@?6_S4GYO3_ipwG-I~Jn#9K=051VlY}vFX9}*l3D6p*3rbCm!Ut
zRl2vewN+N0EKRSd9L<<H(*d(^*b@x5-rb?7%8}7{0hS-^xtMQSlW9w&D6`RQ9^8?9
zJ-^L878As}<Agjngdt*ZJfkI#H4|j`b0x_#<W%J>tXk;d;ZcBmu%{tFIZA7`bx>3b
z@*T;^$s=QX%3@>D0GcQpDB?_rzE%0|t!QzcxIkj?@~X-~^FAI09s-Zxm|z1xOzEn%
zX|)vPY%^+&X06tk`0%ZsvV-oiWf((P6ACLUZ@qiYMN#?qhm(GaygWZYtQ^Z%1P8|$
zj$-eFY&K&HMe&@qgSBS#U>y1I;lrMTWe0DSJ;uTEk_A7GE=#X0yI1L6+4fys4#GYj
z3WXwgNK|4O@}(_W8xMJ2dxx#XrtQ!sU%zyzr{~h8P+u{5Q!u;?zTcIVwUuH^j#i<x
zhvD*QmsJnfbYwmV%8J!$dB`)Faaygdg|1=E$m5rKM!P7)UN~4XnHQFAD3U19^&}6J
z4ps!aM6D`3o;g?>_r7l_;!KKCF{e8_ziEC`Yr`faFTK7B*XSzj79G_&IevcCIaqq$
z0!8D31;Gm%_|^e&Ms17pgVJ^;6L}spjz@ZcHJHuX4)g7t>gp=|U=SFCXivJmYQX|S
zgMqJ9X00KHwbQl8t4RNxRZOO{QEP6o*|eXIT(|DxMF;1X<9BfqhQh<6u~Fd?)eyoT
zrVLOHh-<+iYe89+3hTGWF_DjpbEdoSdei!KBm-_2CoS}#@kWJWK|@1GX{lU^@~*Zx
zvr)@KglXrYzMjXkt5k73Ca(o2fjMj4{{8zWHRA5Yp<&!Ot5wdImj>Z4X4YS)Yi)7U
z(YmvWr?O!G^eRjchrQtOT8ytZT}04<Cp9`2IcrFRT#m}hSl407#7vtrlNYB_*?DVK
z7UV4!iz=R^AQ`6fF0E_C*q@Yv%Ud6AwT6(NYK+p@(g9zR=i=;k``UO!DJnZV+k#H?
zScHl<o!K6c6f%z19~Ux?ERkfD!^ZK^K!EA9up-iRCQonY@$`sN6q_B$&Q`6p>oFx0
zeMKgPgf!4=tAoc3!%#l}XKa?qH19m>*inzCf2x;CrG#g*rFtwuueURKs^OoYA}uPH
zhtO4{Z~i=_!@^8WO+_E0j9#@iUcculo92Z=A@W!QW>7Khc2!Ua`junHl~&R;lnzkR
zw5h46=s_UzD4(4z4eaOr+0D&tDcgeeqlv(eo2*GzdC<r#tCg?;sExdd&^4FV+EwxK
zdOfk;%tmR7!kvF}Dt?zrE%EVKTww0`-4Cy--gzx3K<V(^Pw)D+eJ#4Q5NV;XS?J%#
z3;k1?g+fg5)MC-&Fr@y^JMW-I;B^|uu4UDsHpbP|cnfJ)AY16qW+U#<$3)F^Ho7E>
zUXL>yt)!Z<Y@~5fBHl|Q&s*4Z(}*;?nc#iA&>z3E{r#KiGNjq5*_Z^=l+Gh`Q;};;
z`nS*Wcg~Ws1JO9Kj(T*^^^-H&0&PMnWs~;o$!4d1xSQC<h3Q#-{>~>|H-u86>Mu+|
zv47#|3@PY=+-N4vkV^H!TwGAT#g%F2lP4R_oLPc*$O9=UnBzA*7SW6Yi3jBAz{B63
z{V{*%&cAHvJ+pD&zH1|dY#|P|1DE2H{xAF!z#joJ7qB;UGz<5<L6kRiZAALV4>k##
zQ%K>s38nsvj?8y*qA1!a?(iD&#2y=;A_I(ObGwN0#yR_zT>I1K=+0peZygwahyH7a
zqQ>{dbG+&Z;va3G&sRV0>YX!Z<Hn6kmRviu0tK)-^2~uF{r?M5RR5XNokjjadUS|E
z;zQWH!m<yQuYGlG=-m<Iia+sn;H=vV2L1nX?lk|%_~&$vX4(iT=Yy}l`e5kL54Mb;
zuLzr;kN+na(O>Z-)qJ->ILa^hF<Z8588hZk-*7#D;k#e;H$2|BbM^pT2+{8=1p6)Y
zU-WBfI>pO)yy12h3lXx(8;U5tDhKG%>R0{c_&Yp4FyHOS*+q1m9q;BCtY5GfXTQ(C
Mc;w%n{J;5s05W>2Qvd(}

literal 16512
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
W0=+l}-qv3}bZT1+{~!PNa`GQKC)Vu%

-- 
GitLab