comparison src/libgfx.c @ 812:1e5cf1144f36

Move library source under src/ subdirectory.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 16 May 2014 03:22:39 +0300
parents libgfx.c@f8126fa1df9b
children 9623b9b548d4
comparison
equal deleted inserted replaced
811:aebc2f8b2c2d 812:1e5cf1144f36
1 /*
2 * Functions for reading and converting various restricted
3 * C64/etc and/or indexed/paletted graphics formats.
4 * Programmed and designed by Matti 'ccr' Hamalainen
5 * (C) Copyright 2012 Tecnic Software productions (TNSP)
6 *
7 * Please read file 'COPYING' for information on license and distribution.
8 */
9 #include "libgfx.h"
10 #include "dmfile.h"
11 #include "dmbstr.h"
12
13 #ifdef DM_USE_LIBPNG
14 #include <png.h>
15 #endif
16
17
18 BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, BOOL alpha)
19 {
20 if (c1->r == c2->r &&
21 c1->g == c2->g &&
22 c1->b == c2->b)
23 return alpha ? (c1->a == c2->a) : TRUE;
24 else
25 return FALSE;
26 }
27
28
29 DMImage * dmImageAlloc(int width, int height)
30 {
31 DMImage *img = dmCalloc(1, sizeof(DMImage));
32 if (img == NULL)
33 return NULL;
34
35 img->width = img->pitch = width;
36 img->height = height;
37 img->data = dmMalloc(width * height * sizeof(Uint8));
38 if (img->data == NULL)
39 {
40 dmFree(img);
41 return NULL;
42 }
43
44 return img;
45 }
46
47
48 void dmImageFree(DMImage *img)
49 {
50 if (img != NULL)
51 {
52 if (!img->constpal)
53 {
54 dmFree(img->pal);
55 }
56 dmFree(img->data);
57 dmFree(img);
58 }
59 }
60
61
62 BOOL dmPaletteAlloc(DMColor **ppal, int ncolors, int ctransp)
63 {
64 int i;
65
66 if (ppal == NULL)
67 return FALSE;
68
69 // Allocate desired amount of palette
70 if ((*ppal = dmCalloc(ncolors, sizeof(DMColor))) == NULL)
71 return FALSE;
72
73 // Set alpha values to max, except for transparent color
74 for (i = 0; i < ncolors; i++)
75 {
76 (*ppal)[i].a = (i == ctransp) ? 0 : 255;
77 }
78
79 return TRUE;
80 }
81
82
83 BOOL dmImageAllocPalette(DMImage *img, int ncolors, int ctransp)
84 {
85 if (img == NULL)
86 return FALSE;
87
88 img->ncolors = ncolors;
89 img->ctransp = ctransp;
90 return dmPaletteAlloc(&(img->pal), ncolors, ctransp);
91 }
92
93
94 int dmImageGetBytesPerPixel(int format)
95 {
96 switch (format)
97 {
98 case DM_IFMT_PALETTE : return 1;
99
100 case DM_IFMT_RGB_PLANE :
101 case DM_IFMT_RGB : return 3;
102
103 case DM_IFMT_RGBA : return 4;
104
105 default: return 0;
106 }
107 }
108
109
110 static BOOL dmReadPaletteData(FILE *fp, DMColor *pal, int ncolors)
111 {
112 int i;
113
114 for (i = 0; i < ncolors; i++)
115 {
116 Uint8 colR, colG, colB;
117 if (!dm_fread_byte(fp, &colR) ||
118 !dm_fread_byte(fp, &colG) ||
119 !dm_fread_byte(fp, &colB))
120 return FALSE;
121
122 pal[i].r = colR;
123 pal[i].g = colG;
124 pal[i].b = colB;
125 }
126
127 return TRUE;
128 }
129
130
131 int dmWriteImageData(DMImage *img, void *cbdata, int (*writeRowCB)(void *, Uint8 *, size_t), const DMImageSpec *spec)
132 {
133 int x, y, yscale, xscale, res = 0, rowSize, rowWidth;
134 Uint8 *row = NULL;
135
136 // Allocate memory for row buffer
137 rowWidth = img->width * spec->scale;
138 rowSize = rowWidth * dmImageGetBytesPerPixel(spec->format);
139
140 if ((row = dmMalloc(rowSize + 16)) == NULL)
141 {
142 res = DMERR_MALLOC;
143 goto done;
144 }
145
146 // Generate the image
147 for (y = 0; y < img->height; y++)
148 {
149 Uint8 *ptr1 = row,
150 *ptr2 = ptr1 + rowWidth,
151 *ptr3 = ptr2 + rowWidth;
152
153 for (x = 0; x < img->width; x++)
154 {
155 Uint8 c = img->data[(y * img->pitch) + x], qr, qg, qb, qa;
156 switch (spec->format)
157 {
158 case DM_IFMT_PALETTE:
159 for (xscale = 0; xscale < spec->scale; xscale++)
160 *ptr1++ = c;
161 break;
162
163 case DM_IFMT_RGBA:
164 qr = img->pal[c].r;
165 qg = img->pal[c].g;
166 qb = img->pal[c].b;
167 qa = img->pal[c].a;
168
169 for (xscale = 0; xscale < spec->scale; xscale++)
170 {
171 *ptr1++ = qr;
172 *ptr1++ = qg;
173 *ptr1++ = qb;
174 *ptr1++ = qa;
175 }
176 break;
177
178 case DM_IFMT_RGB:
179 qr = img->pal[c].r;
180 qg = img->pal[c].g;
181 qb = img->pal[c].b;
182
183 for (xscale = 0; xscale < spec->scale; xscale++)
184 {
185 *ptr1++ = qr;
186 *ptr1++ = qg;
187 *ptr1++ = qb;
188 }
189 break;
190
191 case DM_IFMT_RGB_PLANE:
192 qr = img->pal[c].r;
193 qg = img->pal[c].g;
194 qb = img->pal[c].b;
195
196 for (xscale = 0; xscale < spec->scale; xscale++)
197 {
198 *ptr1++ = qr;
199 *ptr2++ = qg;
200 *ptr3++ = qb;
201 }
202 break;
203 }
204 }
205
206 for (yscale = 0; yscale < spec->scale; yscale++)
207 {
208 if ((res = writeRowCB(cbdata, row, rowSize)) != DMERR_OK)
209 goto done;
210 }
211 }
212
213 done:
214 dmFree(row);
215 return res;
216 }
217
218
219 #define DMCOL(x) (((x) >> 4) & 0xf)
220
221 int dmWriteIFFMasterRAWPalette(FILE *fp, DMImage *img, int ncolors,
222 const char *indent, const char *type)
223 {
224 int i;
225
226 for (i = 0; i < ncolors; i++)
227 {
228 int color;
229 if (i < img->ncolors)
230 {
231 color = (DMCOL(img->pal[i].r) << 8) |
232 (DMCOL(img->pal[i].g) << 4) |
233 (DMCOL(img->pal[i].b));
234 }
235 else
236 color = 0;
237
238 fprintf(fp, "%s%s $%04X\n",
239 indent != NULL ? indent : "\t",
240 type != NULL ? type : "dc.w",
241 color);
242 }
243
244 return DMERR_OK;
245 }
246
247
248 int dmWriteRAWImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
249 {
250 int xc, yc, plane, res;
251 DMBitStreamContext bs;
252
253 if ((res = dmInitBitStreamFILE(&bs, fp)) != DMERR_OK)
254 return res;
255
256 if (spec->interleave)
257 {
258 // Output bitplanes in interleaved format (each plane of line sequentially)
259 for (yc = 0; yc < img->height; yc++)
260 {
261 for (plane = 0; plane < spec->nplanes; plane++)
262 {
263 Uint8 *sp = img->data + yc * img->pitch;
264 for (xc = 0; xc < img->width; xc++)
265 {
266 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
267 return DMERR_FWRITE;
268 }
269 }
270 }
271 }
272 else
273 {
274 // Output each bitplane in sequence
275 for (plane = 0; plane < spec->nplanes; plane++)
276 {
277 for (yc = 0; yc < img->height; yc++)
278 {
279 Uint8 *sp = img->data + yc * img->pitch;
280 for (xc = 0; xc < img->width; xc++)
281 {
282 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
283 return DMERR_FWRITE;
284 }
285 }
286 }
287 }
288
289 return dmFlushBitStream(&bs);
290 }
291
292
293 int dmWriteRAWImage(const char *filename, DMImage *img, DMImageSpec *spec)
294 {
295 FILE *fp;
296 int res;
297
298 if ((fp = fopen(filename, "wb")) == NULL)
299 {
300 dmError("RAW: Could not open file '%s' for writing.\n", filename);
301 return DMERR_FOPEN;
302 }
303
304 res = dmWriteRAWImageFILE(fp, img, spec);
305
306 fclose(fp);
307 return res;
308 }
309
310
311 static int dmWritePPMRow(void *cbdata, Uint8 *row, size_t len)
312 {
313 if (fwrite(row, sizeof(Uint8), len, (FILE *) cbdata) == len)
314 return DMERR_OK;
315 else
316 return DMERR_FWRITE;
317 }
318
319
320 int dmWritePPMImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
321 {
322 // Write PPM header
323 fprintf(fp,
324 "P6\n%d %d\n255\n",
325 img->width * spec->scale,
326 img->height * spec->scale);
327
328 // Write image data
329 spec->format = DM_IFMT_RGB;
330 return dmWriteImageData(img, (void *) fp, dmWritePPMRow, spec);
331 }
332
333
334 int dmWritePPMImage(const char *filename, DMImage *img, DMImageSpec *spec)
335 {
336 FILE *fp;
337 int res;
338
339 // Create output file
340 if ((fp = fopen(filename, "wb")) == NULL)
341 {
342 dmError("PPM: could not open file '%s' for writing.\n", filename);
343 return DMERR_FOPEN;
344 }
345
346 res = dmWritePPMImageFILE(fp, img, spec);
347
348 fclose(fp);
349 return res;
350 }
351
352
353 #ifdef DM_USE_LIBPNG
354 static int dmWritePNGRow(void *cbdata, Uint8 *row, size_t len)
355 {
356 png_structp png_ptr = cbdata;
357 (void) len;
358
359 if (setjmp(png_jmpbuf(png_ptr)))
360 return DMERR_INTERNAL;
361
362 png_write_row(png_ptr, row);
363
364 return DMERR_OK;
365 }
366
367
368 int dmWritePNGImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
369 {
370 png_structp png_ptr = NULL;
371 png_infop info_ptr = NULL;
372 png_colorp palette = NULL;
373 int fmt, res = DMERR_OK;
374
375 // Create PNG structures
376 png_ptr = png_create_write_struct(
377 PNG_LIBPNG_VER_STRING,
378 NULL, NULL, NULL);
379
380 if (png_ptr == NULL)
381 {
382 dmError("PNG: png_create_write_struct() failed.\n");
383 res = DMERR_MALLOC;
384 goto error;
385 }
386
387 info_ptr = png_create_info_struct(png_ptr);
388 if (info_ptr == NULL)
389 {
390 dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr);
391 res = DMERR_INIT_FAIL;
392 goto error;
393 }
394
395 if (setjmp(png_jmpbuf(png_ptr)))
396 {
397 dmError("PNG: Error during image writing..\n");
398 res = DMERR_INIT_FAIL;
399 goto error;
400 }
401
402 png_init_io(png_ptr, fp);
403
404 // Write PNG header info
405 switch (spec->format)
406 {
407 case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break;
408 case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break;
409 case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break;
410 default:
411 dmError("PNG: Unsupported image format %d.\n", spec->format);
412 res = DMERR_NOT_SUPPORTED;
413 goto error;
414 }
415
416 png_set_IHDR(png_ptr, info_ptr,
417 img->width * spec->scale,
418 img->height * spec->scale,
419 8, /* bits per component */
420 fmt,
421 PNG_INTERLACE_NONE,
422 PNG_COMPRESSION_TYPE_DEFAULT,
423 PNG_FILTER_TYPE_DEFAULT);
424
425 dmMsg(3, "PNG: %d x %d, depth=%d, type=%d\n",
426 img->width * spec->scale,
427 img->height * spec->scale,
428 8, fmt);
429
430 // Palette
431 if (spec->format == DM_IFMT_PALETTE)
432 {
433 int i;
434
435 palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
436 if (palette == NULL)
437 {
438 dmError("PNG: Could not allocate palette structure.");
439 res = DMERR_MALLOC;
440 goto error;
441 }
442
443 memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
444
445 for (i = 0; i < img->ncolors; i++)
446 {
447 palette[i].red = img->pal[i].r;
448 palette[i].green = img->pal[i].g;
449 palette[i].blue = img->pal[i].b;
450 }
451
452 png_set_PLTE(png_ptr, info_ptr, palette, img->ncolors);
453 }
454
455 // png_set_gAMA(png_ptr, info_ptr, 2.2);
456
457 png_write_info(png_ptr, info_ptr);
458
459
460 // Write compressed image data
461 dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, spec);
462
463 // Write footer
464 png_write_end(png_ptr, NULL);
465
466 error:
467 png_free(png_ptr, palette);
468 palette = NULL;
469
470 if (png_ptr && info_ptr)
471 png_destroy_write_struct(&png_ptr, &info_ptr);
472
473 return res;
474 }
475
476
477 int dmWritePNGImage(const char *filename, DMImage *img, DMImageSpec *spec)
478 {
479 int res;
480 FILE *fp;
481
482 if ((fp = fopen(filename, "wb")) == NULL)
483 {
484 dmError("PNG: could not open file '%s' for writing.\n", filename);
485 return DMERR_FOPEN;
486 }
487
488 res = dmWritePNGImageFILE(fp, img, spec);
489
490 fclose(fp);
491 return res;
492 }
493
494
495 int dmReadPNGImageFILE(FILE *fp, DMImage **pimg)
496 {
497 png_structp png_ptr = NULL;
498 png_infop info_ptr = NULL;
499 png_colorp palette = NULL;
500 png_bytep *row_pointers = NULL;
501 png_bytep trans = NULL;
502 png_uint_32 width, height;
503 int i, bit_depth, color_type, ncolors, ntrans;
504 int res = DMERR_OK;
505 DMImage *img;
506
507 // Create PNG structures
508 png_ptr = png_create_read_struct(
509 PNG_LIBPNG_VER_STRING,
510 NULL, NULL, NULL);
511
512 if (png_ptr == NULL)
513 {
514 dmError("PNG: png_create_write_struct() failed.\n");
515 res = DMERR_MALLOC;
516 goto error;
517 }
518
519 info_ptr = png_create_info_struct(png_ptr);
520 if (info_ptr == NULL)
521 {
522 dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr);
523 res = DMERR_INIT_FAIL;
524 goto error;
525 }
526
527 if (setjmp(png_jmpbuf(png_ptr)))
528 {
529 dmError("PNG: Error during image reading..\n");
530 res = DMERR_INIT_FAIL;
531 goto error;
532 }
533
534 png_init_io(png_ptr, fp);
535
536 // Read image information
537 png_read_info(png_ptr, info_ptr);
538
539 png_get_IHDR(png_ptr, info_ptr, &width, &height,
540 &bit_depth, &color_type, NULL, NULL, NULL);
541
542 if (width < 1 || height < 1)
543 {
544 dmError("PNG: Invalid width or height (%d x %d)\n",
545 width, height);
546 res = DMERR_INVALID_DATA;
547 goto error;
548 }
549
550 switch (color_type)
551 {
552 case PNG_COLOR_TYPE_GRAY:
553 if (bit_depth < 8)
554 png_set_expand_gray_1_2_4_to_8(png_ptr);
555
556 if (bit_depth > 8)
557 {
558 dmError("PNG: Unsupported bit depth for grayscale image: %d\n",
559 bit_depth);
560 res = DMERR_NOT_SUPPORTED;
561 goto error;
562 }
563 break;
564
565 case PNG_COLOR_TYPE_PALETTE:
566 png_set_packing(png_ptr);
567 break;
568
569 default:
570 dmError("PNG: RGB/RGBA images not supported for loading.\n");
571 res = DMERR_NOT_SUPPORTED;
572 goto error;
573 }
574
575 // Allocate image
576 dmMsg(3, "PNG: %d x %d, depth=%d, type=%d\n",
577 width, height, bit_depth, color_type);
578
579 if ((*pimg = img = dmImageAlloc(width, height)) == NULL)
580 {
581 dmError("PNG: Could not allocate image data.\n");
582 res = DMERR_MALLOC;
583 goto error;
584 }
585
586 // ...
587 row_pointers = png_malloc(png_ptr, height * sizeof(png_bytep));
588 for (i = 0; i < img->height; i++)
589 row_pointers[i] = img->data + (i * img->pitch);
590
591 png_read_image(png_ptr, row_pointers);
592
593 png_read_end(png_ptr, NULL);
594
595 // Create palette
596 switch (color_type)
597 {
598 case PNG_COLOR_TYPE_GRAY:
599 ncolors = 256;
600 dmMsg(3, "PNG: Generating %d color grayscale palette.\n", ncolors);
601
602 if (!dmImageAllocPalette(img, ncolors, -1))
603 {
604 res = DMERR_MALLOC;
605 goto error;
606 }
607
608 for (i = 0; i < img->ncolors; i++)
609 {
610 img->pal[i].r = img->pal[i].g = img->pal[i].b = i;
611 }
612 break;
613
614 case PNG_COLOR_TYPE_PALETTE:
615 png_get_PLTE(png_ptr, info_ptr, &palette, &ncolors);
616 dmMsg(3, "PNG: Palette of %d colors found.\n", ncolors);
617 if (ncolors > 0 && palette != NULL)
618 {
619 if (!dmImageAllocPalette(img, ncolors, -1))
620 {
621 res = DMERR_MALLOC;
622 goto error;
623 }
624
625 for (i = 0; i < img->ncolors; i++)
626 {
627 img->pal[i].r = palette[i].red;
628 img->pal[i].g = palette[i].green;
629 img->pal[i].b = palette[i].blue;
630 }
631 }
632 break;
633 }
634
635 if (color_type == PNG_COLOR_TYPE_PALETTE ||
636 color_type == PNG_COLOR_TYPE_GRAY)
637 {
638 png_get_tRNS(png_ptr, info_ptr, &trans, &ntrans, NULL);
639 if (trans != NULL && ntrans > 0)
640 {
641 for (i = 0; i < img->ncolors && i < ntrans; i++)
642 {
643 img->pal[i].a = trans[i];
644 if (img->ctransp < 0 && trans[i] == 0)
645 img->ctransp = i;
646 }
647 }
648 }
649
650 error:
651 // png_free(png_ptr, palette);
652
653 if (png_ptr && info_ptr)
654 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
655
656 return res;
657 }
658
659
660 int dmReadPNGImage(const char *filename, DMImage **img)
661 {
662 int res;
663 FILE *fp;
664
665 if ((fp = fopen(filename, "rb")) == NULL)
666 {
667 dmError("PNG: Could not open file '%s' for reading.\n", filename);
668 return DMERR_FOPEN;
669 }
670
671 res = dmReadPNGImageFILE(fp, img);
672
673 fclose(fp);
674 return res;
675 }
676 #endif
677
678
679 typedef struct
680 {
681 Uint8 r,g,b;
682 } DMPCXColor;
683
684
685 typedef struct
686 {
687 Uint8 manufacturer,
688 version,
689 encoding,
690 bpp;
691 Uint16 xmin, ymin, xmax, ymax;
692 Uint16 hres, vres;
693 DMPCXColor colormap[16];
694 Uint8 reserved;
695 Uint8 nplanes;
696 Uint16 bpl;
697 Uint16 palinfo;
698 Uint8 filler[58];
699 } DMPCXHeader;
700
701
702 typedef struct
703 {
704 DMPCXHeader *header;
705 Uint8 *buf;
706 size_t bufLen, bufOffs;
707 int format;
708 FILE *fp;
709 } DMPCXData;
710
711
712 static inline Uint8 dmPCXGetByte(Uint8 *row, const size_t len, const size_t soffs)
713 {
714 return (soffs < len) ? row[soffs] : 0;
715 }
716
717 static BOOL dmPCXFlush(DMPCXData *pcx)
718 {
719 BOOL ret = TRUE;
720 if (pcx->bufOffs > 0)
721 ret = fwrite(pcx->buf, sizeof(Uint8), pcx->bufOffs, pcx->fp) == pcx->bufOffs;
722 pcx->bufOffs = 0;
723 return ret;
724 }
725
726 static inline BOOL dmPCXPutByte(DMPCXData *pcx, const Uint8 val)
727 {
728 if (pcx->bufOffs < pcx->bufLen)
729 {
730 pcx->buf[pcx->bufOffs++] = val;
731 return TRUE;
732 }
733 else
734 return dmPCXFlush(pcx);
735 }
736
737 static int dmWritePCXRow(void *cbdata, Uint8 *row, size_t len)
738 {
739 DMPCXData *pcx = (DMPCXData *) cbdata;
740 int plane;
741 size_t soffs = 0;
742
743 // fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl);
744
745 pcx->bufOffs = 0;
746
747 for (plane = 0; plane < pcx->header->nplanes; plane++)
748 {
749 Uint8 data = dmPCXGetByte(row, len, soffs++),
750 count = 1;
751
752 // size_t blen = pcx->header->bpl * pcx->header->nplanes;
753 size_t blen = pcx->header->bpl;
754 while (soffs < blen)
755 {
756 if (data == dmPCXGetByte(row, len, soffs) && count < 63)
757 {
758 count++;
759 soffs++;
760 }
761 else
762 {
763 if (count == 1 && (data & 0xC0) != 0xC0)
764 {
765 if (!dmPCXPutByte(pcx, data))
766 return DMERR_FWRITE;
767 }
768 else
769 {
770 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
771 !dmPCXPutByte(pcx, data))
772 return DMERR_FWRITE;
773 }
774
775 data = dmPCXGetByte(row, len, soffs++);
776 count = 1;
777 }
778 }
779
780 if (count > 1)
781 {
782 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
783 !dmPCXPutByte(pcx, data))
784 return DMERR_FWRITE;
785 }
786
787 if (!dmPCXFlush(pcx))
788 return DMERR_FWRITE;
789 }
790
791
792 return DMERR_OK;
793 }
794
795
796 int dmWritePCXImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
797 {
798 DMPCXData pcx;
799 DMPCXHeader hdr;
800 int res;
801
802 // Create output file
803 pcx.buf = NULL;
804 pcx.format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE;
805 pcx.header = &hdr;
806 pcx.fp = fp;
807
808 // Create PCX header
809 memset(&hdr, 0, sizeof(hdr));
810 if (spec->paletted)
811 {
812 int i;
813 for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++)
814 {
815 hdr.colormap[i].r = img->pal[i].r;
816 hdr.colormap[i].g = img->pal[i].g;
817 hdr.colormap[i].b = img->pal[i].b;
818 }
819 }
820 hdr.manufacturer = 10;
821 hdr.version = 5;
822 hdr.encoding = 1;
823 hdr.bpp = 8;
824 hdr.hres = img->width * spec->scale;
825 hdr.vres = img->height * spec->scale;
826 hdr.xmin = hdr.ymin = 0;
827 hdr.xmax = hdr.hres - 1;
828 hdr.ymax = hdr.vres - 1;
829 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format);
830 hdr.palinfo = 1;
831
832 res = (img->width * spec->scale);
833 hdr.bpl = res / 2;
834 if (res % 2) hdr.bpl++;
835 hdr.bpl *= 2;
836
837 dmMsg(3, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n",
838 spec->paletted, hdr.nplanes, hdr.bpp, hdr.bpl);
839
840 pcx.bufLen = hdr.bpl * 4;
841 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
842 {
843 dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n",
844 pcx.bufLen);
845 res = DMERR_MALLOC;
846 goto error;
847 }
848
849 // Write PCX header
850 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) ||
851 !dm_fwrite_byte(pcx.fp, hdr.version) ||
852 !dm_fwrite_byte(pcx.fp, hdr.encoding) ||
853 !dm_fwrite_byte(pcx.fp, hdr.bpp))
854 {
855 dmError("PCX: Could not write basic header data.\n");
856 res = DMERR_FWRITE;
857 goto error;
858 }
859
860 if (!dm_fwrite_le16(pcx.fp, hdr.xmin) ||
861 !dm_fwrite_le16(pcx.fp, hdr.ymin) ||
862 !dm_fwrite_le16(pcx.fp, hdr.xmax) ||
863 !dm_fwrite_le16(pcx.fp, hdr.ymax) ||
864 !dm_fwrite_le16(pcx.fp, hdr.hres) ||
865 !dm_fwrite_le16(pcx.fp, hdr.vres))
866 {
867 dmError("PCX: Could not write image dimensions.\n");
868 res = DMERR_FWRITE;
869 goto error;
870 }
871
872 if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
873 {
874 dmError("PCX: Could not write colormap.\n");
875 res = DMERR_FWRITE;
876 goto error;
877 }
878
879 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) ||
880 !dm_fwrite_byte(pcx.fp, hdr.nplanes) ||
881 !dm_fwrite_le16(pcx.fp, hdr.bpl) ||
882 !dm_fwrite_le16(pcx.fp, hdr.palinfo) ||
883 !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
884 {
885 dmError("PCX: Could not write header remainder.\n");
886 res = DMERR_FWRITE;
887 goto error;
888 }
889
890 // Write image data
891 res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, spec);
892
893 // Write VGA palette
894 if (spec->paletted)
895 {
896 int i;
897 dm_fwrite_byte(pcx.fp, 0x0C);
898 dmMsg(3, "PCX: Writing palette of %d active entries.\n", img->ncolors);
899
900 for (i = 0; i < img->ncolors; i++)
901 {
902 dm_fwrite_byte(pcx.fp, img->pal[i].r);
903 dm_fwrite_byte(pcx.fp, img->pal[i].g);
904 dm_fwrite_byte(pcx.fp, img->pal[i].b);
905 }
906
907 // Pad the palette, if necessary
908 for (; i < 256; i++)
909 {
910 dm_fwrite_byte(pcx.fp, 0);
911 dm_fwrite_byte(pcx.fp, 0);
912 dm_fwrite_byte(pcx.fp, 0);
913 }
914 }
915
916 error:
917 dmFree(pcx.buf);
918 return res;
919 }
920
921
922 int dmWritePCXImage(const char *filename, DMImage *img, DMImageSpec *spec)
923 {
924 FILE *fp;
925 int res;
926
927 if ((fp = fopen(filename, "wb")) == NULL)
928 {
929 dmError("PCX: Could not open file '%s' for writing.\n", filename);
930 return DMERR_FOPEN;
931 }
932
933 res = dmWritePCXImageFILE(fp, img, spec);
934
935 fclose(fp);
936 return res;
937 }
938
939
940 static BOOL dmPCXDecodeRLERow(FILE *fp, Uint8 *buf, const size_t bufLen)
941 {
942 size_t offs = 0;
943 do
944 {
945 int count;
946 Uint8 data;
947
948 if (!dm_fread_byte(fp, &data))
949 return FALSE;
950
951 if ((data & 0xC0) == 0xC0)
952 {
953 count = data & 0x3F;
954 if (!dm_fread_byte(fp, &data))
955 return FALSE;
956 }
957 else
958 count = 1;
959
960 while (count-- && offs < bufLen)
961 buf[offs++] = data;
962
963 } while (offs < bufLen);
964
965 return TRUE;
966 }
967
968
969 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg)
970 {
971 DMImage *img;
972 DMPCXData pcx;
973 DMPCXHeader hdr;
974 BOOL paletted;
975 int res = 0, yc, xc;
976 Uint8 *dp;
977
978 pcx.buf = NULL;
979
980 // Read PCX header
981 if (!dm_fread_byte(fp, &hdr.manufacturer) ||
982 !dm_fread_byte(fp, &hdr.version) ||
983 !dm_fread_byte(fp, &hdr.encoding) ||
984 !dm_fread_byte(fp, &hdr.bpp))
985 {
986 dmError("PCX: Could not read basic header data.\n");
987 res = DMERR_FREAD;
988 goto error;
989 }
990
991 if (hdr.manufacturer != 10 ||
992 hdr.version != 5 ||
993 hdr.encoding != 1 ||
994 hdr.bpp != 8)
995 {
996 dmError("PCX: Not a PCX file, or unsupported variant.\n");
997 res = DMERR_FREAD;
998 goto error;
999 }
1000
1001 if (!dm_fread_le16(fp, &hdr.xmin) ||
1002 !dm_fread_le16(fp, &hdr.ymin) ||
1003 !dm_fread_le16(fp, &hdr.xmax) ||
1004 !dm_fread_le16(fp, &hdr.ymax) ||
1005 !dm_fread_le16(fp, &hdr.hres) ||
1006 !dm_fread_le16(fp, &hdr.vres))
1007 {
1008 dmError("PCX: Could not read image dimensions.\n");
1009 res = DMERR_FREAD;
1010 goto error;
1011 }
1012
1013 if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
1014 {
1015 dmError("PCX: Could not read colormap.\n");
1016 res = DMERR_FREAD;
1017 goto error;
1018 }
1019
1020 if (!dm_fread_byte(fp, &hdr.reserved) ||
1021 !dm_fread_byte(fp, &hdr.nplanes) ||
1022 !dm_fread_le16(fp, &hdr.bpl) ||
1023 !dm_fread_le16(fp, &hdr.palinfo) ||
1024 !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
1025 {
1026 dmError("PCX: Could not read header remainder.\n");
1027 res = DMERR_FREAD;
1028 goto error;
1029 }
1030
1031 if (hdr.nplanes != 3 && hdr.nplanes != 1)
1032 {
1033 dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes);
1034 res = DMERR_FREAD;
1035 goto error;
1036 }
1037
1038 // Allocate image
1039 if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL)
1040 {
1041 dmError("PCX: Could not allocate image structure.\n");
1042 res = DMERR_MALLOC;
1043 goto error;
1044 }
1045
1046 paletted = hdr.nplanes == 1;
1047 pcx.bufLen = hdr.nplanes * hdr.bpl;
1048 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
1049 {
1050 dmError("PCX: Could not allocate RLE buffer.\n");
1051 res = DMERR_MALLOC;
1052 goto error;
1053 }
1054
1055 // Read image data
1056 dp = img->data;
1057 for (yc = 0; yc < img->height; yc++)
1058 {
1059 // Decode row of RLE'd data
1060 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen))
1061 {
1062 dmError("PCX: Error decoding RLE data.\n");
1063 res = DMERR_INVALID_DATA;
1064 goto error;
1065 }
1066
1067 // Decode bitplanes
1068 switch (hdr.nplanes)
1069 {
1070 case 1:
1071 memcpy(dp, pcx.buf, img->width);
1072 break;
1073
1074 case 3:
1075 {
1076 Uint8 *dptr = dp,
1077 *sptr1 = pcx.buf,
1078 *sptr2 = sptr1 + hdr.bpl,
1079 *sptr3 = sptr2 + hdr.bpl;
1080
1081 for (xc = 0; xc < img->width; xc++)
1082 {
1083 *dptr++ = *sptr1++;
1084 *dptr++ = *sptr2++;
1085 *dptr++ = *sptr3++;
1086 }
1087 }
1088 break;
1089 }
1090
1091 dp += img->pitch;
1092 }
1093
1094 // Read VGA palette
1095 if (paletted)
1096 {
1097 int i, ncolors;
1098 Uint8 tmpb;
1099 BOOL read;
1100
1101 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C)
1102 {
1103 read = FALSE;
1104 ncolors = 16;
1105 }
1106 else
1107 {
1108 read = TRUE;
1109 ncolors = 256;
1110 }
1111
1112 if (!dmImageAllocPalette(img, ncolors, -1))
1113 {
1114 dmError("PCX: Could not allocate palette data!\n");
1115 res = DMERR_MALLOC;
1116 goto error;
1117 }
1118
1119 if (read)
1120 {
1121 if (!dmReadPaletteData(fp, img->pal, ncolors))
1122 {
1123 dmError("PCX: Error reading palette.\n");
1124 return DMERR_FREAD;
1125 }
1126 }
1127 else
1128 {
1129 for (i = 0; i < img->ncolors; i++)
1130 {
1131 if (i < 16)
1132 {
1133 img->pal[i].r = hdr.colormap[i].r;
1134 img->pal[i].g = hdr.colormap[i].g;
1135 img->pal[i].b = hdr.colormap[i].b;
1136 }
1137 }
1138 }
1139 }
1140
1141 error:
1142 dmFree(pcx.buf);
1143 return res;
1144 }
1145
1146
1147 int dmReadPCXImage(const char *filename, DMImage **pimg)
1148 {
1149 FILE *fp;
1150 int res;
1151
1152 if ((fp = fopen(filename, "rb")) == NULL)
1153 {
1154 dmError("PCX: Could not open file '%s' for reading.\n", filename);
1155 return -15;
1156 }
1157
1158 res = dmReadPCXImageFILE(fp, pimg);
1159
1160 fclose(fp);
1161 return res;
1162 }
1163
1164
1165 #define IFF_ID_FORM 0x464F524D // "FORM"
1166 #define IFF_ID_ILBM 0x494C424D // "ILBM"
1167 #define IFF_ID_PBM 0x50424D20 // "PBM "
1168 #define IFF_ID_BMHD 0x424D4844 // "BMHD"
1169 #define IFF_ID_CMAP 0x434D4150 // "CMAP"
1170 #define IFF_ID_BODY 0x424F4459 // "BODY"
1171 #define IFF_ID_CAMG 0x43414D47 // "CAMG"
1172
1173 #define IFF_MASK_NONE 0
1174 #define IFF_MASK_HAS_MASK 1
1175 #define IFF_MASK_TRANSP 2
1176 #define IFF_MASK_LASSO 3
1177
1178 #define IFF_COMP_NONE 0
1179 #define IFF_COMP_BYTERUN1 1
1180
1181 #define IFF_CAMG_LACE 0x00000004
1182 #define IFF_CAMG_HALFBRITE 0x00000080
1183 #define IFF_CAMG_HAM 0x00000800
1184
1185 typedef struct
1186 {
1187 Uint32 id;
1188 Uint32 size;
1189 int count;
1190 char str[6];
1191 } DMIFFChunk;
1192
1193
1194 typedef struct
1195 {
1196 Uint16 w, h;
1197 Sint16 x, y;
1198 Uint8 nplanes;
1199 Uint8 masking;
1200 Uint8 compression;
1201 Uint8 pad1;
1202 Uint16 transp;
1203 Uint8 xasp, yasp;
1204 Sint16 pagew, pageh;
1205 } DMIFFBMHD;
1206
1207
1208 typedef struct
1209 {
1210 DMIFFChunk chBMHD, chCMAP, chBODY;
1211 DMIFFBMHD bmhd;
1212 Uint32 camg;
1213 int ncolors;
1214 DMColor *pal;
1215 BOOL paletted, planar;
1216 } DMIFF;
1217
1218
1219 static BOOL dmReadIFFChunk(FILE *fp, DMIFFChunk *chunk)
1220 {
1221 if (!dm_fread_be32(fp, &chunk->id) ||
1222 !dm_fread_be32(fp, &chunk->size))
1223 {
1224 dmError("ILBM: Could not read IFF chunk header.\n");
1225 return FALSE;
1226 }
1227 else
1228 return TRUE;
1229 }
1230
1231 static char * dmGetIFFChunkID(DMIFFChunk *chunk)
1232 {
1233 chunk->str[0] = (chunk->id >> 24) & 0xff;
1234 chunk->str[1] = (chunk->id >> 16) & 0xff;
1235 chunk->str[2] = (chunk->id >> 8) & 0xff;
1236 chunk->str[3] = (chunk->id) & 0xff;
1237 chunk->str[4] = 0;
1238 return chunk->str;
1239 }
1240
1241 static BOOL dmSkipIFFChunkRest(FILE *fp, const DMIFFChunk *chunk, const Uint32 used)
1242 {
1243 if (chunk->size > used)
1244 {
1245 dmMsg(4, "ILBM: Skipping %d bytes (%d of %d consumed)\n",
1246 chunk->size - used, used, chunk->size);
1247 return fseeko(fp, chunk->size - used, SEEK_CUR) == 0;
1248 }
1249 else
1250 return TRUE;
1251 }
1252
1253 static BOOL dmCheckIFFChunk(DMIFFChunk *dest, DMIFFChunk *chunk,
1254 const BOOL multi, const Uint32 minSize)
1255 {
1256 if (dest->count > 0 && !multi)
1257 {
1258 dmError("ILBM: Multiple instances of chunk %s found.\n",
1259 dmGetIFFChunkID(chunk));
1260 return FALSE;
1261 }
1262
1263 dest->count++;
1264
1265 if (chunk->size < minSize)
1266 return FALSE;
1267
1268 return TRUE;
1269 }
1270
1271
1272 static BOOL dmIFFDecodeByteRun1Row(FILE *fp, Uint8 *buf, const size_t bufLen)
1273 {
1274 size_t offs = 0;
1275 do
1276 {
1277 Sint8 dcount;
1278 Uint8 data;
1279
1280 if (!dm_fread_byte(fp, (Uint8 *) &dcount))
1281 return FALSE;
1282
1283 if (dcount == -128)
1284 {
1285 if (!dm_fread_byte(fp, &data))
1286 return FALSE;
1287 }
1288 else
1289 if (dcount < 0)
1290 {
1291 int count = (-dcount) + 1;
1292 if (!dm_fread_byte(fp, &data))
1293 return FALSE;
1294
1295 while (count-- && offs < bufLen)
1296 buf[offs++] = data;
1297 }
1298 else
1299 {
1300 int count = dcount + 1;
1301 while (count-- && offs < bufLen)
1302 {
1303 if (!dm_fread_byte(fp, &data))
1304 return FALSE;
1305
1306 buf[offs++] = data;
1307 }
1308 }
1309 } while (offs < bufLen);
1310
1311 return TRUE;
1312 }
1313
1314
1315 static BOOL dmIFFReadOneRow(FILE *fp, DMIFF *iff, Uint8 *buf, const size_t bufLen)
1316 {
1317 if (iff->bmhd.compression == IFF_COMP_BYTERUN1)
1318 return dmIFFDecodeByteRun1Row(fp, buf, bufLen);
1319 else
1320 return dm_fread_str(fp, buf, bufLen);
1321 }
1322
1323
1324 void dmDecodeBitPlane(Uint8 *dp, Uint8 *src, const int width, const int nplane)
1325 {
1326 int xc;
1327 for (xc = 0; xc < width; xc++)
1328 {
1329 const Uint8 data = (src[xc / 8] >> (7 - (xc & 7))) & 1;
1330 dp[xc] |= (data << nplane);
1331 }
1332 }
1333
1334
1335 int dmDecodeILBMBody(FILE *fp, DMIFF *iff, DMImage **pimg, Uint32 *read)
1336 {
1337 DMImage *img;
1338 Uint8 *buf;
1339 size_t bufLen;
1340 int yc, res = DMERR_OK;
1341
1342 *read = 0;
1343
1344 // Allocate image
1345 if ((*pimg = img = dmImageAlloc(iff->bmhd.w, iff->bmhd.h)) == NULL)
1346 return DMERR_MALLOC;
1347
1348 // Allocate planar decoding buffer
1349 bufLen = ((img->width + 15) / 16) * 2;
1350 if ((buf = dmMalloc(bufLen)) == NULL)
1351 return DMERR_MALLOC;
1352
1353 dmMsg(3, "ILBM: plane row size %d bytes.\n", bufLen);
1354
1355 // Decode the chunk
1356 for (yc = 0; yc < img->height; yc++)
1357 {
1358 int plane;
1359 const int nplanes = iff->bmhd.nplanes;
1360 Uint8 *dp = img->data + (yc * img->pitch);
1361
1362 memset(dp, 0, img->pitch);
1363
1364 for (plane = 0; plane < nplanes; plane++)
1365 {
1366 // Decompress or read data
1367 if (!dmIFFReadOneRow(fp, iff, buf, bufLen))
1368 {
1369 dmError("ILBM: Error in reading image plane #%d @ %d.\n", plane, yc);
1370 res = DMERR_FREAD;
1371 goto error;
1372 }
1373
1374 // Decode bitplane
1375 dmDecodeBitPlane(dp, buf, img->width, plane);
1376
1377 *read += bufLen;
1378 }
1379
1380 // Read mask data
1381 if (iff->bmhd.masking == IFF_MASK_HAS_MASK)
1382 {
1383 int xc;
1384
1385 // Decompress or read data
1386 if (!dmIFFReadOneRow(fp, iff, buf, bufLen))
1387 {
1388 dmError("ILBM: Error in reading mask plane.\n");
1389 res = DMERR_FREAD;
1390 goto error;
1391 }
1392
1393 // Decode mask
1394 for (xc = 0; xc < img->width; xc++)
1395 {
1396 const Uint8 data = (buf[xc / 8] >> (7 - (xc & 7))) & 1;
1397
1398 // Black out any pixels with mask bit 0
1399 if (!data)
1400 dp[xc] = 0;
1401 }
1402
1403 *read += bufLen;
1404 }
1405 }
1406
1407 error:
1408 dmFree(buf);
1409 return res;
1410 }
1411
1412
1413 int dmDecodePBMBody(FILE *fp, DMIFF *iff, DMImage **pimg, Uint32 *read)
1414 {
1415 DMImage *img;
1416 int yc, res = DMERR_OK;
1417
1418 *read = 0;
1419
1420 // Allocate image
1421 if ((*pimg = img = dmImageAlloc(iff->bmhd.w, iff->bmhd.h)) == NULL)
1422 return DMERR_MALLOC;
1423
1424 // Decode the chunk
1425 for (yc = 0; yc < img->height; yc++)
1426 {
1427 Uint8 *dp = img->data + (yc * img->pitch);
1428
1429 if (!dmIFFReadOneRow(fp, iff, dp, img->width))
1430 {
1431 dmError("ILBM: Error in reading image row #%d.\n", yc);
1432 res = DMERR_FREAD;
1433 goto error;
1434 }
1435
1436 *read += img->width;
1437 }
1438
1439 error:
1440 return res;
1441 }
1442
1443
1444 int dmReadILBMImageFILE(FILE *fp, DMImage **pimg)
1445 {
1446 Uint32 idILBM;
1447 DMIFFChunk chunk;
1448 DMIFF iff;
1449 Uint32 read;
1450 BOOL parsed = FALSE;
1451 int i, res = DMERR_OK;
1452
1453 memset(&iff, 0, sizeof(iff));
1454
1455 // Read IFF FORM header
1456 if (!dmReadIFFChunk(fp, &chunk) ||
1457 chunk.id != IFF_ID_FORM ||
1458 chunk.size < 32)
1459 {
1460 dmError("ILBM: Not a IFF file.\n");
1461 return DMERR_FREAD;
1462 }
1463
1464 // Check IFF ILBM signature
1465 if (!dm_fread_be32(fp, &idILBM) ||
1466 (idILBM != IFF_ID_ILBM && idILBM != IFF_ID_PBM))
1467 {
1468 dmError("ILBM: Not a ILBM file.\n");
1469 return DMERR_INVALID_DATA;
1470 }
1471
1472 iff.planar = (idILBM == IFF_ID_ILBM);
1473
1474 while (!parsed && !feof(fp))
1475 {
1476 if (!dmReadIFFChunk(fp, &chunk))
1477 {
1478 dmError("ILBM: Error reading IFF ILBM data.\n");
1479 return DMERR_FREAD;
1480 }
1481
1482 switch (chunk.id)
1483 {
1484 case IFF_ID_BMHD:
1485 // Check for multiple occurences of BMHD
1486 if (!dmCheckIFFChunk(&iff.chBMHD, &chunk, FALSE, sizeof(iff.bmhd)))
1487 return DMERR_FREAD;
1488
1489 // Read BMHD data
1490 if (!dm_fread_be16(fp, &iff.bmhd.w) ||
1491 !dm_fread_be16(fp, &iff.bmhd.h) ||
1492 !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.x) ||
1493 !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.y) ||
1494 !dm_fread_byte(fp, &iff.bmhd.nplanes) ||
1495 !dm_fread_byte(fp, &iff.bmhd.masking) ||
1496 !dm_fread_byte(fp, &iff.bmhd.compression) ||
1497 !dm_fread_byte(fp, &iff.bmhd.pad1) ||
1498 !dm_fread_be16(fp, &iff.bmhd.transp) ||
1499 !dm_fread_byte(fp, &iff.bmhd.xasp) ||
1500 !dm_fread_byte(fp, &iff.bmhd.yasp) ||
1501 !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.pagew) ||
1502 !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.pageh))
1503 {
1504 dmError("ILBM: Error reading BMHD chunk.\n");
1505 return DMERR_FREAD;
1506 }
1507 dmMsg(3, "ILBM: BMHD %d x %d @ %d, %d : nplanes=%d, comp=%d, mask=%d\n",
1508 iff.bmhd.w, iff.bmhd.h, iff.bmhd.x, iff.bmhd.y,
1509 iff.bmhd.nplanes, iff.bmhd.compression, iff.bmhd.masking);
1510
1511 // Sanity check
1512 if (iff.bmhd.nplanes < 1 || iff.bmhd.nplanes > 8 ||
1513 (iff.bmhd.compression != IFF_COMP_NONE &&
1514 iff.bmhd.compression != IFF_COMP_BYTERUN1) ||
1515 (iff.bmhd.masking != IFF_MASK_NONE &&
1516 iff.bmhd.masking != IFF_MASK_HAS_MASK &&
1517 iff.bmhd.masking != IFF_MASK_TRANSP))
1518 {
1519 dmError("ILBM: Unsupported features, refusing to load.\n");
1520 return DMERR_NOT_SUPPORTED;
1521 }
1522
1523 if (!dmSkipIFFChunkRest(fp, &chunk, sizeof(iff.bmhd)))
1524 return DMERR_FREAD;
1525 break;
1526
1527
1528 case IFF_ID_CMAP:
1529 // Check for multiple occurences of CMAP
1530 if (!dmCheckIFFChunk(&iff.chCMAP, &chunk, FALSE, 3))
1531 return DMERR_FREAD;
1532
1533 // Check for sanity
1534 if (chunk.size % 3 != 0)
1535 dmError("ILBM: CMAP chunk size not divisible by 3, possibly broken file.\n");
1536
1537 iff.ncolors = chunk.size / 3;
1538 dmMsg(3, "ILBM: CMAP %d entries (%d bytes)\n",
1539 iff.ncolors, chunk.size, 1 << iff.bmhd.nplanes);
1540
1541 if (iff.bmhd.nplanes > 0 && iff.ncolors != 1 << iff.bmhd.nplanes)
1542 dmMsg(3, "ILBM: Expected %d entries in CMAP.\n", 1 << iff.bmhd.nplanes);
1543
1544 // Read palette
1545 if (iff.ncolors > 0)
1546 {
1547 if (!dmPaletteAlloc(&iff.pal, iff.ncolors,
1548 (iff.bmhd.masking == IFF_MASK_TRANSP) ? iff.bmhd.transp : -1))
1549 {
1550 dmError("ILBM: Could not allocate palette data.\n");
1551 return DMERR_MALLOC;
1552 }
1553 if (!dmReadPaletteData(fp, iff.pal, iff.ncolors))
1554 {
1555 dmError("ILBM: Error reading CMAP.\n");
1556 return DMERR_FREAD;
1557 }
1558 }
1559
1560 if (iff.chBMHD.count && iff.chBODY.count)
1561 parsed = TRUE;
1562 break;
1563
1564 case IFF_ID_BODY:
1565 // Check for multiple occurences of CMAP
1566 if (!dmCheckIFFChunk(&iff.chBODY, &chunk, FALSE, 1))
1567 return DMERR_FREAD;
1568
1569 // Check for sanity
1570 if (!iff.chBMHD.count)
1571 {
1572 dmError("ILBM: BODY chunk before BMHD?\n");
1573 return DMERR_INVALID_DATA;
1574 }
1575
1576 dmMsg(3, "ILBM: BODY chunk size %d bytes\n", chunk.size);
1577
1578 // Decode the body
1579 if (iff.planar)
1580 {
1581 if ((res = dmDecodeILBMBody(fp, &iff, pimg, &read)) != DMERR_OK)
1582 return res;
1583 }
1584 else
1585 {
1586 if ((res = dmDecodePBMBody(fp, &iff, pimg, &read)) != DMERR_OK)
1587 return res;
1588 }
1589
1590 if (!dmSkipIFFChunkRest(fp, &chunk, read))
1591 return DMERR_FREAD;
1592
1593 if (iff.chCMAP.count)
1594 parsed = TRUE;
1595 break;
1596
1597
1598 case IFF_ID_CAMG:
1599 if (!dm_fread_be32(fp, &iff.camg))
1600 {
1601 dmError("ILBM: Error reading CAMG chunk.\n");
1602 return DMERR_FREAD;
1603 }
1604
1605 dmMsg(3, "ILBM: CAMG value 0x%08x\n", iff.camg);
1606
1607 if ((iff.camg & IFF_CAMG_HAM))
1608 {
1609 dmError("ILBM: HAM files are not supported.\n");
1610 return DMERR_NOT_SUPPORTED;
1611 }
1612
1613 if (!dmSkipIFFChunkRest(fp, &chunk, 4))
1614 return DMERR_FREAD;
1615 break;
1616
1617
1618 default:
1619 {
1620 dmMsg(4, "Unknown chunk ID '%s', size %d\n",
1621 dmGetIFFChunkID(&chunk), chunk.size);
1622
1623 if (fseeko(fp, chunk.size, SEEK_CUR) != 0)
1624 {
1625 dmError("ILBM: Error skipping in file.");
1626 return DMERR_FREAD;
1627 }
1628 }
1629 break;
1630 }
1631
1632 if (chunk.size & 1)
1633 fgetc(fp);
1634 }
1635
1636 // Set colormap after finishing
1637 if (iff.pal != NULL && iff.ncolors > 0 && *pimg != NULL)
1638 {
1639 // If halfbrite is used, duplicate the palette
1640 if (iff.camg & IFF_CAMG_HALFBRITE)
1641 {
1642 if (iff.ncolors > 128)
1643 {
1644 dmError("ILBM: Halfbrite enabled, but ncolors > 128.\n");
1645 return DMERR_NOT_SUPPORTED;
1646 }
1647
1648 if ((iff.pal = dmRealloc(iff.pal, sizeof(DMColor) * iff.ncolors * 2)) == NULL)
1649 return DMERR_MALLOC;
1650
1651 for (i = 0; i < iff.ncolors; i++)
1652 {
1653 int i2 = iff.ncolors + i;
1654 iff.pal[i2].r = iff.pal[i].r / 2;
1655 iff.pal[i2].g = iff.pal[i].g / 2;
1656 iff.pal[i2].b = iff.pal[i].b / 2;
1657 }
1658 }
1659
1660 (*pimg)->ncolors = iff.ncolors;
1661 (*pimg)->pal = iff.pal;
1662 }
1663
1664 return res;
1665 }
1666
1667
1668 int dmReadILBMImage(const char *filename, DMImage **pimg)
1669 {
1670 FILE *fp;
1671 int res;
1672
1673 if ((fp = fopen(filename, "rb")) == NULL)
1674 {
1675 dmError("ILBM: Could not open file '%s' for reading.\n", filename);
1676 return DMERR_FOPEN;
1677 }
1678
1679 res = dmReadILBMImageFILE(fp, pimg);
1680
1681 fclose(fp);
1682 return res;
1683 }
1684
1685
1686
1687
1688 static int fmtProbePNG(const Uint8 *buf, const size_t len)
1689 {
1690 if (len > 64 && buf[0] == 0x89 &&
1691 buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' &&
1692 buf[4] == 0x0d && buf[5] == 0x0a)
1693 {
1694 if (buf[12] == 'I' && buf[13] == 'H' &&
1695 buf[14] == 'D' && buf[15] == 'R')
1696 return DM_PROBE_SCORE_MAX;
1697 else
1698 return DM_PROBE_SCORE_GOOD;
1699 }
1700
1701 return DM_PROBE_SCORE_FALSE;
1702 }
1703
1704
1705 static int fmtProbePCX(const Uint8 *buf, const size_t len)
1706 {
1707 if (len > 128 + 32 &&
1708 buf[0] == 10 &&
1709 (buf[1] == 5 || buf[1] == 2 || buf[1] == 3) &&
1710 buf[2] == 1 &&
1711 (buf[3] == 8 || buf[3] == 4 || buf[3] == 3 || buf[3] == 1) &&
1712 buf[65] >= 1 && buf[65] <= 4)
1713 return DM_PROBE_SCORE_GOOD;
1714
1715 return DM_PROBE_SCORE_FALSE;
1716 }
1717
1718
1719 static int fmtProbeILBM(const Uint8 *buf, const size_t len)
1720 {
1721 if (len > 32 &&
1722 buf[ 0] == 'F' && buf[ 1] == 'O' &&
1723 buf[ 2] == 'R' && buf[ 3] == 'M' && (
1724 (buf[ 8] == 'I' && buf[ 9] == 'L' && buf[10] == 'B' && buf[11] == 'M') ||
1725 (buf[ 8] == 'P' && buf[ 9] == 'B' && buf[10] == 'M' && buf[11] == 0x20)
1726 ))
1727 {
1728 if (buf[12] == 'B' && buf[13] == 'M' &&
1729 buf[14] == 'H' && buf[15] == 'D')
1730 return DM_PROBE_SCORE_MAX;
1731 else
1732 return DM_PROBE_SCORE_GOOD;
1733 }
1734
1735 return DM_PROBE_SCORE_FALSE;
1736 }
1737
1738
1739 DMImageFormat dmImageFormatList[IMGFMT_LAST] =
1740 {
1741 {
1742 "PNG", "Portable Network Graphics",
1743 fmtProbePNG,
1744 #ifdef DM_USE_LIBPNG
1745 dmReadPNGImage, dmReadPNGImageFILE,
1746 dmWritePNGImage, dmWritePNGImageFILE,
1747 #else
1748 NULL, NULL,
1749 NULL, NULL,
1750 #endif
1751 },
1752 {
1753 "PPM", "Portable PixMap",
1754 NULL,
1755 NULL, NULL,
1756 dmWritePPMImage, dmWritePPMImageFILE,
1757 },
1758 {
1759 "PCX", "Z-Soft Paintbrush",
1760 fmtProbePCX,
1761 dmReadPCXImage, dmReadPCXImageFILE,
1762 dmWritePCXImage, dmWritePCXImageFILE,
1763 },
1764 {
1765 "ILBM", "IFF ILBM",
1766 fmtProbeILBM,
1767 dmReadILBMImage, dmReadILBMImageFILE,
1768 NULL, NULL,
1769 },
1770 {
1771 "RAW", "Plain bitplaned (interleaved or non-interleaved) RAW",
1772 NULL,
1773 NULL, NULL,
1774 dmWriteRAWImage, dmWriteRAWImageFILE,
1775 },
1776 {
1777 "ARAW", "IFFMaster Amiga RAW",
1778 NULL,
1779 NULL, NULL,
1780 dmWriteRAWImage, dmWriteRAWImageFILE,
1781 }
1782 };
1783
1784
1785 int dmImageProbeGeneric(const Uint8 *buf, const size_t len, DMImageFormat **pfmt, int *index)
1786 {
1787 int i, scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1;
1788
1789 for (i = 0; i < IMGFMT_LAST; i++)
1790 {
1791 DMImageFormat *fmt = &dmImageFormatList[i];
1792 if (fmt->probe != NULL)
1793 {
1794 int score = fmt->probe(buf, len);
1795 if (score > scoreMax)
1796 {
1797 scoreMax = score;
1798 scoreIndex = i;
1799 }
1800 }
1801 }
1802
1803 if (scoreIndex >= 0)
1804 {
1805 *pfmt = &dmImageFormatList[scoreIndex];
1806 *index = scoreIndex;
1807 return scoreMax;
1808 }
1809 else
1810 return DM_PROBE_SCORE_FALSE;
1811 }