A very small library for reading image files

✨ TGA

qeaml c8fd7efa 5186882e

+4 -4
.vscode/launch.json
··· 7 7 { 8 8 "type": "lldb", 9 9 "request": "launch", 10 - "name": "Inspect PBM", 10 + "name": "Inspect TGA", 11 11 "program": "${workspaceFolder}/img-inspect", 12 - "args": ["TestImages/j.pbm"], 12 + "args": ["TestImages/kitty.tga"], 13 13 "cwd": "${workspaceFolder}" 14 14 }, 15 15 { 16 16 "type": "lldb", 17 17 "request": "launch", 18 - "name": "BMP2PPM", 18 + "name": "TGA2PPM", 19 19 "program": "${workspaceFolder}/img2ppm", 20 - "args": ["TestImages/monochrome.bmp", "TestImages/monochrome.bmp.ppm"], 20 + "args": ["TestImages/rgby.tga", "TestImages/rgby.tga.ppm"], 21 21 "cwd": "${workspaceFolder}" 22 22 } 23 23 ]
TestImages/KikiFlash.tga

This is a binary file and will not be displayed.

TestImages/KikiGray.tga

This is a binary file and will not be displayed.

TestImages/cbw8.tga

This is a binary file and will not be displayed.

TestImages/kitty.tga

This is a binary file and will not be displayed.

