435
|
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 }
|