Mercurial > hg > dmlib
view 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 |
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 "libgfx.h" #include "lib64gfx.h" //#define UNFINISHED 1 enum { FFMT_AUTO = 0, FFMT_ASCII, FFMT_ANSI, FFMT_IMAGE, FFMT_CHAR, FFMT_SPRITE, FFMT_BITMAP, FFMT_LAST }; typedef struct { char *name; char *fext; BOOL in, out; int format; int subformat; } DMConvFormat; static DMConvFormat convFormatList[] = { { "ASCII text", "asc", FALSE, TRUE, FFMT_ASCII , 0, }, { "ANSI colored text", "ansi", FALSE, TRUE, FFMT_ANSI , 0, }, { "PNG image file", "png", FALSE, TRUE, FFMT_IMAGE , IMGFMT_PNG, }, { "PPM image file", "ppm", FALSE, TRUE, FFMT_IMAGE , IMGFMT_PPM, }, { "PCX image file", "pcx", TRUE, TRUE, FFMT_IMAGE , IMGFMT_PCX, }, { "IFFMaster RAW image file", "araw", FALSE, TRUE, FFMT_IMAGE , IMGFMT_ARAW, }, { "C64 bitmap image file", NULL, TRUE, FALSE, FFMT_BITMAP , 0, }, { "C64 character/font data", "chr", TRUE, FALSE, FFMT_CHAR , 0 }, { "C64 sprite data", "spr", TRUE, FALSE, FFMT_SPRITE , 0 }, }; static const int nconvFormatList = sizeof(convFormatList) / sizeof(convFormatList[0]); #define ASC_NBITS 8 #define ASC_NCOLORS 4 static const char dmASCIIPalette[ASC_NCOLORS] = ".:X#"; char *optInFilename = NULL, *optOutFilename = NULL; int optInFormat = FFMT_AUTO, optOutFormat = FFMT_ASCII, optInSubFormat = IMGFMT_PNG, optOutSubFormat = IMGFMT_PNG, optItemCount = -1, optPlanedWidth = 1, optForcedFormat = -1; int optInSkip = 0; BOOL optInMulticolor = FALSE, optSequential = FALSE; int optColors[C64_MAX_COLORS]; DMImageSpec optSpec = { .scale = 2, .nplanes = 4, .interleave = FALSE, .paletted = FALSE, .format = 0, }; static DMOptArg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 15, 'v', "verbose", "Increase verbosity", OPT_NONE }, { 3, 'o', "output", "Output filename", OPT_ARGREQ }, { 1, 'i', "informat", "Set input format ([s]prite, [c]har, [b]itmap, [i]mage)", 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 }, { 10, 'b', "bformat", "Force input bitmap format (see below)", OPT_ARGREQ }, { 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', "bplanes", "Bits per pixel OR # of bitplanes (certain output formats)", OPT_ARGREQ }, { 14, 'I', "interleave", "Interleave scanlines (default: output whole planes)", OPT_NONE }, }; static const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp() { int i; dmPrintBanner(stdout, dmProgName, "[options] <input file>"); dmArgsPrintHelp(stdout, optList, optListN); printf("\n" "Available input/output formats:\n" " EXT | I | O | Description\n" "------+---+---+--------------------------------\n" ); for (i = 0; i < nconvFormatList; i++) { DMConvFormat *fmt = &convFormatList[i]; printf("%-5s | %c | %c | %s\n", fmt->fext ? fmt->fext : "", fmt->in ? 'X' : ' ', fmt->out ? 'X' : ' ', fmt->name); } printf("\nAvailable bitmap formats:\n"); for (i = 0; i < ndmC64ImageFormats; i++) { DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; printf("%3d | %-5s | %-15s | %s\n", i, fmt->extension, dmC64ImageTypeNames[fmt->type], fmt->name); } 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" ); } int dmGetConvFormat(int format, int subformat) { int i; for (i = 0; i < nconvFormatList; i++) { DMConvFormat *fmt = &convFormatList[i]; if (fmt->format == format && fmt->subformat == subformat) return i; } return -1; } BOOL dmGetFormatByExt(const char *fext, int *format, int *subformat) { int i; if (fext == NULL) return FALSE; for (i = 0; i < nconvFormatList; i++) { DMConvFormat *fmt = &convFormatList[i]; if (fmt->fext != NULL && strcasecmp(fext, fmt->fext) == 0) { *format = fmt->format; *subformat = fmt->subformat; return TRUE; } } return FALSE; } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 15: dmVerbosity++; break; case 1: switch (tolower(optArg[0])) { case 's': optInFormat = FFMT_SPRITE; break; case 'c': optInFormat = FFMT_CHAR; break; case 'b': optInFormat = FFMT_BITMAP; break; case 'i': optInFormat = FFMT_IMAGE; 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: if (!dmGetFormatByExt(optArg, &optOutFormat, &optOutSubFormat)) { dmError("Invalid output format '%s'.\n", optArg); return FALSE; } 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; } optSpec.scale = 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: optSpec.paletted = TRUE; break; case 13: { int tmp = atoi(optArg); if (tmp < 1 || tmp > 8) { dmError("Invalid bitplanes/bpp value '%s'.\n", optArg); return FALSE; } optSpec.nplanes = tmp; } break; case 14: optSpec.interleave = 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 FFMT_ASCII: ch = dmASCIIPalette[val]; fprintf(out, "%c%c", ch, ch); break; case FFMT_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 FFMT_ASCII: ch = val ? '#' : '.'; fputc(ch, out); break; case FFMT_ANSI: fprintf(out, "%c[0;%d;%dm %c[0m", 0x1b, 1, 31 + optColors[val], 0x1b); break; } } } } void dmDumpCharASCII(FILE *outFile, const Uint8 *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 *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)++; } #ifdef UNFINISHED int dmConvertBMP2(DMImage *screen, const DM64Image *img) { int yc; Uint8 *dp = screen->data; for (yc = 0; yc < screen->height; yc++) { Uint8 *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 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(const char *filename, DMImage *image, DMImageSpec *spec, int iformat, BOOL info) { if (info) { dmMsg(1, "Outputting %s image %d x %d -> %d x %d [%d]\n", dmImageFormatList[iformat], image->width, image->height, image->width * spec->scale, image->height * spec->scale, spec->scale); } switch (iformat) { #ifdef DM_USE_LIBPNG case IMGFMT_PNG: if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "32bit RGBA"); spec->format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGBA; return dmWritePNGImage(filename, image, spec); #endif case IMGFMT_PPM: if (info) dmMsg(2, "24bit RGB output.\n"); spec->format = DM_IFMT_RGB; return dmWritePPMImage(filename, image, spec); case IMGFMT_PCX: if (info) dmMsg(2, "%s output.\n", spec->paletted ? "Indexed 8bpp" : "24bit RGB"); return dmWritePCXImage(filename, image, spec); case IMGFMT_ARAW: { int res; char *palFilename = dm_strdup_printf("%s.pal", filename); res = dmWriteIFFMasterRAWPalette(palFilename, image, 1 << optSpec.nplanes); dmFree(palFilename); if (res != DMERR_OK) return res; if (info) dmMsg(2, "%d bitplanes, %s interleave.\n", spec->nplanes, spec->interleave ? "with" : "without"); return dmWriteIFFMasterRAWImage(filename, image, spec); } default: return FALSE; } } int dmDumpSpritesAndChars(FILE *inFile) { int dataOffs, itemCount, outWidth, outWidthPX, outHeight; size_t bufSize; Uint8 *bufData; switch (optInFormat) { case FFMT_CHAR: bufSize = C64_CHR_SIZE; outWidth = C64_CHR_WIDTH; outWidthPX = C64_CHR_WIDTH_PX; outHeight = C64_CHR_HEIGHT; break; case FFMT_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 == FFMT_ANSI || optOutFormat == FFMT_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 FFMT_CHAR: dmDumpCharASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); break; case FFMT_SPRITE: dmDumpSpriteASCII(outFile, bufData, &dataOffs, optOutFormat, optInMulticolor); break; } itemCount++; } fclose(outFile); } else if (optOutFormat == FFMT_IMAGE) { DMImage *outImage = NULL; char *outFilename = NULL; int outX = 0, outY = 0, err; 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 * optSpec.scale, outImage->height * optSpec.scale); } 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); } 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)) != DMERR_OK) { 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, convFormatList[optOutFormat].fext); if (outFilename == NULL) { dmError("Could not allocate memory for filename template?\n"); goto error; } dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE); dmFree(outFilename); } else { if (++outX >= optPlanedWidth) { outX = 0; outY++; } } itemCount++; } if (!optSequential) { dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE); } dmImageFree(outImage); } dmFree(bufData); return 0; error: dmFree(bufData); return -1; } int main(int argc, char *argv[]) { FILE *inFile; DMC64ImageFormat *cfmt; DMC64Image cimage; Uint8 *dataBuf = NULL; size_t dataSize; int i; // 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); #ifndef DM_USE_LIBPNG if (optOutFormat == IMGFMT_PNG) { dmError("PNG output format support not compiled in, sorry.\n"); goto error; } #endif // Determine input format, if not specified' if (optInFormat == FFMT_AUTO && optInFilename != NULL) { char *dext = strrchr(optInFilename, '.'); if (dext) { dmGetFormatByExt(dext + 1, &optInFormat, &optInSubFormat); } } if (optInFilename == NULL) { if (optInFormat == FFMT_AUTO) { dmError("Standard input cannot be used without specifying input format.\n"); goto error; } inFile = stdin; } else if ((inFile = fopen(optInFilename, "rb")) == NULL) { int res = errno; dmError("Error opening input file '%s'. (%s)\n", optInFilename, strerror(res)); goto error; } if (dmReadDataFile(inFile, NULL, &dataBuf, &dataSize) != 0) goto error; if (optInFormat == FFMT_AUTO || optInFormat == FFMT_BITMAP) { // Probe for format DMC64ImageFormat *forced = NULL; int res; if (optForcedFormat >= 0) { forced = &dmC64ImageFormats[optForcedFormat]; dmMsg(0,"Forced %s format image, type %d, %s\n", forced->name, forced->type, forced->extension); } res = dmC64DecodeBMP(&cimage, dataBuf, dataSize, optInSkip, optInSkip + 2, &cfmt, forced); if (forced == NULL && cfmt != NULL) { dmMsg(1,"Probed %s format image, type %d, %s\n", cfmt->name, cfmt->type, cfmt->extension); } if (res == 0) optInFormat = FFMT_BITMAP; } if (optInFormat == FFMT_AUTO) { DMImageFormat *ifmt = NULL; int index; if (dmImageProbeGeneric(dataBuf + optInSkip, dataSize - optInSkip, &ifmt, &index) > 0) { optInFormat = FFMT_IMAGE; optInSubFormat = index; } } if (optInFormat == FFMT_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)); goto error; } int inFormat = dmGetConvFormat(optInFormat, optInSubFormat), outFormat = dmGetConvFormat(optOutFormat, optOutSubFormat); if (inFormat != -1 && outFormat != -1) { char *inFmtName = convFormatList[inFormat].name, *inFmtExt = convFormatList[inFormat].fext, *outFmtName = convFormatList[outFormat].name, *outFmtExt = convFormatList[outFormat].fext; if (optInFormat == FFMT_BITMAP) inFmtExt = cfmt->name; dmMsg(1, "Attempting conversion %s (%s) -> %s (%s)\n", inFmtName, inFmtExt, outFmtName, outFmtExt); } switch (optInFormat) { case FFMT_SPRITE: case FFMT_CHAR: dmDumpSpritesAndChars(inFile); break; case FFMT_BITMAP: { DMImage *outImage = NULL; int res = DMERR_OK; if (optOutFilename == NULL) { dmError("Output filename not set, required for image formats.\n"); goto error; } switch (optOutFormat) { case FFMT_IMAGE: if ((outImage = dmImageAlloc(C64_SCR_WIDTH, C64_SCR_HEIGHT)) == NULL) { dmError("Could not allocate output image surface %d x %d.\n", C64_SCR_WIDTH, C64_SCR_HEIGHT); goto error; } outImage->pal = (DMColor *) &dmC64Palette; outImage->ncolors = C64_NCOLORS; outImage->constpal = TRUE; if (cfmt->convert != NULL) res = cfmt->convert(outImage, &cimage); else res = dmC64ConvertGenericBMP2Image(outImage, &cimage); if (res != DMERR_OK || outImage == NULL) { dmError("Error in bitmap to image conversion.\n"); goto error; } res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE); break; default: dmError("Unsupported output format for bitmap/image conversion.\n"); break; } dmImageFree(outImage); } break; case FFMT_IMAGE: { DMImage *outImage; int res = DMERR_OK; if (optOutFilename == NULL) { dmError("Output filename not set, required for image formats.\n"); goto error; } // Read input switch (optInSubFormat) { case IMGFMT_PCX: res = dmReadPCXImageFILE(inFile, &outImage); break; // case IMGFMT_PNG: res = dmReadPNGImageFILE(inFile, &outImage); break; // case IMGFMT_ARAW: res = dmReadARAWImageFILE(inFile, &outImage, optSpec.nplanes); break; default: dmError("Unsupported input image format for bitmap/image conversion.\n"); break; } if (res != DMERR_OK || outImage == NULL) break; switch (optOutFormat) { case FFMT_IMAGE: res = dmWriteImage(optOutFilename, outImage, &optSpec, optOutSubFormat, TRUE); break; default: dmError("Unsupported output format for bitmap/image conversion.\n"); break; } dmImageFree(outImage); } break; } fclose(inFile); exit(0); return 0; error: return -3; exit(3); }