comparison libgfx.c @ 435:e4a3f183e463

Modularize some more.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 16:08:30 +0200
parents
children 86f956e4580f
comparison
equal deleted inserted replaced
434:380c226c75af 435:e4a3f183e463
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 DMImage * dmImageAlloc(int width, int height)
19 {
20 DMImage *img = dmCalloc(1, sizeof(DMImage));
21 if (img == NULL)
22 return NULL;
23
24 img->width = img->pitch = width;
25 img->height = height;
26 img->data = dmMalloc(width * height * sizeof(Uint8));
27 if (img->data == NULL)
28 {
29 dmFree(img);
30 return NULL;
31 }
32
33 return img;
34 }
35
36
37 void dmImageFree(DMImage *img)
38 {
39 if (img != NULL)
40 {
41 if (!img->constpal)
42 {
43 dmFree(img->pal);
44 }
45 dmFree(img->data);
46 dmFree(img);
47 }
48 }
49
50
51 int dmImageGetBytesPerPixel(int format)
52 {
53 switch (format)
54 {
55 case DM_IFMT_PALETTE : return 1;
56
57 case DM_IFMT_RGB_PLANE :
58 case DM_IFMT_RGB : return 3;
59
60 case DM_IFMT_RGBA : return 4;
61
62 default: return 0;
63 }
64 }
65
66
67 int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, Uint8 *, size_t), const DMImageSpec *spec)
68 {
69 int x, y, yscale, xscale, res = 0, rowSize, rowWidth;
70 Uint8 *row = NULL;
71
72 // Allocate memory for row buffer
73 rowWidth = img->width * spec->scale;
74 rowSize = rowWidth * dmImageGetBytesPerPixel(spec->format);
75
76 if ((row = dmMalloc(rowSize + 16)) == NULL)
77 {
78 res = DMERR_MALLOC;
79 goto done;
80 }
81
82 // Generate the image
83 for (y = 0; y < img->height; y++)
84 {
85 Uint8 *ptr = row,
86 *ptr1 = row,
87 *ptr2 = ptr1 + rowWidth,
88 *ptr3 = ptr2 + rowWidth;
89
90 for (x = 0; x < img->width; x++)
91 {
92 Uint8 c = img->data[(y * img->pitch) + x], qr, qg, qb, qa;
93 switch (spec->format)
94 {
95 case DM_IFMT_PALETTE:
96 for (xscale = 0; xscale < spec->scale; xscale++)
97 *ptr++ = c;
98 break;
99
100 case DM_IFMT_RGBA:
101 qr = img->pal[c].r;
102 qg = img->pal[c].g;
103 qb = img->pal[c].b;
104 qa = (c == img->ctrans) ? 0 : 255;
105
106 for (xscale = 0; xscale < spec->scale; xscale++)
107 {
108 *ptr++ = qr;
109 *ptr++ = qg;
110 *ptr++ = qb;
111 *ptr++ = qa;
112 }
113 break;
114
115 case DM_IFMT_RGB:
116 qr = img->pal[c].r;
117 qg = img->pal[c].g;
118 qb = img->pal[c].b;
119
120 for (xscale = 0; xscale < spec->scale; xscale++)
121 {
122 *ptr++ = qr;
123 *ptr++ = qg;
124 *ptr++ = qb;
125 }
126 break;
127
128 case DM_IFMT_RGB_PLANE:
129 qr = img->pal[c].r;
130 qg = img->pal[c].g;
131 qb = img->pal[c].b;
132
133 for (xscale = 0; xscale < spec->scale; xscale++)
134 {
135 *ptr1++ = qr;
136 *ptr2++ = qg;
137 *ptr3++ = qb;
138 }
139 break;
140 }
141 }
142
143 for (yscale = 0; yscale < spec->scale; yscale++)
144 {
145 if (!writeRowCB(cbdata, row, rowSize))
146 {
147 res = DMERR_FWRITE;
148 goto done;
149 }
150 }
151 }
152
153 done:
154 dmFree(row);
155 return res;
156 }
157
158
159 #define DMCOL(x) (((x) >> 4) & 0xf)
160
161 int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors)
162 {
163 FILE *fp;
164 int i;
165
166 if ((fp = fopen(filename, "w")) == NULL)
167 {
168 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename);
169 return -15;
170 }
171
172 for (i = 0; i < ncolors; i++)
173 {
174 int color;
175 if (i < img->ncolors)
176 {
177 color = (DMCOL(img->pal[i].r) << 8) |
178 (DMCOL(img->pal[i].g) << 4) |
179 (DMCOL(img->pal[i].b));
180 }
181 else
182 color = 0;
183
184 fprintf(fp, "\tdc.w $%04X\n", color);
185 }
186
187 return 0;
188 }
189
190
191 int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
192 {
193 int xc, yc, plane, res;
194 DMBitStream bs;
195
196 if ((res = dmInitBitStream(&bs, fp)) != DMERR_OK)
197 return res;
198
199 if (spec->interleave)
200 {
201 // Output bitplanes in interleaved format (each plane of line sequentially)
202 for (yc = 0; yc < img->height; yc++)
203 {
204 for (plane = 0; plane < spec->nplanes; plane++)
205 {
206 Uint8 *sp = img->data + yc * img->pitch;
207 for (xc = 0; xc < img->width; xc++)
208 {
209 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
210 return DMERR_FWRITE;
211 }
212 }
213 }
214 }
215 else
216 {
217 // Output each bitplane in sequence
218 for (plane = 0; plane < spec->nplanes; plane++)
219 {
220 for (yc = 0; yc < img->height; yc++)
221 {
222 Uint8 *sp = img->data + yc * img->pitch;
223 for (xc = 0; xc < img->width; xc++)
224 {
225 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
226 return DMERR_FWRITE;
227 }
228 }
229 }
230 }
231
232 return dmFlushBitStream(&bs);
233 }
234
235 int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, DMImageSpec *spec)
236 {
237 FILE *fp;
238 int res;
239
240 if ((fp = fopen(filename, "wb")) == NULL)
241 {
242 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename);
243 return DMERR_FOPEN;
244 }
245
246 res = dmWriteIFFMasterRAWImageFILE(fp, img, spec);
247
248 fclose(fp);
249 return res;
250 }
251
252
253 static BOOL dmWritePPMRow(void *cbdata, Uint8 *row, size_t len)
254 {
255 return fwrite(row, sizeof(Uint8), len, (FILE *) cbdata) == len;
256 }
257
258
259 int dmWritePPMImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
260 {
261 // Write PPM header
262 fprintf(fp,
263 "P6\n%d %d\n255\n",
264 img->width * spec->scale,
265 img->height * spec->scale);
266
267 // Write image data
268 spec->format = DM_IFMT_RGB;
269 return dmWriteImageData(img, (void *) fp, dmWritePPMRow, spec);
270 }
271
272
273 int dmWritePPMImage(const char *filename, DMImage *img, DMImageSpec *spec)
274 {
275 FILE *fp;
276 int res;
277
278 // Create output file
279 if ((fp = fopen(filename, "wb")) == NULL)
280 {
281 dmError("PPM: could not open file '%s' for writing.\n", filename);
282 return DMERR_FOPEN;
283 }
284
285 res = dmWritePPMImageFILE(fp, img, spec);
286
287 fclose(fp);
288 return res;
289 }
290
291
292 #ifdef DM_USE_LIBPNG
293 static BOOL dmWritePNGRow(void *cbdata, Uint8 *row, size_t len)
294 {
295 png_structp png_ptr = cbdata;
296 (void) len;
297
298 if (setjmp(png_jmpbuf(png_ptr)))
299 return FALSE;
300
301 png_write_row(png_ptr, row);
302
303 return TRUE;
304 }
305
306
307 int dmWritePNGImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
308 {
309 png_structp png_ptr = NULL;
310 png_infop info_ptr = NULL;
311 png_colorp palette = NULL;
312 int fmt, res = DMERR_OK;
313
314 // Create PNG structures
315 png_ptr = png_create_write_struct(
316 PNG_LIBPNG_VER_STRING,
317 NULL, NULL, NULL);
318
319 if (png_ptr == NULL)
320 {
321 dmError("PNG: png_create_write_struct() failed.\n");
322 res = DMERR_MALLOC;
323 goto error;
324 }
325
326 info_ptr = png_create_info_struct(png_ptr);
327 if (info_ptr == NULL)
328 {
329 dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr);
330 res = DMERR_INIT_FAIL;
331 goto error;
332 }
333
334 if (setjmp(png_jmpbuf(png_ptr)))
335 {
336 dmError("PNG: Error during image writing..\n");
337 res = DMERR_INIT_FAIL;
338 goto error;
339 }
340
341 png_init_io(png_ptr, fp);
342
343 // Write PNG header info
344 switch (spec->format)
345 {
346 case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break;
347 case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break;
348 case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break;
349 default:
350 dmError("PNG: Internal error, unsupported image format %d.\n", spec->format);
351 goto error;
352 }
353
354 png_set_IHDR(png_ptr, info_ptr,
355 img->width * spec->scale,
356 img->height * spec->scale,
357 8, /* bits per component */
358 fmt,
359 PNG_INTERLACE_NONE,
360 PNG_COMPRESSION_TYPE_DEFAULT,
361 PNG_FILTER_TYPE_DEFAULT);
362
363 // Palette
364 if (spec->format == DM_IFMT_PALETTE)
365 {
366 int i;
367
368 palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
369 if (palette == NULL)
370 {
371 dmError("PNG: Could not allocate palette structure.");
372 goto error;
373 }
374
375 memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
376
377 for (i = 0; i < img->ncolors; i++)
378 {
379 palette[i].red = img->pal[i].r;
380 palette[i].green = img->pal[i].g;
381 palette[i].blue = img->pal[i].b;
382 }
383
384 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
385 }
386
387 // png_set_gAMA(png_ptr, info_ptr, 2.2);
388
389 png_write_info(png_ptr, info_ptr);
390
391
392 // Write compressed image data
393 dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, spec);
394
395 // Write footer
396 png_write_end(png_ptr, NULL);
397
398 error:
399 png_free(png_ptr, palette);
400 palette = NULL;
401
402 if (png_ptr && info_ptr)
403 png_destroy_write_struct(&png_ptr, &info_ptr);
404
405 return res;
406 }
407
408
409 int dmWritePNGImage(const char *filename, DMImage *img, DMImageSpec *spec)
410 {
411 int res;
412 FILE *fp;
413
414 if ((fp = fopen(filename, "wb")) == NULL)
415 {
416 dmError("PNG: could not open file '%s' for writing.\n", filename);
417 return DMERR_FOPEN;
418 }
419
420 res = dmWritePNGImageFILE(fp, img, spec);
421
422 fclose(fp);
423 return res;
424 }
425 #endif
426
427
428 typedef struct
429 {
430 Uint8 r,g,b;
431 } DMPCXColor;
432
433
434 typedef struct
435 {
436 Uint8 manufacturer,
437 version,
438 encoding,
439 bpp;
440 Uint16 xmin, ymin, xmax, ymax;
441 Uint16 hres, vres;
442 DMPCXColor colormap[16];
443 Uint8 reserved;
444 Uint8 nplanes;
445 Uint16 bpl;
446 Uint16 palinfo;
447 Uint8 filler[58];
448 } DMPCXHeader;
449
450
451 typedef struct
452 {
453 DMPCXHeader *header;
454 Uint8 *buf;
455 size_t bufLen, bufOffs;
456 int format;
457 FILE *fp;
458 } DMPCXData;
459
460
461 static inline Uint8 dmPCXGetByte(Uint8 *row, const size_t len, const size_t soffs)
462 {
463 return (soffs < len) ? row[soffs] : 0;
464 }
465
466 static BOOL dmPCXFlush(DMPCXData *pcx)
467 {
468 BOOL ret = TRUE;
469 if (pcx->bufOffs > 0)
470 ret = fwrite(pcx->buf, sizeof(Uint8), pcx->bufOffs, pcx->fp) == pcx->bufOffs;
471 pcx->bufOffs = 0;
472 return ret;
473 }
474
475 static inline BOOL dmPCXPutByte(DMPCXData *pcx, const Uint8 val)
476 {
477 if (pcx->bufOffs < pcx->bufLen)
478 {
479 pcx->buf[pcx->bufOffs++] = val;
480 return TRUE;
481 }
482 else
483 return dmPCXFlush(pcx);
484 }
485
486 static BOOL dmWritePCXRow(void *cbdata, Uint8 *row, size_t len)
487 {
488 DMPCXData *pcx = (DMPCXData *) cbdata;
489 int plane;
490 size_t soffs = 0;
491
492 // fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl);
493
494 pcx->bufOffs = 0;
495
496 for (plane = 0; plane < pcx->header->nplanes; plane++)
497 {
498 Uint8 data = dmPCXGetByte(row, len, soffs++),
499 count = 1;
500
501 // size_t blen = pcx->header->bpl * pcx->header->nplanes;
502 size_t blen = pcx->header->bpl;
503 while (soffs < blen)
504 {
505 if (data == dmPCXGetByte(row, len, soffs) && count < 63)
506 {
507 count++;
508 soffs++;
509 }
510 else
511 {
512 if (count == 1 && (data & 0xC0) != 0xC0)
513 {
514 if (!dmPCXPutByte(pcx, data))
515 return FALSE;
516 }
517 else
518 {
519 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
520 !dmPCXPutByte(pcx, data))
521 return FALSE;
522 }
523
524 data = dmPCXGetByte(row, len, soffs++);
525 count = 1;
526 }
527 }
528
529 if (count > 1)
530 {
531 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
532 !dmPCXPutByte(pcx, data))
533 return FALSE;
534 }
535
536 if (!dmPCXFlush(pcx))
537 return FALSE;
538 }
539
540
541 return TRUE;
542 }
543
544
545 int dmWritePCXImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec)
546 {
547 DMPCXData pcx;
548 DMPCXHeader hdr;
549 int res;
550
551 // Create output file
552 pcx.buf = NULL;
553 pcx.format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE;
554 pcx.header = &hdr;
555 pcx.fp = fp;
556
557 // Create PCX header
558 memset(&hdr, 0, sizeof(hdr));
559 if (spec->paletted)
560 {
561 int i;
562 for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++)
563 {
564 hdr.colormap[i].r = img->pal[i].r;
565 hdr.colormap[i].g = img->pal[i].g;
566 hdr.colormap[i].b = img->pal[i].b;
567 }
568 }
569 hdr.manufacturer = 10;
570 hdr.version = 5;
571 hdr.encoding = 1;
572 hdr.bpp = 8;
573 hdr.hres = img->width * spec->scale;
574 hdr.vres = img->height * spec->scale;
575 hdr.xmin = hdr.ymin = 0;
576 hdr.xmax = hdr.hres - 1;
577 hdr.ymax = hdr.vres - 1;
578 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format);
579 hdr.palinfo = 1;
580
581 res = (img->width * spec->scale);
582 hdr.bpl = res / 2;
583 if (res % 2) hdr.bpl++;
584 hdr.bpl *= 2;
585
586 dmMsg(2, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n",
587 spec->paletted, hdr.nplanes, hdr.bpp, hdr.bpl);
588
589 pcx.bufLen = hdr.bpl * 4;
590 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
591 {
592 dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n",
593 pcx.bufLen);
594 res = DMERR_MALLOC;
595 goto error;
596 }
597
598 // Write PCX header
599 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) ||
600 !dm_fwrite_byte(pcx.fp, hdr.version) ||
601 !dm_fwrite_byte(pcx.fp, hdr.encoding) ||
602 !dm_fwrite_byte(pcx.fp, hdr.bpp))
603 {
604 dmError("PCX: Could not write basic header data.\n");
605 res = DMERR_FWRITE;
606 goto error;
607 }
608
609 if (!dm_fwrite_le16(pcx.fp, hdr.xmin) ||
610 !dm_fwrite_le16(pcx.fp, hdr.ymin) ||
611 !dm_fwrite_le16(pcx.fp, hdr.xmax) ||
612 !dm_fwrite_le16(pcx.fp, hdr.ymax) ||
613 !dm_fwrite_le16(pcx.fp, hdr.hres) ||
614 !dm_fwrite_le16(pcx.fp, hdr.vres))
615 {
616 dmError("PCX: Could not write image dimensions.\n");
617 res = DMERR_FWRITE;
618 goto error;
619 }
620
621 if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
622 {
623 dmError("PCX: Could not write colormap.\n");
624 res = DMERR_FWRITE;
625 goto error;
626 }
627
628 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) ||
629 !dm_fwrite_byte(pcx.fp, hdr.nplanes) ||
630 !dm_fwrite_le16(pcx.fp, hdr.bpl) ||
631 !dm_fwrite_le16(pcx.fp, hdr.palinfo) ||
632 !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
633 {
634 dmError("PCX: Could not write header remainder.\n");
635 res = DMERR_FWRITE;
636 goto error;
637 }
638
639 // Write image data
640 res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, spec);
641
642 // Write VGA palette
643 if (spec->paletted)
644 {
645 int i;
646 dm_fwrite_byte(pcx.fp, 0x0C);
647 dmMsg(2, "PCX: Writing palette of %d active entries.\n", img->ncolors);
648
649 for (i = 0; i < img->ncolors; i++)
650 {
651 dm_fwrite_byte(pcx.fp, img->pal[i].r);
652 dm_fwrite_byte(pcx.fp, img->pal[i].g);
653 dm_fwrite_byte(pcx.fp, img->pal[i].b);
654 }
655
656 // Pad the palette, if necessary
657 for (; i < 256; i++)
658 {
659 dm_fwrite_byte(pcx.fp, 0);
660 dm_fwrite_byte(pcx.fp, 0);
661 dm_fwrite_byte(pcx.fp, 0);
662 }
663 }
664
665 error:
666 dmFree(pcx.buf);
667 return res;
668 }
669
670
671 int dmWritePCXImage(const char *filename, DMImage *img, DMImageSpec *spec)
672 {
673 FILE *fp;
674 int res;
675
676 if ((fp = fopen(filename, "wb")) == NULL)
677 {
678 dmError("PCX: Could not open file '%s' for writing.\n", filename);
679 return DMERR_FOPEN;
680 }
681
682 res = dmWritePCXImageFILE(fp, img, spec);
683
684 fclose(fp);
685 return res;
686 }
687
688
689 static BOOL dmPCXDecodeRLERow(FILE *fp, Uint8 *buf, const size_t bufLen)
690 {
691 size_t offs = 0;
692 do
693 {
694 int count;
695 Uint8 data;
696
697 if (!dm_fread_byte(fp, &data))
698 return FALSE;
699
700 if ((data & 0xC0) == 0xC0)
701 {
702 count = data & 0x3F;
703 if (!dm_fread_byte(fp, &data))
704 return FALSE;
705 }
706 else
707 count = 1;
708
709 while (count-- && offs < bufLen)
710 buf[offs++] = data;
711
712 } while (offs < bufLen);
713
714 return TRUE;
715 }
716
717
718 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg)
719 {
720 DMImage *img;
721 DMPCXData pcx;
722 DMPCXHeader hdr;
723 BOOL paletted;
724 int res = 0, yc, xc;
725 Uint8 *dp;
726
727 pcx.buf = NULL;
728
729 // Read PCX header
730 if (!dm_fread_byte(fp, &hdr.manufacturer) ||
731 !dm_fread_byte(fp, &hdr.version) ||
732 !dm_fread_byte(fp, &hdr.encoding) ||
733 !dm_fread_byte(fp, &hdr.bpp))
734 {
735 dmError("PCX: Could not read basic header data.\n");
736 res = DMERR_FREAD;
737 goto error;
738 }
739
740 if (hdr.manufacturer != 10 ||
741 hdr.version != 5 ||
742 hdr.encoding != 1 ||
743 hdr.bpp != 8)
744 {
745 dmError("PCX: Not a PCX file, or unsupported variant.\n");
746 res = DMERR_FREAD;
747 goto error;
748 }
749
750 if (!dm_fread_le16(fp, &hdr.xmin) ||
751 !dm_fread_le16(fp, &hdr.ymin) ||
752 !dm_fread_le16(fp, &hdr.xmax) ||
753 !dm_fread_le16(fp, &hdr.ymax) ||
754 !dm_fread_le16(fp, &hdr.hres) ||
755 !dm_fread_le16(fp, &hdr.vres))
756 {
757 dmError("PCX: Could not read image dimensions.\n");
758 res = DMERR_FREAD;
759 goto error;
760 }
761
762 if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
763 {
764 dmError("PCX: Could not read colormap.\n");
765 res = DMERR_FREAD;
766 goto error;
767 }
768
769 if (!dm_fread_byte(fp, &hdr.reserved) ||
770 !dm_fread_byte(fp, &hdr.nplanes) ||
771 !dm_fread_le16(fp, &hdr.bpl) ||
772 !dm_fread_le16(fp, &hdr.palinfo) ||
773 !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
774 {
775 dmError("PCX: Could not read header remainder.\n");
776 res = DMERR_FREAD;
777 goto error;
778 }
779
780 if (hdr.nplanes != 3 && hdr.nplanes != 1)
781 {
782 dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes);
783 res = DMERR_FREAD;
784 goto error;
785 }
786
787 // Allocate image
788 if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL)
789 {
790 dmError("PCX: Could not allocate image structure.\n");
791 res = DMERR_MALLOC;
792 goto error;
793 }
794
795 paletted = hdr.nplanes == 1;
796 pcx.bufLen = hdr.nplanes * hdr.bpl;
797 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
798 {
799 dmError("PCX: Could not allocate RLE buffer.\n");
800 res = DMERR_MALLOC;
801 goto error;
802 }
803
804 // Read image data
805 dp = img->data;
806 for (yc = 0; yc < img->height; yc++)
807 {
808 // Decode row of RLE'd data
809 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen))
810 {
811 dmError("PCX: Error decoding RLE data.\n");
812 res = DMERR_INVALID_DATA;
813 goto error;
814 }
815
816 // Decode bitplanes
817 switch (hdr.nplanes)
818 {
819 case 1:
820 memcpy(dp, pcx.buf, img->width);
821 break;
822
823 case 3:
824 {
825 Uint8 *dptr = dp,
826 *sptr1 = pcx.buf,
827 *sptr2 = sptr1 + hdr.bpl,
828 *sptr3 = sptr2 + hdr.bpl;
829
830 for (xc = 0; xc < img->width; xc++)
831 {
832 *dptr++ = *sptr1++;
833 *dptr++ = *sptr2++;
834 *dptr++ = *sptr3++;
835 }
836 }
837 break;
838 }
839
840 dp += img->pitch;
841 }
842
843 // Read VGA palette
844 if (paletted)
845 {
846 int i;
847 Uint8 tmpb;
848 BOOL read;
849
850 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C)
851 {
852 read = FALSE;
853 img->ncolors = 16;
854 }
855 else
856 {
857 read = TRUE;
858 img->ncolors = 256;
859 }
860
861 if ((img->pal = dmCalloc(img->ncolors, sizeof(DMColor))) == NULL)
862 {
863 dmError("PCX: Could not allocate palette data!\n");
864 res = DMERR_MALLOC;
865 goto error;
866 }
867
868 if (read)
869 {
870 for (i = 0; i < img->ncolors; i++)
871 {
872 Uint8 tmpR, tmpG, tmpB;
873 if (!dm_fread_byte(fp, &tmpR) ||
874 !dm_fread_byte(fp, &tmpG) ||
875 !dm_fread_byte(fp, &tmpB))
876 goto error;
877
878 img->pal[i].r = tmpR;
879 img->pal[i].g = tmpG;
880 img->pal[i].b = tmpB;
881 }
882 }
883 else
884 {
885 for (i = 0; i < img->ncolors; i++)
886 {
887 if (i < 16)
888 {
889 img->pal[i].r = hdr.colormap[i].r;
890 img->pal[i].g = hdr.colormap[i].g;
891 img->pal[i].b = hdr.colormap[i].b;
892 }
893 }
894 }
895
896
897 }
898
899 error:
900 dmFree(pcx.buf);
901 return res;
902 }
903
904
905 int dmReadPCXImage(const char *filename, DMImage **pimg)
906 {
907 FILE *fp;
908 int res;
909
910 if ((fp = fopen(filename, "rb")) == NULL)
911 {
912 dmError("PCX: Could not open file '%s' for reading.\n", filename);
913 return -15;
914 }
915
916 res = dmReadPCXImageFILE(fp, pimg);
917
918 fclose(fp);
919 return res;
920 }
921
922
923 static int fmtProbePNG(const Uint8 *buf, const size_t len)
924 {
925 if (len > 64 && buf[0] == 0x89 &&
926 buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' &&
927 buf[4] == 0x0d && buf[5] == 0x0a)
928 return DM_PROBE_SCORE_GOOD;
929
930 return DM_PROBE_SCORE_FALSE;
931 }
932
933
934 static int fmtProbePCX(const Uint8 *buf, const size_t len)
935 {
936 if (len > 128 + 64 &&
937 buf[0] == 10 &&
938 buf[1] == 5 &&
939 buf[2] == 1 &&
940 buf[3] == 8)
941 return DM_PROBE_SCORE_GOOD;
942
943 return DM_PROBE_SCORE_FALSE;
944 }
945
946
947 DMImageFormat dmImageFormatList[IMGFMT_LAST] =
948 {
949 {
950 "PNG", "Portable Network Graphics",
951 fmtProbePNG,
952 NULL, NULL,
953 #ifdef DM_USE_LIBPNG
954 dmWritePNGImage, dmWritePNGImageFILE,
955 #else
956 NULL, NULL,
957 #endif
958 },
959 {
960 "PPM", "Portable PixMap",
961 NULL,
962 NULL, NULL,
963 dmWritePPMImage, dmWritePPMImageFILE,
964 },
965 {
966 "PCX", "Z-Soft Paintbrush",
967 fmtProbePCX,
968 dmReadPCXImage, dmReadPCXImageFILE,
969 dmWritePCXImage, dmWritePCXImageFILE,
970 },
971 {
972 "ARAW", "IFFMaster Amiga RAW",
973 NULL,
974 NULL, NULL,
975 dmWriteIFFMasterRAWImage, dmWriteIFFMasterRAWImageFILE,
976 }
977 };
978
979
980 int dmImageProbeGeneric(const Uint8 *buf, const size_t len, DMImageFormat **pfmt, int *index)
981 {
982 int i, scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1;
983
984 for (i = 0; i < IMGFMT_LAST; i++)
985 {
986 DMImageFormat *fmt = &dmImageFormatList[i];
987 int score = fmt->probe(buf, len);
988 if (score > scoreMax)
989 {
990 scoreMax = score;
991 scoreIndex = i;
992 }
993 }
994
995 if (scoreIndex >= 0)
996 {
997 *pfmt = &dmImageFormatList[scoreIndex];
998 *index = scoreIndex;
999 return scoreMax;
1000 }
1001 else
1002 return DM_PROBE_SCORE_FALSE;
1003 }