A very small library for reading image files

✨ GIF but without decoding

qeaml 693ec852 33c13155

Changed files
+258 -5
.vscode
TestImages
+2 -2
.vscode/launch.json
··· 15 15 { 16 16 "type": "lldb", 17 17 "request": "launch", 18 - "name": "TGA2PPM", 18 + "name": "GIF2PPM", 19 19 "program": "${workspaceFolder}/img2ppm", 20 - "args": ["TestImages/rgby.tga", "TestImages/rgby.tga.ppm"], 20 + "args": ["TestImages/gifdecod.gif", "TestImages/gifdecod.gif.ppm"], 21 21 "cwd": "${workspaceFolder}" 22 22 } 23 23 ]
TestImages/gifdecod.gif

This is a binary file and will not be displayed.

+24 -1
img-inspect.c
··· 40 40 for(int i = 2; i < argc; ++i) { 41 41 /* BMP Color Table, TGA Color Map */ 42 42 if(strcmp(argv[i], "-colorMap") == 0 43 - || strcmp(argv[1], "-colorTable") == 0) { 43 + || strcmp(argv[i], "-colorTable") == 0) { 44 44 dumpColorMap = 1; 45 45 } 46 46 } ··· 143 143 const struct ImgTgaColor *color = &img.tga.colorMap[i]; 144 144 printf(" %u,%u,%u,%u\n", 145 145 color->red, color->green, color->blue, color->alpha); 146 + } 147 + } 148 + break; 149 + case ImgFormatGif: 150 + printf( 151 + "Global Color Table Flag: %u\n" 152 + "Color Resolution: %u\n" 153 + "Sort Flag: %u\n" 154 + "Global Color Table Size: %u\n" 155 + "Background Color Index: %u\n" 156 + "Pixel Aspect Ratio: %u\n", 157 + img.gif.globalColorTableFlag, 158 + img.gif.colorResolution, 159 + img.gif.sortFlag, 160 + img.gif.globalColorTableSize, 161 + img.gif.backgroundColorIndex, 162 + img.gif.pixelAspectRatio); 163 + if(dumpColorMap && img.gif.globalColorTableFlag) { 164 + printf(" Global Color Table:\n"); 165 + for(int i = 0; i < img.gif.globalColorTableSize; ++i) { 166 + const struct ImgGifColor *color = &img.gif.globalColorTable[i]; 167 + printf(" %u,%u,%u\n", 168 + color->red, color->green, color->blue); 146 169 } 147 170 } 148 171 break;
+210 -1
img.c
··· 10 10 static ImgStatus readBmp(Img *img, ImgAny buf, ImgSz bufSz); 11 11 static ImgStatus openTga(Img *img); 12 12 static ImgStatus readTga(Img *img, ImgAny buf, ImgSz bufSz); 13 + static ImgStatus openGif(Img *img); 14 + static ImgStatus readGif(Img *img, ImgAny buf, ImgSz bufSz); 13 15 14 16 ImgStatus imgOpen(Img *img, const ImgFuncs *funcs) 15 17 { ··· 27 29 return openBmp(img); 28 30 case ImgFormatTga: 29 31 return openTga(img); 32 + case ImgFormatGif: 33 + return openGif(img); 30 34 } 31 35 } 32 36 ··· 46 50 return readBmp(img, buf, bufSz); 47 51 case ImgFormatTga: 48 52 return readTga(img, buf, bufSz); 53 + case ImgFormatGif: 54 + return readGif(img, buf, bufSz); 49 55 } 50 56 } 51 57 ··· 74 80 return "BMP"; 75 81 case ImgFormatTga: 76 82 return "TGA"; 83 + case ImgFormatGif: 84 + return "GIF"; 77 85 } 78 86 } 79 87 ··· 118 126 return "TGA: Attempt to decode an image not containing image data"; 119 127 case ImgErrTgaUnsupportedPixelDepth: 120 128 return "TGA: Unsupported pixel depth"; 129 + case ImgErrGifExpectedImageDescriptor: 130 + return "GIF: Expected Image Descriptor"; 131 + case ImgErrGifExpectedTrailer: 132 + return "GIF: Expected trailer"; 121 133 } 122 134 } 123 135 ··· 199 211 static ImgStatus determineFormat(Img *img) 200 212 { 201 213 ImgStatus status; 202 - char magic[4]; 214 + char magic[8]; 215 + 203 216 status = read(img, magic, 2); 204 217 if(status != ImgOK) { return status; } 205 218 status = seek(img, -2); 206 219 if(status != ImgOK) { return status; } 220 + 207 221 if(magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6') { 208 222 img->format = ImgFormatPbm; 209 223 return ImgOK; ··· 212 226 img->format = ImgFormatBmp; 213 227 return ImgOK; 214 228 } 229 + 215 230 status = read(img, magic, 4); 216 231 if(status != ImgOK) { return status; } 217 232 status = seek(img, -4); 218 233 if(status != ImgOK) { return status; } 234 + 219 235 if(magic[0] == 'q' && magic[1] == 'o' && magic[2] == 'i' && magic[3] == 'f') { 220 236 img->format = ImgFormatQoi; 221 237 return ImgOK; 222 238 } 239 + 240 + status = read(img, magic, 6); 241 + if(status != ImgOK) { return status; } 242 + status = seek(img, -6); 243 + if(status != ImgOK) { return status; } 244 + 245 + if(magic[0] == 'G' && magic[1] == 'I' && magic[2] == 'F' 246 + && magic[3] == '8' && (magic[4] == '7' || magic[4] == '9') && magic[5] == 'a') { 247 + img->format = ImgFormatGif; 248 + return ImgOK; 249 + } 250 + 223 251 status = determineTga(img); 224 252 if(status != ImgOK) { return status; } 253 + 225 254 if(img->format == ImgFormatUnknown) { 226 255 return ImgErrUnknownFormat; 227 256 } ··· 1422 1451 { 1423 1452 return ImgErrUnimplemented; 1424 1453 } 1454 + 1455 + ImgStatus openGif(Img *img) 1456 + { 1457 + ImgStatus status; 1458 + 1459 + /* skip over header */ 1460 + status = seek(img, 6); 1461 + if(status != ImgOK) { return status; } 1462 + 1463 + ImgU16 width; 1464 + ImgU16 height; 1465 + 1466 + status = readU16LE(img, &width); 1467 + if(status != ImgOK) { return status; } 1468 + status = readU16LE(img, &height); 1469 + if(status != ImgOK) { return status; } 1470 + 1471 + img->width = width; 1472 + img->height = height; 1473 + img->channelCount = 4; 1474 + 1475 + ImgU8 flags; 1476 + status = read(img, &flags, 1); 1477 + if(status != ImgOK) { return status; } 1478 + 1479 + img->gif.globalColorTableFlag = flags >> 7; 1480 + img->gif.colorResolution = (flags >> 4) & 7; 1481 + img->gif.sortFlag = (flags >> 3) & 1; 1482 + ImgU16 gctSize = flags & 7; 1483 + img->gif.globalColorTableSize = 1 << (gctSize + 1); 1484 + 1485 + if(img->gif.globalColorTableFlag) { 1486 + status = read(img, img->gif.globalColorTable, 3 * img->gif.globalColorTableSize); 1487 + if(status != ImgOK) { return status; } 1488 + } 1489 + 1490 + status = read(img, &img->gif.backgroundColorIndex, 1); 1491 + if(status != ImgOK) { return status; } 1492 + status = read(img, &img->gif.pixelAspectRatio, 1); 1493 + if(status != ImgOK) { return status; } 1494 + 1495 + return ImgOK; 1496 + } 1497 + 1498 + struct GifBlock { 1499 + ImgU8 size; 1500 + ImgU8 data[255]; 1501 + }; 1502 + 1503 + struct GifImage { 1504 + ImgU16 left; 1505 + ImgU16 right; 1506 + ImgU16 width; 1507 + ImgU16 height; 1508 + ImgU8 localColorTableFlag; 1509 + ImgU8 interlaceFlag; 1510 + ImgU8 sortFlag; 1511 + ImgU16 localColorTableSize; 1512 + struct ImgGifColor localColorTable[256]; 1513 + ImgU8 lzwCodeSize; 1514 + struct GifBlock block; 1515 + }; 1516 + 1517 + #define GIF_TABLE_SIZE 5021 1518 + #define GIF_STACK_SIZE 1024 1519 + 1520 + struct GifDecompressor { 1521 + struct GifImage gifImage; 1522 + /* byte offset into current block */ 1523 + ImgU16 byteOff; 1524 + /* bit offset into current block byte */ 1525 + ImgU8 bitOff; 1526 + ImgU16 code; 1527 + ImgU8 *curr; 1528 + 1529 + ImgU8 stack[GIF_STACK_SIZE]; 1530 + ImgU16 stackOff; 1531 + 1532 + ImgU8 shade[GIF_TABLE_SIZE]; 1533 + ImgU16 child[GIF_TABLE_SIZE]; 1534 + }; 1535 + 1536 + /* read the Image Descriptor + code size */ 1537 + static ImgStatus gifReadImage(Img *img, struct GifImage *desc); 1538 + static ImgStatus gifReadBlock(Img *img, struct GifBlock *block); 1539 + static ImgStatus gifDecompress(Img *img, struct GifDecompressor *decompressor); 1540 + 1541 + ImgStatus readGif(Img *img, ImgAny buf, ImgSz bufSz) 1542 + { 1543 + ImgStatus status; 1544 + ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount; 1545 + ImgU8 *curr = buf; 1546 + ImgU8 *end = curr + expectedSize; 1547 + 1548 + struct GifDecompressor decompressor; 1549 + ImgU8 trailer; 1550 + 1551 + status = gifReadImage(img, &decompressor.gifImage); 1552 + if(status != ImgOK) { return status; } 1553 + 1554 + status = gifReadBlock(img, &decompressor.gifImage.block); 1555 + if(status != ImgOK) { return status; } 1556 + 1557 + decompressor.byteOff = 0; 1558 + decompressor.bitOff = 0; 1559 + decompressor.curr = curr; 1560 + decompressor.stackOff = 0; 1561 + status = gifDecompress(img, &decompressor); 1562 + if(status != ImgOK) { return status; }; 1563 + 1564 + status = read(img, &trailer, 1); 1565 + if(status != ImgOK) { return status; } 1566 + 1567 + if(trailer != 0x3B) { 1568 + return ImgErrGifExpectedTrailer; 1569 + } 1570 + 1571 + return ImgOK; 1572 + } 1573 + 1574 + ImgStatus gifReadImage(Img *img, struct GifImage *gifImage) 1575 + { 1576 + ImgStatus status; 1577 + ImgU8 identifier; 1578 + 1579 + status = read(img, &identifier, 1); 1580 + if(status != ImgOK) { return status; } 1581 + if(identifier != 0x2C) { 1582 + return ImgErrGifExpectedImageDescriptor; 1583 + } 1584 + 1585 + status = readU16LE(img, &gifImage->left); 1586 + if(status != ImgOK) { return status; } 1587 + status = readU16LE(img, &gifImage->right); 1588 + if(status != ImgOK) { return status; } 1589 + status = readU16LE(img, &gifImage->width); 1590 + if(status != ImgOK) { return status; } 1591 + status = readU16LE(img, &gifImage->height); 1592 + if(status != ImgOK) { return status; } 1593 + 1594 + ImgU8 flags; 1595 + status = read(img, &flags, 1); 1596 + if(status != ImgOK) { return status; } 1597 + 1598 + gifImage->localColorTableFlag = flags >> 7; 1599 + gifImage->interlaceFlag = (flags >> 6) & 1; 1600 + gifImage->sortFlag = (flags >> 5) & 1; 1601 + ImgU16 lctSize = flags & 7; 1602 + gifImage->localColorTableSize = 1 << (lctSize + 1); 1603 + 1604 + if(gifImage->localColorTableFlag) { 1605 + status = read(img, gifImage->localColorTable, 3 * gifImage->localColorTableSize); 1606 + if(status != ImgOK) { return status; } 1607 + } 1608 + 1609 + status = read(img, &gifImage->lzwCodeSize, 1); 1610 + if(status != ImgOK) { return status; } 1611 + 1612 + return ImgOK; 1613 + } 1614 + 1615 + ImgStatus gifReadBlock(Img *img, struct GifBlock *block) 1616 + { 1617 + ImgStatus status; 1618 + 1619 + status = read(img, &block->size, 1); 1620 + if(status != ImgOK) { return status; } 1621 + 1622 + if(block->size) { 1623 + status = read(img, block->data, block->size); 1624 + if(status != ImgOK) { return status; } 1625 + } 1626 + 1627 + return ImgOK; 1628 + } 1629 + 1630 + ImgStatus gifDecompress(Img *img, struct GifDecompressor *dec) 1631 + { 1632 + return ImgErrUnimplemented; 1633 + }
+22 -1
img.h
··· 29 29 ImgErrTgaColorMapTooLong, 30 30 ImgErrTgaNoImageData, 31 31 ImgErrTgaUnsupportedPixelDepth, 32 + ImgErrGifExpectedImageDescriptor, 33 + ImgErrGifExpectedTrailer, 32 34 }; 33 35 34 36 typedef enum ImgStatusE ImgStatus; ··· 51 53 ImgFormatQoi, 52 54 ImgFormatPbm, 53 55 ImgFormatBmp, 54 - ImgFormatTga 56 + ImgFormatTga, 57 + ImgFormatGif, 55 58 }; 56 59 57 60 typedef enum ImgFormatE ImgFormat; ··· 117 120 struct ImgTgaColor colorMap[IMG_TGA_MAX_COLOR_MAP_SIZE]; 118 121 }; 119 122 123 + /* for color table, converted to RGB24 for convenience */ 124 + struct ImgGifColor { 125 + ImgU8 red; 126 + ImgU8 green; 127 + ImgU8 blue; 128 + }; 129 + 130 + struct ImgGif { 131 + ImgU8 globalColorTableFlag; 132 + ImgU8 colorResolution; 133 + ImgU8 sortFlag; 134 + ImgU16 globalColorTableSize; 135 + ImgU8 backgroundColorIndex; 136 + ImgU8 pixelAspectRatio; 137 + struct ImgGifColor globalColorTable[256]; 138 + }; 139 + 120 140 struct ImgS { 121 141 ImgFormat format; 122 142 ImgU32 width; ··· 128 148 struct ImgPbm pbm; 129 149 struct ImgBmp bmp; 130 150 struct ImgTga tga; 151 + struct ImgGif gif; 131 152 }; 132 153 }; 133 154