Mercurial > hg > dmlib
view lib64gfx.c @ 409:b529b7e8ff83
Various improvements and cruft cleanups.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 03 Nov 2012 02:40:07 +0200 |
parents | 59244a7ae37f |
children | e4b2f689aff6 |
line wrap: on
line source
/* * Functions for reading and converting various restricted * C64/etc and/or indexed/paletted 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 "lib64gfx.h" #include <errno.h> const char *dmC64ImageTypeNames[DM_C64IFMT_LAST_TYPE] = { "hires", "multicolor", "hires interlace", "mc interlace", "hires fli", "mc fli", }; // Based on Pepto's palette, stolen from VICE DMColor dmC64Palette[C64_NCOLORS] = { { 0x00, 0x00, 0x00, 0 }, { 0xFF, 0xFF, 0xFF, 0 }, { 0x68, 0x37, 0x2B, 0 }, { 0x70, 0xA4, 0xB2, 0 }, { 0x6F, 0x3D, 0x86, 0 }, { 0x58, 0x8D, 0x43, 0 }, { 0x35, 0x28, 0x79, 0 }, { 0xB8, 0xC7, 0x6F, 0 }, { 0x6F, 0x4F, 0x25, 0 }, { 0x43, 0x39, 0x00, 0 }, { 0x9A, 0x67, 0x59, 0 }, { 0x44, 0x44, 0x44, 0 }, { 0x6C, 0x6C, 0x6C, 0 }, { 0x9A, 0xD2, 0x84, 0 }, { 0x6C, 0x5E, 0xB5, 0 }, { 0x95, 0x95, 0x95, 0 }, }; const size_t dmC64DefaultSizes[DT_LAST] = { C64_SCR_COLOR_SIZE, C64_SCR_BITMAP_SIZE, C64_SCR_SCREEN_SIZE, 1, C64_SCR_EXTRADATA, }; DMImage * dmImageAlloc(int width, int height) { DMImage *img = dmCalloc(1, sizeof(DMImage)); if (img == NULL) return NULL; img->width = img->pitch = width; img->height = height; img->data = dmMalloc(width * height * sizeof(uint8_t)); if (img->data == NULL) { dmFree(img); return NULL; } return img; } void dmImageFree(DMImage *img) { if (img != NULL) { if (!img->constpal) { dmFree(img->pal); } dmFree(img->data); dmFree(img); } } int dmImageGetBytesPerPixel(int format) { switch (format) { case DM_IFMT_PALETTE : return 1; case DM_IFMT_RGB : return 3; case DM_IFMT_RGBA : return 4; case DM_IFMT_RGB_PLANE : return 3; default: return 0; } } int dmC64ConvertCSData(DMImage *img, int xoffs, int yoffs, const uint8_t *buf, int width, int height, BOOL multicolor, int *colors) { int yc, widthpx = width * 8; uint8_t *dp; if (img == NULL) return -1; if (xoffs < 0 || yoffs < 0) return -2; if (xoffs > img->width - widthpx || yoffs > img->height - height) return -3; dp = img->data + (yoffs * img->pitch) + xoffs; if (multicolor) { for (yc = 0; yc < height; yc++) { const int offs = yc * width; int xc; uint8_t *d = dp; for (xc = 0; xc < widthpx / 2; xc++) { const int b = buf[offs + (xc / 4)]; const int v = 6 - ((xc * 2) & 6); const uint8_t c = colors[(b >> v) & 3]; *d++ = c; *d++ = c; } dp += img->pitch; } } else { for (yc = 0; yc < height; yc++) { const int offs = yc * width; int xc; uint8_t *d = dp; for (xc = 0; xc < widthpx; xc++) { const int b = buf[offs + (xc / 8)]; const int v = 7 - (xc & 7); const uint8_t c = colors[(b >> v) & 1]; *d++ = c; } dp += img->pitch; } } return 0; } static int fmtProbeDrazPaint(const uint8_t *buf, const size_t len) { if (len == 10051 && buf[0] == 0x00 && buf[1] == 0x58) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static int fmtProbeDrazPaint20Packed(const uint8_t *buf, const size_t len) { const char *ident = (const char *) buf + 2; if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 && strncmp(ident, "DRAZPAINT ", 10) == 0 && ident[11] == '.' && ( (ident[10] == '1' && ident[12] == '4') || (ident[10] == '2' && ident[12] == '0') )) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static int fmtDecodeDrazPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt) { int res; uint8_t rleMarker; uint8_t *mem, *dst, *dstEnd; const uint8_t *src, *srcEnd; if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL) return -1; rleMarker = *(buf + 0x0d); src = buf + 0x0e; srcEnd = buf + len; dst = mem; dstEnd = mem + C64_RAM_SIZE; while (src <= srcEnd && dst <= dstEnd) { int c = *src++; if (c == rleMarker && src + 2 <= srcEnd) { int cnt = *src++; c = *src++; while (cnt-- && dst <= dstEnd) *dst++ = c; } else *dst++ = c; } res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt); dmFree(mem); return res; } static int fmtProbeDrazLace10(const uint8_t *buf, const size_t len) { if (len == 18242 && buf[0] == 0x00 && buf[1] == 0x58) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static int fmtProbeDrazLace10Packed(const uint8_t *buf, const size_t len) { const char *ident = (const char *) buf + 2; if (len > 22 && buf[0] == 0x00 && buf[1] == 0x58 && strncmp(ident, "DRAZLACE! 1.0", 13) == 0) return DM_PROBE_SCORE_MAX; return DM_PROBE_SCORE_FALSE; } static BOOL fmtDrazLaceSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len) { img->laceType = buf[op->offs] ? DM_C64ILACE_RES : DM_C64ILACE_COLOR; img->laceBank2 = 0; return TRUE; } #define AMICA_DM_PROBE_SIZE 1024 static int fmtProbeAmicaPaintPacked(const uint8_t *buf, const size_t len) { int i, n; if (len < AMICA_DM_PROBE_SIZE || buf[0] != 0x00 || buf[1] != 0x40) return DM_PROBE_SCORE_FALSE; for (n = 0, i = 2; i < AMICA_DM_PROBE_SIZE; i++) if (buf[i] == 0xC2) n++; if (n > 5) return DM_PROBE_SCORE_GOOD; if (n > 3) return DM_PROBE_SCORE_AVG; return DM_PROBE_SCORE_MAYBE; } static int fmtDecodeAmicaPaintPacked(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt) { int res; uint8_t *mem, *dst, *dstEnd; const uint8_t *src, *srcEnd; if ((mem = dmMalloc(C64_RAM_SIZE)) == NULL) return -1; src = buf; srcEnd = buf + len; dst = mem; dstEnd = mem + C64_RAM_SIZE; while (src <= srcEnd && dst <= dstEnd) { int c = *src++; if (c == 0xC2 && src + 2 <= srcEnd) { int cnt = *src++; c = *src++; while (cnt-- && dst <= dstEnd) *dst++ = c; } else *dst++ = c; } res = dmC64DecodeGenericBMP(img, mem, dst - mem + 1, fmt); dmFree(mem); return res; } static int fmtProbeKoalaPaint(const uint8_t *buf, const size_t len) { if (len == 10003 && buf[0] == 0x00 && buf[1] == 0x60) return DM_PROBE_SCORE_AVG; return DM_PROBE_SCORE_FALSE; } static int fmtProbeTruePaint(const uint8_t *buf, const size_t len) { if (len == 19434 && buf[0] == 0x00 && buf[1] == 0x9c) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static BOOL fmtTruePaintSetLaceType(DMC64Image *img, const struct _DMDecodeOp *op, const uint8_t *buf, const size_t len) { img->laceType = DM_C64ILACE_RES; img->laceBank2 = 1; return TRUE; } DMC64ImageFormat dmC64ImageFormats[] = { { DM_C64IFMT_MC, ".drp", "DrazPaint 2.0 (packed)", fmtProbeDrazPaint20Packed, fmtDecodeDrazPaintPacked, NULL, 4, { { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, { DT_BITMAP, 0x0800, 0, 0, NULL }, { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, { DT_BGCOLOR, 0x2740, 0, 0, NULL }, } }, { DM_C64IFMT_MC_ILACE, ".dlp", "DrazLace 1.0 (packed)", fmtProbeDrazLace10Packed, fmtDecodeDrazPaintPacked, NULL, 6, { { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, { DT_BITMAP, 0x0800, 0, 0, NULL }, { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, { DT_BGCOLOR, 0x2740, 0, 0, NULL }, { DT_BITMAP, 0x2800, 1, 0, NULL }, { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType }, } }, { DM_C64IFMT_MC, ".drp", "DrazPaint (unpacked)", fmtProbeDrazPaint, NULL, NULL, 4, { { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, { DT_BITMAP, 0x0800, 0, 0, NULL }, { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, { DT_BGCOLOR, 0x2740, 0, 0, NULL }, } }, { DM_C64IFMT_MC_ILACE, ".drl", "DrazLace 1.0 (unpacked)", fmtProbeDrazLace10, NULL, NULL, 6, { { DT_COLOR_RAM, 0x0000, 0, 0, NULL }, { DT_BITMAP, 0x0800, 0, 0, NULL }, { DT_SCREEN_RAM, 0x0400, 0, 0, NULL }, { DT_BGCOLOR, 0x2740, 0, 0, NULL }, { DT_BITMAP, 0x2800, 1, 0, NULL }, { DT_FUNCTION, 0x2742, 0, 1, fmtDrazLaceSetLaceType }, } }, { DM_C64IFMT_MC_ILACE, ".mci", "Truepaint (unpacked)", fmtProbeTruePaint, NULL, NULL, 6, { { DT_SCREEN_RAM, 0x0000, 0, 0, NULL }, { DT_BGCOLOR, 0x03e8, 0, 0, NULL }, { DT_BITMAP, 0x0400, 0, 0, NULL }, { DT_BITMAP, 0x2400, 1, 0, NULL }, { DT_SCREEN_RAM, 0x4400, 1, 0, NULL }, { DT_COLOR_RAM, 0x4800, 0, 0, NULL }, { DT_FUNCTION, 0x0000, 0, 0, fmtTruePaintSetLaceType }, } }, { DM_C64IFMT_MC, ".kla", "Koala Paint (unpacked)", fmtProbeKoalaPaint, NULL, NULL, 4, { { DT_COLOR_RAM, 0x2328, 0, 0, NULL }, { DT_BITMAP, 0x0000, 0, 0, NULL }, { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL }, { DT_BGCOLOR, 0x2710, 0, 0, NULL }, } }, { DM_C64IFMT_MC, ".ami", "Amica Paint (packed)", fmtProbeAmicaPaintPacked, fmtDecodeAmicaPaintPacked, NULL, 4, { { DT_COLOR_RAM, 0x2328, 0, 0, NULL }, { DT_BITMAP, 0x0000, 0, 0, NULL }, { DT_SCREEN_RAM, 0x1f40, 0, 0, NULL }, { DT_BGCOLOR, 0x2710, 0, 0, NULL }, } }, }; const int ndmC64ImageFormats = sizeof(dmC64ImageFormats) / sizeof(dmC64ImageFormats[0]); int dmC64ProbeGeneric(const uint8_t *buf, const size_t len, DMC64ImageFormat **pfmt) { int i, scoreMax = 0, scoreIndex = -1; for (i = 0; i < ndmC64ImageFormats; i++) { DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; int score = fmt->probe(buf, len); if (score > scoreMax) { scoreMax = score; scoreIndex = i; } } if (scoreIndex >= 0) { *pfmt = &dmC64ImageFormats[scoreIndex]; return scoreMax; } else return 0; } int dmC64DecodeGenericBMP(DMC64Image *img, const uint8_t *buf, const size_t len, const DMC64ImageFormat *fmt) { int i; memset(img, 0, sizeof(*img)); img->type = fmt->type; for (i = 0; i < fmt->ndecodeOps; i++) { const DMDecodeOp *op = &fmt->decodeOps[i]; const uint8_t *src; size_t size; if (op->bank < 0 || op->bank >= C64_SCR_MAX_BANK) { dmError("Invalid bank %d definition in generic decode operator %d @ #%d.\n", op->bank, op->type, i); return -1; } if (op->type < 0 || op->type >= DT_LAST) { dmError("Invalid decode operator type %d @ #%d.\n", op->type, i); return -1; } size = (op->size == 0) ? dmC64DefaultSizes[op->type] : op->size; if (op->offs + size > len) { dmError("Decode out of bounds, op #%d type=%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", i, op->type, op->offs, op->offs, op->bank, size, size, len, len); return -2; } src = buf + op->offs; switch (op->type) { case DT_COLOR_RAM: memcpy(img->color[op->bank], src, size); break; case DT_BITMAP: memcpy(img->bitmap[op->bank], src, size); break; case DT_SCREEN_RAM: memcpy(img->screen[op->bank], src, size); break; case DT_BGCOLOR: img->bgcolor = *src; break; case DT_EXTRADATA: memcpy(img->extradata, src, size); break; case DT_FUNCTION: if (op->function == NULL) { dmError("Decode op is a function, but function ptr is NULL: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", i, op->offs, op->offs, op->bank, size, size, len, len); return -6; } if (!op->function(img, op, buf, len)) { dmError("Decode op custom function failed: op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x\n", i, op->offs, op->offs, op->bank, size, size, len, len); return -5; } break; } } return 0; } static int dmC64ConvertHiResBMP(DMImage *screen, const DMC64Image *img) { int yc; uint8_t *dp = screen->data; for (yc = 0; yc < C64_SCR_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 < C64_SCR_WIDTH; xc++) { const int x = xc / 8; const int scroffs = scroffsy + x; const int b = img->bitmap[0][bmoffsy + (x * 8) + yb]; const int v = 7 - (xc & 7); uint8_t c; if ((b >> v) & 1) c = img->screen[0][scroffs] & 15; else c = img->screen[0][scroffs] >> 4; *d++ = c; } dp += screen->pitch; } return 0; } static int dmC64ConvertMultiColorBMP(DMImage *screen, const DMC64Image *img) { int yc; uint8_t *dp = screen->data; for (yc = 0; yc < C64_SCR_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 < C64_SCR_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; } static int dmC64ConvertLaceMultiColorBMP(DMImage *screen, const DMC64Image *img) { int yc; uint8_t *dp = screen->data; for (yc = 0; yc < C64_SCR_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 < C64_SCR_WIDTH / 2; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int bmoffs = bmoffsy + (x * 8) + yb; const int v = 6 - ((xc * 2) & 6); const int b1 = (img->bitmap[0][bmoffs] >> v) & 3; const int b2 = (img->bitmap[1][bmoffs] >> v) & 3; uint8_t c1, c2; switch (b1) { case 0: c1 = img->bgcolor; break; case 1: c1 = img->screen[0][scroffs] >> 4; break; case 2: c1 = img->screen[0][scroffs] & 15; break; case 3: c1 = img->color[0][scroffs] & 15; break; } switch (b2) { case 0: c2 = img->bgcolor; break; case 1: c2 = img->screen[img->laceBank2][scroffs] >> 4; break; case 2: c2 = img->screen[img->laceBank2][scroffs] & 15; break; case 3: c2 = img->color[img->laceBank2][scroffs] & 15; break; } *d++ = c1; *d++ = c2; } dp += screen->pitch; } return 0; } int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src) { switch (src->type) { case DM_C64IFMT_HIRES: return dmC64ConvertHiResBMP(dst, src); case DM_C64IFMT_MC: return dmC64ConvertMultiColorBMP(dst, src); case DM_C64IFMT_MC_ILACE: return dmC64ConvertLaceMultiColorBMP(dst, src); default: return -1; } } #define BUF_SIZE_INITIAL (16*1024) #define BUF_SIZE_GROW (2*1024) int dmReadDataFile(const char *filename, uint8_t **pbuf, size_t *pbufSize) { FILE *f; uint8_t *dataBuf = NULL, *dataPtr; size_t bufSize, readSize, dataSize; if (filename == NULL) f = stdin; else if ((f = fopen(filename, "rb")) == NULL) { int err = errno; dmError("Could not open input file '%s': %d, %s\n", filename, err, strerror(err)); return -1; } readSize = bufSize = BUF_SIZE_INITIAL; if ((dataBuf = dmMalloc(bufSize)) == NULL) { fclose(f); dmError("Error allocating memory for data, %d bytes.\n", bufSize); return -4; } dataPtr = dataBuf; dataSize = 0; while (!feof(f) && !ferror(f)) { size_t read = fread(dataPtr, 1, readSize, f); dataSize += read; dataPtr += read; if (read == readSize && !feof(f)) { readSize = BUF_SIZE_GROW; bufSize += BUF_SIZE_GROW; if ((dataBuf = dmRealloc(dataBuf, bufSize)) == NULL) { dmError("Error reallocating memory for data, %d bytes.\n", bufSize); return -4; } } else break; } fclose(f); *pbuf = dataBuf; *pbufSize = dataSize; return 0; }