Mercurial > hg > dmlib
annotate libgfx.c @ 442:a67600e186d0
Fix probing to handle NULL probe functions.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 04 Nov 2012 06:41:10 +0200 |
parents | b4ed5292d7bf |
children | f7c9d1619c74 |
rev | line source |
---|---|
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 { | |
436
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
85 Uint8 *ptr1 = row, |
435 | 86 *ptr2 = ptr1 + rowWidth, |
87 *ptr3 = ptr2 + rowWidth; | |
88 | |
89 for (x = 0; x < img->width; x++) | |
90 { | |
91 Uint8 c = img->data[(y * img->pitch) + x], qr, qg, qb, qa; | |
92 switch (spec->format) | |
93 { | |
94 case DM_IFMT_PALETTE: | |
95 for (xscale = 0; xscale < spec->scale; xscale++) | |
436
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
96 *ptr1++ = c; |
435 | 97 break; |
98 | |
99 case DM_IFMT_RGBA: | |
100 qr = img->pal[c].r; | |
101 qg = img->pal[c].g; | |
102 qb = img->pal[c].b; | |
103 qa = (c == img->ctrans) ? 0 : 255; | |
104 | |
105 for (xscale = 0; xscale < spec->scale; xscale++) | |
106 { | |
436
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
107 *ptr1++ = qr; |
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
108 *ptr1++ = qg; |
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
109 *ptr1++ = qb; |
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
110 *ptr1++ = qa; |
435 | 111 } |
112 break; | |
113 | |
114 case DM_IFMT_RGB: | |
115 qr = img->pal[c].r; | |
116 qg = img->pal[c].g; | |
117 qb = img->pal[c].b; | |
118 | |
119 for (xscale = 0; xscale < spec->scale; xscale++) | |
120 { | |
436
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
121 *ptr1++ = qr; |
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
122 *ptr1++ = qg; |
86f956e4580f
Cosmetics, rename a variable.
Matti Hamalainen <ccr@tnsp.org>
parents:
435
diff
changeset
|
123 *ptr1++ = qb; |
435 | 124 } |
125 break; | |
126 | |
127 case DM_IFMT_RGB_PLANE: | |
128 qr = img->pal[c].r; | |
129 qg = img->pal[c].g; | |
130 qb = img->pal[c].b; | |
131 | |
132 for (xscale = 0; xscale < spec->scale; xscale++) | |
133 { | |
134 *ptr1++ = qr; | |
135 *ptr2++ = qg; | |
136 *ptr3++ = qb; | |
137 } | |
138 break; | |
139 } | |
140 } | |
141 | |
142 for (yscale = 0; yscale < spec->scale; yscale++) | |
143 { | |
144 if (!writeRowCB(cbdata, row, rowSize)) | |
145 { | |
146 res = DMERR_FWRITE; | |
147 goto done; | |
148 } | |
149 } | |
150 } | |
151 | |
152 done: | |
153 dmFree(row); | |
154 return res; | |
155 } | |
156 | |
157 | |
158 #define DMCOL(x) (((x) >> 4) & 0xf) | |
159 | |
160 int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors) | |
161 { | |
162 FILE *fp; | |
163 int i; | |
164 | |
165 if ((fp = fopen(filename, "w")) == NULL) | |
166 { | |
167 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); | |
168 return -15; | |
169 } | |
170 | |
171 for (i = 0; i < ncolors; i++) | |
172 { | |
173 int color; | |
174 if (i < img->ncolors) | |
175 { | |
176 color = (DMCOL(img->pal[i].r) << 8) | | |
177 (DMCOL(img->pal[i].g) << 4) | | |
178 (DMCOL(img->pal[i].b)); | |
179 } | |
180 else | |
181 color = 0; | |
182 | |
183 fprintf(fp, "\tdc.w $%04X\n", color); | |
184 } | |
185 | |
440
b4ed5292d7bf
Close the file after writing.
Matti Hamalainen <ccr@tnsp.org>
parents:
436
diff
changeset
|
186 fclose(fp); |
435 | 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]; | |
442
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
987 if (fmt->probe != NULL) |
435 | 988 { |
442
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
989 int score = fmt->probe(buf, len); |
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
990 if (score > scoreMax) |
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
991 { |
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
992 scoreMax = score; |
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
993 scoreIndex = i; |
a67600e186d0
Fix probing to handle NULL probe functions.
Matti Hamalainen <ccr@tnsp.org>
parents:
440
diff
changeset
|
994 } |
435 | 995 } |
996 } | |
997 | |
998 if (scoreIndex >= 0) | |
999 { | |
1000 *pfmt = &dmImageFormatList[scoreIndex]; | |
1001 *index = scoreIndex; | |
1002 return scoreMax; | |
1003 } | |
1004 else | |
1005 return DM_PROBE_SCORE_FALSE; | |
1006 } |