Mercurial > hg > dmlib
comparison gfxconv.c @ 407:59244a7ae37f
Move c64 utilities to the engine lib, as we benefit from a common framework.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 03 Nov 2012 02:19:51 +0200 |
parents | |
children | b529b7e8ff83 |
comparison
equal
deleted
inserted
replaced
406:a0160ffdf7e5 | 407:59244a7ae37f |
---|---|
1 /* | |
2 * gfxconv - Convert various graphics formats | |
3 * Programmed and designed by Matti 'ccr' Hamalainen | |
4 * (C) Copyright 2012 Tecnic Software productions (TNSP) | |
5 * | |
6 * Please read file 'COPYING' for information on license and distribution. | |
7 */ | |
8 #include <errno.h> | |
9 #include "dmlib.h" | |
10 #include "dmargs.h" | |
11 #include "dmfile.h" | |
12 #include "dmmutex.h" | |
13 #include "lib64gfx.h" | |
14 | |
15 //#define UNFINISHED 1 | |
16 | |
17 #ifdef HAVE_LIBPNG | |
18 #include <png.h> | |
19 #endif | |
20 | |
21 enum | |
22 { | |
23 INFMT_AUTO = 0, | |
24 INFMT_CHAR, | |
25 INFMT_SPRITE, | |
26 INFMT_BITMAP, | |
27 INFMT_IMAGE, | |
28 }; | |
29 | |
30 enum | |
31 { | |
32 OUTFMT_ASCII, | |
33 OUTFMT_ANSI, | |
34 OUTFMT_PNG, | |
35 OUTFMT_PPM, | |
36 OUTFMT_PCX, | |
37 OUTFMT_ARAW, | |
38 | |
39 #ifdef UNFINISHED | |
40 OUTFMT_SPRITE, | |
41 OUTFMT_CHAR, | |
42 #endif | |
43 | |
44 OUTFMT_LAST | |
45 }; | |
46 | |
47 char * outFormatList[OUTFMT_LAST] = | |
48 { | |
49 "ascii", | |
50 "ansi", | |
51 "png", | |
52 "ppm", | |
53 "pcx", | |
54 "araw", | |
55 #ifdef UNFINISHED | |
56 "spr", | |
57 "char", | |
58 #endif | |
59 }; | |
60 | |
61 static const int noutFormatList = sizeof(outFormatList) / sizeof(outFormatList[0]); | |
62 | |
63 | |
64 #define ASC_NBITS 8 | |
65 #define ASC_NCOLORS 4 | |
66 static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#"; | |
67 | |
68 | |
69 char *optInFilename = NULL, | |
70 *optOutFilename = NULL; | |
71 int optInFormat = INFMT_AUTO, | |
72 optOutFormat = OUTFMT_ASCII, | |
73 optItemCount = -1, | |
74 optScale = 2, | |
75 optPlanedWidth = 1, | |
76 optBPP = 4; | |
77 int optInSkip = 0; | |
78 BOOL optInMulticolor = FALSE, | |
79 optSequential = FALSE, | |
80 optPaletted = FALSE; | |
81 int optColors[C64_MAX_COLORS]; | |
82 | |
83 | |
84 static DMOptArg optList[] = | |
85 { | |
86 { 0, '?', "help", "Show this help", OPT_NONE }, | |
87 { 3, 'o', "output", "Output filename", OPT_ARGREQ }, | |
88 { 1, 'i', "informat", "Set input format ([s]prite, [c]har, [b]itmap)", OPT_ARGREQ }, | |
89 { 2, 'm', "multicolor", "Input is multicolor", OPT_NONE }, | |
90 { 4, 's', "skip", "Skip bytes in input", OPT_ARGREQ }, | |
91 { 5, 'f', "format", "Output format (see list below)", OPT_ARGREQ }, | |
92 { 8, 'q', "sequential", "Output sequential files (image output only)", OPT_NONE }, | |
93 { 6, 'c', "colormap", "Color mappings (see below for information)", OPT_ARGREQ }, | |
94 { 7, 'n', "numitems", "How many 'items' to view (default: all)", OPT_ARGREQ }, | |
95 { 9, 'S', "scale", "Scale output by x (image output only)", OPT_ARGREQ }, | |
96 #ifdef UNFINISHED | |
97 {10, 'b', "bformat", "Force input bitmap format (see below)", OPT_ARGREQ }, | |
98 #endif | |
99 {11, 'w', "width", "Item width (number of items per row, min 1)", OPT_ARGREQ }, | |
100 {12, 'P', "paletted", "Use indexed/paletted output (png, pcx output only)", OPT_NONE }, | |
101 {13, 'b', "bpp", "Bits per pixel (certain image output formats)", OPT_ARGREQ }, | |
102 }; | |
103 | |
104 static const int optListN = sizeof(optList) / sizeof(optList[0]); | |
105 | |
106 | |
107 void argShowHelp() | |
108 { | |
109 int i; | |
110 | |
111 dmPrintBanner(stdout, dmProgName, "[options] <input file>"); | |
112 dmArgsPrintHelp(stdout, optList, optListN); | |
113 | |
114 printf("\nAvailable output formats: "); | |
115 for (i = 0; i < noutFormatList; i++) | |
116 { | |
117 printf("%s", outFormatList[i]); | |
118 if (i < noutFormatList - 1) | |
119 printf(", "); | |
120 else | |
121 printf("\n"); | |
122 } | |
123 | |
124 #ifdef UNFINISHED | |
125 printf("\nAvailable bitmap formats:\n"); | |
126 for (i = 0; i < ndmC64ImageFormats; i++) | |
127 { | |
128 DM64ImageFormat *fmt = &dmC64ImageFormats[i]; | |
129 printf("%3d | %-5s | %-15s | %s\n", | |
130 i, fmt->extension, | |
131 dmC64ImageTypeNames[fmt->type], | |
132 fmt->name); | |
133 } | |
134 #endif | |
135 | |
136 printf( | |
137 "\n" | |
138 "Color map definitions are used for ANSI, PCX, PPM and PNG output, to declare what\n" | |
139 "output colors of the C64 palette are used for each single color/multi color\n" | |
140 "bit-combination. For example, if the input is multi color sprite or char,\n" | |
141 "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n" | |
142 "The numbers are palette indexes, and the order is for bit(pair)-values\n" | |
143 "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n" | |
144 "special color that can be used for transparency.\n" | |
145 ); | |
146 } | |
147 | |
148 | |
149 BOOL argHandleOpt(const int optN, char *optArg, char *currArg) | |
150 { | |
151 switch (optN) | |
152 { | |
153 case 0: | |
154 argShowHelp(); | |
155 exit(0); | |
156 break; | |
157 | |
158 case 1: | |
159 switch (tolower(optArg[0])) | |
160 { | |
161 case 's': | |
162 optInFormat = INFMT_SPRITE; | |
163 break; | |
164 case 'c': | |
165 optInFormat = INFMT_CHAR; | |
166 break; | |
167 default: | |
168 dmError("Invalid input format '%s'.\n", optArg); | |
169 return FALSE; | |
170 } | |
171 break; | |
172 | |
173 case 2: | |
174 optInMulticolor = TRUE; | |
175 break; | |
176 | |
177 case 3: | |
178 optOutFilename = optArg; | |
179 break; | |
180 | |
181 case 4: | |
182 if (!dmGetIntVal(optArg, &optInSkip)) | |
183 { | |
184 dmError("Invalid skip value argument '%s'.\n", optArg); | |
185 return FALSE; | |
186 } | |
187 break; | |
188 | |
189 case 5: | |
190 { | |
191 int i, format = -1; | |
192 for (i = 0; i < noutFormatList; i++) | |
193 if (strcasecmp(optArg, outFormatList[i]) == 0) | |
194 { | |
195 format = i; | |
196 break; | |
197 } | |
198 | |
199 if (format < 0) | |
200 { | |
201 dmError("Invalid output format '%s'.\n", optArg); | |
202 return FALSE; | |
203 } | |
204 | |
205 optOutFormat = format; | |
206 } | |
207 break; | |
208 | |
209 case 6: | |
210 { | |
211 int index = 0, tmp; | |
212 char *s, *p = optArg; | |
213 | |
214 while (index < C64_MAX_COLORS && *p != 0 && (s = strchr(p, ':')) != NULL) | |
215 { | |
216 *s = 0; | |
217 if (sscanf(p, "%d", &tmp) == 1) | |
218 optColors[index++] = tmp; | |
219 p = s + 1; | |
220 } | |
221 | |
222 if (*p && index < C64_MAX_COLORS) | |
223 { | |
224 if (sscanf(p, "%d", &tmp) == 1) | |
225 optColors[index++] = tmp; | |
226 } | |
227 | |
228 dmMsg(1, "Set color table: "); | |
229 for (tmp = 0; tmp < index; tmp++) | |
230 { | |
231 dmPrint(1, "[%d:%d]%s", | |
232 tmp, optColors[tmp], | |
233 (tmp < index - 1) ? ", " : ""); | |
234 } | |
235 dmPrint(1, "\n"); | |
236 } | |
237 break; | |
238 | |
239 case 7: | |
240 if (sscanf(optArg, "%d", &optItemCount) != 1) | |
241 { | |
242 dmError("Invalid count value argument '%s'.\n", optArg); | |
243 return FALSE; | |
244 } | |
245 break; | |
246 | |
247 case 8: | |
248 optSequential = TRUE; | |
249 break; | |
250 | |
251 case 9: | |
252 { | |
253 int tmp = atoi(optArg); | |
254 if (tmp < 1 || tmp > 50) | |
255 { | |
256 dmError("Invalid scale value '%s'.\n", optArg); | |
257 return FALSE; | |
258 } | |
259 optScale = tmp; | |
260 } | |
261 break; | |
262 | |
263 case 11: | |
264 { | |
265 int tmp = atoi(optArg); | |
266 if (tmp < 1 || tmp > 512) | |
267 { | |
268 dmError("Invalid width value '%s'.\n", optArg); | |
269 return FALSE; | |
270 } | |
271 optPlanedWidth = tmp; | |
272 } | |
273 break; | |
274 | |
275 case 12: | |
276 optPaletted = TRUE; | |
277 break; | |
278 | |
279 default: | |
280 dmError("Unknown option '%s'.\n", currArg); | |
281 return FALSE; | |
282 } | |
283 | |
284 return TRUE; | |
285 } | |
286 | |
287 | |
288 BOOL argHandleFile(char *currArg) | |
289 { | |
290 if (!optInFilename) | |
291 optInFilename = currArg; | |
292 else | |
293 { | |
294 dmError("Source filename already specified, extraneous argument '%s'.\n", | |
295 currArg); | |
296 return FALSE; | |
297 } | |
298 | |
299 return TRUE; | |
300 } | |
301 | |
302 | |
303 void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor) | |
304 { | |
305 int i; | |
306 | |
307 if (multicolor) | |
308 { | |
309 for (i = ASC_NBITS; i; i -= 2) | |
310 { | |
311 int val = (byte & (3ULL << (i - 2))) >> (i - 2); | |
312 char ch; | |
313 switch (format) | |
314 { | |
315 case OUTFMT_ASCII: | |
316 ch = dmASCIIPalette[val]; | |
317 fprintf(out, "%c%c", ch, ch); | |
318 break; | |
319 case OUTFMT_ANSI: | |
320 fprintf(out, "%c[0;%d;%dm##%c[0m", | |
321 0x1b, | |
322 1, | |
323 31 + optColors[val], | |
324 0x1b); | |
325 break; | |
326 } | |
327 } | |
328 } | |
329 else | |
330 { | |
331 for (i = ASC_NBITS; i; i--) | |
332 { | |
333 int val = (byte & (1ULL << (i - 1))) >> (i - 1); | |
334 char ch; | |
335 switch (format) | |
336 { | |
337 case OUTFMT_ASCII: | |
338 ch = val ? '#' : '.'; | |
339 fputc(ch, out); | |
340 break; | |
341 case OUTFMT_ANSI: | |
342 fprintf(out, "%c[0;%d;%dm %c[0m", | |
343 0x1b, | |
344 1, | |
345 31 + optColors[val], | |
346 0x1b); | |
347 break; | |
348 } | |
349 } | |
350 } | |
351 } | |
352 | |
353 | |
354 void dmDumpCharASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) | |
355 { | |
356 int yc; | |
357 | |
358 for (yc = 0; yc < C64_CHR_HEIGHT; yc++) | |
359 { | |
360 fprintf(outFile, "%04x : ", *offs); | |
361 dmPrintByte(outFile, buf[yc], format, multicolor); | |
362 fprintf(outFile, "\n"); | |
363 (*offs)++; | |
364 } | |
365 } | |
366 | |
367 | |
368 void dmDumpSpriteASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) | |
369 { | |
370 int bufOffs, xc, yc; | |
371 | |
372 for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++) | |
373 { | |
374 fprintf(outFile, "%04x : ", *offs); | |
375 for (xc = 0; xc < C64_SPR_WIDTH; xc++) | |
376 { | |
377 dmPrintByte(outFile, buf[bufOffs], format, multicolor); | |
378 fprintf(outFile, " "); | |
379 bufOffs++; | |
380 (*offs)++; | |
381 } | |
382 fprintf(outFile, "\n"); | |
383 } | |
384 (*offs)++; | |
385 } | |
386 | |
387 | |
388 int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, uint8_t *, size_t), int scale, int format) | |
389 { | |
390 int x, y, yscale, xscale, res = 0, rowSize, rowWidth; | |
391 uint8_t *row = NULL; | |
392 | |
393 // Allocate memory for row buffer | |
394 rowWidth = img->width * scale; | |
395 rowSize = rowWidth * dmImageGetBytesPerPixel(format); | |
396 | |
397 if ((row = dmMalloc(rowSize + 16)) == NULL) | |
398 { | |
399 res = -16; | |
400 goto done; | |
401 } | |
402 | |
403 // Generate the image | |
404 for (y = 0; y < img->height; y++) | |
405 { | |
406 uint8_t *ptr = row, | |
407 *ptr1 = row, | |
408 *ptr2 = ptr1 + rowWidth, | |
409 *ptr3 = ptr2 + rowWidth; | |
410 | |
411 for (x = 0; x < img->width; x++) | |
412 { | |
413 uint8_t c = img->data[(y * img->pitch) + x], qr, qg, qb, qa; | |
414 switch (format) | |
415 { | |
416 case DM_IFMT_PALETTE: | |
417 for (xscale = 0; xscale < scale; xscale++) | |
418 *ptr++ = c; | |
419 break; | |
420 | |
421 case DM_IFMT_RGBA: | |
422 qr = img->pal[c].r; | |
423 qg = img->pal[c].g; | |
424 qb = img->pal[c].b; | |
425 qa = (c == img->ctrans) ? 0 : 255; | |
426 | |
427 for (xscale = 0; xscale < scale; xscale++) | |
428 { | |
429 *ptr++ = qr; | |
430 *ptr++ = qg; | |
431 *ptr++ = qb; | |
432 *ptr++ = qa; | |
433 } | |
434 break; | |
435 | |
436 case DM_IFMT_RGB: | |
437 qr = img->pal[c].r; | |
438 qg = img->pal[c].g; | |
439 qb = img->pal[c].b; | |
440 | |
441 for (xscale = 0; xscale < scale; xscale++) | |
442 { | |
443 *ptr++ = qr; | |
444 *ptr++ = qg; | |
445 *ptr++ = qb; | |
446 } | |
447 break; | |
448 | |
449 case DM_IFMT_RGB_PLANE: | |
450 qr = img->pal[c].r; | |
451 qg = img->pal[c].g; | |
452 qb = img->pal[c].b; | |
453 | |
454 for (xscale = 0; xscale < scale; xscale++) | |
455 { | |
456 *ptr1++ = qr; | |
457 *ptr2++ = qg; | |
458 *ptr3++ = qb; | |
459 } | |
460 break; | |
461 } | |
462 } | |
463 | |
464 for (yscale = 0; yscale < scale; yscale++) | |
465 { | |
466 if (!writeRowCB(cbdata, row, rowSize)) | |
467 { | |
468 res = -32; | |
469 goto done; | |
470 } | |
471 } | |
472 } | |
473 | |
474 done: | |
475 dmFree(row); | |
476 return res; | |
477 } | |
478 | |
479 | |
480 #define DMCOL(x) (((x) >> 4) & 0xf) | |
481 | |
482 int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors) | |
483 { | |
484 FILE *fp; | |
485 int i; | |
486 | |
487 if ((fp = fopen(filename, "w")) == NULL) | |
488 { | |
489 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); | |
490 return -15; | |
491 } | |
492 | |
493 for (i = 0; i < ncolors; i++) | |
494 { | |
495 int color; | |
496 if (i < img->ncolors) | |
497 { | |
498 color = (DMCOL(img->pal[i].r) << 8) | | |
499 (DMCOL(img->pal[i].g) << 4) | | |
500 (DMCOL(img->pal[i].b)); | |
501 } | |
502 else | |
503 color = 0; | |
504 | |
505 fprintf(fp, "\tdc.w $%04X\n", color); | |
506 } | |
507 | |
508 return 0; | |
509 } | |
510 | |
511 | |
512 typedef struct | |
513 { | |
514 int bpp; | |
515 DMImage *img; | |
516 FILE *fp; | |
517 } DMRawData; | |
518 | |
519 | |
520 static BOOL dmWriteIFFMasterRAWRow(void *cbdata, uint8_t *row, size_t len) | |
521 { | |
522 DMRawData *raw = (DMRawData *) cbdata; | |
523 size_t i; | |
524 | |
525 for (i = 0; i < len; i++) | |
526 { | |
527 } | |
528 | |
529 return fwrite(row, sizeof(uint8_t), len, raw->fp) == len; | |
530 } | |
531 | |
532 | |
533 int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, int scale, int bpp) | |
534 { | |
535 DMRawData raw; | |
536 | |
537 raw.fp = fp; | |
538 raw.img = img; | |
539 raw.bpp = bpp; | |
540 | |
541 return dmWriteImageData(img, (void *) &raw, dmWriteIFFMasterRAWRow, scale, DM_IFMT_PALETTE); | |
542 } | |
543 | |
544 int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, int scale, int bpp) | |
545 { | |
546 FILE *fp; | |
547 int res; | |
548 | |
549 if ((fp = fopen(filename, "wb")) == NULL) | |
550 { | |
551 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); | |
552 return -15; | |
553 } | |
554 | |
555 res = dmWriteIFFMasterRAWImageFILE(fp, img, scale, bpp); | |
556 | |
557 fclose(fp); | |
558 return res; | |
559 } | |
560 | |
561 | |
562 static BOOL dmWritePPMRow(void *cbdata, uint8_t *row, size_t len) | |
563 { | |
564 return fwrite(row, sizeof(uint8_t), len, (FILE *) cbdata) == len; | |
565 } | |
566 | |
567 | |
568 int dmWritePPMImageFILE(FILE *fp, DMImage *img, int scale) | |
569 { | |
570 // Write PPM header | |
571 fprintf(fp, | |
572 "P6\n%d %d\n255\n", | |
573 img->width * scale, img->height * scale); | |
574 | |
575 // Write image data | |
576 return dmWriteImageData(img, (void *) fp, dmWritePPMRow, scale, DM_IFMT_RGB); | |
577 } | |
578 | |
579 | |
580 int dmWritePPMImage(const char *filename, DMImage *img, int scale) | |
581 { | |
582 FILE *fp; | |
583 int res; | |
584 | |
585 // Create output file | |
586 if ((fp = fopen(filename, "wb")) == NULL) | |
587 { | |
588 dmError("PPM: could not open file '%s' for writing.\n", filename); | |
589 return -15; | |
590 } | |
591 | |
592 res = dmWritePPMImageFILE(fp, img, scale); | |
593 | |
594 fclose(fp); | |
595 return res; | |
596 } | |
597 | |
598 | |
599 #ifdef HAVE_LIBPNG | |
600 static BOOL dmWritePNGRow(void *cbdata, uint8_t *row, size_t len) | |
601 { | |
602 png_structp png_ptr = cbdata; | |
603 (void) len; | |
604 | |
605 if (setjmp(png_jmpbuf(png_ptr))) | |
606 return FALSE; | |
607 | |
608 png_write_row(png_ptr, row); | |
609 | |
610 return TRUE; | |
611 } | |
612 | |
613 | |
614 int dmWritePNGImageFILE(FILE *fp, DMImage *img, int scale, int format) | |
615 { | |
616 png_structp png_ptr = NULL; | |
617 png_infop info_ptr = NULL; | |
618 png_colorp palette = NULL; | |
619 int fmt; | |
620 | |
621 // Create PNG structures | |
622 png_ptr = png_create_write_struct( | |
623 PNG_LIBPNG_VER_STRING, | |
624 NULL, NULL, NULL); | |
625 | |
626 if (png_ptr == NULL) | |
627 { | |
628 dmError("PNG: png_create_write_struct() failed.\n"); | |
629 goto error; | |
630 } | |
631 | |
632 info_ptr = png_create_info_struct(png_ptr); | |
633 if (info_ptr == NULL) | |
634 { | |
635 dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr); | |
636 goto error; | |
637 } | |
638 | |
639 if (setjmp(png_jmpbuf(png_ptr))) | |
640 { | |
641 dmError("PNG: Error during image writing..\n"); | |
642 goto error; | |
643 } | |
644 | |
645 png_init_io(png_ptr, fp); | |
646 | |
647 // Write PNG header info | |
648 switch (format) | |
649 { | |
650 case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break; | |
651 case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break; | |
652 case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break; | |
653 default: | |
654 dmError("PNG: Internal error, unsupported image format %d.\n", format); | |
655 goto error; | |
656 } | |
657 | |
658 png_set_IHDR(png_ptr, info_ptr, | |
659 img->width * scale, | |
660 img->height * scale, | |
661 8, /* bits per component */ | |
662 fmt, | |
663 PNG_INTERLACE_NONE, | |
664 PNG_COMPRESSION_TYPE_DEFAULT, | |
665 PNG_FILTER_TYPE_DEFAULT); | |
666 | |
667 // Palette | |
668 if (format == DM_IFMT_PALETTE) | |
669 { | |
670 int i; | |
671 | |
672 palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); | |
673 if (palette == NULL) | |
674 { | |
675 dmError("PNG: Could not allocate palette structure."); | |
676 goto error; | |
677 } | |
678 | |
679 memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); | |
680 | |
681 for (i = 0; i < img->ncolors; i++) | |
682 { | |
683 palette[i].red = img->pal[i].r; | |
684 palette[i].green = img->pal[i].g; | |
685 palette[i].blue = img->pal[i].b; | |
686 } | |
687 | |
688 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); | |
689 } | |
690 | |
691 // png_set_gAMA(png_ptr, info_ptr, 2.2); | |
692 | |
693 png_write_info(png_ptr, info_ptr); | |
694 | |
695 | |
696 // Write compressed image data | |
697 dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, scale, format); | |
698 | |
699 // Write footer | |
700 png_write_end(png_ptr, NULL); | |
701 | |
702 png_free(png_ptr, palette); | |
703 palette = NULL; | |
704 | |
705 // Deallocate shit | |
706 if (png_ptr && info_ptr) | |
707 { | |
708 png_destroy_write_struct(&png_ptr, &info_ptr); | |
709 } | |
710 | |
711 return 0; | |
712 | |
713 error: | |
714 png_free(png_ptr, palette); | |
715 palette = NULL; | |
716 | |
717 if (png_ptr && info_ptr) | |
718 { | |
719 png_destroy_write_struct(&png_ptr, &info_ptr); | |
720 } | |
721 return -15; | |
722 } | |
723 | |
724 | |
725 int dmWritePNGImage(const char *filename, DMImage *img, int scale, int format) | |
726 { | |
727 int res; | |
728 FILE *fp; | |
729 | |
730 if ((fp = fopen(filename, "wb")) == NULL) | |
731 { | |
732 dmError("PNG: could not open file '%s' for writing.\n", filename); | |
733 return -15; | |
734 } | |
735 | |
736 res = dmWritePNGImageFILE(fp, img, scale, format); | |
737 | |
738 fclose(fp); | |
739 return res; | |
740 } | |
741 #endif | |
742 | |
743 | |
744 typedef struct | |
745 { | |
746 uint8_t r,g,b; | |
747 } DMPCXColor; | |
748 | |
749 | |
750 typedef struct | |
751 { | |
752 uint8_t manufacturer, | |
753 version, | |
754 encoding, | |
755 bpp; | |
756 uint16_t xmin, ymin, xmax, ymax; | |
757 uint16_t hres, vres; | |
758 DMPCXColor colormap[16]; | |
759 uint8_t reserved; | |
760 uint8_t nplanes; | |
761 uint16_t bpl; | |
762 uint16_t palinfo; | |
763 uint8_t filler[58]; | |
764 } DMPCXHeader; | |
765 | |
766 typedef struct | |
767 { | |
768 DMPCXHeader *header; | |
769 uint8_t *buf; | |
770 size_t bufLen, bufOffs; | |
771 int format; | |
772 FILE *fp; | |
773 } DMPCXData; | |
774 | |
775 | |
776 static inline uint8_t dmPCXGetByte(uint8_t *row, const size_t len, const size_t soffs) | |
777 { | |
778 return (soffs < len) ? row[soffs] : 0; | |
779 } | |
780 | |
781 static BOOL dmPCXFlush(DMPCXData *pcx) | |
782 { | |
783 BOOL ret = fwrite(pcx->buf, sizeof(uint8_t), pcx->bufOffs, pcx->fp) == pcx->bufOffs; | |
784 pcx->bufOffs = 0; | |
785 return ret; | |
786 } | |
787 | |
788 static inline BOOL dmPCXPutByte(DMPCXData *pcx, const uint8_t val) | |
789 { | |
790 if (pcx->bufOffs < pcx->bufLen) | |
791 { | |
792 pcx->buf[pcx->bufOffs++] = val; | |
793 return TRUE; | |
794 } | |
795 else | |
796 return dmPCXFlush(pcx); | |
797 } | |
798 | |
799 BOOL dmWritePCXRow(void *cbdata, uint8_t *row, size_t len) | |
800 { | |
801 DMPCXData *pcx = (DMPCXData *) cbdata; | |
802 int plane; | |
803 size_t soffs = 0; | |
804 | |
805 for (plane = 0; plane < pcx->header->nplanes; plane++) | |
806 { | |
807 uint8_t data = dmPCXGetByte(row, len, soffs++), | |
808 count = 1; | |
809 | |
810 pcx->bufOffs = 0; | |
811 | |
812 while (soffs < pcx->header->bpl) | |
813 { | |
814 if (data == dmPCXGetByte(row, len, soffs) && count < 63) | |
815 { | |
816 count++; | |
817 soffs++; | |
818 } | |
819 else | |
820 { | |
821 if (count == 1 && (data & 0xC0) != 0xC0) | |
822 { | |
823 if (!dmPCXPutByte(pcx, data)) | |
824 return FALSE; | |
825 } | |
826 else | |
827 { | |
828 if (!dmPCXPutByte(pcx, 0xC0 | count) || | |
829 !dmPCXPutByte(pcx, data)) | |
830 return FALSE; | |
831 } | |
832 | |
833 data = dmPCXGetByte(row, len, soffs++); | |
834 count = 1; | |
835 } | |
836 } | |
837 | |
838 if (count > 1) | |
839 { | |
840 if (!dmPCXPutByte(pcx, 0xC0 | count) || | |
841 !dmPCXPutByte(pcx, data)) | |
842 return FALSE; | |
843 } | |
844 | |
845 if (!dmPCXFlush(pcx)) | |
846 return FALSE; | |
847 } | |
848 | |
849 return TRUE; | |
850 } | |
851 | |
852 | |
853 int dmWritePCXImage(const char *filename, DMImage *img, int scale, BOOL paletted) | |
854 { | |
855 DMPCXData pcx; | |
856 DMPCXHeader hdr; | |
857 int res; | |
858 | |
859 // Create output file | |
860 pcx.buf = NULL; | |
861 pcx.format = paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE; | |
862 pcx.header = &hdr; | |
863 if ((pcx.fp = fopen(filename, "wb")) == NULL) | |
864 { | |
865 dmError("PCX: Could not open file '%s' for writing.\n", filename); | |
866 res = -15; | |
867 goto error; | |
868 } | |
869 | |
870 // Create PCX header | |
871 memset(&hdr, 0, sizeof(hdr)); | |
872 if (paletted) | |
873 { | |
874 int i; | |
875 for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++) | |
876 { | |
877 hdr.colormap[i].r = img->pal[i].r; | |
878 hdr.colormap[i].g = img->pal[i].g; | |
879 hdr.colormap[i].b = img->pal[i].b; | |
880 } | |
881 } | |
882 hdr.manufacturer = 10; | |
883 hdr.version = 5; | |
884 hdr.encoding = 1; | |
885 hdr.bpp = 8; | |
886 hdr.hres = img->width * scale; | |
887 hdr.vres = img->height * scale; | |
888 hdr.xmin = hdr.ymin = 0; | |
889 hdr.xmax = hdr.hres - 1; | |
890 hdr.ymax = hdr.vres - 1; | |
891 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); | |
892 hdr.bpl = (((img->width * scale) / 2) + 1) * 2; | |
893 hdr.palinfo = 1; | |
894 | |
895 dmMsg(1, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", | |
896 paletted, hdr.nplanes, hdr.bpp, hdr.bpl); | |
897 | |
898 pcx.bufLen = hdr.bpl * 4; | |
899 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) | |
900 { | |
901 dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n", | |
902 pcx.bufLen); | |
903 res = -11; | |
904 goto error; | |
905 } | |
906 | |
907 // Write PCX header | |
908 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) || | |
909 !dm_fwrite_byte(pcx.fp, hdr.version) || | |
910 !dm_fwrite_byte(pcx.fp, hdr.encoding) || | |
911 !dm_fwrite_byte(pcx.fp, hdr.bpp)) | |
912 { | |
913 dmError("PCX: Could not write basic header data.\n"); | |
914 res = -10; | |
915 goto error; | |
916 } | |
917 | |
918 if (!dm_fwrite_le16(pcx.fp, hdr.xmin) || | |
919 !dm_fwrite_le16(pcx.fp, hdr.ymin) || | |
920 !dm_fwrite_le16(pcx.fp, hdr.xmax) || | |
921 !dm_fwrite_le16(pcx.fp, hdr.ymax) || | |
922 !dm_fwrite_le16(pcx.fp, hdr.hres) || | |
923 !dm_fwrite_le16(pcx.fp, hdr.vres)) | |
924 { | |
925 dmError("PCX: Could not write image dimensions.\n"); | |
926 res = -9; | |
927 goto error; | |
928 } | |
929 | |
930 if (!dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) | |
931 { | |
932 dmError("PCX: Could not write colormap.\n"); | |
933 res = -8; | |
934 goto error; | |
935 } | |
936 | |
937 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) || | |
938 !dm_fwrite_byte(pcx.fp, hdr.nplanes) || | |
939 !dm_fwrite_le16(pcx.fp, hdr.bpl) || | |
940 !dm_fwrite_le16(pcx.fp, hdr.palinfo) || | |
941 !dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) | |
942 { | |
943 dmError("PCX: Could not write header remainder.\n"); | |
944 res = -7; | |
945 goto error; | |
946 } | |
947 | |
948 // Write image data | |
949 res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, scale, pcx.format); | |
950 | |
951 // Write VGA palette | |
952 if (paletted) | |
953 { | |
954 int i; | |
955 dm_fwrite_byte(pcx.fp, 0x0C); | |
956 | |
957 for (i = 0; i < img->ncolors; i++) | |
958 { | |
959 dm_fwrite_byte(pcx.fp, img->pal[i].r); | |
960 dm_fwrite_byte(pcx.fp, img->pal[i].g); | |
961 dm_fwrite_byte(pcx.fp, img->pal[i].b); | |
962 } | |
963 | |
964 // Pad the palette, if necessary | |
965 for (; i < 256; i++) | |
966 { | |
967 dm_fwrite_byte(pcx.fp, 0); | |
968 dm_fwrite_byte(pcx.fp, 0); | |
969 dm_fwrite_byte(pcx.fp, 0); | |
970 } | |
971 } | |
972 | |
973 error: | |
974 if (pcx.fp != NULL) | |
975 fclose(pcx.fp); | |
976 | |
977 dmFree(pcx.buf); | |
978 | |
979 return res; | |
980 } | |
981 | |
982 | |
983 static BOOL dmPCXDecodeRLERow(FILE *fp, uint8_t *buf, const size_t bufLen) | |
984 { | |
985 size_t offs = 0; | |
986 do | |
987 { | |
988 int count; | |
989 uint8_t data; | |
990 | |
991 if (!dm_fread_byte(fp, &data)) | |
992 return FALSE; | |
993 | |
994 if ((data & 0xC0) == 0xC0) | |
995 { | |
996 count = data & 0x3F; | |
997 if (!dm_fread_byte(fp, &data)) | |
998 return FALSE; | |
999 } | |
1000 else | |
1001 count = 1; | |
1002 | |
1003 while (count-- && offs < bufLen) | |
1004 buf[offs++] = data; | |
1005 | |
1006 } while (offs < bufLen); | |
1007 | |
1008 return TRUE; | |
1009 } | |
1010 | |
1011 | |
1012 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg) | |
1013 { | |
1014 DMImage *img; | |
1015 DMPCXData pcx; | |
1016 DMPCXHeader hdr; | |
1017 BOOL paletted; | |
1018 int res = 0, yc, xc; | |
1019 uint8_t *dp; | |
1020 | |
1021 pcx.buf = NULL; | |
1022 | |
1023 // Read PCX header | |
1024 if (!dm_fread_byte(fp, &hdr.manufacturer) || | |
1025 !dm_fread_byte(fp, &hdr.version) || | |
1026 !dm_fread_byte(fp, &hdr.encoding) || | |
1027 !dm_fread_byte(fp, &hdr.bpp)) | |
1028 { | |
1029 dmError("PCX: Could not read basic header data.\n"); | |
1030 res = -9; | |
1031 } | |
1032 | |
1033 if (hdr.manufacturer != 10 || | |
1034 hdr.version != 5 || | |
1035 hdr.encoding != 1 || | |
1036 hdr.bpp != 8) | |
1037 { | |
1038 dmError("PCX: Not a PCX file, or unsupported variant.\n"); | |
1039 res = -11; | |
1040 goto error; | |
1041 } | |
1042 | |
1043 if (!dm_fread_le16(fp, &hdr.xmin) || | |
1044 !dm_fread_le16(fp, &hdr.ymin) || | |
1045 !dm_fread_le16(fp, &hdr.xmax) || | |
1046 !dm_fread_le16(fp, &hdr.ymax) || | |
1047 !dm_fread_le16(fp, &hdr.hres) || | |
1048 !dm_fread_le16(fp, &hdr.vres)) | |
1049 { | |
1050 dmError("PCX: Could not read image dimensions.\n"); | |
1051 res = -8; | |
1052 goto error; | |
1053 } | |
1054 | |
1055 if (!dm_fread_str(fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) | |
1056 { | |
1057 dmError("PCX: Could not read colormap.\n"); | |
1058 res = -7; | |
1059 goto error; | |
1060 } | |
1061 | |
1062 if (!dm_fread_byte(fp, &hdr.reserved) || | |
1063 !dm_fread_byte(fp, &hdr.nplanes) || | |
1064 !dm_fread_le16(fp, &hdr.bpl) || | |
1065 !dm_fread_le16(fp, &hdr.palinfo) || | |
1066 !dm_fread_str(fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) | |
1067 { | |
1068 dmError("PCX: Could not read header remainder.\n"); | |
1069 res = -6; | |
1070 goto error; | |
1071 } | |
1072 | |
1073 if (hdr.nplanes != 3 && hdr.nplanes != 1) | |
1074 { | |
1075 dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes); | |
1076 res = -4; | |
1077 goto error; | |
1078 } | |
1079 | |
1080 // Allocate image | |
1081 if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL) | |
1082 { | |
1083 dmError("PCX: Could not allocate image structure.\n"); | |
1084 res = -5; | |
1085 goto error; | |
1086 } | |
1087 | |
1088 paletted = hdr.nplanes == 1; | |
1089 pcx.bufLen = hdr.nplanes * hdr.bpl; | |
1090 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) | |
1091 { | |
1092 dmError("PCX: Could not allocate RLE buffer.\n"); | |
1093 res = -3; | |
1094 goto error; | |
1095 } | |
1096 | |
1097 // Read image data | |
1098 dp = img->data; | |
1099 for (yc = 0; yc < img->height; yc++) | |
1100 { | |
1101 // Decode row of RLE'd data | |
1102 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen)) | |
1103 { | |
1104 dmError("PCX: Error decoding RLE data.\n"); | |
1105 res = -100; | |
1106 goto error; | |
1107 } | |
1108 | |
1109 // Decode bitplanes | |
1110 switch (hdr.nplanes) | |
1111 { | |
1112 case 1: | |
1113 memcpy(dp, pcx.buf, img->width); | |
1114 break; | |
1115 | |
1116 case 3: | |
1117 { | |
1118 uint8_t *dptr = dp, | |
1119 *sptr1 = pcx.buf, | |
1120 *sptr2 = sptr1 + hdr.bpl, | |
1121 *sptr3 = sptr2 + hdr.bpl; | |
1122 | |
1123 for (xc = 0; xc < img->width; xc++) | |
1124 { | |
1125 *dptr++ = *sptr1++; | |
1126 *dptr++ = *sptr2++; | |
1127 *dptr++ = *sptr3++; | |
1128 } | |
1129 } | |
1130 break; | |
1131 } | |
1132 | |
1133 dp += img->pitch; | |
1134 } | |
1135 | |
1136 // Read VGA palette | |
1137 if (paletted) | |
1138 { | |
1139 int i; | |
1140 uint8_t tmpb; | |
1141 | |
1142 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) | |
1143 goto error; | |
1144 | |
1145 for (i = 0; i < img->ncolors; i++) | |
1146 { | |
1147 if (!dm_fread_byte(fp, &tmpb)) | |
1148 goto error; | |
1149 img->pal[i].r = tmpb; | |
1150 | |
1151 if (!dm_fread_byte(fp, &tmpb)) | |
1152 goto error; | |
1153 img->pal[i].g = tmpb; | |
1154 | |
1155 if (!dm_fread_byte(fp, &tmpb)) | |
1156 goto error; | |
1157 img->pal[i].b = tmpb; | |
1158 } | |
1159 } | |
1160 | |
1161 error: | |
1162 dmFree(pcx.buf); | |
1163 return res; | |
1164 } | |
1165 | |
1166 | |
1167 int dmReadPCXImage(const char *filename, DMImage **pimg) | |
1168 { | |
1169 FILE *fp; | |
1170 int res; | |
1171 | |
1172 if ((fp = fopen(filename, "rb")) == NULL) | |
1173 { | |
1174 dmError("PCX: Could not open file '%s' for reading.\n", filename); | |
1175 return -15; | |
1176 } | |
1177 | |
1178 res = dmReadPCXImageFILE(fp, pimg); | |
1179 | |
1180 fclose(fp); | |
1181 return res; | |
1182 } | |
1183 | |
1184 | |
1185 int fmtProbePNGImageFILE(FILE *fp) | |
1186 { | |
1187 uint8_t buf[6]; | |
1188 // if (!dm_fread_str(fp, | |
1189 return DM_PROBE_SCORE_FALSE; | |
1190 } | |
1191 | |
1192 | |
1193 int fmtProbePCXImageFILE(FILE *fp) | |
1194 { | |
1195 DMPCXHeader hdr; | |
1196 | |
1197 if (!dm_fread_byte(fp, &hdr.manufacturer) || | |
1198 !dm_fread_byte(fp, &hdr.version) || | |
1199 !dm_fread_byte(fp, &hdr.encoding) || | |
1200 !dm_fread_byte(fp, &hdr.bpp)) | |
1201 return DM_PROBE_SCORE_FALSE; | |
1202 | |
1203 if (hdr.manufacturer == 10 && | |
1204 hdr.version == 5 && | |
1205 hdr.encoding == 1 && | |
1206 hdr.bpp == 8) | |
1207 return DM_PROBE_SCORE_GOOD; | |
1208 | |
1209 return DM_PROBE_SCORE_FALSE; | |
1210 } | |
1211 | |
1212 | |
1213 #ifdef UNFINISHED | |
1214 int dmConvertBMP2(DMImage *screen, const DM64Image *img) | |
1215 { | |
1216 int yc; | |
1217 uint8_t *dp = screen->data; | |
1218 | |
1219 for (yc = 0; yc < screen->height; yc++) | |
1220 { | |
1221 uint8_t *d = dp; | |
1222 const int y = yc / 8, yb = yc & 7; | |
1223 const int scroffsy = y * C64_SCR_CH_WIDTH; | |
1224 const int bmoffsy = y * C64_SCR_WIDTH; | |
1225 int xc; | |
1226 | |
1227 for (xc = 0; xc < screen->width / 2; xc++) | |
1228 { | |
1229 const int x = xc / 4; | |
1230 const int scroffs = scroffsy + x; | |
1231 const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; | |
1232 const int v = 6 - ((xc * 2) & 6); | |
1233 uint8_t c; | |
1234 | |
1235 switch ((b >> v) & 3) | |
1236 { | |
1237 case 0: c = img->bgcolor; break; | |
1238 case 1: c = img->screen[0][scroffs] >> 4; break; | |
1239 case 2: c = img->screen[0][scroffs] & 15; break; | |
1240 case 3: c = img->color[0][scroffs] & 15; break; | |
1241 } | |
1242 | |
1243 *d++ = c; | |
1244 *d++ = c; | |
1245 } | |
1246 | |
1247 dp += screen->pitch; | |
1248 } | |
1249 | |
1250 return 0; | |
1251 } | |
1252 #endif | |
1253 | |
1254 | |
1255 int dmWriteImage(char *filename, DMImage *image, int format, BOOL paletted, int scale, int bpp) | |
1256 { | |
1257 switch (format) | |
1258 { | |
1259 #ifdef HAVE_LIBPNG | |
1260 case OUTFMT_PNG: | |
1261 return dmWritePNGImage(filename, image, scale, paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA); | |
1262 #endif | |
1263 | |
1264 case OUTFMT_PPM: | |
1265 return dmWritePPMImage(filename, image, scale); | |
1266 | |
1267 case OUTFMT_PCX: | |
1268 return dmWritePCXImage(filename, image, scale, paletted); | |
1269 | |
1270 case OUTFMT_ARAW: | |
1271 { | |
1272 int res; | |
1273 char *palFilename = dm_strdup_printf("%s.pal", filename); | |
1274 res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << bpp); | |
1275 dmFree(palFilename); | |
1276 if (res != 0) | |
1277 return res; | |
1278 | |
1279 return dmWriteIFFMasterRAWImage(filename, image, scale, bpp); | |
1280 } | |
1281 | |
1282 default: | |
1283 return FALSE; | |
1284 } | |
1285 } | |
1286 | |
1287 | |
1288 int dmDumpSpritesAndChars(FILE *inFile) | |
1289 { | |
1290 int dataOffs, itemCount, outWidth, outWidthPX, outHeight; | |
1291 size_t bufSize; | |
1292 uint8_t *bufData; | |
1293 | |
1294 switch (optInFormat) | |
1295 { | |
1296 case INFMT_CHAR: | |
1297 bufSize = C64_CHR_SIZE; | |
1298 outWidth = C64_CHR_WIDTH; | |
1299 outWidthPX = C64_CHR_WIDTH_PX; | |
1300 outHeight = C64_CHR_HEIGHT; | |
1301 break; | |
1302 case INFMT_SPRITE: | |
1303 bufSize = C64_SPR_SIZE; | |
1304 outWidth = C64_SPR_WIDTH; | |
1305 outWidthPX = C64_SPR_WIDTH_PX; | |
1306 outHeight = C64_SPR_HEIGHT; | |
1307 break; | |
1308 default: | |
1309 dmError("Invalid input format %d, internal error.\n", optInFormat); | |
1310 return -1; | |
1311 } | |
1312 | |
1313 if ((bufData = dmMalloc(bufSize)) == NULL) | |
1314 { | |
1315 dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize); | |
1316 return -2; | |
1317 } | |
1318 | |
1319 | |
1320 dataOffs = optInSkip; | |
1321 itemCount = 0; | |
1322 | |
1323 if (optOutFormat == OUTFMT_ANSI || optOutFormat == OUTFMT_ASCII) | |
1324 { | |
1325 BOOL error = FALSE; | |
1326 FILE *outFile; | |
1327 | |
1328 if (optOutFilename == NULL) | |
1329 outFile = stdout; | |
1330 else | |
1331 if ((outFile = fopen(optOutFilename, "w")) == NULL) | |
1332 { | |
1333 int res = errno; | |
1334 dmError("Error opening output file '%s'. (%s)\n", | |
1335 optOutFilename, strerror(res)); | |
1336 goto error; | |
1337 } | |
1338 | |
1339 while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount)) | |
1340 { | |
1341 memset(bufData, 0, bufSize); | |
1342 | |
1343 if (fread(bufData, 1, bufSize, inFile) != bufSize) | |
1344 { | |
1345 dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", | |
1346 bufSize, dataOffs); | |
1347 error = TRUE; | |
1348 } | |
1349 | |
1350 fprintf(outFile, "---- : -------------- #%d\n", itemCount); | |
1351 | |
1352 switch (optInFormat) | |
1353 { | |
1354 case INFMT_CHAR: | |
1355 dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); | |
1356 break; | |
1357 case INFMT_SPRITE: | |
1358 dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); | |
1359 break; | |
1360 } | |
1361 itemCount++; | |
1362 } | |
1363 | |
1364 fclose(outFile); | |
1365 } | |
1366 else | |
1367 if (optOutFormat == OUTFMT_PNG || optOutFormat == OUTFMT_PPM || optOutFormat == OUTFMT_PCX) | |
1368 { | |
1369 DMImage *outImage = NULL; | |
1370 char *outFilename = NULL; | |
1371 int outX = 0, outY = 0, err; | |
1372 | |
1373 #ifndef HAVE_LIBPNG | |
1374 if (optOutFormat == OUTFMT_PNG) | |
1375 { | |
1376 dmError("PNG output format support not compiled in, sorry.\n"); | |
1377 goto error; | |
1378 } | |
1379 #endif | |
1380 | |
1381 if (optSequential) | |
1382 { | |
1383 if (optOutFilename == NULL) | |
1384 { | |
1385 dmError("Sequential image output requires filename template.\n"); | |
1386 goto error; | |
1387 } | |
1388 | |
1389 outImage = dmImageAlloc(outWidthPX, outHeight); | |
1390 dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n", | |
1391 optItemCount, | |
1392 outImage->width, outImage->height, | |
1393 outImage->width * optScale, outImage->height * optScale); | |
1394 } | |
1395 else | |
1396 { | |
1397 int outIWidth, outIHeight; | |
1398 if (optItemCount <= 0) | |
1399 { | |
1400 dmError("Single-image output requires count to be set (-n).\n"); | |
1401 goto error; | |
1402 } | |
1403 | |
1404 outIWidth = optPlanedWidth; | |
1405 outIHeight = (optItemCount / optPlanedWidth); | |
1406 if (optItemCount % optPlanedWidth) | |
1407 outIHeight++; | |
1408 | |
1409 outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight); | |
1410 dmMsg(1, "Outputting image %d x %d -> %d x %d.\n", | |
1411 outImage->width, outImage->height, | |
1412 outImage->width * optScale, outImage->height * optScale); | |
1413 } | |
1414 | |
1415 outImage->constpal = TRUE; | |
1416 outImage->pal = dmC64Palette; | |
1417 outImage->ncolors = C64_NCOLORS; | |
1418 outImage->ctrans = 255; | |
1419 | |
1420 while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount)) | |
1421 { | |
1422 memset(bufData, 0, bufSize); | |
1423 | |
1424 if (fread(bufData, 1, bufSize, inFile) != bufSize) | |
1425 { | |
1426 dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", | |
1427 bufSize, dataOffs); | |
1428 break; | |
1429 } | |
1430 | |
1431 if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight, | |
1432 bufData, outWidth, outHeight, optInMulticolor, optColors)) != 0) | |
1433 { | |
1434 dmError("Internal error in conversion of raw data to bitmap: %d.\n", err); | |
1435 break; | |
1436 } | |
1437 | |
1438 if (optSequential) | |
1439 { | |
1440 outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, outFormatList[optOutFormat]); | |
1441 if (outFilename == NULL) | |
1442 { | |
1443 dmError("Could not allocate memory for filename template?\n"); | |
1444 goto error; | |
1445 } | |
1446 | |
1447 dmWriteImage(outFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); | |
1448 dmFree(outFilename); | |
1449 } | |
1450 else | |
1451 { | |
1452 if (++outX >= optPlanedWidth) | |
1453 { | |
1454 outX = 0; | |
1455 outY++; | |
1456 } | |
1457 } | |
1458 | |
1459 itemCount++; | |
1460 } | |
1461 | |
1462 if (!optSequential) | |
1463 { | |
1464 dmWriteImage(optOutFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); | |
1465 } | |
1466 | |
1467 dmImageFree(outImage); | |
1468 } | |
1469 | |
1470 dmFree(bufData); | |
1471 return 0; | |
1472 | |
1473 error: | |
1474 dmFree(bufData); | |
1475 return -1; | |
1476 } | |
1477 | |
1478 | |
1479 int main(int argc, char *argv[]) | |
1480 { | |
1481 FILE *inFile; | |
1482 int i, optInImageFormat; | |
1483 | |
1484 // Default colors | |
1485 for (i = 0; i < C64_MAX_COLORS; i++) | |
1486 optColors[i] = i + 1; | |
1487 | |
1488 // Initialize and parse commandline | |
1489 dmInitProg("gfxconv", "Simple c64 graphics converter", "0.4", NULL, NULL); | |
1490 | |
1491 if (!dmArgsProcess(argc, argv, optList, optListN, | |
1492 argHandleOpt, argHandleFile, TRUE)) | |
1493 exit(1); | |
1494 | |
1495 // Determine input format, if not specified' | |
1496 if (optInFormat == INFMT_AUTO && optInFilename != NULL) | |
1497 { | |
1498 char *dext = strrchr(optInFilename, '.'); | |
1499 if (dext) | |
1500 { | |
1501 dext++; | |
1502 if (!strcasecmp(dext, "fnt") || !strcasecmp(dext, "chr")) | |
1503 optInFormat = INFMT_CHAR; | |
1504 else if (!strcasecmp(dext, "spr")) | |
1505 optInFormat = INFMT_SPRITE; | |
1506 else if (!strcasecmp(dext, "png") || !strcasecmp(dext, "pcx")) | |
1507 optInFormat = INFMT_IMAGE; | |
1508 } | |
1509 } | |
1510 | |
1511 if (optInFilename == NULL) | |
1512 { | |
1513 if (optInFormat == INFMT_AUTO) | |
1514 { | |
1515 dmError("Standard input cannot be used without specifying input format.\n"); | |
1516 exit(3); | |
1517 } | |
1518 inFile = stdin; | |
1519 } | |
1520 else | |
1521 if ((inFile = fopen(optInFilename, "rb")) == NULL) | |
1522 { | |
1523 int res = errno; | |
1524 dmError("Error opening input file '%s'. (%s)\n", | |
1525 optInFilename, strerror(res)); | |
1526 exit(3); | |
1527 } | |
1528 | |
1529 if (optInFormat == INFMT_AUTO) | |
1530 { | |
1531 // Skip, if needed | |
1532 if (fseek(inFile, optInSkip, SEEK_SET) != 0) | |
1533 { | |
1534 int res = errno; | |
1535 dmError("Could not seek to file position %d (0x%x): %s\n", | |
1536 optInSkip, optInSkip, strerror(res)); | |
1537 exit(3); | |
1538 } | |
1539 #if 0 | |
1540 if (optInFormat == INFMT_AUTO) | |
1541 { | |
1542 int ret = dmC64ProbeGeneric | |
1543 } | |
1544 #endif | |
1545 | |
1546 if (optInFormat == INFMT_AUTO || optInFormat == INFMT_IMAGE) | |
1547 { | |
1548 if (fmtProbePNGImageFILE(inFile)) | |
1549 { | |
1550 optInFormat = INFMT_IMAGE; | |
1551 optInImageFormat = OUTFMT_PNG; | |
1552 } | |
1553 else | |
1554 if (fmtProbePCXImageFILE(inFile)) | |
1555 { | |
1556 optInFormat = INFMT_IMAGE; | |
1557 optInImageFormat = OUTFMT_PCX; | |
1558 } | |
1559 else | |
1560 if (optInFormat == INFMT_IMAGE) | |
1561 { | |
1562 dmError("Unsupported image input format.\n"); | |
1563 exit(4); | |
1564 } | |
1565 } | |
1566 } | |
1567 | |
1568 if (optInFormat == INFMT_AUTO) | |
1569 { | |
1570 dmError("No input format specified, and could not be determined automatically.\n"); | |
1571 exit(1); | |
1572 } | |
1573 | |
1574 // Skip, if needed | |
1575 if (fseek(inFile, optInSkip, SEEK_SET) != 0) | |
1576 { | |
1577 int res = errno; | |
1578 dmError("Could not seek to file position %d (0x%x): %s\n", | |
1579 optInSkip, optInSkip, strerror(res)); | |
1580 exit(3); | |
1581 } | |
1582 | |
1583 switch (optInFormat) | |
1584 { | |
1585 case INFMT_SPRITE: | |
1586 case INFMT_CHAR: | |
1587 dmDumpSpritesAndChars(inFile); | |
1588 break; | |
1589 | |
1590 case INFMT_BITMAP: | |
1591 case INFMT_IMAGE: | |
1592 { | |
1593 DMImage *img; | |
1594 int res; | |
1595 | |
1596 if (optOutFilename == NULL) | |
1597 { | |
1598 dmError("Output filename not set, required for image formats.\n"); | |
1599 exit(3); | |
1600 } | |
1601 | |
1602 // Read input | |
1603 switch (optInImageFormat) | |
1604 { | |
1605 case OUTFMT_PCX: | |
1606 res = dmReadPCXImageFILE(inFile, &img); | |
1607 break; | |
1608 case OUTFMT_PNG: | |
1609 // res = dmReadPNGImageFILE(inFile, &img); | |
1610 break; | |
1611 } | |
1612 | |
1613 switch (optOutFormat) | |
1614 { | |
1615 case OUTFMT_PCX: | |
1616 case OUTFMT_PPM: | |
1617 case OUTFMT_PNG: | |
1618 case OUTFMT_ARAW: | |
1619 res = dmWriteImage(optOutFilename, img, optOutFormat, optPaletted, optScale, optBPP); | |
1620 break; | |
1621 } | |
1622 } | |
1623 break; | |
1624 } | |
1625 | |
1626 fclose(inFile); | |
1627 | |
1628 exit(0); | |
1629 return 0; | |
1630 } |