+51
img-inspect.c
··· 1 1 #include "img.h" 2 2 #include <stdio.h> 3 + #include <string.h> 3 4 4 5 struct Ctx { 5 6 FILE *file; ··· 33 34 if(argc < 2) { 34 35 printf("Usage: %s <file>\n", argv[0]); 35 36 return 1; 37 + } 38 + 39 + int dumpColorMap = 0; 40 + for(int i = 2; i < argc; ++i) { 41 + /* BMP Color Table, TGA Color Map */ 42 + if(strcmp(argv[i], "-colorMap") == 0 43 + || strcmp(argv[1], "-colorTable") == 0) { 44 + dumpColorMap = 1; 45 + } 36 46 } 37 47 38 48 FILE *file = fopen(argv[1], "rb"); ··· 94 104 img.bmp.xPPM, 95 105 img.bmp.yPPM, 96 106 img.bmp.colorTableSize); 107 + if(dumpColorMap && img.bmp.colorTableSize) { 108 + printf(" Color table:\n"); 109 + for(int i = 0; i < img.bmp.colorTableSize; ++i) { 110 + const struct ImgBmpRGBQuad *color = &img.bmp.colorTable[i]; 111 + printf(" %u,%u,%u,%u\n", 112 + color->red, color->green, color->blue, color->reserved); 113 + } 114 + } 115 + break; 116 + case ImgFormatTga: 117 + printf( 118 + "ID: %s\n" 119 + "Color map type: %u\n" 120 + "Image type: %u\n" 121 + "Color map first index: %u\n" 122 + "Color map length: %u\n" 123 + "Color map entry size: %u\n" 124 + "Origin X: %u\n" 125 + "Origin Y: %u\n" 126 + "Pixel Depth: %u\n" 127 + "Image Origin: %u\n" 128 + "Alpha Bits: %u\n", 129 + img.tga.id, 130 + img.tga.colorMapType, 131 + img.tga.imageType, 132 + img.tga.colorMapFirstIndex, 133 + img.tga.colorMapLength, 134 + img.tga.colorMapEntrySize, 135 + img.tga.originX, 136 + img.tga.originY, 137 + img.tga.pixelDepth, 138 + img.tga.imageOrigin, 139 + img.tga.alphaBits); 140 + if(dumpColorMap && img.tga.colorMapType) { 141 + printf(" Color map:\n"); 142 + for(int i = 0; i < img.tga.colorMapLength; ++i) { 143 + const struct ImgTgaColor *color = &img.tga.colorMap[i]; 144 + printf(" %u,%u,%u,%u\n", 145 + color->red, color->green, color->blue, color->alpha); 146 + } 147 + } 97 148 break; 98 149 default: 99 150 break;
+414 -1
img.c
··· 8 8 static ImgStatus readPbm(Img *img, ImgAny buf, ImgSz bufSz); 9 9 static ImgStatus openBmp(Img *img); 10 10 static ImgStatus readBmp(Img *img, ImgAny buf, ImgSz bufSz); 11 + static ImgStatus openTga(Img *img); 12 + static ImgStatus readTga(Img *img, ImgAny buf, ImgSz bufSz); 11 13 12 14 ImgStatus imgOpen(Img *img, const ImgFuncs *funcs) 13 15 { ··· 23 25 return openPbm(img); 24 26 case ImgFormatBmp: 25 27 return openBmp(img); 28 + case ImgFormatTga: 29 + return openTga(img); 26 30 } 27 31 } 28 32 ··· 40 44 return readPbm(img, buf, bufSz); 41 45 case ImgFormatBmp: 42 46 return readBmp(img, buf, bufSz); 47 + case ImgFormatTga: 48 + return readTga(img, buf, bufSz); 43 49 } 44 50 } 45 51 ··· 66 72 return "PBM"; 67 73 case ImgFormatBmp: 68 74 return "BMP"; 75 + case ImgFormatTga: 76 + return "TGA"; 69 77 } 70 78 } 71 79 ··· 102 110 return "BMP: Unsupported compression mode"; 103 111 case ImgErrBmpUnsupportedBitDepth: 104 112 return "BMP: Unsupported bit depth"; 113 + case ImgErrTgaMissingColorMap: 114 + return "TGA: Missing color map"; 115 + case ImgErrTgaColorMapTooLong: 116 + return "TGA: Color map too long (>256 colors)"; 117 + case ImgErrTgaNoImageData: 118 + return "TGA: Attempt to decode an image not containing image data"; 119 + case ImgErrTgaUnsupportedPixelDepth: 120 + return "TGA: Unsupported pixel depth"; 105 121 } 106 122 } 107 123 ··· 130 146 return ImgOK; 131 147 } 132 148 149 + static ImgStatus readU24LE(Img *img, ImgU32 *out) 150 + { 151 + ImgU8 bytes[3]; 152 + ImgStatus status = img->funcs.read(img->funcs.user, bytes, 3); 153 + if(status != ImgOK) { return status; } 154 + #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 155 + *out = ((ImgU32)bytes[2] << 16) | ((ImgU32)bytes[1] << 8) | (ImgU32)bytes[0]; 156 + #else 157 + *out = ((ImgU32)bytes[0] << 16) | ((ImgU32)bytes[1] << 8) | (ImgU32)bytes[2]; 158 + #endif 159 + return ImgOK; 160 + } 161 + 133 162 static ImgStatus readU32LE(Img *img, ImgU32 *out) 134 163 { 135 164 ImgStatus status = img->funcs.read(img->funcs.user, out, 4); ··· 165 194 return img->funcs.seek(img->funcs.user, off); 166 195 } 167 196 197 + static ImgStatus determineTga(Img *img); 198 + 168 199 static ImgStatus determineFormat(Img *img) 169 200 { 170 201 ImgStatus status; ··· 189 220 img->format = ImgFormatQoi; 190 221 return ImgOK; 191 222 } 192 - return ImgErrUnknownFormat; 223 + status = determineTga(img); 224 + if(status != ImgOK) { return status; } 225 + if(img->format == ImgFormatUnknown) { 226 + return ImgErrUnknownFormat; 227 + } 228 + return ImgOK; 193 229 } 194 230 195 231 struct RGBA { ··· 1009 1045 1010 1046 return ImgOK; 1011 1047 } 1048 + 1049 + #define TGA_NO_IMAGE_DATA 0 1050 + #define TGA_COLOR_MAPPED 1 1051 + #define TGA_TRUE_COLOR 2 1052 + #define TGA_BLACK_AND_WHITE 3 1053 + #define TGA_RLE_COLOR_MAPPED 9 1054 + #define TGA_RLE_TRUE_COLOR 10 1055 + #define TGA_RLE_BLACK_AND_WHITE 11 1056 + 1057 + ImgStatus determineTga(Img *img) 1058 + { 1059 + ImgOff returnOff = 0; 1060 + ImgStatus status; 1061 + 1062 + status = read(img, &img->tga.idLength, 1); 1063 + ++returnOff; 1064 + if(status != ImgOK) { return status; } 1065 + 1066 + status = read(img, &img->tga.colorMapType, 1); 1067 + ++returnOff; 1068 + if(status != ImgOK) { return status; } 1069 + 1070 + /* invalid color map type, likely not TGA */ 1071 + if(img->tga.colorMapType != 0 && img->tga.colorMapType != 1) { 1072 + return seek(img, -returnOff); 1073 + } 1074 + 1075 + status = read(img, &img->tga.imageType, 1); 1076 + ++returnOff; 1077 + if(status != ImgOK) { return status; } 1078 + 1079 + switch(img->tga.imageType) { 1080 + case TGA_NO_IMAGE_DATA: 1081 + case TGA_COLOR_MAPPED: 1082 + case TGA_TRUE_COLOR: 1083 + case TGA_BLACK_AND_WHITE: 1084 + case TGA_RLE_COLOR_MAPPED: 1085 + case TGA_RLE_TRUE_COLOR: 1086 + case TGA_RLE_BLACK_AND_WHITE: 1087 + break; 1088 + /* invalid image type, likely not TGA */ 1089 + default: 1090 + return seek(img, -returnOff); 1091 + } 1092 + 1093 + status = readU16LE(img, &img->tga.colorMapFirstIndex); 1094 + returnOff += 2; 1095 + if(status != ImgOK) { return status; } 1096 + 1097 + if(!img->tga.colorMapType && img->tga.colorMapFirstIndex != 0) { 1098 + return seek(img, -returnOff); 1099 + } 1100 + 1101 + status = readU16LE(img, &img->tga.colorMapLength); 1102 + returnOff += 2; 1103 + if(status != ImgOK) { return status; } 1104 + 1105 + if(!img->tga.colorMapType && img->tga.colorMapLength != 0) { 1106 + return seek(img, -returnOff); 1107 + } 1108 + if(img->tga.colorMapType && img->tga.colorMapLength == 0) { 1109 + return seek(img, -returnOff); 1110 + } 1111 + 1112 + status = read(img, &img->tga.colorMapEntrySize, 1); 1113 + ++returnOff; 1114 + if(status != ImgOK) { return status; } 1115 + 1116 + if(!img->tga.colorMapType && img->tga.colorMapEntrySize != 0) { 1117 + return seek(img, -returnOff); 1118 + } 1119 + 1120 + status = readU16LE(img, &img->tga.originX); 1121 + returnOff += 2; 1122 + if(status != ImgOK) { return status; } 1123 + 1124 + status = readU16LE(img, &img->tga.originY); 1125 + returnOff += 2; 1126 + if(status != ImgOK) { return status; } 1127 + 1128 + status = readU16LE(img, &img->tga.width); 1129 + returnOff += 2; 1130 + if(status != ImgOK) { return status; } 1131 + 1132 + status = readU16LE(img, &img->tga.height); 1133 + returnOff += 2; 1134 + if(status != ImgOK) { return status; } 1135 + 1136 + status = read(img, &img->tga.pixelDepth, 1); 1137 + returnOff += 1; 1138 + if(status != ImgOK) { return status; } 1139 + 1140 + ImgU8 imageDescriptor; 1141 + status = read(img, &imageDescriptor, 1); 1142 + ++returnOff; 1143 + if(status != ImgOK) { return status; } 1144 + 1145 + /* unused bits must be 0 */ 1146 + if((imageDescriptor >> 6) != 0) { 1147 + return seek(img, -returnOff); 1148 + } 1149 + 1150 + img->tga.alphaBits = imageDescriptor & 15; 1151 + img->tga.imageOrigin = (imageDescriptor >> 4) & 3; 1152 + 1153 + img->format = ImgFormatTga; 1154 + return ImgOK; 1155 + } 1156 + 1157 + /* Number of bytes needed to store the given amount of bits */ 1158 + #define BITS_TO_BYTES(bits) (((bits) + 7) >> 3) 1159 + 1160 + ImgStatus openTga(Img *img) 1161 + { 1162 + ImgStatus status; 1163 + 1164 + if(img->tga.colorMapType) { 1165 + if(img->tga.colorMapLength > IMG_TGA_MAX_COLOR_MAP_SIZE) { 1166 + return ImgErrTgaColorMapTooLong; 1167 + } 1168 + } else if(img->tga.imageType == TGA_COLOR_MAPPED || img->tga.imageType == TGA_RLE_COLOR_MAPPED) { 1169 + return ImgErrTgaMissingColorMap; 1170 + } 1171 + 1172 + /* the header was already read in by determineTga() */ 1173 + if(img->tga.idLength) { 1174 + status = read(img, img->tga.id, img->tga.idLength); 1175 + if(status != ImgOK) { return status; } 1176 + } 1177 + /* NUL-terminate for convenience */ 1178 + img->tga.id[img->tga.idLength] = '\0'; 1179 + 1180 + /* set this here in case there is no color map */ 1181 + if(img->tga.alphaBits) { 1182 + img->channelCount = 4; 1183 + } else { 1184 + img->channelCount = 3; 1185 + } 1186 + img->width = img->tga.width; 1187 + img->height = img->tga.height; 1188 + 1189 + /* image data follows color map and we'll read that in readTga() */ 1190 + /* so if we don't have a color map, we just return early here */ 1191 + if(!img->tga.colorMapType) { 1192 + return ImgOK; 1193 + } 1194 + 1195 + /* bits in a color field */ 1196 + ImgU32 fieldBits = img->tga.colorMapEntrySize / 3; 1197 + /* spec requires fields be at most 8 bits each */ 1198 + if(fieldBits > 8) { 1199 + fieldBits = 8; 1200 + } 1201 + /* mask of a field's bits */ 1202 + ImgU32 fieldMask = (1 << fieldBits) - 1; 1203 + /* bits in a full color entry (3 fields) */ 1204 + ImgU32 entryBits = fieldBits * 3; 1205 + /* total bits needed to store the entry (round up to 8) */ 1206 + ImgU32 totalBits = BITS_TO_BYTES(img->tga.colorMapEntrySize) << 3; 1207 + /* bits not used by entry */ 1208 + ImgU32 alphaBits = totalBits - entryBits; 1209 + /* alpha bit mask */ 1210 + ImgU32 alphaMask = (1 << alphaBits) - 1; 1211 + if(alphaBits != 0) { 1212 + img->channelCount = 4; 1213 + } else { 1214 + img->channelCount = 3; 1215 + } 1216 + 1217 + ImgU8 entry8; 1218 + ImgU16 entry16; 1219 + ImgU32 entry32; 1220 + for(ImgU16 i = img->tga.colorMapFirstIndex; i < img->tga.colorMapLength; ++i) { 1221 + struct ImgTgaColor *color = &img->tga.colorMap[i]; 1222 + 1223 + switch(totalBits) { 1224 + case 8: 1225 + status = read(img, &entry8, 1); 1226 + if(status != ImgOK) { return status; } 1227 + entry32 = entry8; 1228 + break; 1229 + case 16: 1230 + status = readU16LE(img, &entry16); 1231 + if(status != ImgOK) { return status; } 1232 + entry32 = entry16; 1233 + break; 1234 + case 32: 1235 + status = readU32LE(img, &entry32); 1236 + if(status != ImgOK) { return status; } 1237 + break; 1238 + } 1239 + 1240 + color->red = (entry32 >> (fieldBits * 2)) & fieldMask; 1241 + color->green = (entry32 >> fieldBits) & fieldMask; 1242 + color->blue = entry32 & fieldMask; 1243 + if(alphaBits > 0) { 1244 + color->alpha = (entry32 >> (fieldBits * 3)) & alphaMask; 1245 + } 1246 + } 1247 + 1248 + return ImgOK; 1249 + } 1250 + 1251 + static ImgStatus readTgaColorMapped(Img *img, ImgAny buf, ImgSz bufSz); 1252 + static ImgStatus readTgaTrueColor(Img *img, ImgAny buf, ImgSz bufSz); 1253 + static ImgStatus readTgaBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz); 1254 + static ImgStatus readTgaRleColorMapped(Img *img, ImgAny buf, ImgSz bufSz); 1255 + static ImgStatus readTgaRleTrueColor(Img *img, ImgAny buf, ImgSz bufSz); 1256 + static ImgStatus readTgaRleBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz); 1257 + 1258 + /* TODO: Implement RLE */ 1259 + ImgStatus readTga(Img *img, ImgAny buf, ImgSz bufSz) 1260 + { 1261 + ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount; 1262 + if(bufSz < expectedSize) { 1263 + return ImgErrBufferTooSmall; 1264 + } 1265 + 1266 + switch(img->tga.imageType) { 1267 + case TGA_NO_IMAGE_DATA: 1268 + return ImgErrTgaNoImageData; 1269 + case TGA_COLOR_MAPPED: 1270 + return readTgaColorMapped(img, buf, bufSz); 1271 + case TGA_TRUE_COLOR: 1272 + return readTgaTrueColor(img, buf, bufSz); 1273 + case TGA_BLACK_AND_WHITE: 1274 + return readTgaBlackAndWhite(img, buf, bufSz); 1275 + case TGA_RLE_COLOR_MAPPED: 1276 + return readTgaRleColorMapped(img, buf, bufSz); 1277 + case TGA_RLE_TRUE_COLOR: 1278 + return readTgaRleTrueColor(img, buf, bufSz); 1279 + case TGA_RLE_BLACK_AND_WHITE: 1280 + return readTgaRleBlackAndWhite(img, buf, bufSz); 1281 + } 1282 + return ImgErrUnimplemented; 1283 + } 1284 + 1285 + ImgStatus readTgaColorMapped(Img *img, ImgAny buf, ImgSz bufSz) 1286 + { 1287 + ImgStatus status; 1288 + ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount; 1289 + ImgU8 *cur = buf; 1290 + ImgU8 *end = cur + expectedSize; 1291 + 1292 + /* above 8 not supported, also enforced by IMG_TGA_MAX_COLOR_MAP_LENGTH */ 1293 + if(img->tga.pixelDepth > 8) { 1294 + return ImgErrTgaUnsupportedPixelDepth; 1295 + } 1296 + 1297 + ImgU8 pixel; 1298 + while(cur < end) { 1299 + status = read(img, &pixel, 1); 1300 + if(status != ImgOK) { return status; } 1301 + 1302 + const struct ImgTgaColor *color = &img->tga.colorMap[pixel]; 1303 + *cur++ = color->red; 1304 + *cur++ = color->green; 1305 + *cur++ = color->blue; 1306 + if(img->channelCount == 4) { 1307 + *cur++ = color->alpha; 1308 + } 1309 + } 1310 + 1311 + return ImgOK; 1312 + } 1313 + 1314 + ImgStatus readTgaTrueColor(Img *img, ImgAny buf, ImgSz bufSz) 1315 + { 1316 + ImgStatus status; 1317 + ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount; 1318 + ImgU8 *curr = buf; 1319 + ImgU8 *end = curr + expectedSize; 1320 + 1321 + ImgU32 pixelSize = BITS_TO_BYTES(img->tga.pixelDepth); 1322 + ImgU32 alphaMask = (1 << img->tga.alphaBits) - 1; 1323 + ImgU32 fieldBits = (img->tga.pixelDepth - img->tga.alphaBits) / 3; 1324 + ImgU32 fieldMask = (1 << fieldBits) - 1; 1325 + 1326 + ImgU8 pixel8; 1327 + ImgU16 pixel16; 1328 + ImgU32 pixel32; 1329 + while(curr < end) { 1330 + switch(pixelSize) { 1331 + case 1: 1332 + status = read(img, &pixel8, 1); 1333 + if(status != ImgOK) { return status; } 1334 + pixel32 = pixel8; 1335 + break; 1336 + case 2: 1337 + status = readU16LE(img, &pixel16); 1338 + if(status != ImgOK) { return status; } 1339 + pixel32 = pixel16; 1340 + break; 1341 + case 3: 1342 + status = readU24LE(img, &pixel32); 1343 + if(status != ImgOK) { return status; }; 1344 + break; 1345 + case 4: 1346 + status = readU32LE(img, &pixel32); 1347 + if(status != ImgOK) { return status; } 1348 + break; 1349 + default: 1350 + return ImgErrTgaUnsupportedPixelDepth; 1351 + } 1352 + 1353 + *curr++ = (pixel32 >> (2 * fieldBits)) & fieldMask; 1354 + *curr++ = (pixel32 >> fieldBits) & fieldMask; 1355 + *curr++ = pixel32 & fieldMask; 1356 + if(img->channelCount > 3) { 1357 + *curr++ = (pixel32 >> (3 * fieldBits)) & alphaMask; 1358 + } 1359 + } 1360 + 1361 + return ImgOK; 1362 + } 1363 + 1364 + ImgStatus readTgaBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz) 1365 + { 1366 + ImgStatus status; 1367 + ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount; 1368 + ImgU8 *curr = buf; 1369 + ImgU8 *end = curr + expectedSize; 1370 + 1371 + ImgU32 pixelSize = BITS_TO_BYTES(img->tga.pixelDepth); 1372 + ImgU8 pixel8; 1373 + ImgU16 pixel16; 1374 + ImgU32 pixel32; 1375 + while(curr < end) { 1376 + switch(pixelSize) { 1377 + case 1: 1378 + status = read(img, &pixel8, 1); 1379 + if(status != ImgOK) { return status; } 1380 + break; 1381 + case 2: 1382 + status = readU16LE(img, &pixel16); 1383 + if(status != ImgOK) { return status; } 1384 + pixel8 = pixel16 >> 8; 1385 + break; 1386 + case 3: 1387 + status = readU24LE(img, &pixel32); 1388 + if(status != ImgOK) { return status; } 1389 + pixel8 = pixel32 >> 16; 1390 + break; 1391 + case 4: 1392 + status = readU32LE(img, &pixel32); 1393 + if(status != ImgOK) { return status; } 1394 + pixel8 = pixel32 >> 24; 1395 + break; 1396 + default: 1397 + return ImgErrTgaUnsupportedPixelDepth; 1398 + } 1399 + 1400 + *curr++ = pixel8; 1401 + *curr++ = pixel8; 1402 + *curr++ = pixel8; 1403 + if(img->channelCount > 3) { 1404 + *curr++ = 255; 1405 + } 1406 + } 1407 + 1408 + return ImgOK; 1409 + } 1410 + 1411 + ImgStatus readTgaRleColorMapped(Img *img, ImgAny buf, ImgSz bufSz) 1412 + { 1413 + return ImgErrUnimplemented; 1414 + } 1415 + 1416 + ImgStatus readTgaRleTrueColor(Img *img, ImgAny buf, ImgSz bufSz) 1417 + { 1418 + return ImgErrUnimplemented; 1419 + } 1420 + 1421 + ImgStatus readTgaRleBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz) 1422 + { 1423 + return ImgErrUnimplemented; 1424 + }
+45 -8
img.h
··· 24 24 ImgErrBmpUnsupportedVariant, 25 25 ImgErrBmpPlaneCount, 26 26 ImgErrBmpUnsupportedCompression, 27 - ImgErrBmpUnsupportedBitDepth 27 + ImgErrBmpUnsupportedBitDepth, 28 + ImgErrTgaMissingColorMap, 29 + ImgErrTgaColorMapTooLong, 30 + ImgErrTgaNoImageData, 31 + ImgErrTgaUnsupportedPixelDepth, 28 32 }; 29 33 30 34 typedef enum ImgStatusE ImgStatus; ··· 46 50 ImgFormatUnknown, 47 51 ImgFormatQoi, 48 52 ImgFormatPbm, 49 - ImgFormatBmp 53 + ImgFormatBmp, 54 + ImgFormatTga 50 55 }; 51 56 52 57 typedef enum ImgFormatE ImgFormat; 53 58 54 - struct ImgQoiInfoS { 59 + struct ImgQoi { 55 60 ImgU8 colorSpace; 56 61 }; 57 62 58 - struct ImgPbmInfoS { 63 + struct ImgPbm { 59 64 char variant; 60 65 ImgU16 max; 61 66 }; ··· 67 72 ImgU8 reserved; 68 73 }; 69 74 70 - struct ImgBmpInfoS { 75 + struct ImgBmp { 71 76 ImgU32 bitmapOffset; 72 77 ImgS32 width; 73 78 ImgS32 height; ··· 81 86 struct ImgBmpRGBQuad colorTable[256]; 82 87 }; 83 88 89 + /* for color map, converted to RGBA32 for convenience */ 90 + struct ImgTgaColor { 91 + ImgU8 red; 92 + ImgU8 green; 93 + ImgU8 blue; 94 + ImgU8 alpha; 95 + }; 96 + 97 + /* just use true-color at that point */ 98 + #define IMG_TGA_MAX_COLOR_MAP_SIZE 256 99 + 100 + struct ImgTga { 101 + ImgU8 idLength; 102 + /* 1 extra for null byte */ 103 + char id[256]; 104 + /* 1 if has color map, 0 otherwise */ 105 + ImgU8 colorMapType; 106 + ImgU8 imageType; 107 + ImgU16 colorMapFirstIndex; 108 + ImgU16 colorMapLength; 109 + ImgU8 colorMapEntrySize; 110 + ImgU16 originX; 111 + ImgU16 originY; 112 + ImgU16 width; 113 + ImgU16 height; 114 + ImgU8 imageOrigin; 115 + ImgU8 pixelDepth; 116 + ImgU8 alphaBits; 117 + struct ImgTgaColor colorMap[IMG_TGA_MAX_COLOR_MAP_SIZE]; 118 + }; 119 + 84 120 struct ImgS { 85 121 ImgFormat format; 86 122 ImgU32 width; ··· 88 124 ImgU8 channelCount; 89 125 ImgFuncs funcs; 90 126 union { 91 - struct ImgQoiInfoS qoi; 92 - struct ImgPbmInfoS pbm; 93 - struct ImgBmpInfoS bmp; 127 + struct ImgQoi qoi; 128 + struct ImgPbm pbm; 129 + struct ImgBmp bmp; 130 + struct ImgTga tga; 94 131 }; 95 132 }; 96 133