A very small library for reading image files
1#include "img.h"
2#include <stdlib.h>
3
4static ImgStatus determineFormat(Img *img);
5static ImgStatus openQoi(Img *img);
6static ImgStatus readQoi(Img *img, ImgAny buf, ImgSz bufSz);
7static ImgStatus openPbm(Img *img);
8static ImgStatus readPbm(Img *img, ImgAny buf, ImgSz bufSz);
9static ImgStatus openBmp(Img *img);
10static ImgStatus readBmp(Img *img, ImgAny buf, ImgSz bufSz);
11static ImgStatus openTga(Img *img);
12static ImgStatus readTga(Img *img, ImgAny buf, ImgSz bufSz);
13static ImgStatus openGif(Img *img);
14static ImgStatus readGif(Img *img, ImgAny buf, ImgSz bufSz);
15
16ImgStatus imgOpen(Img *img, const ImgFuncs *funcs)
17{
18 img->funcs = *funcs;
19 ImgStatus status = determineFormat(img);
20 if(status != ImgOK) { return status; }
21 switch(img->format) {
22 case ImgFormatUnknown:
23 return ImgErrUnknownFormat;
24 case ImgFormatQoi:
25 return openQoi(img);
26 case ImgFormatPbm:
27 return openPbm(img);
28 case ImgFormatBmp:
29 return openBmp(img);
30 case ImgFormatTga:
31 return openTga(img);
32 case ImgFormatGif:
33 return openGif(img);
34 }
35}
36
37ImgStatus imgRead(Img *img, ImgAny buf, ImgSz bufSz)
38{
39 if(img->width == 0 || img->height == 0 || img->channelCount == 0) {
40 return ImgErrUnopened;
41 }
42 switch(img->format) {
43 case ImgFormatUnknown:
44 return ImgErrUnknownFormat;
45 case ImgFormatQoi:
46 return readQoi(img, buf, bufSz);
47 case ImgFormatPbm:
48 return readPbm(img, buf, bufSz);
49 case ImgFormatBmp:
50 return readBmp(img, buf, bufSz);
51 case ImgFormatTga:
52 return readTga(img, buf, bufSz);
53 case ImgFormatGif:
54 return readGif(img, buf, bufSz);
55 }
56}
57
58ImgStatus imgClose(Img *img)
59{
60 img->format = ImgFormatUnknown;
61 img->width = 0;
62 img->height = 0;
63 img->channelCount = 0;
64 if(img->funcs.close) {
65 return img->funcs.close(img->funcs.user);
66 }
67 return ImgOK;
68}
69
70const char *imgFormatName(ImgFormat format)
71{
72 switch(format) {
73 case ImgFormatUnknown:
74 return "Unknown";
75 case ImgFormatQoi:
76 return "QOI";
77 case ImgFormatPbm:
78 return "PBM";
79 case ImgFormatBmp:
80 return "BMP";
81 case ImgFormatTga:
82 return "TGA";
83 case ImgFormatGif:
84 return "GIF";
85 }
86}
87
88const char *imgStatusMessage(ImgStatus status)
89{
90 switch(status) {
91 case ImgOK:
92 return "OK";
93 case ImgErrUnimplemented:
94 return "Unimplemented";
95 case ImgErrIO:
96 return "I/O error";
97 case ImgErrUnknownFormat:
98 return "Unknown image format";
99 case ImgErrUnopened:
100 return "No image opened";
101 case ImgErrBufferTooSmall:
102 return "Buffer to small to decode into";
103 case ImgErrQoiInvalidChannelCount:
104 return "QOI: Invalid channel count";
105 case ImgErrQoiInvalidColorSpace:
106 return "QOI: Invalid color space";
107 case ImgErrPbmValueTooLarge:
108 return "PBM: Value too large";
109 case ImgErrPbmInvalidInteger:
110 return "PBM: Invalid integer";
111 case ImgErrBmpMalformedHeader:
112 return "BMP: Malformed header";
113 case ImgErrBmpUnsupportedVariant:
114 return "BMP: Unsupported BMP variant";
115 case ImgErrBmpPlaneCount:
116 return "BMP: Invalid plane count";
117 case ImgErrBmpUnsupportedCompression:
118 return "BMP: Unsupported compression mode";
119 case ImgErrBmpUnsupportedBitDepth:
120 return "BMP: Unsupported bit depth";
121 case ImgErrBmpUnexpectedEndOfBitmap:
122 return "BMP: Unexpected end of bitmap in compressed data";
123 case ImgErrTgaMissingColorMap:
124 return "TGA: Missing color map";
125 case ImgErrTgaColorMapTooLong:
126 return "TGA: Color map too long (>256 colors)";
127 case ImgErrTgaNoImageData:
128 return "TGA: Attempt to decode an image not containing image data";
129 case ImgErrTgaUnsupportedPixelDepth:
130 return "TGA: Unsupported pixel depth";
131 case ImgErrGifExpectedImageDescriptor:
132 return "GIF: Expected Image Descriptor";
133 case ImgErrGifExpectedTrailer:
134 return "GIF: Expected trailer";
135 }
136}
137
138static ImgStatus read(Img *img, ImgAny buf, ImgSz bufSz)
139{
140 return img->funcs.read(img->funcs.user, buf, bufSz);
141}
142
143static ImgStatus readU16LE(Img *img, ImgU16 *out)
144{
145 ImgStatus status = img->funcs.read(img->funcs.user, out, 2);
146 if(status != ImgOK) { return status; }
147 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
148 *out = __builtin_bswap16(*out);
149 #endif
150 return ImgOK;
151}
152
153static ImgStatus readU16BE(Img *img, ImgU16 *out)
154{
155 ImgStatus status = img->funcs.read(img->funcs.user, out, 2);
156 if(status != ImgOK) { return status; }
157 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
158 *out = __builtin_bswap16(*out);
159 #endif
160 return ImgOK;
161}
162
163static ImgStatus readU24LE(Img *img, ImgU32 *out)
164{
165 ImgU8 bytes[3];
166 ImgStatus status = img->funcs.read(img->funcs.user, bytes, 3);
167 if(status != ImgOK) { return status; }
168 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
169 *out = ((ImgU32)bytes[2] << 16) | ((ImgU32)bytes[1] << 8) | (ImgU32)bytes[0];
170 #else
171 *out = ((ImgU32)bytes[0] << 16) | ((ImgU32)bytes[1] << 8) | (ImgU32)bytes[2];
172 #endif
173 return ImgOK;
174}
175
176static ImgStatus readU32LE(Img *img, ImgU32 *out)
177{
178 ImgStatus status = img->funcs.read(img->funcs.user, out, 4);
179 if(status != ImgOK) { return status; }
180 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
181 *out = __builtin_bswap32(*out);
182 #endif
183 return ImgOK;
184}
185
186static ImgStatus readU32BE(Img *img, ImgU32 *out)
187{
188 ImgStatus status = img->funcs.read(img->funcs.user, out, 4);
189 if(status != ImgOK) { return status; }
190 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
191 *out = __builtin_bswap32(*out);
192 #endif
193 return ImgOK;
194}
195
196static ImgStatus readS32LE(Img *img, ImgS32 *out)
197{
198 ImgStatus status = img->funcs.read(img->funcs.user, out, 4);
199 if(status != ImgOK) { return status; }
200 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
201 *out = __builtin_bswap32(*out);
202 #endif
203 return ImgOK;
204}
205
206static ImgStatus seek(Img *img, ImgOff off)
207{
208 return img->funcs.seek(img->funcs.user, off);
209}
210
211static ImgStatus determineTga(Img *img);
212
213static ImgStatus determineFormat(Img *img)
214{
215 ImgStatus status;
216 char magic[8];
217
218 status = read(img, magic, 2);
219 if(status != ImgOK) { return status; }
220 status = seek(img, -2);
221 if(status != ImgOK) { return status; }
222
223 if(magic[0] == 'P' && magic[1] >= '1' && magic[1] <= '6') {
224 img->format = ImgFormatPbm;
225 return ImgOK;
226 }
227 if(magic[0] == 'B' && magic[1] == 'M') {
228 img->format = ImgFormatBmp;
229 return ImgOK;
230 }
231
232 status = read(img, magic, 4);
233 if(status != ImgOK) { return status; }
234 status = seek(img, -4);
235 if(status != ImgOK) { return status; }
236
237 if(magic[0] == 'q' && magic[1] == 'o' && magic[2] == 'i' && magic[3] == 'f') {
238 img->format = ImgFormatQoi;
239 return ImgOK;
240 }
241
242 status = read(img, magic, 6);
243 if(status != ImgOK) { return status; }
244 status = seek(img, -6);
245 if(status != ImgOK) { return status; }
246
247 if(magic[0] == 'G' && magic[1] == 'I' && magic[2] == 'F'
248 && magic[3] == '8' && (magic[4] == '7' || magic[4] == '9') && magic[5] == 'a') {
249 img->format = ImgFormatGif;
250 return ImgOK;
251 }
252
253 status = determineTga(img);
254 if(status != ImgOK) { return status; }
255
256 if(img->format == ImgFormatUnknown) {
257 return ImgErrUnknownFormat;
258 }
259 return ImgOK;
260}
261
262struct RGBA {
263 ImgU8 r;
264 ImgU8 g;
265 ImgU8 b;
266 ImgU8 a;
267};
268
269ImgStatus openQoi(Img *img)
270{
271 ImgStatus status = seek(img, 4);
272 if(status != ImgOK) { return status; }
273 status = readU32BE(img, &img->width);
274 if(status != ImgOK) { return status; }
275 status = readU32BE(img, &img->height);
276 if(status != ImgOK) { return status; }
277 status = read(img, &img->channelCount, 1);
278 if(status != ImgOK) { return status; }
279 status = read(img, &img->qoi.colorSpace, 1);
280 if(status != ImgOK) { return status; }
281
282 if(img->channelCount != 3 && img->channelCount != 4) {
283 return ImgErrQoiInvalidChannelCount;
284 }
285 if(img->qoi.colorSpace != 0 && img->qoi.colorSpace != 1) {
286 return ImgErrQoiInvalidColorSpace;
287 }
288
289 return ImgOK;
290}
291
292static ImgU32 qoiIndex(struct RGBA color) {
293 return (color.r * 3 + color.g * 5 + color.b * 7 + color.a * 11) % 64;
294}
295
296/* FIXME: doesn't decode 4-chanel images correctly */
297ImgStatus readQoi(Img *img, ImgAny buf, ImgSz bufSz)
298{
299 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
300 if(bufSz < expectedSize) {
301 return ImgErrBufferTooSmall;
302 }
303
304 struct RGBA memory[64];
305 for(ImgSz i = 0; i < 64; ++i) {
306 memory[i].r = 0;
307 memory[i].g = 0;
308 memory[i].b = 0;
309 memory[i].a = 0;
310 }
311 struct RGBA pixel;
312 pixel.r = 0;
313 pixel.g = 0;
314 pixel.b = 0;
315 pixel.a = 255;
316
317 ImgU8 *curr = buf;
318 ImgU8 *end = curr + bufSz;
319 ImgSz run = 0;
320 ImgU8 chunk;
321 ImgStatus status;
322 ImgS8 dr;
323 ImgS8 dg;
324 ImgS8 db;
325
326 while(curr < end) {
327 if(run > 0) {
328 --run;
329 *curr++ = pixel.r;
330 *curr++ = pixel.g;
331 *curr++ = pixel.b;
332 if(img->channelCount == 4) {
333 *curr++ = pixel.a;
334 }
335 }
336
337 status = read(img, &chunk, 1);
338 if(status != ImgOK) { return status; }
339
340 if(chunk == 0xFF) {
341 status = read(img, &pixel.r, 1);
342 if(status != ImgOK) { return status; }
343 status = read(img, &pixel.g, 1);
344 if(status != ImgOK) { return status; }
345 status = read(img, &pixel.b, 1);
346 if(status != ImgOK) { return status; }
347 status = read(img, &pixel.a, 1);
348 if(status != ImgOK) { return status; }
349 } else if(chunk == 0xFE) {
350 status = read(img, &pixel.r, 1);
351 if(status != ImgOK) { return status; }
352 status = read(img, &pixel.g, 1);
353 if(status != ImgOK) { return status; }
354 status = read(img, &pixel.b, 1);
355 if(status != ImgOK) { return status; }
356 } else if((chunk & 0xC0) == 0x00) {
357 pixel = memory[chunk];
358 } else if((chunk & 0xC0) == 0x40) {
359 dr = (ImgS8)((chunk & 0x30) >> 4) - 2;
360 dg = (ImgS8)((chunk & 0x0C) >> 2) - 2;
361 db = (ImgS8)(chunk & 0x03) - 2;
362 pixel.r += dr;
363 pixel.g += dg;
364 pixel.b += db;
365 } else if((chunk & 0xC0) == 0x80) {
366 dg = (ImgS8)(chunk & 0x3F) - 32;
367 status = read(img, &chunk, 1);
368 if(status != ImgOK) { return status; }
369 dr = (ImgS8)(chunk >> 4) - 8 + dg;
370 db = (ImgS8)(chunk & 0xF) - 8 + dg;
371 pixel.r += dr;
372 pixel.g += dg;
373 pixel.b += db;
374 } else if((chunk & 0xC0) == 0xC0) {
375 run = chunk & 0x3F;
376 }
377
378 memory[qoiIndex(pixel)] = pixel;
379 *curr++ = pixel.r;
380 *curr++ = pixel.g;
381 *curr++ = pixel.b;
382 if(img->channelCount == 4) {
383 *curr++ = pixel.a;
384 }
385 }
386
387 return ImgOK;
388}
389
390static ImgStatus pbmReadNum(Img *img, ImgU16 *out)
391{
392 ImgStatus status;
393 char buf[6];
394 ImgSz len = 0;
395 char chr;
396
397 do {
398 status = read(img, &chr, 1);
399 if(status != ImgOK) { return status; }
400 if(chr == '#') {
401 do {
402 status = read(img, &chr, 1);
403 if(status != ImgOK) { return status; }
404 } while(chr != '\n');
405 continue;
406 }
407 } while(chr == ' ' || chr == '\n');
408 buf[len++] = chr;
409
410 do {
411 status = read(img, &chr, 1);
412 if(status != ImgOK) { return status; }
413 if(chr == ' ' || chr == '\n') {
414 buf[len] = '\0';
415 break;
416 }
417 buf[len++] = chr;
418 if(len >= 5) {
419 return ImgErrPbmValueTooLarge;
420 }
421 } while(1);
422
423 char *endptr;
424 long value = strtol(buf, &endptr, 10);
425 if(endptr != buf + len) {
426 return ImgErrPbmInvalidInteger;
427 }
428
429 *out = value;
430 return ImgOK;
431}
432
433ImgStatus openPbm(Img *img)
434{
435 char magic[2];
436 ImgStatus status;
437
438 status = read(img, magic, 2);
439 if(status != ImgOK) { return status; }
440 img->pbm.variant = magic[1];
441
442 ImgU16 width;
443 ImgU16 height;
444 status = pbmReadNum(img, &width);
445 if(status != ImgOK) { return status; }
446 status = pbmReadNum(img, &height);
447 if(status != ImgOK) { return status; }
448 if(img->pbm.variant != '1' && img->pbm.variant != '4') {
449 status = pbmReadNum(img, &img->pbm.max);
450 if(status != ImgOK) { return status; }
451 }
452
453 img->width = width;
454 img->height = height;
455 img->channelCount = 3;
456
457 return ImgOK;
458}
459
460static ImgStatus readPbm1(Img *img, ImgAny buf, ImgSz bufSz);
461static ImgStatus readPbm2(Img *img, ImgAny buf, ImgSz bufSz);
462static ImgStatus readPbm3(Img *img, ImgAny buf, ImgSz bufSz);
463static ImgStatus readPbm4(Img *img, ImgAny buf, ImgSz bufSz);
464static ImgStatus readPbm5(Img *img, ImgAny buf, ImgSz bufSz);
465static ImgStatus readPbm6(Img *img, ImgAny buf, ImgSz bufSz);
466
467ImgStatus readPbm(Img *img, ImgAny buf, ImgSz bufSz)
468{
469 switch(img->pbm.variant) {
470 case '1': return readPbm1(img, buf, bufSz);
471 case '2': return readPbm2(img, buf, bufSz);
472 case '3': return readPbm3(img, buf, bufSz);
473 case '5': return readPbm5(img, buf, bufSz);
474 case '6': return readPbm6(img, buf, bufSz);
475 }
476 return ImgErrUnknownFormat;
477}
478
479ImgStatus readPbm1(Img *img, ImgAny buf, ImgSz bufSz)
480{
481 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
482 if(expectedSize != bufSz) {
483 return ImgErrBufferTooSmall;
484 }
485
486 ImgU8 *curr = buf;
487 ImgU8 *end = curr + expectedSize;
488 char chr;
489 ImgStatus status;
490 do {
491 status = read(img, &chr, 1);
492 if(status != ImgOK) { return status; }
493
494 if(chr == '1') {
495 *curr++ = 0;
496 *curr++ = 0;
497 *curr++ = 0;
498 } else {
499 *curr++ = 255;
500 *curr++ = 255;
501 *curr++ = 255;
502 }
503 } while(curr < end);
504
505 return ImgOK;
506}
507
508ImgStatus readPbm2(Img *img, ImgAny buf, ImgSz bufSz)
509{
510 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
511 if(expectedSize != bufSz) {
512 return ImgErrBufferTooSmall;
513 }
514
515 ImgU8 *curr = buf;
516 ImgU8 *end = curr + expectedSize;
517 ImgU16 pixel;
518 ImgU8 pixelU8;
519 ImgStatus status;
520 do {
521 status = pbmReadNum(img, &pixel);
522 if(status != ImgOK) { return status; }
523
524 pixelU8 = (ImgSz)pixel * 255 / img->pbm.max;
525 *curr++ = pixelU8;
526 *curr++ = pixelU8;
527 *curr++ = pixelU8;
528 } while(curr < end);
529
530 return ImgOK;
531}
532
533ImgStatus readPbm3(Img *img, ImgAny buf, ImgSz bufSz)
534{
535 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
536 if(expectedSize != bufSz) {
537 return ImgErrBufferTooSmall;
538 }
539
540 ImgU8 *curr = buf;
541 ImgU8 *end = curr + expectedSize;
542 ImgU16 pixel;
543 ImgStatus status;
544 do {
545 status = pbmReadNum(img, &pixel);
546 if(status != ImgOK) { return status; }
547 *curr++ = (ImgSz)pixel * 255 / img->pbm.max;
548 status = pbmReadNum(img, &pixel);
549 if(status != ImgOK) { return status; }
550 *curr++ = (ImgSz)pixel * 255 / img->pbm.max;
551 status = pbmReadNum(img, &pixel);
552 if(status != ImgOK) { return status; }
553 *curr++ = (ImgSz)pixel * 255 / img->pbm.max;
554 } while(curr < end);
555
556 return ImgOK;
557}
558
559ImgStatus readPbm4(Img *img, ImgAny buf, ImgSz bufSz)
560{
561 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
562 if(expectedSize != bufSz) {
563 return ImgErrBufferTooSmall;
564 }
565
566 ImgU8 *curr = buf;
567 ImgU8 *end = curr + expectedSize;
568 ImgU8 batch;
569 ImgStatus status;
570 do {
571 status = read(img, &batch, 1);
572 if(status != ImgOK) { return status; }
573 *curr++ = (batch >> 7) * 255;
574 if(curr >= end) { break; }
575 *curr += ((batch >> 6) & 1) * 255;
576 if(curr >= end) { break; }
577 *curr += ((batch >> 5) & 1) * 255;
578 if(curr >= end) { break; }
579 *curr += ((batch >> 4) & 1) * 255;
580 if(curr >= end) { break; }
581 *curr += ((batch >> 3) & 1) * 255;
582 if(curr >= end) { break; }
583 *curr += ((batch >> 2) & 1) * 255;
584 if(curr >= end) { break; }
585 *curr += ((batch >> 1) & 1) * 255;
586 if(curr >= end) { break; }
587 *curr += (batch & 1) * 255;
588 } while(curr < end);
589
590 return ImgOK;
591}
592
593ImgStatus readPbm5(Img *img, ImgAny buf, ImgSz bufSz)
594{
595 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
596 if(expectedSize != bufSz) {
597 return ImgErrBufferTooSmall;
598 }
599
600 ImgU8 *curr = buf;
601 ImgU8 *end = curr + expectedSize;
602 ImgU8 pixel8;
603 ImgU16 pixel16;
604 ImgStatus status;
605 do {
606 if(img->pbm.max >= 256) {
607 status = read(img, &pixel8, 1);
608 if(status != ImgOK) { return status; }
609 } else {
610 status = readU16BE(img, &pixel16);
611 if(status != ImgOK) { return status; }
612 pixel8 = (ImgSz)pixel16 * 255 / img->pbm.max;
613 }
614 *curr++ = pixel8;
615 *curr++ = pixel8;
616 *curr++ = pixel8;
617 } while(curr < end);
618
619 return ImgOK;
620}
621
622ImgStatus readPbm6(Img *img, ImgAny buf, ImgSz bufSz)
623{
624 ImgSz expectedSize = (ImgSz)img->width * img->height * 3;
625 if(expectedSize != bufSz) {
626 return ImgErrBufferTooSmall;
627 }
628
629 ImgU8 *curr = buf;
630 ImgU8 *end = curr + expectedSize;
631 ImgU8 pixel8[3];
632 ImgU16 pixel16[3];
633 ImgStatus status;
634 do {
635 if(img->pbm.max >= 256) {
636 status = read(img, pixel8, 3);
637 if(status != ImgOK) { return status; }
638 } else {
639 status = readU16BE(img, &pixel16[0]);
640 if(status != ImgOK) { return status; }
641 status = readU16BE(img, &pixel16[1]);
642 if(status != ImgOK) { return status; }
643 status = readU16BE(img, &pixel16[2]);
644 if(status != ImgOK) { return status; }
645 pixel8[0] = (ImgSz)pixel16[0] * 255 / img->pbm.max;
646 pixel8[1] = (ImgSz)pixel16[1] * 255 / img->pbm.max;
647 pixel8[2] = (ImgSz)pixel16[2] * 255 / img->pbm.max;
648 }
649 *curr++ = pixel8[0];
650 *curr++ = pixel8[1];
651 *curr++ = pixel8[2];
652 } while(curr < end);
653
654 return ImgOK;
655}
656
657static ImgStatus openBmpBITMAPINFOHEADER(Img *img);
658static ImgStatus openBmpBITMAPV4HEADER(Img *img);
659static ImgStatus openBmpBITMAPV5HEADER(Img *img);
660static ImgStatus bmpReadColorTable(Img *img);
661
662ImgStatus openBmp(Img *img)
663{
664 /* BITMAPFILEHEADER
665 WORD bfType; "BM"
666 DWORD bfSize; size of the file
667 WORD bfReserved1; must be 0
668 WORD bfReserved2; must be 0
669 DWORD bfOffBits; offset to th bitmap data
670 */
671
672 ImgStatus status;
673 ImgU32 size;
674 ImgU16 reserved;
675
676 status = seek(img, 2);
677 if(status != ImgOK) { return status; }
678 status = readU32LE(img, &size);
679 if(status != ImgOK) { return status; }
680 status = read(img, &reserved, 2);
681 if(status != ImgOK) { return status; }
682 if(reserved != 0) {
683 return ImgErrBmpMalformedHeader;
684 }
685 status = read(img, &reserved, 2);
686 if(status != ImgOK) { return status; }
687 if(reserved != 0) {
688 return ImgErrBmpMalformedHeader;
689 }
690 status = readU32LE(img, &img->bmp.bitmapOffset);
691 if(status != ImgOK) { return status; }
692 if(img->bmp.bitmapOffset >= size) {
693 return ImgErrBmpMalformedHeader;
694 }
695
696 status = readU32LE(img, &img->bmp.headerSize);
697 if(status != ImgOK) { return status; }
698 if(img->bmp.headerSize == 40) {
699 status = openBmpBITMAPINFOHEADER(img);
700 } else if(img->bmp.headerSize == 108) {
701 status = openBmpBITMAPV4HEADER(img);
702 } else if(img->bmp.headerSize == 124) {
703 status = openBmpBITMAPV5HEADER(img);
704 } else {
705 return ImgErrBmpUnsupportedVariant;
706 }
707 if(status != ImgOK) { return status; }
708
709 return bmpReadColorTable(img);
710}
711
712ImgStatus openBmpBITMAPINFOHEADER(Img *img)
713{
714 /* BITMAPINFOHEADER
715 DWORD biSize; 40 for BITMAPINFOHEADER
716 LONG biWidth;
717 LONG biHeight; if positive : bottom-up bitmap
718 if negative : top-down, origin at top left corner
719 WORD biPlanes; must be 1
720 WORD biBitCount;
721 DWORD biCompression;
722 DWORD biSizeImage; can be 0 for uncompressed
723 LONG biXPelsPerMeter;
724 LONG biYPelsPerMeter;
725 DWORD biClrUsed; colors count in color table
726 DWORD biClrImportant; numbers of color indices considered important
727 */
728
729 ImgStatus status;
730 ImgU16 planes;
731
732 status = readS32LE(img, &img->bmp.width);
733 if(status != ImgOK) { return status; }
734 img->width = img->bmp.width;
735 status = readS32LE(img, &img->bmp.height);
736 if(status != ImgOK) { return status; }
737 if(img->bmp.height < 0) {
738 img->height = -img->bmp.height;
739 } else {
740 img->height = img->bmp.height;
741 }
742 status = readU16LE(img, &planes);
743 if(status != ImgOK) { return status; }
744 if(planes != 1) {
745 return ImgErrBmpPlaneCount;
746 }
747 status = readU16LE(img, &img->bmp.bitCount);
748 if(status != ImgOK) { return status; }
749 status = readU32LE(img, &img->bmp.compression);
750 if(status != ImgOK) { return status; }
751 if(img->bmp.compression != IMG_BMP_COMPRESSION_RGB
752 && img->bmp.compression != IMG_BMP_COMPRESSION_RLE8
753 && img->bmp.compression != IMG_BMP_COMPRESSION_RLE4
754 && img->bmp.compression != IMG_BMP_COMPRESSION_BITFIELDS) {
755 return ImgErrBmpUnsupportedCompression;
756 }
757 status = readU32LE(img, &img->bmp.imageSize);
758 if(status != ImgOK) { return status; }
759 status = readS32LE(img, &img->bmp.xPPM);
760 if(status != ImgOK) { return status; }
761 status = readS32LE(img, &img->bmp.yPPM);
762 if(status != ImgOK) { return status; }
763 status = readU32LE(img, &img->bmp.colorsUsed);
764 if(status != ImgOK) { return status; }
765 status = seek(img, 4);
766 if(status != ImgOK) { return status; }
767
768 return ImgOK;
769}
770
771ImgStatus openBmpBITMAPV4HEADER(Img *img)
772{
773 /* BITMAPV4HEADER
774 <BITMAPINFOHEADER fields>
775 DWORD bV4RedMask;
776 DWORD bV4GreenMask;
777 DWORD bV4BlueMask;
778 DWORD bV4AlphaMask;
779 DWORD bV4CSType; ignored
780 CIEXYZTRIPLE bV4Endpoints; ignored
781 DWORD bV4GammaRed; ignored
782 DWORD bV4GammaGreen; ignored
783 DWORD bV4GammaBlue; ignored
784 */
785
786 ImgStatus status;
787
788 status = openBmpBITMAPINFOHEADER(img);
789 if(status != ImgOK) { return status; }
790 status = readU32LE(img, &img->bmp.redMask);
791 if(status != ImgOK) { return status; }
792 status = readU32LE(img, &img->bmp.greenMask);
793 if(status != ImgOK) { return status; }
794 status = readU32LE(img, &img->bmp.blueMask);
795 if(status != ImgOK) { return status; }
796 status = readU32LE(img, &img->bmp.alphaMask);
797 if(status != ImgOK) { return status; }
798
799 ImgOff off = 4 /* bV4CSType*/
800 + (4 * 3 * 3) /* bV4Endpoints */
801 + 4 /* bV4GammaRed */
802 + 4 /* bV4GammaGreen */
803 + 4; /* bV4GammaBlue */
804 status = seek(img, off);
805 if(status != ImgOK) { return status; }
806
807 return ImgOK;
808}
809
810ImgStatus openBmpBITMAPV5HEADER(Img *img)
811{
812 /* BITMAPV5HEADER
813 <BITMAPV4HEADER fields>
814 DWORD bV5Intent; ignored
815 DWORD bV5ProfileData; ignored
816 DWORD bV5ProfileSize; ignored
817 DWORD bV5Reserved; must be 0
818 */
819
820 ImgStatus status;
821
822 status = openBmpBITMAPV4HEADER(img);
823 if(status != ImgOK) { return status; }
824
825 ImgOff off = 4 /* bV5Intent */
826 + 4 /* bV5ProfileData */
827 + 4; /* bV5ProfileSize */
828 status = seek(img, off);
829 if(status != ImgOK) { return status; }
830
831 ImgU32 reserved;
832 status = readU32LE(img, &reserved);
833 if(status != ImgOK) { return status; }
834 if(reserved != 0) {
835 return ImgErrBmpMalformedHeader;
836 }
837
838 return ImgOK;
839}
840
841ImgStatus bmpReadColorTable(Img *img)
842{
843 ImgStatus status;
844
845 switch(img->bmp.bitCount) {
846 case 1:
847 case 2:
848 case 4:
849 case 8:
850 img->channelCount = 3;
851 if(img->bmp.colorsUsed == 0) {
852 img->bmp.colorTableSize = 1 << img->bmp.bitCount;
853 } else {
854 img->bmp.colorTableSize = img->bmp.colorsUsed;
855 }
856 status = read(img, img->bmp.colorTable, img->bmp.colorTableSize * 4);
857 if(status != ImgOK) { return status; }
858 break;
859 case 24:
860 img->channelCount = 3;
861 img->bmp.colorTableSize = 0;
862 break;
863 case 32:
864 img->channelCount = 4;
865 img->bmp.colorTableSize = 0;
866 break;
867 default:
868 return ImgErrBmpUnsupportedBitDepth;
869 }
870
871 return ImgOK;
872}
873
874static ImgStatus readBmp1BPP(Img *img, ImgAny buf, ImgSz bufSz);
875static ImgStatus readBmp2BPP(Img *img, ImgAny buf, ImgSz bufSz);
876static ImgStatus readBmp4BPP(Img *img, ImgAny buf, ImgSz bufSz);
877static ImgStatus readBmp8BPP(Img *img, ImgAny buf, ImgSz bufSz);
878static ImgStatus readBmp16BPP(Img *img, ImgAny buf, ImgSz bufSz);
879static ImgStatus readBmp24BPP(Img *img, ImgAny buf, ImgSz bufSz);
880static ImgStatus readBmp32BPP(Img *img, ImgAny buf, ImgSz bufSz);
881static ImgStatus readBmpRLE8(Img *img, ImgAny buf, ImgSz bufSz);
882static ImgStatus readBmpRLE4(Img *img, ImgAny buf, ImgSz bufSz);
883
884ImgStatus readBmp(Img *img, ImgAny buf, ImgSz bufSz)
885{
886 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
887 if(bufSz < expectedSize) {
888 return ImgErrBufferTooSmall;
889 }
890
891 /*
892 ImgStatus status = seek(img, img->bmp.bitmapOffset - 14 - img->bmp.headerSize);
893 if(status != ImgOK) { return status; }
894 */
895
896 if(img->bmp.compression == IMG_BMP_COMPRESSION_RLE8) {
897 return readBmpRLE8(img, buf, bufSz);
898 }
899 if(img->bmp.compression == IMG_BMP_COMPRESSION_RLE4) {
900 return readBmpRLE8(img, buf, bufSz);
901 }
902
903 switch(img->bmp.bitCount) {
904 case 1: return readBmp1BPP(img, buf, bufSz);
905 case 2: return readBmp2BPP(img, buf, bufSz);
906 case 4: return readBmp4BPP(img, buf, bufSz);
907 case 8: return readBmp8BPP(img, buf, bufSz);
908 case 16: return readBmp16BPP(img, buf, bufSz);
909 case 24: return readBmp24BPP(img, buf, bufSz);
910 case 32: return readBmp32BPP(img, buf, bufSz);
911 }
912
913 return ImgErrUnimplemented;
914}
915
916ImgStatus readBmp1BPP(Img *img, ImgAny buf, ImgSz bufSz)
917{
918 ImgU8 *out = buf;
919 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
920
921 ImgStatus status;
922 ImgU8 batch;
923 struct ImgBmpRGBQuad pixel;
924 for(ImgU32 y = 0; y < img->height; ++y) {
925 ImgU32 trueY = y;
926 if(img->bmp.height > 0) {
927 trueY = img->height - y - 1;
928 }
929 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
930 for(ImgU32 x = 0; x < img->width; x += 8) {
931 status = read(img, &batch, 1);
932 if(status != ImgOK) { return status; }
933
934 pixel = img->bmp.colorTable[(batch >> 7) & 1];
935 *scanline++ = pixel.red;
936 *scanline++ = pixel.green;
937 *scanline++ = pixel.blue;
938 pixel = img->bmp.colorTable[(batch >> 6) & 1];
939 *scanline++ = pixel.red;
940 *scanline++ = pixel.green;
941 *scanline++ = pixel.blue;
942 pixel = img->bmp.colorTable[(batch >> 5) & 1];
943 *scanline++ = pixel.red;
944 *scanline++ = pixel.green;
945 *scanline++ = pixel.blue;
946 pixel = img->bmp.colorTable[(batch >> 4) & 1];
947 *scanline++ = pixel.red;
948 *scanline++ = pixel.green;
949 *scanline++ = pixel.blue;
950 pixel = img->bmp.colorTable[(batch >> 3) & 1];
951 *scanline++ = pixel.red;
952 *scanline++ = pixel.green;
953 *scanline++ = pixel.blue;
954 pixel = img->bmp.colorTable[(batch >> 2) & 1];
955 *scanline++ = pixel.red;
956 *scanline++ = pixel.green;
957 *scanline++ = pixel.blue;
958 pixel = img->bmp.colorTable[(batch >> 1) & 1];
959 *scanline++ = pixel.red;
960 *scanline++ = pixel.green;
961 *scanline++ = pixel.blue;
962 pixel = img->bmp.colorTable[batch & 1];
963 *scanline++ = pixel.red;
964 *scanline++ = pixel.green;
965 *scanline++ = pixel.blue;
966 }
967 if(pad != 0) {
968 status = seek(img, pad);
969 if(status != ImgOK) { return status; }
970 }
971 }
972
973 return ImgOK;
974}
975
976ImgStatus readBmp2BPP(Img *img, ImgAny buf, ImgSz bufSz)
977{
978 ImgU8 *out = buf;
979 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
980
981 ImgStatus status;
982 ImgU8 batch;
983 struct ImgBmpRGBQuad pixel;
984 for(ImgU32 y = 0; y < img->height; ++y) {
985 ImgU32 trueY = y;
986 if(img->bmp.height > 0) {
987 trueY = img->height - y - 1;
988 }
989 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
990 for(ImgU32 x = 0; x < img->width; x += 4) {
991 status = read(img, &batch, 1);
992 if(status != ImgOK) { return status; }
993
994 pixel = img->bmp.colorTable[(batch >> 6) & 3];
995 *scanline++ = pixel.red;
996 *scanline++ = pixel.green;
997 *scanline++ = pixel.blue;
998 pixel = img->bmp.colorTable[(batch >> 4) & 3];
999 *scanline++ = pixel.red;
1000 *scanline++ = pixel.green;
1001 *scanline++ = pixel.blue;
1002 pixel = img->bmp.colorTable[(batch >> 2) & 3];
1003 *scanline++ = pixel.red;
1004 *scanline++ = pixel.green;
1005 *scanline++ = pixel.blue;
1006 pixel = img->bmp.colorTable[batch & 3];
1007 *scanline++ = pixel.red;
1008 *scanline++ = pixel.green;
1009 *scanline++ = pixel.blue;
1010 }
1011 if(pad != 0) {
1012 status = seek(img, pad);
1013 if(status != ImgOK) { return status; }
1014 }
1015 }
1016
1017 return ImgOK;
1018}
1019
1020ImgStatus readBmp4BPP(Img *img, ImgAny buf, ImgSz bufSz)
1021{
1022 ImgU8 *out = buf;
1023 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
1024
1025 ImgStatus status;
1026 ImgU8 batch;
1027 struct ImgBmpRGBQuad pixel;
1028 for(ImgU32 y = 0; y < img->height; ++y) {
1029 ImgU32 trueY = y;
1030 if(img->bmp.height > 0) {
1031 trueY = img->height - y - 1;
1032 }
1033 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
1034 for(ImgU32 x = 0; x < img->width; x += 2) {
1035 status = read(img, &batch, 1);
1036 if(status != ImgOK) { return status; }
1037
1038 pixel = img->bmp.colorTable[(batch >> 4) & 15];
1039 *scanline++ = pixel.red;
1040 *scanline++ = pixel.green;
1041 *scanline++ = pixel.blue;
1042 pixel = img->bmp.colorTable[batch & 15];
1043 *scanline++ = pixel.red;
1044 *scanline++ = pixel.green;
1045 *scanline++ = pixel.blue;
1046 }
1047 if(pad != 0) {
1048 status = seek(img, pad);
1049 if(status != ImgOK) { return status; }
1050 }
1051 }
1052
1053 return ImgOK;
1054}
1055
1056ImgStatus readBmp8BPP(Img *img, ImgAny buf, ImgSz bufSz)
1057{
1058 ImgU8 *out = buf;
1059 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
1060
1061 ImgStatus status;
1062 ImgU8 idx;
1063 struct ImgBmpRGBQuad pixel;
1064 for(ImgU32 y = 0; y < img->height; ++y) {
1065 ImgU32 trueY = y;
1066 if(img->bmp.height > 0) {
1067 trueY = img->height - y - 1;
1068 }
1069 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
1070 for(ImgU32 x = 0; x < img->width; x += 1) {
1071 status = read(img, &idx, 1);
1072 if(status != ImgOK) { return status; }
1073
1074 pixel = img->bmp.colorTable[idx];
1075 *scanline++ = pixel.red;
1076 *scanline++ = pixel.green;
1077 *scanline++ = pixel.blue;
1078 }
1079 if(pad != 0) {
1080 status = seek(img, pad);
1081 if(status != ImgOK) { return status; }
1082 }
1083 }
1084
1085 return ImgOK;
1086}
1087
1088ImgStatus readBmp16BPP(Img *img, ImgAny buf, ImgSz bufSz)
1089{
1090 ImgU8 *out = buf;
1091 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
1092
1093 ImgStatus status;
1094 ImgU8 idx;
1095 ImgU16 pixel;
1096 for(ImgU32 y = 0; y < img->height; ++y) {
1097 ImgU32 trueY = y;
1098 if(img->bmp.height > 0) {
1099 trueY = img->height - y - 1;
1100 }
1101 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
1102 for(ImgU32 x = 0; x < img->width; x += 1) {
1103 status = readU16LE(img, &pixel);
1104 if(status != ImgOK) { return status; }
1105 *scanline++ = (pixel >> 11) & 63;
1106 *scanline++ = (pixel >> 6) & 31;
1107 *scanline++ = pixel & 63;
1108 }
1109 if(pad != 0) {
1110 status = seek(img, pad);
1111 if(status != ImgOK) { return status; }
1112 }
1113 }
1114
1115 return ImgOK;
1116}
1117
1118ImgStatus readBmp24BPP(Img *img, ImgAny buf, ImgSz bufSz)
1119{
1120 ImgU8 *out = buf;
1121 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
1122
1123 ImgStatus status;
1124 ImgU8 idx;
1125 struct ImgBmpRGBQuad pixel;
1126 for(ImgU32 y = 0; y < img->height; ++y) {
1127 ImgU32 trueY = y;
1128 if(img->bmp.height > 0) {
1129 trueY = img->height - y - 1;
1130 }
1131 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
1132 for(ImgU32 x = 0; x < img->width; x += 1) {
1133 status = read(img, &pixel, 3);
1134 if(status != ImgOK) { return status; }
1135 *scanline++ = pixel.red;
1136 *scanline++ = pixel.green;
1137 *scanline++ = pixel.blue;
1138 }
1139 if(pad != 0) {
1140 status = seek(img, pad);
1141 if(status != ImgOK) { return status; }
1142 }
1143 }
1144
1145 return ImgOK;
1146}
1147
1148ImgStatus readBmp32BPP(Img *img, ImgAny buf, ImgSz bufSz)
1149{
1150 ImgU8 *out = buf;
1151 ImgOff pad = (4 - (img->width * img->bmp.bitCount / 8)) & 3;
1152
1153 ImgStatus status;
1154 ImgU8 idx;
1155 struct ImgBmpRGBQuad pixel;
1156 for(ImgU32 y = 0; y < img->height; ++y) {
1157 ImgU32 trueY = y;
1158 if(img->bmp.height > 0) {
1159 trueY = img->height - y - 1;
1160 }
1161 ImgU8 *scanline = &out[trueY * img->width * img->channelCount];
1162 for(ImgU32 x = 0; x < img->width; x += 1) {
1163 status = read(img, &pixel, 4);
1164 if(status != ImgOK) { return status; }
1165 *scanline++ = pixel.red;
1166 *scanline++ = pixel.green;
1167 *scanline++ = pixel.blue;
1168 *scanline++ = pixel.reserved;
1169 }
1170 if(pad != 0) {
1171 status = seek(img, pad);
1172 if(status != ImgOK) { return status; }
1173 }
1174 }
1175
1176 return ImgOK;
1177}
1178
1179ImgStatus readBmpRLE8(Img *img, ImgAny buf, ImgSz bufSz)
1180{
1181 ImgStatus status;
1182 ImgU8 *out = buf;
1183
1184 ImgU8 packet[2];
1185 ImgU8 pixel;
1186 ImgU8 delta[2];
1187 const struct ImgBmpRGBQuad *color;
1188
1189 ImgU32 x = 0;
1190 ImgU32 y = 0;
1191 if(img->bmp.height > 0) {
1192 y = img->height - 1;
1193 }
1194
1195 do {
1196 status = read(img, packet, 2);
1197 if(status != ImgOK) { return status; }
1198
1199 ImgU8 * curr = out + img->channelCount * (y * img->width + x);
1200
1201 /* encoded mode */
1202 if(packet[0] != 0) {
1203 color = &img->bmp.colorTable[packet[1]];
1204 for(ImgU8 i = 0; i < packet[0]; ++i) {
1205 *curr++ = color->red;
1206 *curr++ = color->green;
1207 *curr++ = color->blue;
1208 }
1209 x += packet[0];
1210 continue;
1211 }
1212
1213 /* absolute mode */
1214 if(packet[1] >= 3) {
1215 for(ImgU8 i = 0; i < packet[1]; ++i) {
1216 status = read(img, &pixel, 1);
1217 if(status != ImgOK) { return status; }
1218 color = &img->bmp.colorTable[pixel];
1219 *curr++ = color->red;
1220 *curr++ = color->green;
1221 *curr++ = color->blue;
1222 }
1223 x += packet[1];
1224 /* pad to word */
1225 if((packet[1] & 1) != 0) {
1226 status = seek(img, 1);
1227 if(status != ImgOK) { return status; }
1228 }
1229 continue;
1230 }
1231
1232 /* move */
1233 if(packet[1] == 2) {
1234 status = read(img, delta, 2);
1235 if(status != ImgOK) { return status; }
1236 x += delta[0];
1237 if(img->bmp.height > 0) {
1238 y -= delta[1];
1239 } else {
1240 y += delta[1];
1241 }
1242 continue;
1243 }
1244
1245 /* end of scanline */
1246 if(packet[1] == 0) {
1247 x = 0;
1248 if(img->bmp.height > 0) {
1249 --y;
1250 } else {
1251 ++y;
1252 }
1253 continue;
1254 }
1255
1256 /* end of bitmap */
1257 break;
1258 } while(1);
1259
1260 return ImgOK;
1261}
1262
1263ImgStatus readBmpRLE4(Img *img, ImgAny buf, ImgSz bufSz)
1264{
1265 ImgStatus status;
1266 ImgU8 *out = buf;
1267
1268 ImgU8 packet[2];
1269 ImgU8 pixel;
1270 ImgU8 delta[2];
1271 const struct ImgBmpRGBQuad *colors[2];
1272
1273 ImgU32 x = 0;
1274 ImgU32 y = 0;
1275 if(img->bmp.height > 0) {
1276 y = img->height - 1;
1277 }
1278
1279 do {
1280 status = read(img, packet, 2);
1281 if(status != ImgOK) { return status; }
1282
1283 ImgU8 * curr = out + img->channelCount * (y * img->width + x);
1284
1285 /* encoded mode */
1286 if(packet[0] != 0) {
1287 colors[0] = &img->bmp.colorTable[packet[1] >> 4];
1288 colors[1] = &img->bmp.colorTable[packet[1] & 15];
1289 /* first output all pairs */
1290 for(ImgU8 i = 0; i < packet[0] >> 1; ++i) {
1291 *curr++ = colors[0]->red;
1292 *curr++ = colors[0]->green;
1293 *curr++ = colors[0]->blue;
1294 *curr++ = colors[1]->red;
1295 *curr++ = colors[1]->green;
1296 *curr++ = colors[1]->blue;
1297 }
1298 /* output last odd one */
1299 if(packet[0] & 1) {
1300 *curr++ = colors[0]->red;
1301 *curr++ = colors[0]->green;
1302 *curr++ = colors[0]->blue;
1303 }
1304 x += packet[0];
1305 /* pad to word boundary by skipping a byte */
1306 if(packet[0] & 3) {
1307 status = seek(img, 1);
1308 if(status != ImgOK) { return status; }
1309 }
1310 continue;
1311 }
1312
1313 /* absolute mode */
1314 if(packet[1] >= 3) {
1315 /* first output all pairs */
1316 for(ImgU8 i = 0; i < packet[1] >> 1; ++i) {
1317 status = read(img, &pixel, 1);
1318 if(status != ImgOK) { return status; }
1319 colors[0] = &img->bmp.colorTable[pixel >> 4];
1320 colors[1] = &img->bmp.colorTable[pixel & 15];
1321 *curr++ = colors[0]->red;
1322 *curr++ = colors[0]->green;
1323 *curr++ = colors[0]->blue;
1324 *curr++ = colors[1]->red;
1325 *curr++ = colors[1]->green;
1326 *curr++ = colors[1]->blue;
1327 }
1328 /* output last odd one */
1329 if(packet[0] & 1) {
1330 status = read(img, &pixel, 1);
1331 if(status != ImgOK) { return status; }
1332 colors[0] = &img->bmp.colorTable[pixel >> 4];
1333 *curr++ = colors[0]->red;
1334 *curr++ = colors[0]->green;
1335 *curr++ = colors[0]->blue;
1336 }
1337 x += packet[1];
1338 /* pad to word */
1339 if((packet[1] & 3) != 0) {
1340 status = seek(img, 1);
1341 if(status != ImgOK) { return status; }
1342 }
1343 continue;
1344 }
1345
1346 /* move */
1347 if(packet[1] == 2) {
1348 status = read(img, delta, 2);
1349 if(status != ImgOK) { return status; }
1350 x += delta[0];
1351 if(img->bmp.height > 0) {
1352 y -= delta[1];
1353 } else {
1354 y += delta[1];
1355 }
1356 continue;
1357 }
1358
1359 /* end of scanline */
1360 if(packet[1] == 0) {
1361 x = 0;
1362 if(img->bmp.height > 0) {
1363 --y;
1364 } else {
1365 ++y;
1366 }
1367 continue;
1368 }
1369
1370 /* end of bitmap */
1371 break;
1372 } while(1);
1373
1374 return ImgOK;
1375}
1376
1377#define TGA_NO_IMAGE_DATA 0
1378#define TGA_COLOR_MAPPED 1
1379#define TGA_TRUE_COLOR 2
1380#define TGA_BLACK_AND_WHITE 3
1381#define TGA_RLE_COLOR_MAPPED 9
1382#define TGA_RLE_TRUE_COLOR 10
1383#define TGA_RLE_BLACK_AND_WHITE 11
1384
1385ImgStatus determineTga(Img *img)
1386{
1387 ImgOff returnOff = 0;
1388 ImgStatus status;
1389
1390 status = read(img, &img->tga.idLength, 1);
1391 ++returnOff;
1392 if(status != ImgOK) { return status; }
1393
1394 status = read(img, &img->tga.colorMapType, 1);
1395 ++returnOff;
1396 if(status != ImgOK) { return status; }
1397
1398 /* invalid color map type, likely not TGA */
1399 if(img->tga.colorMapType != 0 && img->tga.colorMapType != 1) {
1400 return seek(img, -returnOff);
1401 }
1402
1403 status = read(img, &img->tga.imageType, 1);
1404 ++returnOff;
1405 if(status != ImgOK) { return status; }
1406
1407 switch(img->tga.imageType) {
1408 case TGA_NO_IMAGE_DATA:
1409 case TGA_COLOR_MAPPED:
1410 case TGA_TRUE_COLOR:
1411 case TGA_BLACK_AND_WHITE:
1412 case TGA_RLE_COLOR_MAPPED:
1413 case TGA_RLE_TRUE_COLOR:
1414 case TGA_RLE_BLACK_AND_WHITE:
1415 break;
1416 /* invalid image type, likely not TGA */
1417 default:
1418 return seek(img, -returnOff);
1419 }
1420
1421 status = readU16LE(img, &img->tga.colorMapFirstIndex);
1422 returnOff += 2;
1423 if(status != ImgOK) { return status; }
1424
1425 if(!img->tga.colorMapType && img->tga.colorMapFirstIndex != 0) {
1426 return seek(img, -returnOff);
1427 }
1428
1429 status = readU16LE(img, &img->tga.colorMapLength);
1430 returnOff += 2;
1431 if(status != ImgOK) { return status; }
1432
1433 if(!img->tga.colorMapType && img->tga.colorMapLength != 0) {
1434 return seek(img, -returnOff);
1435 }
1436 if(img->tga.colorMapType && img->tga.colorMapLength == 0) {
1437 return seek(img, -returnOff);
1438 }
1439
1440 status = read(img, &img->tga.colorMapEntrySize, 1);
1441 ++returnOff;
1442 if(status != ImgOK) { return status; }
1443
1444 if(!img->tga.colorMapType && img->tga.colorMapEntrySize != 0) {
1445 return seek(img, -returnOff);
1446 }
1447
1448 status = readU16LE(img, &img->tga.originX);
1449 returnOff += 2;
1450 if(status != ImgOK) { return status; }
1451
1452 status = readU16LE(img, &img->tga.originY);
1453 returnOff += 2;
1454 if(status != ImgOK) { return status; }
1455
1456 status = readU16LE(img, &img->tga.width);
1457 returnOff += 2;
1458 if(status != ImgOK) { return status; }
1459
1460 status = readU16LE(img, &img->tga.height);
1461 returnOff += 2;
1462 if(status != ImgOK) { return status; }
1463
1464 status = read(img, &img->tga.pixelDepth, 1);
1465 returnOff += 1;
1466 if(status != ImgOK) { return status; }
1467
1468 ImgU8 imageDescriptor;
1469 status = read(img, &imageDescriptor, 1);
1470 ++returnOff;
1471 if(status != ImgOK) { return status; }
1472
1473 /* unused bits must be 0 */
1474 if((imageDescriptor >> 6) != 0) {
1475 return seek(img, -returnOff);
1476 }
1477
1478 img->tga.alphaBits = imageDescriptor & 15;
1479 img->tga.imageOrigin = (imageDescriptor >> 4) & 3;
1480
1481 img->format = ImgFormatTga;
1482 return ImgOK;
1483}
1484
1485/* Number of bytes needed to store the given amount of bits */
1486#define BITS_TO_BYTES(bits) (((bits) + 7) >> 3)
1487
1488ImgStatus openTga(Img *img)
1489{
1490 ImgStatus status;
1491
1492 if(img->tga.colorMapType) {
1493 if(img->tga.colorMapLength > IMG_TGA_MAX_COLOR_MAP_SIZE) {
1494 return ImgErrTgaColorMapTooLong;
1495 }
1496 } else if(img->tga.imageType == TGA_COLOR_MAPPED || img->tga.imageType == TGA_RLE_COLOR_MAPPED) {
1497 return ImgErrTgaMissingColorMap;
1498 }
1499
1500 /* the header was already read in by determineTga() */
1501 if(img->tga.idLength) {
1502 status = read(img, img->tga.id, img->tga.idLength);
1503 if(status != ImgOK) { return status; }
1504 }
1505 /* NUL-terminate for convenience */
1506 img->tga.id[img->tga.idLength] = '\0';
1507
1508 /* set this here in case there is no color map */
1509 if(img->tga.alphaBits) {
1510 img->channelCount = 4;
1511 } else {
1512 img->channelCount = 3;
1513 }
1514 img->width = img->tga.width;
1515 img->height = img->tga.height;
1516
1517 /* image data follows color map and we'll read that in readTga() */
1518 /* so if we don't have a color map, we just return early here */
1519 if(!img->tga.colorMapType) {
1520 return ImgOK;
1521 }
1522
1523 /* bits in a color field */
1524 ImgU32 fieldBits = img->tga.colorMapEntrySize / 3;
1525 /* spec requires fields be at most 8 bits each */
1526 if(fieldBits > 8) {
1527 fieldBits = 8;
1528 }
1529 /* mask of a field's bits */
1530 ImgU32 fieldMask = (1 << fieldBits) - 1;
1531 /* bits in a full color entry (3 fields) */
1532 ImgU32 entryBits = fieldBits * 3;
1533 /* total bits needed to store the entry (round up to 8) */
1534 ImgU32 totalBits = BITS_TO_BYTES(img->tga.colorMapEntrySize) << 3;
1535 /* bits not used by entry */
1536 ImgU32 alphaBits = totalBits - entryBits;
1537 /* alpha bit mask */
1538 ImgU32 alphaMask = (1 << alphaBits) - 1;
1539 if(alphaBits != 0) {
1540 img->channelCount = 4;
1541 } else {
1542 img->channelCount = 3;
1543 }
1544
1545 ImgU8 entry8;
1546 ImgU16 entry16;
1547 ImgU32 entry32;
1548 for(ImgU16 i = img->tga.colorMapFirstIndex; i < img->tga.colorMapLength; ++i) {
1549 struct ImgTgaColor *color = &img->tga.colorMap[i];
1550
1551 switch(totalBits) {
1552 case 8:
1553 status = read(img, &entry8, 1);
1554 if(status != ImgOK) { return status; }
1555 entry32 = entry8;
1556 break;
1557 case 16:
1558 status = readU16LE(img, &entry16);
1559 if(status != ImgOK) { return status; }
1560 entry32 = entry16;
1561 break;
1562 case 32:
1563 status = readU32LE(img, &entry32);
1564 if(status != ImgOK) { return status; }
1565 break;
1566 }
1567
1568 color->red = (entry32 >> (fieldBits * 2)) & fieldMask;
1569 color->green = (entry32 >> fieldBits) & fieldMask;
1570 color->blue = entry32 & fieldMask;
1571 if(alphaBits > 0) {
1572 color->alpha = (entry32 >> (fieldBits * 3)) & alphaMask;
1573 }
1574 }
1575
1576 return ImgOK;
1577}
1578
1579static ImgStatus readTgaColorMapped(Img *img, ImgAny buf, ImgSz bufSz);
1580static ImgStatus readTgaTrueColor(Img *img, ImgAny buf, ImgSz bufSz);
1581static ImgStatus readTgaBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz);
1582static ImgStatus readTgaRleColorMapped(Img *img, ImgAny buf, ImgSz bufSz);
1583static ImgStatus readTgaRleTrueColor(Img *img, ImgAny buf, ImgSz bufSz);
1584static ImgStatus readTgaRleBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz);
1585
1586/* TODO: Implement RLE */
1587ImgStatus readTga(Img *img, ImgAny buf, ImgSz bufSz)
1588{
1589 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
1590 if(bufSz < expectedSize) {
1591 return ImgErrBufferTooSmall;
1592 }
1593
1594 switch(img->tga.imageType) {
1595 case TGA_NO_IMAGE_DATA:
1596 return ImgErrTgaNoImageData;
1597 case TGA_COLOR_MAPPED:
1598 return readTgaColorMapped(img, buf, bufSz);
1599 case TGA_TRUE_COLOR:
1600 return readTgaTrueColor(img, buf, bufSz);
1601 case TGA_BLACK_AND_WHITE:
1602 return readTgaBlackAndWhite(img, buf, bufSz);
1603 case TGA_RLE_COLOR_MAPPED:
1604 return readTgaRleColorMapped(img, buf, bufSz);
1605 case TGA_RLE_TRUE_COLOR:
1606 return readTgaRleTrueColor(img, buf, bufSz);
1607 case TGA_RLE_BLACK_AND_WHITE:
1608 return readTgaRleBlackAndWhite(img, buf, bufSz);
1609 }
1610 return ImgErrUnimplemented;
1611}
1612
1613ImgStatus readTgaColorMapped(Img *img, ImgAny buf, ImgSz bufSz)
1614{
1615 ImgStatus status;
1616 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
1617 ImgU8 *cur = buf;
1618 ImgU8 *end = cur + expectedSize;
1619
1620 /* above 8 not supported, also enforced by IMG_TGA_MAX_COLOR_MAP_LENGTH */
1621 if(img->tga.pixelDepth > 8) {
1622 return ImgErrTgaUnsupportedPixelDepth;
1623 }
1624
1625 ImgU8 pixel;
1626 while(cur < end) {
1627 status = read(img, &pixel, 1);
1628 if(status != ImgOK) { return status; }
1629
1630 const struct ImgTgaColor *color = &img->tga.colorMap[pixel];
1631 *cur++ = color->red;
1632 *cur++ = color->green;
1633 *cur++ = color->blue;
1634 if(img->channelCount == 4) {
1635 *cur++ = color->alpha;
1636 }
1637 }
1638
1639 return ImgOK;
1640}
1641
1642ImgStatus readTgaTrueColor(Img *img, ImgAny buf, ImgSz bufSz)
1643{
1644 ImgStatus status;
1645 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
1646 ImgU8 *curr = buf;
1647 ImgU8 *end = curr + expectedSize;
1648
1649 ImgU32 pixelSize = BITS_TO_BYTES(img->tga.pixelDepth);
1650 ImgU32 alphaMask = (1 << img->tga.alphaBits) - 1;
1651 ImgU32 fieldBits = (img->tga.pixelDepth - img->tga.alphaBits) / 3;
1652 ImgU32 fieldMask = (1 << fieldBits) - 1;
1653
1654 ImgU8 pixel8;
1655 ImgU16 pixel16;
1656 ImgU32 pixel32;
1657 while(curr < end) {
1658 switch(pixelSize) {
1659 case 1:
1660 status = read(img, &pixel8, 1);
1661 if(status != ImgOK) { return status; }
1662 pixel32 = pixel8;
1663 break;
1664 case 2:
1665 status = readU16LE(img, &pixel16);
1666 if(status != ImgOK) { return status; }
1667 pixel32 = pixel16;
1668 break;
1669 case 3:
1670 status = readU24LE(img, &pixel32);
1671 if(status != ImgOK) { return status; };
1672 break;
1673 case 4:
1674 status = readU32LE(img, &pixel32);
1675 if(status != ImgOK) { return status; }
1676 break;
1677 default:
1678 return ImgErrTgaUnsupportedPixelDepth;
1679 }
1680
1681 *curr++ = (pixel32 >> (2 * fieldBits)) & fieldMask;
1682 *curr++ = (pixel32 >> fieldBits) & fieldMask;
1683 *curr++ = pixel32 & fieldMask;
1684 if(img->channelCount > 3) {
1685 *curr++ = (pixel32 >> (3 * fieldBits)) & alphaMask;
1686 }
1687 }
1688
1689 return ImgOK;
1690}
1691
1692ImgStatus readTgaBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz)
1693{
1694 ImgStatus status;
1695 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
1696 ImgU8 *curr = buf;
1697 ImgU8 *end = curr + expectedSize;
1698
1699 ImgU32 pixelSize = BITS_TO_BYTES(img->tga.pixelDepth);
1700 ImgU8 pixel8;
1701 ImgU16 pixel16;
1702 ImgU32 pixel32;
1703 while(curr < end) {
1704 switch(pixelSize) {
1705 case 1:
1706 status = read(img, &pixel8, 1);
1707 if(status != ImgOK) { return status; }
1708 break;
1709 case 2:
1710 status = readU16LE(img, &pixel16);
1711 if(status != ImgOK) { return status; }
1712 pixel8 = pixel16 >> 8;
1713 break;
1714 case 3:
1715 status = readU24LE(img, &pixel32);
1716 if(status != ImgOK) { return status; }
1717 pixel8 = pixel32 >> 16;
1718 break;
1719 case 4:
1720 status = readU32LE(img, &pixel32);
1721 if(status != ImgOK) { return status; }
1722 pixel8 = pixel32 >> 24;
1723 break;
1724 default:
1725 return ImgErrTgaUnsupportedPixelDepth;
1726 }
1727
1728 *curr++ = pixel8;
1729 *curr++ = pixel8;
1730 *curr++ = pixel8;
1731 if(img->channelCount > 3) {
1732 *curr++ = 255;
1733 }
1734 }
1735
1736 return ImgOK;
1737}
1738
1739ImgStatus readTgaRleColorMapped(Img *img, ImgAny buf, ImgSz bufSz)
1740{
1741 return ImgErrUnimplemented;
1742}
1743
1744ImgStatus readTgaRleTrueColor(Img *img, ImgAny buf, ImgSz bufSz)
1745{
1746 return ImgErrUnimplemented;
1747}
1748
1749ImgStatus readTgaRleBlackAndWhite(Img *img, ImgAny buf, ImgSz bufSz)
1750{
1751 return ImgErrUnimplemented;
1752}
1753
1754ImgStatus openGif(Img *img)
1755{
1756 ImgStatus status;
1757
1758 /* skip over header */
1759 status = seek(img, 6);
1760 if(status != ImgOK) { return status; }
1761
1762 ImgU16 width;
1763 ImgU16 height;
1764
1765 status = readU16LE(img, &width);
1766 if(status != ImgOK) { return status; }
1767 status = readU16LE(img, &height);
1768 if(status != ImgOK) { return status; }
1769
1770 img->width = width;
1771 img->height = height;
1772 img->channelCount = 4;
1773
1774 ImgU8 flags;
1775 status = read(img, &flags, 1);
1776 if(status != ImgOK) { return status; }
1777
1778 img->gif.globalColorTableFlag = flags >> 7;
1779 img->gif.colorResolution = (flags >> 4) & 7;
1780 img->gif.sortFlag = (flags >> 3) & 1;
1781 ImgU16 gctSize = flags & 7;
1782 img->gif.globalColorTableSize = 1 << (gctSize + 1);
1783
1784 if(img->gif.globalColorTableFlag) {
1785 status = read(img, img->gif.globalColorTable, 3 * img->gif.globalColorTableSize);
1786 if(status != ImgOK) { return status; }
1787 }
1788
1789 status = read(img, &img->gif.backgroundColorIndex, 1);
1790 if(status != ImgOK) { return status; }
1791 status = read(img, &img->gif.pixelAspectRatio, 1);
1792 if(status != ImgOK) { return status; }
1793
1794 return ImgOK;
1795}
1796
1797struct GifBlock {
1798 ImgU8 size;
1799 ImgU8 data[255];
1800};
1801
1802struct GifImage {
1803 ImgU16 left;
1804 ImgU16 right;
1805 ImgU16 width;
1806 ImgU16 height;
1807 ImgU8 localColorTableFlag;
1808 ImgU8 interlaceFlag;
1809 ImgU8 sortFlag;
1810 ImgU16 localColorTableSize;
1811 struct ImgGifColor localColorTable[256];
1812 ImgU8 lzwCodeSize;
1813 struct GifBlock block;
1814};
1815
1816#define GIF_TABLE_SIZE 5021
1817#define GIF_STACK_SIZE 1024
1818
1819struct GifDecompressor {
1820 struct GifImage gifImage;
1821 /* byte offset into current block */
1822 ImgU16 byteOff;
1823 /* bit offset into current block byte */
1824 ImgU8 bitOff;
1825 ImgU16 code;
1826 ImgU8 *curr;
1827
1828 ImgU8 stack[GIF_STACK_SIZE];
1829 ImgU16 stackOff;
1830
1831 ImgU8 shade[GIF_TABLE_SIZE];
1832 ImgU16 child[GIF_TABLE_SIZE];
1833};
1834
1835/* read the Image Descriptor + code size */
1836static ImgStatus gifReadImage(Img *img, struct GifImage *desc);
1837static ImgStatus gifReadBlock(Img *img, struct GifBlock *block);
1838static ImgStatus gifDecompress(Img *img, struct GifDecompressor *decompressor);
1839
1840ImgStatus readGif(Img *img, ImgAny buf, ImgSz bufSz)
1841{
1842 ImgStatus status;
1843 ImgSz expectedSize = (ImgSz)img->width * img->height * img->channelCount;
1844 ImgU8 *curr = buf;
1845 ImgU8 *end = curr + expectedSize;
1846
1847 struct GifDecompressor decompressor;
1848 ImgU8 trailer;
1849
1850 status = gifReadImage(img, &decompressor.gifImage);
1851 if(status != ImgOK) { return status; }
1852
1853 status = gifReadBlock(img, &decompressor.gifImage.block);
1854 if(status != ImgOK) { return status; }
1855
1856 decompressor.byteOff = 0;
1857 decompressor.bitOff = 0;
1858 decompressor.curr = curr;
1859 decompressor.stackOff = 0;
1860 status = gifDecompress(img, &decompressor);
1861 if(status != ImgOK) { return status; };
1862
1863 status = read(img, &trailer, 1);
1864 if(status != ImgOK) { return status; }
1865
1866 if(trailer != 0x3B) {
1867 return ImgErrGifExpectedTrailer;
1868 }
1869
1870 return ImgOK;
1871}
1872
1873ImgStatus gifReadImage(Img *img, struct GifImage *gifImage)
1874{
1875 ImgStatus status;
1876 ImgU8 identifier;
1877
1878 status = read(img, &identifier, 1);
1879 if(status != ImgOK) { return status; }
1880 if(identifier != 0x2C) {
1881 return ImgErrGifExpectedImageDescriptor;
1882 }
1883
1884 status = readU16LE(img, &gifImage->left);
1885 if(status != ImgOK) { return status; }
1886 status = readU16LE(img, &gifImage->right);
1887 if(status != ImgOK) { return status; }
1888 status = readU16LE(img, &gifImage->width);
1889 if(status != ImgOK) { return status; }
1890 status = readU16LE(img, &gifImage->height);
1891 if(status != ImgOK) { return status; }
1892
1893 ImgU8 flags;
1894 status = read(img, &flags, 1);
1895 if(status != ImgOK) { return status; }
1896
1897 gifImage->localColorTableFlag = flags >> 7;
1898 gifImage->interlaceFlag = (flags >> 6) & 1;
1899 gifImage->sortFlag = (flags >> 5) & 1;
1900 ImgU16 lctSize = flags & 7;
1901 gifImage->localColorTableSize = 1 << (lctSize + 1);
1902
1903 if(gifImage->localColorTableFlag) {
1904 status = read(img, gifImage->localColorTable, 3 * gifImage->localColorTableSize);
1905 if(status != ImgOK) { return status; }
1906 }
1907
1908 status = read(img, &gifImage->lzwCodeSize, 1);
1909 if(status != ImgOK) { return status; }
1910
1911 return ImgOK;
1912}
1913
1914ImgStatus gifReadBlock(Img *img, struct GifBlock *block)
1915{
1916 ImgStatus status;
1917
1918 status = read(img, &block->size, 1);
1919 if(status != ImgOK) { return status; }
1920
1921 if(block->size) {
1922 status = read(img, block->data, block->size);
1923 if(status != ImgOK) { return status; }
1924 }
1925
1926 return ImgOK;
1927}
1928
1929ImgStatus gifDecompress(Img *img, struct GifDecompressor *dec)
1930{
1931 return ImgErrUnimplemented;
1932}