comparison gfxconv.c @ 435:e4a3f183e463

Modularize some more.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 03 Nov 2012 16:08:30 +0200
parents b583a682f12d
children 2a4de5fe4003
comparison
equal deleted inserted replaced
434:380c226c75af 435:e4a3f183e463
7 */ 7 */
8 #include <errno.h> 8 #include <errno.h>
9 #include "dmlib.h" 9 #include "dmlib.h"
10 #include "dmargs.h" 10 #include "dmargs.h"
11 #include "dmfile.h" 11 #include "dmfile.h"
12 #include "dmbstr.h"
13 #include "dmmutex.h" 12 #include "dmmutex.h"
13 #include "libgfx.h"
14 #include "lib64gfx.h" 14 #include "lib64gfx.h"
15 15
16 //#define UNFINISHED 1 16 //#define UNFINISHED 1
17
18 #ifdef DM_USE_LIBPNG
19 #include <png.h>
20 #endif
21
22 enum
23 {
24 IMGFMT_PNG,
25 IMGFMT_PPM,
26 IMGFMT_PCX,
27 IMGFMT_ARAW,
28
29 IMGFMT_LAST
30 };
31
32 static const char *imageFormatList[IMGFMT_LAST] =
33 {
34 "PNG",
35 "PPM",
36 "PCX",
37 "ARAW",
38 };
39 17
40 enum 18 enum
41 { 19 {
42 FFMT_AUTO = 0, 20 FFMT_AUTO = 0,
43 21
118 int optInFormat = FFMT_AUTO, 96 int optInFormat = FFMT_AUTO,
119 optOutFormat = FFMT_ASCII, 97 optOutFormat = FFMT_ASCII,
120 optInSubFormat = IMGFMT_PNG, 98 optInSubFormat = IMGFMT_PNG,
121 optOutSubFormat = IMGFMT_PNG, 99 optOutSubFormat = IMGFMT_PNG,
122 optItemCount = -1, 100 optItemCount = -1,
123 optScale = 2,
124 optPlanedWidth = 1, 101 optPlanedWidth = 1,
125 optForcedFormat = -1, 102 optForcedFormat = -1;
126 optBPP = 4;
127 int optInSkip = 0; 103 int optInSkip = 0;
128 BOOL optInMulticolor = FALSE, 104 BOOL optInMulticolor = FALSE,
129 optSequential = FALSE, 105 optSequential = FALSE;
130 optPaletted = FALSE,
131 optInterleave = FALSE;
132 int optColors[C64_MAX_COLORS]; 106 int optColors[C64_MAX_COLORS];
133 107
108 DMImageSpec optSpec =
109 {
110 .scale = 2,
111 .nplanes = 4,
112 .interleave = FALSE,
113 .paletted = FALSE,
114 .format = 0,
115 };
134 116
135 static DMOptArg optList[] = 117 static DMOptArg optList[] =
136 { 118 {
137 { 0, '?', "help", "Show this help", OPT_NONE }, 119 { 0, '?', "help", "Show this help", OPT_NONE },
138 { 15, 'v', "verbose", "Increase verbosity", OPT_NONE }, 120 { 15, 'v', "verbose", "Increase verbosity", OPT_NONE },
342 if (tmp < 1 || tmp > 50) 324 if (tmp < 1 || tmp > 50)
343 { 325 {
344 dmError("Invalid scale value '%s'.\n", optArg); 326 dmError("Invalid scale value '%s'.\n", optArg);
345 return FALSE; 327 return FALSE;
346 } 328 }
347 optScale = tmp; 329 optSpec.scale = tmp;
348 } 330 }
349 break; 331 break;
350 332
351 case 11: 333 case 11:
352 { 334 {
359 optPlanedWidth = tmp; 341 optPlanedWidth = tmp;
360 } 342 }
361 break; 343 break;
362 344
363 case 12: 345 case 12:
364 optPaletted = TRUE; 346 optSpec.paletted = TRUE;
365 break; 347 break;
366 348
367 case 13: 349 case 13:
368 { 350 {
369 int tmp = atoi(optArg); 351 int tmp = atoi(optArg);
370 if (tmp < 1 || tmp > 8) 352 if (tmp < 1 || tmp > 8)
371 { 353 {
372 dmError("Invalid bitplanes/bpp value '%s'.\n", optArg); 354 dmError("Invalid bitplanes/bpp value '%s'.\n", optArg);
373 return FALSE; 355 return FALSE;
374 } 356 }
375 optBPP = tmp; 357 optSpec.nplanes = tmp;
376 } 358 }
377 break; 359 break;
378 360
379 case 14: 361 case 14:
380 optInterleave = TRUE; 362 optSpec.interleave = TRUE;
381 break; 363 break;
382 364
383 default: 365 default:
384 dmError("Unknown option '%s'.\n", currArg); 366 dmError("Unknown option '%s'.\n", currArg);
385 return FALSE; 367 return FALSE;
487 } 469 }
488 (*offs)++; 470 (*offs)++;
489 } 471 }
490 472
491 473
492 int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, Uint8 *, size_t), int scale, int format)
493 {
494 int x, y, yscale, xscale, res = 0, rowSize, rowWidth;
495 Uint8 *row = NULL;
496
497 // Allocate memory for row buffer
498 rowWidth = img->width * scale;
499 rowSize = rowWidth * dmImageGetBytesPerPixel(format);
500
501 if ((row = dmMalloc(rowSize + 16)) == NULL)
502 {
503 res = DMERR_MALLOC;
504 goto done;
505 }
506
507 // Generate the image
508 for (y = 0; y < img->height; y++)
509 {
510 Uint8 *ptr = row,
511 *ptr1 = row,
512 *ptr2 = ptr1 + rowWidth,
513 *ptr3 = ptr2 + rowWidth;
514
515 for (x = 0; x < img->width; x++)
516 {
517 Uint8 c = img->data[(y * img->pitch) + x], qr, qg, qb, qa;
518 switch (format)
519 {
520 case DM_IFMT_PALETTE:
521 for (xscale = 0; xscale < scale; xscale++)
522 *ptr++ = c;
523 break;
524
525 case DM_IFMT_RGBA:
526 qr = img->pal[c].r;
527 qg = img->pal[c].g;
528 qb = img->pal[c].b;
529 qa = (c == img->ctrans) ? 0 : 255;
530
531 for (xscale = 0; xscale < scale; xscale++)
532 {
533 *ptr++ = qr;
534 *ptr++ = qg;
535 *ptr++ = qb;
536 *ptr++ = qa;
537 }
538 break;
539
540 case DM_IFMT_RGB:
541 qr = img->pal[c].r;
542 qg = img->pal[c].g;
543 qb = img->pal[c].b;
544
545 for (xscale = 0; xscale < scale; xscale++)
546 {
547 *ptr++ = qr;
548 *ptr++ = qg;
549 *ptr++ = qb;
550 }
551 break;
552
553 case DM_IFMT_RGB_PLANE:
554 qr = img->pal[c].r;
555 qg = img->pal[c].g;
556 qb = img->pal[c].b;
557
558 for (xscale = 0; xscale < scale; xscale++)
559 {
560 *ptr1++ = qr;
561 *ptr2++ = qg;
562 *ptr3++ = qb;
563 }
564 break;
565 }
566 }
567
568 for (yscale = 0; yscale < scale; yscale++)
569 {
570 if (!writeRowCB(cbdata, row, rowSize))
571 {
572 res = DMERR_FWRITE;
573 goto done;
574 }
575 }
576 }
577
578 done:
579 dmFree(row);
580 return res;
581 }
582
583
584 #define DMCOL(x) (((x) >> 4) & 0xf)
585
586 int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors)
587 {
588 FILE *fp;
589 int i;
590
591 if ((fp = fopen(filename, "w")) == NULL)
592 {
593 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename);
594 return -15;
595 }
596
597 for (i = 0; i < ncolors; i++)
598 {
599 int color;
600 if (i < img->ncolors)
601 {
602 color = (DMCOL(img->pal[i].r) << 8) |
603 (DMCOL(img->pal[i].g) << 4) |
604 (DMCOL(img->pal[i].b));
605 }
606 else
607 color = 0;
608
609 fprintf(fp, "\tdc.w $%04X\n", color);
610 }
611
612 return 0;
613 }
614
615
616 int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, int scale, int nplanes, BOOL interleave)
617 {
618 int xc, yc, plane, res;
619 DMBitStream bs;
620
621 if ((res = dmInitBitStream(&bs, fp)) != DMERR_OK)
622 return res;
623
624 if (interleave)
625 {
626 // Output bitplanes in interleaved format (each plane of line sequentially)
627 for (yc = 0; yc < img->height; yc++)
628 {
629 for (plane = 0; plane < nplanes; plane++)
630 {
631 Uint8 *sp = img->data + yc * img->pitch;
632 for (xc = 0; xc < img->width; xc++)
633 {
634 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
635 return DMERR_FWRITE;
636 }
637 }
638 }
639 }
640 else
641 {
642 // Output each bitplane in sequence
643 for (plane = 0; plane < nplanes; plane++)
644 {
645 for (yc = 0; yc < img->height; yc++)
646 {
647 Uint8 *sp = img->data + yc * img->pitch;
648 for (xc = 0; xc < img->width; xc++)
649 {
650 if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1))
651 return DMERR_FWRITE;
652 }
653 }
654 }
655 }
656
657 return dmFlushBitStream(&bs);
658 }
659
660 int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, int scale, int nplanes, BOOL interleave)
661 {
662 FILE *fp;
663 int res;
664
665 if ((fp = fopen(filename, "wb")) == NULL)
666 {
667 dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename);
668 return DMERR_FOPEN;
669 }
670
671 res = dmWriteIFFMasterRAWImageFILE(fp, img, scale, nplanes, interleave);
672
673 fclose(fp);
674 return res;
675 }
676
677
678 static BOOL dmWritePPMRow(void *cbdata, Uint8 *row, size_t len)
679 {
680 return fwrite(row, sizeof(Uint8), len, (FILE *) cbdata) == len;
681 }
682
683
684 int dmWritePPMImageFILE(FILE *fp, DMImage *img, int scale)
685 {
686 // Write PPM header
687 fprintf(fp,
688 "P6\n%d %d\n255\n",
689 img->width * scale, img->height * scale);
690
691 // Write image data
692 return dmWriteImageData(img, (void *) fp, dmWritePPMRow, scale, DM_IFMT_RGB);
693 }
694
695
696 int dmWritePPMImage(const char *filename, DMImage *img, int scale)
697 {
698 FILE *fp;
699 int res;
700
701 // Create output file
702 if ((fp = fopen(filename, "wb")) == NULL)
703 {
704 dmError("PPM: could not open file '%s' for writing.\n", filename);
705 return DMERR_FOPEN;
706 }
707
708 res = dmWritePPMImageFILE(fp, img, scale);
709
710 fclose(fp);
711 return res;
712 }
713
714
715 #ifdef DM_USE_LIBPNG
716 static BOOL dmWritePNGRow(void *cbdata, Uint8 *row, size_t len)
717 {
718 png_structp png_ptr = cbdata;
719 (void) len;
720
721 if (setjmp(png_jmpbuf(png_ptr)))
722 return FALSE;
723
724 png_write_row(png_ptr, row);
725
726 return TRUE;
727 }
728
729
730 int dmWritePNGImageFILE(FILE *fp, DMImage *img, int scale, int format)
731 {
732 png_structp png_ptr = NULL;
733 png_infop info_ptr = NULL;
734 png_colorp palette = NULL;
735 int fmt, res = DMERR_OK;
736
737 // Create PNG structures
738 png_ptr = png_create_write_struct(
739 PNG_LIBPNG_VER_STRING,
740 NULL, NULL, NULL);
741
742 if (png_ptr == NULL)
743 {
744 dmError("PNG: png_create_write_struct() failed.\n");
745 res = DMERR_MALLOC;
746 goto error;
747 }
748
749 info_ptr = png_create_info_struct(png_ptr);
750 if (info_ptr == NULL)
751 {
752 dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr);
753 res = DMERR_INIT_FAIL;
754 goto error;
755 }
756
757 if (setjmp(png_jmpbuf(png_ptr)))
758 {
759 dmError("PNG: Error during image writing..\n");
760 res = DMERR_INIT_FAIL;
761 goto error;
762 }
763
764 png_init_io(png_ptr, fp);
765
766 // Write PNG header info
767 switch (format)
768 {
769 case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break;
770 case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break;
771 case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break;
772 default:
773 dmError("PNG: Internal error, unsupported image format %d.\n", format);
774 goto error;
775 }
776
777 png_set_IHDR(png_ptr, info_ptr,
778 img->width * scale,
779 img->height * scale,
780 8, /* bits per component */
781 fmt,
782 PNG_INTERLACE_NONE,
783 PNG_COMPRESSION_TYPE_DEFAULT,
784 PNG_FILTER_TYPE_DEFAULT);
785
786 // Palette
787 if (format == DM_IFMT_PALETTE)
788 {
789 int i;
790
791 palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
792 if (palette == NULL)
793 {
794 dmError("PNG: Could not allocate palette structure.");
795 goto error;
796 }
797
798 memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
799
800 for (i = 0; i < img->ncolors; i++)
801 {
802 palette[i].red = img->pal[i].r;
803 palette[i].green = img->pal[i].g;
804 palette[i].blue = img->pal[i].b;
805 }
806
807 png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH);
808 }
809
810 // png_set_gAMA(png_ptr, info_ptr, 2.2);
811
812 png_write_info(png_ptr, info_ptr);
813
814
815 // Write compressed image data
816 dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, scale, format);
817
818 // Write footer
819 png_write_end(png_ptr, NULL);
820
821 error:
822 png_free(png_ptr, palette);
823 palette = NULL;
824
825 if (png_ptr && info_ptr)
826 png_destroy_write_struct(&png_ptr, &info_ptr);
827
828 return res;
829 }
830
831
832 int dmWritePNGImage(const char *filename, DMImage *img, int scale, int format)
833 {
834 int res;
835 FILE *fp;
836
837 if ((fp = fopen(filename, "wb")) == NULL)
838 {
839 dmError("PNG: could not open file '%s' for writing.\n", filename);
840 return DMERR_FOPEN;
841 }
842
843 res = dmWritePNGImageFILE(fp, img, scale, format);
844
845 fclose(fp);
846 return res;
847 }
848 #endif
849
850
851 typedef struct
852 {
853 Uint8 r,g,b;
854 } DMPCXColor;
855
856
857 typedef struct
858 {
859 Uint8 manufacturer,
860 version,
861 encoding,
862 bpp;
863 Uint16 xmin, ymin, xmax, ymax;
864 Uint16 hres, vres;
865 DMPCXColor colormap[16];
866 Uint8 reserved;
867 Uint8 nplanes;
868 Uint16 bpl;
869 Uint16 palinfo;
870 Uint8 filler[58];
871 } DMPCXHeader;
872
873 typedef struct
874 {
875 DMPCXHeader *header;
876 Uint8 *buf;
877 size_t bufLen, bufOffs;
878 int format;
879 FILE *fp;
880 } DMPCXData;
881
882
883 static inline Uint8 dmPCXGetByte(Uint8 *row, const size_t len, const size_t soffs)
884 {
885 return (soffs < len) ? row[soffs] : 0;
886 }
887
888 static BOOL dmPCXFlush(DMPCXData *pcx)
889 {
890 BOOL ret = fwrite(pcx->buf, sizeof(Uint8), pcx->bufOffs, pcx->fp) == pcx->bufOffs;
891 pcx->bufOffs = 0;
892 return ret;
893 }
894
895 static inline BOOL dmPCXPutByte(DMPCXData *pcx, const Uint8 val)
896 {
897 if (pcx->bufOffs < pcx->bufLen)
898 {
899 pcx->buf[pcx->bufOffs++] = val;
900 return TRUE;
901 }
902 else
903 return dmPCXFlush(pcx);
904 }
905
906 BOOL dmWritePCXRow(void *cbdata, Uint8 *row, size_t len)
907 {
908 DMPCXData *pcx = (DMPCXData *) cbdata;
909 int plane;
910
911 for (plane = 0; plane < pcx->header->nplanes; plane++)
912 {
913 size_t soffs = 0;
914 Uint8 data = dmPCXGetByte(row, len, soffs++),
915 count = 1;
916
917 pcx->bufOffs = 0;
918
919 while (soffs < pcx->header->bpl)
920 {
921 if (data == dmPCXGetByte(row, len, soffs) && count < 63)
922 {
923 count++;
924 soffs++;
925 }
926 else
927 {
928 if (count == 1 && (data & 0xC0) != 0xC0)
929 {
930 if (!dmPCXPutByte(pcx, data))
931 return FALSE;
932 }
933 else
934 {
935 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
936 !dmPCXPutByte(pcx, data))
937 return FALSE;
938 }
939
940 data = dmPCXGetByte(row, len, soffs++);
941 count = 1;
942 }
943 }
944
945 if (count > 1)
946 {
947 if (!dmPCXPutByte(pcx, 0xC0 | count) ||
948 !dmPCXPutByte(pcx, data))
949 return FALSE;
950 }
951
952 if (!dmPCXFlush(pcx))
953 return FALSE;
954 }
955
956 return TRUE;
957 }
958
959
960 int dmWritePCXImageFILE(FILE *fp, DMImage *img, int scale, BOOL paletted)
961 {
962 DMPCXData pcx;
963 DMPCXHeader hdr;
964 int res;
965
966 // Create output file
967 pcx.buf = NULL;
968 pcx.format = paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE;
969 pcx.header = &hdr;
970 pcx.fp = fp;
971
972 // Create PCX header
973 memset(&hdr, 0, sizeof(hdr));
974 if (paletted)
975 {
976 int i;
977 for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++)
978 {
979 hdr.colormap[i].r = img->pal[i].r;
980 hdr.colormap[i].g = img->pal[i].g;
981 hdr.colormap[i].b = img->pal[i].b;
982 }
983 }
984 hdr.manufacturer = 10;
985 hdr.version = 5;
986 hdr.encoding = 1;
987 hdr.bpp = 8;
988 hdr.hres = img->width * scale;
989 hdr.vres = img->height * scale;
990 hdr.xmin = hdr.ymin = 0;
991 hdr.xmax = hdr.hres - 1;
992 hdr.ymax = hdr.vres - 1;
993 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format);
994 hdr.palinfo = 1;
995 res = (img->width * scale);
996 hdr.bpl = res / 2;
997 if (res % 2) hdr.bpl++;
998 hdr.bpl *= 2;
999
1000 dmMsg(2, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n",
1001 paletted, hdr.nplanes, hdr.bpp, hdr.bpl);
1002
1003 pcx.bufLen = hdr.bpl * 4;
1004 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
1005 {
1006 dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n",
1007 pcx.bufLen);
1008 res = DMERR_MALLOC;
1009 goto error;
1010 }
1011
1012 // Write PCX header
1013 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) ||
1014 !dm_fwrite_byte(pcx.fp, hdr.version) ||
1015 !dm_fwrite_byte(pcx.fp, hdr.encoding) ||
1016 !dm_fwrite_byte(pcx.fp, hdr.bpp))
1017 {
1018 dmError("PCX: Could not write basic header data.\n");
1019 res = DMERR_FWRITE;
1020 goto error;
1021 }
1022
1023 if (!dm_fwrite_le16(pcx.fp, hdr.xmin) ||
1024 !dm_fwrite_le16(pcx.fp, hdr.ymin) ||
1025 !dm_fwrite_le16(pcx.fp, hdr.xmax) ||
1026 !dm_fwrite_le16(pcx.fp, hdr.ymax) ||
1027 !dm_fwrite_le16(pcx.fp, hdr.hres) ||
1028 !dm_fwrite_le16(pcx.fp, hdr.vres))
1029 {
1030 dmError("PCX: Could not write image dimensions.\n");
1031 res = DMERR_FWRITE;
1032 goto error;
1033 }
1034
1035 if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
1036 {
1037 dmError("PCX: Could not write colormap.\n");
1038 res = DMERR_FWRITE;
1039 goto error;
1040 }
1041
1042 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) ||
1043 !dm_fwrite_byte(pcx.fp, hdr.nplanes) ||
1044 !dm_fwrite_le16(pcx.fp, hdr.bpl) ||
1045 !dm_fwrite_le16(pcx.fp, hdr.palinfo) ||
1046 !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
1047 {
1048 dmError("PCX: Could not write header remainder.\n");
1049 res = DMERR_FWRITE;
1050 goto error;
1051 }
1052
1053 // Write image data
1054 res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, scale, pcx.format);
1055
1056 // Write VGA palette
1057 if (paletted)
1058 {
1059 int i;
1060 dm_fwrite_byte(pcx.fp, 0x0C);
1061 dmMsg(2, "PCX: Writing palette of %d active entries.\n", img->ncolors);
1062
1063 for (i = 0; i < img->ncolors; i++)
1064 {
1065 dm_fwrite_byte(pcx.fp, img->pal[i].r);
1066 dm_fwrite_byte(pcx.fp, img->pal[i].g);
1067 dm_fwrite_byte(pcx.fp, img->pal[i].b);
1068 }
1069
1070 // Pad the palette, if necessary
1071 for (; i < 256; i++)
1072 {
1073 dm_fwrite_byte(pcx.fp, 0);
1074 dm_fwrite_byte(pcx.fp, 0);
1075 dm_fwrite_byte(pcx.fp, 0);
1076 }
1077 }
1078
1079 error:
1080 dmFree(pcx.buf);
1081 return res;
1082 }
1083
1084
1085 int dmWritePCXImage(const char *filename, DMImage *img, int scale, BOOL paletted)
1086 {
1087 FILE *fp;
1088 int res;
1089
1090 if ((fp = fopen(filename, "wb")) == NULL)
1091 {
1092 dmError("PCX: Could not open file '%s' for writing.\n", filename);
1093 return DMERR_FOPEN;
1094 }
1095
1096 res = dmWritePCXImageFILE(fp, img, scale, paletted);
1097
1098 fclose(fp);
1099 return res;
1100 }
1101
1102
1103 static BOOL dmPCXDecodeRLERow(FILE *fp, Uint8 *buf, const size_t bufLen)
1104 {
1105 size_t offs = 0;
1106 do
1107 {
1108 int count;
1109 Uint8 data;
1110
1111 if (!dm_fread_byte(fp, &data))
1112 return FALSE;
1113
1114 if ((data & 0xC0) == 0xC0)
1115 {
1116 count = data & 0x3F;
1117 if (!dm_fread_byte(fp, &data))
1118 return FALSE;
1119 }
1120 else
1121 count = 1;
1122
1123 while (count-- && offs < bufLen)
1124 buf[offs++] = data;
1125
1126 } while (offs < bufLen);
1127
1128 return TRUE;
1129 }
1130
1131
1132 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg)
1133 {
1134 DMImage *img;
1135 DMPCXData pcx;
1136 DMPCXHeader hdr;
1137 BOOL paletted;
1138 int res = 0, yc, xc;
1139 Uint8 *dp;
1140
1141 pcx.buf = NULL;
1142
1143 // Read PCX header
1144 if (!dm_fread_byte(fp, &hdr.manufacturer) ||
1145 !dm_fread_byte(fp, &hdr.version) ||
1146 !dm_fread_byte(fp, &hdr.encoding) ||
1147 !dm_fread_byte(fp, &hdr.bpp))
1148 {
1149 dmError("PCX: Could not read basic header data.\n");
1150 res = DMERR_FREAD;
1151 goto error;
1152 }
1153
1154 if (hdr.manufacturer != 10 ||
1155 hdr.version != 5 ||
1156 hdr.encoding != 1 ||
1157 hdr.bpp != 8)
1158 {
1159 dmError("PCX: Not a PCX file, or unsupported variant.\n");
1160 res = DMERR_FREAD;
1161 goto error;
1162 }
1163
1164 if (!dm_fread_le16(fp, &hdr.xmin) ||
1165 !dm_fread_le16(fp, &hdr.ymin) ||
1166 !dm_fread_le16(fp, &hdr.xmax) ||
1167 !dm_fread_le16(fp, &hdr.ymax) ||
1168 !dm_fread_le16(fp, &hdr.hres) ||
1169 !dm_fread_le16(fp, &hdr.vres))
1170 {
1171 dmError("PCX: Could not read image dimensions.\n");
1172 res = DMERR_FREAD;
1173 goto error;
1174 }
1175
1176 if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap)))
1177 {
1178 dmError("PCX: Could not read colormap.\n");
1179 res = DMERR_FREAD;
1180 goto error;
1181 }
1182
1183 if (!dm_fread_byte(fp, &hdr.reserved) ||
1184 !dm_fread_byte(fp, &hdr.nplanes) ||
1185 !dm_fread_le16(fp, &hdr.bpl) ||
1186 !dm_fread_le16(fp, &hdr.palinfo) ||
1187 !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler)))
1188 {
1189 dmError("PCX: Could not read header remainder.\n");
1190 res = DMERR_FREAD;
1191 goto error;
1192 }
1193
1194 if (hdr.nplanes != 3 && hdr.nplanes != 1)
1195 {
1196 dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes);
1197 res = DMERR_FREAD;
1198 goto error;
1199 }
1200
1201 // Allocate image
1202 if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL)
1203 {
1204 dmError("PCX: Could not allocate image structure.\n");
1205 res = DMERR_MALLOC;
1206 goto error;
1207 }
1208
1209 paletted = hdr.nplanes == 1;
1210 pcx.bufLen = hdr.nplanes * hdr.bpl;
1211 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL)
1212 {
1213 dmError("PCX: Could not allocate RLE buffer.\n");
1214 res = DMERR_MALLOC;
1215 goto error;
1216 }
1217
1218 // Read image data
1219 dp = img->data;
1220 for (yc = 0; yc < img->height; yc++)
1221 {
1222 // Decode row of RLE'd data
1223 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen))
1224 {
1225 dmError("PCX: Error decoding RLE data.\n");
1226 res = DMERR_INVALID_DATA;
1227 goto error;
1228 }
1229
1230 // Decode bitplanes
1231 switch (hdr.nplanes)
1232 {
1233 case 1:
1234 memcpy(dp, pcx.buf, img->width);
1235 break;
1236
1237 case 3:
1238 {
1239 Uint8 *dptr = dp,
1240 *sptr1 = pcx.buf,
1241 *sptr2 = sptr1 + hdr.bpl,
1242 *sptr3 = sptr2 + hdr.bpl;
1243
1244 for (xc = 0; xc < img->width; xc++)
1245 {
1246 *dptr++ = *sptr1++;
1247 *dptr++ = *sptr2++;
1248 *dptr++ = *sptr3++;
1249 }
1250 }
1251 break;
1252 }
1253
1254 dp += img->pitch;
1255 }
1256
1257 // Read VGA palette
1258 if (paletted)
1259 {
1260 int i;
1261 Uint8 tmpb;
1262 BOOL read;
1263
1264 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C)
1265 {
1266 read = FALSE;
1267 img->ncolors = 16;
1268 }
1269 else
1270 {
1271 read = TRUE;
1272 img->ncolors = 256;
1273 }
1274
1275 if ((img->pal = dmCalloc(img->ncolors, sizeof(DMColor))) == NULL)
1276 {
1277 dmError("PCX: Could not allocate palette data!\n");
1278 res = DMERR_MALLOC;
1279 goto error;
1280 }
1281
1282 if (read)
1283 {
1284 for (i = 0; i < img->ncolors; i++)
1285 {
1286 Uint8 tmpR, tmpG, tmpB;
1287 if (!dm_fread_byte(fp, &tmpR) ||
1288 !dm_fread_byte(fp, &tmpG) ||
1289 !dm_fread_byte(fp, &tmpB))
1290 goto error;
1291
1292 img->pal[i].r = tmpR;
1293 img->pal[i].g = tmpG;
1294 img->pal[i].b = tmpB;
1295 }
1296 }
1297 else
1298 {
1299 for (i = 0; i < img->ncolors; i++)
1300 {
1301 if (i < 16)
1302 {
1303 img->pal[i].r = hdr.colormap[i].r;
1304 img->pal[i].g = hdr.colormap[i].g;
1305 img->pal[i].b = hdr.colormap[i].b;
1306 }
1307 }
1308 }
1309
1310
1311 }
1312
1313 error:
1314 dmFree(pcx.buf);
1315 return res;
1316 }
1317
1318
1319 int dmReadPCXImage(const char *filename, DMImage **pimg)
1320 {
1321 FILE *fp;
1322 int res;
1323
1324 if ((fp = fopen(filename, "rb")) == NULL)
1325 {
1326 dmError("PCX: Could not open file '%s' for reading.\n", filename);
1327 return -15;
1328 }
1329
1330 res = dmReadPCXImageFILE(fp, pimg);
1331
1332 fclose(fp);
1333 return res;
1334 }
1335
1336
1337 static int fmtProbePNG(const Uint8 *buf, const size_t len)
1338 {
1339 if (len > 64 && buf[0] == 0x89 &&
1340 buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' &&
1341 buf[4] == 0x0d && buf[5] == 0x0a)
1342 return DM_PROBE_SCORE_GOOD;
1343
1344 return DM_PROBE_SCORE_FALSE;
1345 }
1346
1347
1348 static int fmtProbePCX(const Uint8 *buf, const size_t len)
1349 {
1350 if (len > 128 + 64 &&
1351 buf[0] == 10 &&
1352 buf[1] == 5 &&
1353 buf[2] == 1 &&
1354 buf[3] == 8)
1355 return DM_PROBE_SCORE_GOOD;
1356
1357 return DM_PROBE_SCORE_FALSE;
1358 }
1359
1360
1361 #ifdef UNFINISHED 474 #ifdef UNFINISHED
1362 int dmConvertBMP2(DMImage *screen, const DM64Image *img) 475 int dmConvertBMP2(DMImage *screen, const DM64Image *img)
1363 { 476 {
1364 int yc; 477 int yc;
1365 Uint8 *dp = screen->data; 478 Uint8 *dp = screen->data;
1398 return 0; 511 return 0;
1399 } 512 }
1400 #endif 513 #endif
1401 514
1402 515
1403 int dmWriteImage(char *filename, DMImage *image, BOOL info) 516 int dmWriteImage(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info)
1404 { 517 {
1405 if (info) 518 if (info)
1406 { 519 {
1407 dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n", 520 dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n",
1408 imageFormatList[optOutSubFormat], 521 dmImageFormatList[iformat],
1409 image->width, image->height, 522 image->width, image->height,
1410 image->width * optScale, image->height * optScale, optScale); 523 image->width * spec->scale, image->height * spec->scale,
1411 } 524 spec->scale);
1412 525 }
1413 switch (optOutSubFormat) 526
527 switch (iformat)
1414 { 528 {
1415 #ifdef DM_USE_LIBPNG 529 #ifdef DM_USE_LIBPNG
1416 case IMGFMT_PNG: 530 case IMGFMT_PNG:
1417 if (info) dmMsg(2, "%s output.\n", optPaletted ? "Indexed 8bpp" : "32bit RGBA"); 531 if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "32bit RGBA");
1418 return dmWritePNGImage(filename, image, optScale, optPaletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA); 532 spec->format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA;
533 return dmWritePNGImage(filename, image, spec);
1419 #endif 534 #endif
1420 535
1421 case IMGFMT_PPM: 536 case IMGFMT_PPM:
1422 if (info) dmMsg(2, "24bit RGB output.\n"); 537 if (info) dmMsg(2, "24bit RGB output.\n");
1423 return dmWritePPMImage(filename, image, optScale); 538 spec->format = DM_IFMT_RGB;
539 return dmWritePPMImage(filename, image, spec);
1424 540
1425 case IMGFMT_PCX: 541 case IMGFMT_PCX:
1426 if (info) dmMsg(2, "%s output.\n", optPaletted ? "Indexed 8bpp" : "24bit RGB"); 542 if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "24bit RGB");
1427 return dmWritePCXImage(filename, image, optScale, optPaletted); 543 return dmWritePCXImage(filename, image, spec);
1428 544
1429 case IMGFMT_ARAW: 545 case IMGFMT_ARAW:
1430 { 546 {
1431 int res; 547 int res;
1432 char *palFilename = dm_strdup_printf("%s.pal", filename); 548 char *palFilename = dm_strdup_printf("%s.pal", filename);
1433 res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << optBPP); 549 res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << optSpec.nplanes);
1434 dmFree(palFilename); 550 dmFree(palFilename);
1435 if (res != DMERR_OK) 551 if (res != DMERR_OK)
1436 return res; 552 return res;
1437 553
1438 if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", optBPP, optInterleave ? "with" : "without"); 554 if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", spec->nplanes, spec->interleave ? "with" : "without");
1439 return dmWriteIFFMasterRAWImage(filename, image, optScale, optBPP, optInterleave); 555 return dmWriteIFFMasterRAWImage(filename, image, spec);
1440 } 556 }
1441 557
1442 default: 558 default:
1443 return FALSE; 559 return FALSE;
1444 } 560 }
1542 658
1543 outImage = dmImageAlloc(outWidthPX, outHeight); 659 outImage = dmImageAlloc(outWidthPX, outHeight);
1544 dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n", 660 dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n",
1545 optItemCount, 661 optItemCount,
1546 outImage->width, outImage->height, 662 outImage->width, outImage->height,
1547 outImage->width * optScale, outImage->height * optScale); 663 outImage->width * optSpec.scale, outImage->height * optSpec.scale);
1548 } 664 }
1549 else 665 else
1550 { 666 {
1551 int outIWidth, outIHeight; 667 int outIWidth, outIHeight;
1552 if (optItemCount <= 0) 668 if (optItemCount <= 0)
1593 { 709 {
1594 dmError("Could not allocate memory for filename template?\n"); 710 dmError("Could not allocate memory for filename template?\n");
1595 goto error; 711 goto error;
1596 } 712 }
1597 713
1598 dmWriteImage(outFilename, outImage, FALSE); 714 dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
1599 dmFree(outFilename); 715 dmFree(outFilename);
1600 } 716 }
1601 else 717 else
1602 { 718 {
1603 if (++outX >= optPlanedWidth) 719 if (++outX >= optPlanedWidth)
1610 itemCount++; 726 itemCount++;
1611 } 727 }
1612 728
1613 if (!optSequential) 729 if (!optSequential)
1614 { 730 {
1615 dmWriteImage(optOutFilename, outImage, TRUE); 731 dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
1616 } 732 }
1617 733
1618 dmImageFree(outImage); 734 dmImageFree(outImage);
1619 } 735 }
1620 736
1710 optInFormat = FFMT_BITMAP; 826 optInFormat = FFMT_BITMAP;
1711 } 827 }
1712 828
1713 if (optInFormat == FFMT_AUTO) 829 if (optInFormat == FFMT_AUTO)
1714 { 830 {
1715 // XXX, needs a proper probe loop 831 DMImageFormat *ifmt = NULL;
1716 if (fmtProbePNG(dataBuf + optInSkip, dataSize - optInSkip)) 832 int index;
833 if (dmImageProbeGeneric(dataBuf + optInSkip, dataSize - optInSkip, &ifmt, &index) > 0)
1717 { 834 {
1718 optInFormat = FFMT_IMAGE; 835 optInFormat = FFMT_IMAGE;
1719 optInSubFormat = IMGFMT_PNG; 836 optInSubFormat = index;
1720 }
1721 else
1722 if (fmtProbePCX(dataBuf + optInSkip, dataSize - optInSkip))
1723 {
1724 optInFormat = FFMT_IMAGE;
1725 optInSubFormat = IMGFMT_PCX;
1726 } 837 }
1727 } 838 }
1728 839
1729 if (optInFormat == FFMT_AUTO) 840 if (optInFormat == FFMT_AUTO)
1730 { 841 {
1765 dmDumpSpritesAndChars(inFile); 876 dmDumpSpritesAndChars(inFile);
1766 break; 877 break;
1767 878
1768 case FFMT_BITMAP: 879 case FFMT_BITMAP:
1769 { 880 {
1770 DMImage *img = NULL; 881 DMImage *outImage = NULL;
1771 int res = DMERR_OK; 882 int res = DMERR_OK;
1772 883
1773 if (optOutFilename == NULL) 884 if (optOutFilename == NULL)
1774 { 885 {
1775 dmError("Output filename not set, required for image formats.\n"); 886 dmError("Output filename not set, required for image formats.\n");
1777 } 888 }
1778 889
1779 switch (optOutFormat) 890 switch (optOutFormat)
1780 { 891 {
1781 case FFMT_IMAGE: 892 case FFMT_IMAGE:
1782 if ((img = dmImageAlloc(C64_SCR_WIDTH, C64_SCR_HEIGHT)) == NULL) 893 if ((outImage = dmImageAlloc(C64_SCR_WIDTH, C64_SCR_HEIGHT)) == NULL)
1783 { 894 {
1784 dmError("Could not allocate output image surface %d x %d.\n", 895 dmError("Could not allocate output image surface %d x %d.\n",
1785 C64_SCR_WIDTH, C64_SCR_HEIGHT); 896 C64_SCR_WIDTH, C64_SCR_HEIGHT);
1786 goto error; 897 goto error;
1787 } 898 }
1788 899
1789 img->pal = (DMColor *) &dmC64Palette; 900 outImage->pal = (DMColor *) &dmC64Palette;
1790 img->ncolors = C64_NCOLORS; 901 outImage->ncolors = C64_NCOLORS;
1791 img->constpal = TRUE; 902 outImage->constpal = TRUE;
1792 903
1793 if (cfmt->convert != NULL) 904 if (cfmt->convert != NULL)
1794 res = cfmt->convert(img, &cimage); 905 res = cfmt->convert(outImage, &cimage);
1795 else 906 else
1796 res = dmC64ConvertGenericBMP2Image(img, &cimage); 907 res = dmC64ConvertGenericBMP2Image(outImage, &cimage);
1797 908
1798 if (res != DMERR_OK || img == NULL) 909 if (res != DMERR_OK || outImage == NULL)
1799 { 910 {
1800 dmError("Error in bitmap to image conversion.\n"); 911 dmError("Error in bitmap to image conversion.\n");
1801 goto error; 912 goto error;
1802 } 913 }
1803 914
1804 res = dmWriteImage(optOutFilename, img, TRUE); 915 res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
1805 break; 916 break;
1806 917
1807 default: 918 default:
1808 dmError("Unsupported output format for bitmap/image conversion.\n"); 919 dmError("Unsupported output format for bitmap/image conversion.\n");
1809 break; 920 break;
1810 } 921 }
1811 922
1812 dmImageFree(img); 923 dmImageFree(outImage);
1813 } 924 }
1814 break; 925 break;
1815 926
1816 case FFMT_IMAGE: 927 case FFMT_IMAGE:
1817 { 928 {
1818 DMImage *img; 929 DMImage *outImage;
1819 int res = DMERR_OK; 930 int res = DMERR_OK;
1820 931
1821 if (optOutFilename == NULL) 932 if (optOutFilename == NULL)
1822 { 933 {
1823 dmError("Output filename not set, required for image formats.\n"); 934 dmError("Output filename not set, required for image formats.\n");
1825 } 936 }
1826 937
1827 // Read input 938 // Read input
1828 switch (optInSubFormat) 939 switch (optInSubFormat)
1829 { 940 {
1830 case IMGFMT_PCX: res = dmReadPCXImageFILE(inFile, &img); break; 941 case IMGFMT_PCX: res = dmReadPCXImageFILE(inFile, &outImage); break;
1831 // case IMGFMT_PNG: res = dmReadPNGImageFILE(inFile, &img); break; 942 // case IMGFMT_PNG: res = dmReadPNGImageFILE(inFile, &outImage); break;
1832 // case IMGFMT_ARAW: res = dmReadARAWImageFILE(inFile, &img, optBPP); break; 943 // case IMGFMT_ARAW: res = dmReadARAWImageFILE(inFile, &outImage, optSpec.nplanes); break;
1833 default: 944 default:
1834 dmError("Unsupported input image format for bitmap/image conversion.\n"); 945 dmError("Unsupported input image format for bitmap/image conversion.\n");
1835 break; 946 break;
1836 947
1837 } 948 }
1838 949
1839 if (res != DMERR_OK || img == NULL) 950 if (res != DMERR_OK || outImage == NULL)
1840 break; 951 break;
1841 952
1842 switch (optOutFormat) 953 switch (optOutFormat)
1843 { 954 {
1844 case FFMT_IMAGE: 955 case FFMT_IMAGE:
1845 res = dmWriteImage(optOutFilename, img, TRUE); 956 res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE);
1846 break; 957 break;
1847 958
1848 default: 959 default:
1849 dmError("Unsupported output format for bitmap/image conversion.\n"); 960 dmError("Unsupported output format for bitmap/image conversion.\n");
1850 break; 961 break;
1851 } 962 }
1852 963
1853 dmImageFree(img); 964 dmImageFree(outImage);
1854 } 965 }
1855 break; 966 break;
1856 } 967 }
1857 968
1858 fclose(inFile); 969 fclose(inFile);