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