+2
-2
.vscode/launch.json
+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
TestImages/gifdecod.gif
This is a binary file and will not be displayed.
+24
-1
img-inspect.c
+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
+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
+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