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