Mercurial > hg > dmlib
view 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 |
line wrap: on
line source
/* * gfxconv - Convert various graphics formats * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include <errno.h> #include "dmlib.h" #include "dmargs.h" #include "dmfile.h" #include "dmmutex.h" #include "lib64gfx.h" //#define UNFINISHED 1 #ifdef HAVE_LIBPNG #include <png.h> #endif enum { INFMT_AUTO = 0, INFMT_CHAR, INFMT_SPRITE, INFMT_BITMAP, INFMT_IMAGE, }; enum { OUTFMT_ASCII, OUTFMT_ANSI, OUTFMT_PNG, OUTFMT_PPM, OUTFMT_PCX, OUTFMT_ARAW, #ifdef UNFINISHED OUTFMT_SPRITE, OUTFMT_CHAR, #endif OUTFMT_LAST }; char * outFormatList[OUTFMT_LAST] = { "ascii", "ansi", "png", "ppm", "pcx", "araw", #ifdef UNFINISHED "spr", "char", #endif }; static const int noutFormatList = sizeof(outFormatList) / sizeof(outFormatList[0]); #define ASC_NBITS 8 #define ASC_NCOLORS 4 static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#"; char *optInFilename = NULL, *optOutFilename = NULL; int optInFormat = INFMT_AUTO, optOutFormat = OUTFMT_ASCII, optItemCount = -1, optScale = 2, optPlanedWidth = 1, optBPP = 4; int optInSkip = 0; BOOL optInMulticolor = FALSE, optSequential = FALSE, optPaletted = FALSE; int optColors[C64_MAX_COLORS]; static DMOptArg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 3, 'o', "output", "Output filename", OPT_ARGREQ }, { 1, 'i', "informat", "Set input format ([s]prite, [c]har, [b]itmap)", OPT_ARGREQ }, { 2, 'm', "multicolor", "Input is multicolor", OPT_NONE }, { 4, 's', "skip", "Skip bytes in input", OPT_ARGREQ }, { 5, 'f', "format", "Output format (see list below)", OPT_ARGREQ }, { 8, 'q', "sequential", "Output sequential files (image output only)", OPT_NONE }, { 6, 'c', "colormap", "Color mappings (see below for information)", OPT_ARGREQ }, { 7, 'n', "numitems", "How many 'items' to view (default: all)", OPT_ARGREQ }, { 9, 'S', "scale", "Scale output by x (image output only)", OPT_ARGREQ }, #ifdef UNFINISHED {10, 'b', "bformat", "Force input bitmap format (see below)", OPT_ARGREQ }, #endif {11, 'w', "width", "Item width (number of items per row, min 1)", OPT_ARGREQ }, {12, 'P', "paletted", "Use indexed/paletted output (png, pcx output only)", OPT_NONE }, {13, 'b', "bpp", "Bits per pixel (certain image output formats)", OPT_ARGREQ }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp() { int i; dmPrintBanner(stdout, dmProgName, "[options] <input file>"); dmArgsPrintHelp(stdout, optList, optListN); printf("\nAvailable output formats: "); for (i = 0; i < noutFormatList; i++) { printf("%s", outFormatList[i]); if (i < noutFormatList - 1) printf(", "); else printf("\n"); } #ifdef UNFINISHED printf("\nAvailable bitmap formats:\n"); for (i = 0; i < ndmC64ImageFormats; i++) { DM64ImageFormat *fmt = &dmC64ImageFormats[i]; printf("%3d | %-5s | %-15s | %s\n", i, fmt->extension, dmC64ImageTypeNames[fmt->type], fmt->name); } #endif printf( "\n" "Color map definitions are used for ANSI, PCX, PPM and PNG output, to declare what\n" "output colors of the C64 palette are used for each single color/multi color\n" "bit-combination. For example, if the input is multi color sprite or char,\n" "you can define colors like: -c 0,8,3,15 .. for single color: -c 0,1\n" "The numbers are palette indexes, and the order is for bit(pair)-values\n" "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n" "special color that can be used for transparency.\n" ); } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: switch (tolower(optArg[0])) { case 's': optInFormat = INFMT_SPRITE; break; case 'c': optInFormat = INFMT_CHAR; break; default: dmError("Invalid input format '%s'.\n", optArg); return FALSE; } break; case 2: optInMulticolor = TRUE; break; case 3: optOutFilename = optArg; break; case 4: if (!dmGetIntVal(optArg, &optInSkip)) { dmError("Invalid skip value argument '%s'.\n", optArg); return FALSE; } break; case 5: { int i, format = -1; for (i = 0; i < noutFormatList; i++) if (strcasecmp(optArg, outFormatList[i]) == 0) { format = i; break; } if (format < 0) { dmError("Invalid output format '%s'.\n", optArg); return FALSE; } optOutFormat = format; } break; case 6: { int index = 0, tmp; char *s, *p = optArg; while (index < C64_MAX_COLORS && *p != 0 && (s = strchr(p, ':')) != NULL) { *s = 0; if (sscanf(p, "%d", &tmp) == 1) optColors[index++] = tmp; p = s + 1; } if (*p && index < C64_MAX_COLORS) { if (sscanf(p, "%d", &tmp) == 1) optColors[index++] = tmp; } dmMsg(1, "Set color table: "); for (tmp = 0; tmp < index; tmp++) { dmPrint(1, "[%d:%d]%s", tmp, optColors[tmp], (tmp < index - 1) ? ", " : ""); } dmPrint(1, "\n"); } break; case 7: if (sscanf(optArg, "%d", &optItemCount) != 1) { dmError("Invalid count value argument '%s'.\n", optArg); return FALSE; } break; case 8: optSequential = TRUE; break; case 9: { int tmp = atoi(optArg); if (tmp < 1 || tmp > 50) { dmError("Invalid scale value '%s'.\n", optArg); return FALSE; } optScale = tmp; } break; case 11: { int tmp = atoi(optArg); if (tmp < 1 || tmp > 512) { dmError("Invalid width value '%s'.\n", optArg); return FALSE; } optPlanedWidth = tmp; } break; case 12: optPaletted = TRUE; break; default: dmError("Unknown option '%s'.\n", currArg); return FALSE; } return TRUE; } BOOL argHandleFile(char *currArg) { if (!optInFilename) optInFilename = currArg; else { dmError("Source filename already specified, extraneous argument '%s'.\n", currArg); return FALSE; } return TRUE; } void dmPrintByte(FILE *out, int byte, int format, BOOL multicolor) { int i; if (multicolor) { for (i = ASC_NBITS; i; i -= 2) { int val = (byte & (3ULL << (i - 2))) >> (i - 2); char ch; switch (format) { case OUTFMT_ASCII: ch = dmASCIIPalette[val]; fprintf(out, "%c%c", ch, ch); break; case OUTFMT_ANSI: fprintf(out, "%c[0;%d;%dm##%c[0m", 0x1b, 1, 31 + optColors[val], 0x1b); break; } } } else { for (i = ASC_NBITS; i; i--) { int val = (byte & (1ULL << (i - 1))) >> (i - 1); char ch; switch (format) { case OUTFMT_ASCII: ch = val ? '#' : '.'; fputc(ch, out); break; case OUTFMT_ANSI: fprintf(out, "%c[0;%d;%dm %c[0m", 0x1b, 1, 31 + optColors[val], 0x1b); break; } } } } void dmDumpCharASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) { int yc; for (yc = 0; yc < C64_CHR_HEIGHT; yc++) { fprintf(outFile, "%04x : ", *offs); dmPrintByte(outFile, buf[yc], format, multicolor); fprintf(outFile, "\n"); (*offs)++; } } void dmDumpSpriteASCII(FILE *outFile, const uint8_t *buf, int *offs, int format, BOOL multicolor) { int bufOffs, xc, yc; for (bufOffs = yc = 0; yc < C64_SPR_HEIGHT; yc++) { fprintf(outFile, "%04x : ", *offs); for (xc = 0; xc < C64_SPR_WIDTH; xc++) { dmPrintByte(outFile, buf[bufOffs], format, multicolor); fprintf(outFile, " "); bufOffs++; (*offs)++; } fprintf(outFile, "\n"); } (*offs)++; } int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, uint8_t *, size_t), int scale, int format) { int x, y, yscale, xscale, res = 0, rowSize, rowWidth; uint8_t *row = NULL; // Allocate memory for row buffer rowWidth = img->width * scale; rowSize = rowWidth * dmImageGetBytesPerPixel(format); if ((row = dmMalloc(rowSize + 16)) == NULL) { res = -16; goto done; } // Generate the image for (y = 0; y < img->height; y++) { uint8_t *ptr = row, *ptr1 = row, *ptr2 = ptr1 + rowWidth, *ptr3 = ptr2 + rowWidth; for (x = 0; x < img->width; x++) { uint8_t c = img->data[(y * img->pitch) + x], qr, qg, qb, qa; switch (format) { case DM_IFMT_PALETTE: for (xscale = 0; xscale < scale; xscale++) *ptr++ = c; break; case DM_IFMT_RGBA: qr = img->pal[c].r; qg = img->pal[c].g; qb = img->pal[c].b; qa = (c == img->ctrans) ? 0 : 255; for (xscale = 0; xscale < scale; xscale++) { *ptr++ = qr; *ptr++ = qg; *ptr++ = qb; *ptr++ = qa; } break; case DM_IFMT_RGB: qr = img->pal[c].r; qg = img->pal[c].g; qb = img->pal[c].b; for (xscale = 0; xscale < scale; xscale++) { *ptr++ = qr; *ptr++ = qg; *ptr++ = qb; } break; case DM_IFMT_RGB_PLANE: qr = img->pal[c].r; qg = img->pal[c].g; qb = img->pal[c].b; for (xscale = 0; xscale < scale; xscale++) { *ptr1++ = qr; *ptr2++ = qg; *ptr3++ = qb; } break; } } for (yscale = 0; yscale < scale; yscale++) { if (!writeRowCB(cbdata, row, rowSize)) { res = -32; goto done; } } } done: dmFree(row); return res; } #define DMCOL(x) (((x) >> 4) & 0xf) int dmWriteIFFMasterRAWPalette(const char *filename, DMImage *img, int ncolors) { FILE *fp; int i; if ((fp = fopen(filename, "w")) == NULL) { dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); return -15; } for (i = 0; i < ncolors; i++) { int color; if (i < img->ncolors) { color = (DMCOL(img->pal[i].r) << 8) | (DMCOL(img->pal[i].g) << 4) | (DMCOL(img->pal[i].b)); } else color = 0; fprintf(fp, "\tdc.w $%04X\n", color); } return 0; } typedef struct { int bpp; DMImage *img; FILE *fp; } DMRawData; static BOOL dmWriteIFFMasterRAWRow(void *cbdata, uint8_t *row, size_t len) { DMRawData *raw = (DMRawData *) cbdata; size_t i; for (i = 0; i < len; i++) { } return fwrite(row, sizeof(uint8_t), len, raw->fp) == len; } int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, int scale, int bpp) { DMRawData raw; raw.fp = fp; raw.img = img; raw.bpp = bpp; return dmWriteImageData(img, (void *) &raw, dmWriteIFFMasterRAWRow, scale, DM_IFMT_PALETTE); } int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, int scale, int bpp) { FILE *fp; int res; if ((fp = fopen(filename, "wb")) == NULL) { dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); return -15; } res = dmWriteIFFMasterRAWImageFILE(fp, img, scale, bpp); fclose(fp); return res; } static BOOL dmWritePPMRow(void *cbdata, uint8_t *row, size_t len) { return fwrite(row, sizeof(uint8_t), len, (FILE *) cbdata) == len; } int dmWritePPMImageFILE(FILE *fp, DMImage *img, int scale) { // Write PPM header fprintf(fp, "P6\n%d %d\n255\n", img->width * scale, img->height * scale); // Write image data return dmWriteImageData(img, (void *) fp, dmWritePPMRow, scale, DM_IFMT_RGB); } int dmWritePPMImage(const char *filename, DMImage *img, int scale) { FILE *fp; int res; // Create output file if ((fp = fopen(filename, "wb")) == NULL) { dmError("PPM: could not open file '%s' for writing.\n", filename); return -15; } res = dmWritePPMImageFILE(fp, img, scale); fclose(fp); return res; } #ifdef HAVE_LIBPNG static BOOL dmWritePNGRow(void *cbdata, uint8_t *row, size_t len) { png_structp png_ptr = cbdata; (void) len; if (setjmp(png_jmpbuf(png_ptr))) return FALSE; png_write_row(png_ptr, row); return TRUE; } int dmWritePNGImageFILE(FILE *fp, DMImage *img, int scale, int format) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_colorp palette = NULL; int fmt; // Create PNG structures png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { dmError("PNG: png_create_write_struct() failed.\n"); goto error; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { dmError("PNG: png_create_info_struct(%p) failed.\n", png_ptr); goto error; } if (setjmp(png_jmpbuf(png_ptr))) { dmError("PNG: Error during image writing..\n"); goto error; } png_init_io(png_ptr, fp); // Write PNG header info switch (format) { case DM_IFMT_PALETTE: fmt = PNG_COLOR_TYPE_PALETTE; break; case DM_IFMT_RGB : fmt = PNG_COLOR_TYPE_RGB; break; case DM_IFMT_RGBA : fmt = PNG_COLOR_TYPE_RGB_ALPHA; break; default: dmError("PNG: Internal error, unsupported image format %d.\n", format); goto error; } png_set_IHDR(png_ptr, info_ptr, img->width * scale, img->height * scale, 8, /* bits per component */ fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Palette if (format == DM_IFMT_PALETTE) { int i; palette = png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); if (palette == NULL) { dmError("PNG: Could not allocate palette structure."); goto error; } memset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color)); for (i = 0; i < img->ncolors; i++) { palette[i].red = img->pal[i].r; palette[i].green = img->pal[i].g; palette[i].blue = img->pal[i].b; } png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); } // png_set_gAMA(png_ptr, info_ptr, 2.2); png_write_info(png_ptr, info_ptr); // Write compressed image data dmWriteImageData(img, (void *) png_ptr, dmWritePNGRow, scale, format); // Write footer png_write_end(png_ptr, NULL); png_free(png_ptr, palette); palette = NULL; // Deallocate shit if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } return 0; error: png_free(png_ptr, palette); palette = NULL; if (png_ptr && info_ptr) { png_destroy_write_struct(&png_ptr, &info_ptr); } return -15; } int dmWritePNGImage(const char *filename, DMImage *img, int scale, int format) { int res; FILE *fp; if ((fp = fopen(filename, "wb")) == NULL) { dmError("PNG: could not open file '%s' for writing.\n", filename); return -15; } res = dmWritePNGImageFILE(fp, img, scale, format); fclose(fp); return res; } #endif typedef struct { uint8_t r,g,b; } DMPCXColor; typedef struct { uint8_t manufacturer, version, encoding, bpp; uint16_t xmin, ymin, xmax, ymax; uint16_t hres, vres; DMPCXColor colormap[16]; uint8_t reserved; uint8_t nplanes; uint16_t bpl; uint16_t palinfo; uint8_t filler[58]; } DMPCXHeader; typedef struct { DMPCXHeader *header; uint8_t *buf; size_t bufLen, bufOffs; int format; FILE *fp; } DMPCXData; static inline uint8_t dmPCXGetByte(uint8_t *row, const size_t len, const size_t soffs) { return (soffs < len) ? row[soffs] : 0; } static BOOL dmPCXFlush(DMPCXData *pcx) { BOOL ret = fwrite(pcx->buf, sizeof(uint8_t), pcx->bufOffs, pcx->fp) == pcx->bufOffs; pcx->bufOffs = 0; return ret; } static inline BOOL dmPCXPutByte(DMPCXData *pcx, const uint8_t val) { if (pcx->bufOffs < pcx->bufLen) { pcx->buf[pcx->bufOffs++] = val; return TRUE; } else return dmPCXFlush(pcx); } BOOL dmWritePCXRow(void *cbdata, uint8_t *row, size_t len) { DMPCXData *pcx = (DMPCXData *) cbdata; int plane; size_t soffs = 0; for (plane = 0; plane < pcx->header->nplanes; plane++) { uint8_t data = dmPCXGetByte(row, len, soffs++), count = 1; pcx->bufOffs = 0; while (soffs < pcx->header->bpl) { if (data == dmPCXGetByte(row, len, soffs) && count < 63) { count++; soffs++; } else { if (count == 1 && (data & 0xC0) != 0xC0) { if (!dmPCXPutByte(pcx, data)) return FALSE; } else { if (!dmPCXPutByte(pcx, 0xC0 | count) || !dmPCXPutByte(pcx, data)) return FALSE; } data = dmPCXGetByte(row, len, soffs++); count = 1; } } if (count > 1) { if (!dmPCXPutByte(pcx, 0xC0 | count) || !dmPCXPutByte(pcx, data)) return FALSE; } if (!dmPCXFlush(pcx)) return FALSE; } return TRUE; } int dmWritePCXImage(const char *filename, DMImage *img, int scale, BOOL paletted) { DMPCXData pcx; DMPCXHeader hdr; int res; // Create output file pcx.buf = NULL; pcx.format = paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE; pcx.header = &hdr; if ((pcx.fp = fopen(filename, "wb")) == NULL) { dmError("PCX: Could not open file '%s' for writing.\n", filename); res = -15; goto error; } // Create PCX header memset(&hdr, 0, sizeof(hdr)); if (paletted) { int i; for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++) { hdr.colormap[i].r = img->pal[i].r; hdr.colormap[i].g = img->pal[i].g; hdr.colormap[i].b = img->pal[i].b; } } hdr.manufacturer = 10; hdr.version = 5; hdr.encoding = 1; hdr.bpp = 8; hdr.hres = img->width * scale; hdr.vres = img->height * scale; hdr.xmin = hdr.ymin = 0; hdr.xmax = hdr.hres - 1; hdr.ymax = hdr.vres - 1; hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); hdr.bpl = (((img->width * scale) / 2) + 1) * 2; hdr.palinfo = 1; dmMsg(1, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", paletted, hdr.nplanes, hdr.bpp, hdr.bpl); pcx.bufLen = hdr.bpl * 4; if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) { dmError("PCX: Could not allocate %d bytes for RLE compression buffer.\n", pcx.bufLen); res = -11; goto error; } // Write PCX header if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) || !dm_fwrite_byte(pcx.fp, hdr.version) || !dm_fwrite_byte(pcx.fp, hdr.encoding) || !dm_fwrite_byte(pcx.fp, hdr.bpp)) { dmError("PCX: Could not write basic header data.\n"); res = -10; goto error; } if (!dm_fwrite_le16(pcx.fp, hdr.xmin) || !dm_fwrite_le16(pcx.fp, hdr.ymin) || !dm_fwrite_le16(pcx.fp, hdr.xmax) || !dm_fwrite_le16(pcx.fp, hdr.ymax) || !dm_fwrite_le16(pcx.fp, hdr.hres) || !dm_fwrite_le16(pcx.fp, hdr.vres)) { dmError("PCX: Could not write image dimensions.\n"); res = -9; goto error; } if (!dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) { dmError("PCX: Could not write colormap.\n"); res = -8; goto error; } if (!dm_fwrite_byte(pcx.fp, hdr.reserved) || !dm_fwrite_byte(pcx.fp, hdr.nplanes) || !dm_fwrite_le16(pcx.fp, hdr.bpl) || !dm_fwrite_le16(pcx.fp, hdr.palinfo) || !dm_fwrite_str(pcx.fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) { dmError("PCX: Could not write header remainder.\n"); res = -7; goto error; } // Write image data res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, scale, pcx.format); // Write VGA palette if (paletted) { int i; dm_fwrite_byte(pcx.fp, 0x0C); for (i = 0; i < img->ncolors; i++) { dm_fwrite_byte(pcx.fp, img->pal[i].r); dm_fwrite_byte(pcx.fp, img->pal[i].g); dm_fwrite_byte(pcx.fp, img->pal[i].b); } // Pad the palette, if necessary for (; i < 256; i++) { dm_fwrite_byte(pcx.fp, 0); dm_fwrite_byte(pcx.fp, 0); dm_fwrite_byte(pcx.fp, 0); } } error: if (pcx.fp != NULL) fclose(pcx.fp); dmFree(pcx.buf); return res; } static BOOL dmPCXDecodeRLERow(FILE *fp, uint8_t *buf, const size_t bufLen) { size_t offs = 0; do { int count; uint8_t data; if (!dm_fread_byte(fp, &data)) return FALSE; if ((data & 0xC0) == 0xC0) { count = data & 0x3F; if (!dm_fread_byte(fp, &data)) return FALSE; } else count = 1; while (count-- && offs < bufLen) buf[offs++] = data; } while (offs < bufLen); return TRUE; } int dmReadPCXImageFILE(FILE *fp, DMImage **pimg) { DMImage *img; DMPCXData pcx; DMPCXHeader hdr; BOOL paletted; int res = 0, yc, xc; uint8_t *dp; pcx.buf = NULL; // Read PCX header if (!dm_fread_byte(fp, &hdr.manufacturer) || !dm_fread_byte(fp, &hdr.version) || !dm_fread_byte(fp, &hdr.encoding) || !dm_fread_byte(fp, &hdr.bpp)) { dmError("PCX: Could not read basic header data.\n"); res = -9; } if (hdr.manufacturer != 10 || hdr.version != 5 || hdr.encoding != 1 || hdr.bpp != 8) { dmError("PCX: Not a PCX file, or unsupported variant.\n"); res = -11; goto error; } if (!dm_fread_le16(fp, &hdr.xmin) || !dm_fread_le16(fp, &hdr.ymin) || !dm_fread_le16(fp, &hdr.xmax) || !dm_fread_le16(fp, &hdr.ymax) || !dm_fread_le16(fp, &hdr.hres) || !dm_fread_le16(fp, &hdr.vres)) { dmError("PCX: Could not read image dimensions.\n"); res = -8; goto error; } if (!dm_fread_str(fp, (uint8_t *) &hdr.colormap, sizeof(hdr.colormap))) { dmError("PCX: Could not read colormap.\n"); res = -7; goto error; } if (!dm_fread_byte(fp, &hdr.reserved) || !dm_fread_byte(fp, &hdr.nplanes) || !dm_fread_le16(fp, &hdr.bpl) || !dm_fread_le16(fp, &hdr.palinfo) || !dm_fread_str(fp, (uint8_t *) &hdr.filler, sizeof(hdr.filler))) { dmError("PCX: Could not read header remainder.\n"); res = -6; goto error; } if (hdr.nplanes != 3 && hdr.nplanes != 1) { dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes); res = -4; goto error; } // Allocate image if ((*pimg = img = dmImageAlloc(hdr.xmax - hdr.xmin + 1, hdr.ymax - hdr.ymin + 1)) == NULL) { dmError("PCX: Could not allocate image structure.\n"); res = -5; goto error; } paletted = hdr.nplanes == 1; pcx.bufLen = hdr.nplanes * hdr.bpl; if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) { dmError("PCX: Could not allocate RLE buffer.\n"); res = -3; goto error; } // Read image data dp = img->data; for (yc = 0; yc < img->height; yc++) { // Decode row of RLE'd data if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen)) { dmError("PCX: Error decoding RLE data.\n"); res = -100; goto error; } // Decode bitplanes switch (hdr.nplanes) { case 1: memcpy(dp, pcx.buf, img->width); break; case 3: { uint8_t *dptr = dp, *sptr1 = pcx.buf, *sptr2 = sptr1 + hdr.bpl, *sptr3 = sptr2 + hdr.bpl; for (xc = 0; xc < img->width; xc++) { *dptr++ = *sptr1++; *dptr++ = *sptr2++; *dptr++ = *sptr3++; } } break; } dp += img->pitch; } // Read VGA palette if (paletted) { int i; uint8_t tmpb; if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) goto error; for (i = 0; i < img->ncolors; i++) { if (!dm_fread_byte(fp, &tmpb)) goto error; img->pal[i].r = tmpb; if (!dm_fread_byte(fp, &tmpb)) goto error; img->pal[i].g = tmpb; if (!dm_fread_byte(fp, &tmpb)) goto error; img->pal[i].b = tmpb; } } error: dmFree(pcx.buf); return res; } int dmReadPCXImage(const char *filename, DMImage **pimg) { FILE *fp; int res; if ((fp = fopen(filename, "rb")) == NULL) { dmError("PCX: Could not open file '%s' for reading.\n", filename); return -15; } res = dmReadPCXImageFILE(fp, pimg); fclose(fp); return res; } int fmtProbePNGImageFILE(FILE *fp) { uint8_t buf[6]; // if (!dm_fread_str(fp, return DM_PROBE_SCORE_FALSE; } int fmtProbePCXImageFILE(FILE *fp) { DMPCXHeader hdr; if (!dm_fread_byte(fp, &hdr.manufacturer) || !dm_fread_byte(fp, &hdr.version) || !dm_fread_byte(fp, &hdr.encoding) || !dm_fread_byte(fp, &hdr.bpp)) return DM_PROBE_SCORE_FALSE; if (hdr.manufacturer == 10 && hdr.version == 5 && hdr.encoding == 1 && hdr.bpp == 8) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } #ifdef UNFINISHED int dmConvertBMP2(DMImage *screen, const DM64Image *img) { int yc; uint8_t *dp = screen->data; for (yc = 0; yc < screen->height; yc++) { uint8_t *d = dp; const int y = yc / 8, yb = yc & 7; const int scroffsy = y * C64_SCR_CH_WIDTH; const int bmoffsy = y * C64_SCR_WIDTH; int xc; for (xc = 0; xc < screen->width / 2; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; const int v = 6 - ((xc * 2) & 6); uint8_t c; switch ((b >> v) & 3) { case 0: c = img->bgcolor; break; case 1: c = img->screen[0][scroffs] >> 4; break; case 2: c = img->screen[0][scroffs] & 15; break; case 3: c = img->color[0][scroffs] & 15; break; } *d++ = c; *d++ = c; } dp += screen->pitch; } return 0; } #endif int dmWriteImage(char *filename, DMImage *image, int format, BOOL paletted, int scale, int bpp) { switch (format) { #ifdef HAVE_LIBPNG case OUTFMT_PNG: return dmWritePNGImage(filename, image, scale, paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA); #endif case OUTFMT_PPM: return dmWritePPMImage(filename, image, scale); case OUTFMT_PCX: return dmWritePCXImage(filename, image, scale, paletted); case OUTFMT_ARAW: { int res; char *palFilename = dm_strdup_printf("%s.pal", filename); res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << bpp); dmFree(palFilename); if (res != 0) return res; return dmWriteIFFMasterRAWImage(filename, image, scale, bpp); } default: return FALSE; } } int dmDumpSpritesAndChars(FILE *inFile) { int dataOffs, itemCount, outWidth, outWidthPX, outHeight; size_t bufSize; uint8_t *bufData; switch (optInFormat) { case INFMT_CHAR: bufSize = C64_CHR_SIZE; outWidth = C64_CHR_WIDTH; outWidthPX = C64_CHR_WIDTH_PX; outHeight = C64_CHR_HEIGHT; break; case INFMT_SPRITE: bufSize = C64_SPR_SIZE; outWidth = C64_SPR_WIDTH; outWidthPX = C64_SPR_WIDTH_PX; outHeight = C64_SPR_HEIGHT; break; default: dmError("Invalid input format %d, internal error.\n", optInFormat); return -1; } if ((bufData = dmMalloc(bufSize)) == NULL) { dmError("Could not allocate temporary buffer of %d bytes.\n", bufSize); return -2; } dataOffs = optInSkip; itemCount = 0; if (optOutFormat == OUTFMT_ANSI || optOutFormat == OUTFMT_ASCII) { BOOL error = FALSE; FILE *outFile; if (optOutFilename == NULL) outFile = stdout; else if ((outFile = fopen(optOutFilename, "w")) == NULL) { int res = errno; dmError("Error opening output file '%s'. (%s)\n", optOutFilename, strerror(res)); goto error; } while (!feof(inFile) && !error && (optItemCount < 0 || itemCount < optItemCount)) { memset(bufData, 0, bufSize); if (fread(bufData, 1, bufSize, inFile) != bufSize) { dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", bufSize, dataOffs); error = TRUE; } fprintf(outFile, "---- : -------------- #%d\n", itemCount); switch (optInFormat) { case INFMT_CHAR: dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); break; case INFMT_SPRITE: dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); break; } itemCount++; } fclose(outFile); } else if (optOutFormat == OUTFMT_PNG || optOutFormat == OUTFMT_PPM || optOutFormat == OUTFMT_PCX) { DMImage *outImage = NULL; char *outFilename = NULL; int outX = 0, outY = 0, err; #ifndef HAVE_LIBPNG if (optOutFormat == OUTFMT_PNG) { dmError("PNG output format support not compiled in, sorry.\n"); goto error; } #endif if (optSequential) { if (optOutFilename == NULL) { dmError("Sequential image output requires filename template.\n"); goto error; } outImage = dmImageAlloc(outWidthPX, outHeight); dmMsg(1, "Outputting sequence of %d images @ %d x %d -> %d x %d.\n", optItemCount, outImage->width, outImage->height, outImage->width * optScale, outImage->height * optScale); } else { int outIWidth, outIHeight; if (optItemCount <= 0) { dmError("Single-image output requires count to be set (-n).\n"); goto error; } outIWidth = optPlanedWidth; outIHeight = (optItemCount / optPlanedWidth); if (optItemCount % optPlanedWidth) outIHeight++; outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight); dmMsg(1, "Outputting image %d x %d -> %d x %d.\n", outImage->width, outImage->height, outImage->width * optScale, outImage->height * optScale); } outImage->constpal = TRUE; outImage->pal = dmC64Palette; outImage->ncolors = C64_NCOLORS; outImage->ctrans = 255; while (!feof(inFile) && (optItemCount < 0 || itemCount < optItemCount)) { memset(bufData, 0, bufSize); if (fread(bufData, 1, bufSize, inFile) != bufSize) { dmError("Could not read full bufferful (%d bytes) of data at 0x%x.\n", bufSize, dataOffs); break; } if ((err = dmC64ConvertCSData(outImage, outX * outWidthPX, outY * outHeight, bufData, outWidth, outHeight, optInMulticolor, optColors)) != 0) { dmError("Internal error in conversion of raw data to bitmap: %d.\n", err); break; } if (optSequential) { outFilename = dm_strdup_printf("%s%04d.%s", optOutFilename, itemCount, outFormatList[optOutFormat]); if (outFilename == NULL) { dmError("Could not allocate memory for filename template?\n"); goto error; } dmWriteImage(outFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); dmFree(outFilename); } else { if (++outX >= optPlanedWidth) { outX = 0; outY++; } } itemCount++; } if (!optSequential) { dmWriteImage(optOutFilename, outImage, optOutFormat, optPaletted, optScale, optBPP); } dmImageFree(outImage); } dmFree(bufData); return 0; error: dmFree(bufData); return -1; } int main(int argc, char *argv[]) { FILE *inFile; int i, optInImageFormat; // Default colors for (i = 0; i < C64_MAX_COLORS; i++) optColors[i] = i + 1; // Initialize and parse commandline dmInitProg("gfxconv", "Simple c64 graphics converter", "0.4", NULL, NULL); if (!dmArgsProcess(argc, argv, optList, optListN, argHandleOpt, argHandleFile, TRUE)) exit(1); // Determine input format, if not specified' if (optInFormat == INFMT_AUTO && optInFilename != NULL) { char *dext = strrchr(optInFilename, '.'); if (dext) { dext++; if (!strcasecmp(dext, "fnt") || !strcasecmp(dext, "chr")) optInFormat = INFMT_CHAR; else if (!strcasecmp(dext, "spr")) optInFormat = INFMT_SPRITE; else if (!strcasecmp(dext, "png") || !strcasecmp(dext, "pcx")) optInFormat = INFMT_IMAGE; } } if (optInFilename == NULL) { if (optInFormat == INFMT_AUTO) { dmError("Standard input cannot be used without specifying input format.\n"); exit(3); } inFile = stdin; } else if ((inFile = fopen(optInFilename, "rb")) == NULL) { int res = errno; dmError("Error opening input file '%s'. (%s)\n", optInFilename, strerror(res)); exit(3); } if (optInFormat == INFMT_AUTO) { // Skip, if needed if (fseek(inFile, optInSkip, SEEK_SET) != 0) { int res = errno; dmError("Could not seek to file position %d (0x%x): %s\n", optInSkip, optInSkip, strerror(res)); exit(3); } #if 0 if (optInFormat == INFMT_AUTO) { int ret = dmC64ProbeGeneric } #endif if (optInFormat == INFMT_AUTO || optInFormat == INFMT_IMAGE) { if (fmtProbePNGImageFILE(inFile)) { optInFormat = INFMT_IMAGE; optInImageFormat = OUTFMT_PNG; } else if (fmtProbePCXImageFILE(inFile)) { optInFormat = INFMT_IMAGE; optInImageFormat = OUTFMT_PCX; } else if (optInFormat == INFMT_IMAGE) { dmError("Unsupported image input format.\n"); exit(4); } } } if (optInFormat == INFMT_AUTO) { dmError("No input format specified, and could not be determined automatically.\n"); exit(1); } // Skip, if needed if (fseek(inFile, optInSkip, SEEK_SET) != 0) { int res = errno; dmError("Could not seek to file position %d (0x%x): %s\n", optInSkip, optInSkip, strerror(res)); exit(3); } switch (optInFormat) { case INFMT_SPRITE: case INFMT_CHAR: dmDumpSpritesAndChars(inFile); break; case INFMT_BITMAP: case INFMT_IMAGE: { DMImage *img; int res; if (optOutFilename == NULL) { dmError("Output filename not set, required for image formats.\n"); exit(3); } // Read input switch (optInImageFormat) { case OUTFMT_PCX: res = dmReadPCXImageFILE(inFile, &img); break; case OUTFMT_PNG: // res = dmReadPNGImageFILE(inFile, &img); break; } switch (optOutFormat) { case OUTFMT_PCX: case OUTFMT_PPM: case OUTFMT_PNG: case OUTFMT_ARAW: res = dmWriteImage(optOutFilename, img, optOutFormat, optPaletted, optScale, optBPP); break; } } break; } fclose(inFile); exit(0); return 0; }