From e6357c22f7ce4c982a903dd842d0e73f1f6a0ef5 Mon Sep 17 00:00:00 2001 From: Kai Uwe Broulik Date: Thu, 26 Jun 2025 14:00:11 +0000 Subject: [PATCH] tga: Use Format_Indexed8 for indexed formats and support 32-bit BGRA colormap --- autotests/read/tga/colormapped.png | Bin 0 -> 2168 bytes autotests/read/tga/colormapped.tga | Bin 0 -> 1797 bytes src/imageformats/tga.cpp | 63 +++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 16 deletions(-) create mode 100644 autotests/read/tga/colormapped.png create mode 100644 autotests/read/tga/colormapped.tga diff --git a/autotests/read/tga/colormapped.png b/autotests/read/tga/colormapped.png new file mode 100644 index 0000000000000000000000000000000000000000..aa7e2ab2191d35983613d9c2019ea06a5e9d8228 GIT binary patch literal 2168 zcmV-;2#5EHP)EX>4Tx04R}tkv&MmKpe$iTeYGU5jzxd$WUFhi;6hbDionYs1;guFuC+YXws0R zxHt-~1qVMCs}3&Cx;nTDg5U>;yWphgA|?JWDYS_3;J6>}?mh0_0seZKsb)tUP&La) zClf+8w;}{z(S-U5saWpZjz4DS49tK7lySbi*QEC!XH4 zbk6(4VOEk9;&bA0gDyz?$aUG}H_klWYTlQVPc`!#&R38lA#h$5l0nOqkMnX zWrgz=XSG~q&3p0}hVt6-64z-CBZ&npLWBSrRcxRP3o%+XQcR?2KjGmYa{MB>WO8kQ zkz*besE`~#_#gc4)+|g;xJjV|(D`E9A0t3;7iiRM`}^3o8z(^E8Mx9~{z@H~`6Rv8 z(xOK||2A-O-O}Ve;Bp6uJn51lIZ}Y8zfb_)&*+`}J7A(bQT<-t?26ah9K~!ko?U`#(l;;)4fA3yj zmc9d^ytMatxngk<4Sq3Z)jSim%@&MV8fq#`81x#92EBAk zA~5K13$;d#N~xl=*TJFB?Og72;B?>ogjk0{{PAEnGp0>q$n6teub2GX9BD!c*zAKs zQl%ryG8U_g6PKN|_xTvG`p3NKrE4yJ-S42fBnMA0B6@p!DVct^lr#c2p5qJM0a|;z z>9YC|VMWq%MT8X`ZnbgZs)xttWa07$#iTT?G;RVh5fNcWkZlLr7;;2zc%zsQrSiFh zynVc%Jd>Mlt4GKxLC)liQ4@)?Dq?dIY1qTFi}NTj%PEuGK8uH!8?JEZb35)pc+|2R zM<5!D;e2uv?#)|xW}%to<>r*uj&uL8^ZfpcbXr1qLs8Ln?Vlnc0{?;kFK=L3xQNP} z7(ZE>kED*Jj6^gD^=Aip`Mq}hQWow&Sd5y0Qt`#3*k>28<`rRVJI{;D?j|Q&n=%p_ zosxw`CX87EwKhmN8WLlYQGo~h!_{Q`;+H(TWiz`kx@o>>oe-mS8CsUl%Hp1{NociN zx&lWz9NbI0`w&}=J)=`0h2qOkmHUO~@t-`->OHm0GTp__V?9YRIy+O%!=ye!gNFuN>*a>5n9Av?)i&lG04> zovK8mit?%NUH15Q)9pBuf@ZFNfT8>EBV)&FY+YN!>u2p;y5_p+v;wotqS7f8n;@zT z)9TyHF3-Dk2hWTvf^qH+xSe7s!!WAJ^1Ah8zP5u?JpuNf>W_P0oS(sir3Q*}^tiCl z8my(=x0l{Xd%}ipHTI%a%TiJa04PiEA@hR|v8(jF(%45~zMcnWPQsk6#UH!Q*}y(> z71P=0dU{-*9*)4MlXzIoZf(al?LIOOyu+{7K7>_taVGc{2W++2qW_M{GQ)2sOm5=| zNQw760uMtg9wjFl;?=<_gh*Y{Q*fK(4uM+@)kH0p&$4U2#_(V9lg={3Fbt_k)o?DKv^x@xENBH$Wjmc}LeSaoXSxmLzceMI; z(-~??-Vuu3=soik^sCpQyuXy#^}Y6JWYkXiIPc=@Hg)t@I@`j zsl@WS^~hCA2=!dUx4)j)_TNvK44w(>Bt2qgss3kN3LK&>_|f=spt~jy4B|!<@AB=ZZcHf&U(`Z_douw9j~ZSgU1c779ncuFB~O39P?XHX z|J-vaCDInC<7)WtRO+`R_D%f1(3{PY}Qxv&Vw6KhgNAYWTSxpD&~s^xTDy^36% zCXKn7GZhs{J%xp2HlG9YlnFChr!=uZeu}3ipXOWA8(cZ8Mz6_~N{i=6<8CEpl$S|O zjfchjQ>Sn}v@BtT8VS=imCRLtk0N;yZEbDj%`O`I&)tzwS+!KEKYk)93YD%hvqbk( zel+<4RpH;#Qa6ZRlPOiqUXai)_Jo_cnhhHzfRNQD*!*LHZ@!(-{Cnknl@#Jz z^F-VS%?x=B6{;uEOVc=evIRqRw!~Kv9p_twzxkX9M?+$#ZKXKxdPjIdcG24LiQsm@ u$Hy8)ByYj#M{&~E04;Q$A!I@V^I`P z6HP?~6$NiV5%2d9?DH>v^MsYdkbU5DQN>e1&-0|pN?Veqg<1PwP}M3509N18Bplo?-s zX2DkzjF>;qiUo@-Si0DXWlMOhSnj~e)i$hK>%fj(@d)4QMEEuv%J%(^ZIMn$WiBXX zPLv*852cbv+2M^SJGd454kY7F>YuoKM2h=qn^BRz1?A~d96WRscTdQ0FGG&|Cl#nT zrNq%=X-G6e`Zf;la5bNIu|3<@tDIp2@=F>;zQj zB;v`XB&4O=ke)`odHZo8=OVIm^KmJ+2)UOFQFHY$dNypusVpCQ8NyL_BNaK<^3d0y z#89IWITyWnR&o;0iq2t}NrhoX9X>I}V4PV8Luod~n-amA6JRXM#kI0x=x#QFDU^;p+^?y^PJ2G8Yn~w1bs6^B`_Q@bkl?(EL-t#+*4Dz(a1YkHM@V#E!(oRW z$*y7~d9EYba}|%DHsO(>2DXN3WI1k=JboPTl)zNq2$P`}CgUTdy33I2B|SAhL%zKd zC*AkZ(BOihz8NlaEqId!&v*m8);i+%BF|fe5?2k(CLc_^1@(3lEM`BPHWOUD16B)( zvzx(NTi|wB@X%WiH{S%0(+Zp01f!cgZLKKx8&Kyp!{p&%clu!V+Td^iK93D9Cs5gJ zfyHNs)9pu-m*j17pr*x)W}g#oPYZaz3oT797+dXV^}FHq`|!Nk17C9!9L+6ow0h9g z;)nUUAO2R-6MVpPADYoZ)^jpL6qUgVYZoU<&A6Qq&+XtkAG@)4)|llB3xC|s#c}~h zt{hu3en5moyioF?A}x;75_F;P_X!^;B-FwP2{S%pM=Yo00uE)|i1~JehQLUPsQ-qRS4sjGj@evrH6m2quAEiVOpo(RWs-n#@{&C1 z?5GYwLwD(-+y3qs@x|GH?Tl^5xmr3vG_fda{dZE9(sbbbXqwgslWzVW)JY%APWefD zAT*}EE-4_^uU1P#r7ZL3-@zSi=4)uN==GrTFgeT2+!dWpUG4 za(+sV-u;eBaK^Dzh*V2mmvJhN{^N6{z;vuAG_qiooKthm+(m)%7lp(j>yGamuH-Zv z^Ho%5nEZic`6%3{RhqLFNgpD literal 0 HcmV?d00001 diff --git a/src/imageformats/tga.cpp b/src/imageformats/tga.cpp index 612d5de..82cc78a 100644 --- a/src/imageformats/tga.cpp +++ b/src/imageformats/tga.cpp @@ -99,7 +99,11 @@ static bool IsSupported(const TgaHeader &head) return false; } if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) { - if (head.colormap_length > 256 || head.colormap_size != 24 || head.colormap_type != 1) { + if (head.colormap_length > 256 || head.colormap_type != 1) { + return false; + } + // colormap_size == 16 would be ARRRRRGG GGGBBBBB but we don't support that. + if (head.colormap_size != 24 && head.colormap_size != 32) { return false; } } @@ -189,6 +193,8 @@ static QImage::Format imageFormat(const TgaHeader &head) if (numAlphaBits == 8) { format = QImage::Format_ARGB32; } + } else if (head.image_type == TGA_TYPE_INDEXED || head.image_type == TGA_TYPE_RLE_INDEXED) { + format = QImage::Format_Indexed8; } else { format = QImage::Format_RGB32; } @@ -232,21 +238,40 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) } // Read palette. - static const int max_palette_size = 768; - char palette[max_palette_size]; if (info.pal) { - // @todo Support palettes in other formats! - const int palette_size = 3 * tga.colormap_length; - if (palette_size > max_palette_size) { + QList colorTable; +#if QT_VERSION < QT_VERSION_CHECK(6, 8, 0) + colorTable.resize(tga.colormap_length); +#else + colorTable.resizeForOverwrite(tga.colormap_length); +#endif + + if (tga.colormap_size == 32) { // BGRA. + char data[4]; + for (QRgb &rgb : colorTable) { + const auto dataRead = s.readRawData(data, 4); + if (dataRead < 4) { + return false; + } + // BGRA. + rgb = qRgba(data[2], data[1], data[0], data[3]); + } + } else if (tga.colormap_size == 24) { // BGR. + char data[3]; + for (QRgb &rgb : colorTable) { + const auto dataRead = s.readRawData(data, 3); + if (dataRead < 3) { + return false; + } + // BGR. + rgb = qRgb(data[2], data[1], data[0]); + } + // TODO tga.colormap_size == 16 ARRRRRGG GGGBBBBB + } else { return false; } - const int dataRead = s.readRawData(palette, palette_size); - if (dataRead < 0) { - return false; - } - if (dataRead < max_palette_size) { - memset(&palette[dataRead], 0, max_palette_size - dataRead); - } + + img.setColorTable(colorTable); } // Allocate image. @@ -355,14 +380,19 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) uchar *src = image; for (int y = y_start; y != y_end; y += y_step) { - auto scanline = reinterpret_cast(img.scanLine(y)); if (info.pal) { // Paletted. + auto scanline = img.scanLine(y); for (int x = 0; x < tga.width; x++) { uchar idx = *src++; - scanline[x] = qRgb(palette[3 * idx + 2], palette[3 * idx + 1], palette[3 * idx + 0]); + if (Q_UNLIKELY(idx >= tga.colormap_length)) { + valid = false; + break; + } + scanline[x] = idx; } } else if (info.grey) { + auto scanline = reinterpret_cast(img.scanLine(y)); // Greyscale. for (int x = 0; x < tga.width; x++) { if (tga.pixel_size == 16) { @@ -375,6 +405,7 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) } } } else { + auto scanline = reinterpret_cast(img.scanLine(y)); // True Color. if (tga.pixel_size == 16) { for (int x = 0; x < tga.width; x++) { @@ -401,7 +432,7 @@ static bool LoadTGA(QDataStream &s, const TgaHeader &tga, QImage &img) // Free image. free(image); - return true; + return valid; } } // namespace