Mercurial > hg > dmlib
view tools/lib64gfx.c @ 1542:69fa95707e65
Implement dmGenericRLEAnalyze() and use it where appropriate.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 12 May 2018 05:31:40 +0300 |
parents | 776aa43b2c57 |
children | 20cd589366d7 |
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-2018 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include "lib64gfx.h" #define BUF_SIZE_INITIAL (16*1024) #define BUF_SIZE_GROW (4*1024) void dmC64ImageDump(FILE *fh, const DMC64Image *img, const DMC64ImageFormat *fmt) { char typeStr[64]; dmC64GetImageTypeString(typeStr, sizeof(typeStr), img->type, TRUE); if (fmt != NULL) { fprintf(fh, "Format : %s [%s]\n", fmt->name, fmt->fext); } fprintf(fh, "Type : %s\n" "Banks : %d\n", typeStr, img->nbanks); if (img->type & D64_FMT_ILACE) { char *tmps; switch(img->laceType) { case D64_ILACE_COLOR: tmps = "color"; break; case D64_ILACE_RES: tmps = "resolution"; break; default: tmps = "ERROR"; break; } fprintf(fh, "Interlace type : %s\n", tmps); } fprintf(fh, "Width x Height : %d x %d\n" "CHwidth x CHheight : %d x %d\n", img->width, img->height, img->chWidth, img->chHeight); } void dmSetDefaultC64Palette(DMImage *img) { img->constpal = TRUE; img->pal = dmDefaultC64Palette; img->ncolors = C64_NCOLORS; img->ctransp = 255; } char * dmC64GetImageTypeString(char *buf, const size_t len, const int type, const BOOL lng) { snprintf(buf, len, "%s%s%s%s", (type & D64_FMT_MC) ? (lng ? "MultiColor " : "MC ") : "HiRes ", (type & D64_FMT_ILACE) ? (lng ? "Interlaced " : "ILace ") : "", (type & D64_FMT_FLI) ? "FLI " : "", (type & D64_FMT_CHAR) ? "CHAR" : "" ); return buf; } BOOL dmCompareAddr16(const Uint8 *buf, const size_t offs, const Uint16 addr) { return buf[offs ] == DM_GET_ADDR_LO(addr) && buf[offs + 1] == DM_GET_ADDR_HI(addr); } int dmC64ImageGetNumBanks(const DMC64ImageFormat *fmt) { int nbanks = 0; for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; if (op->type == DT_LAST) break; if (op->bank > nbanks) nbanks = op->bank; } return nbanks + 1; } DMC64Image *dmC64ImageAlloc(const DMC64ImageFormat *fmt) { DMC64Image *img = dmMalloc0(sizeof(DMC64Image)); if (img == NULL) return NULL; // Initialize image information img->type = fmt->type; img->width = fmt->width; img->height = fmt->height; img->chWidth = fmt->chWidth; img->chHeight = fmt->chHeight; img->nbanks = dmC64ImageGetNumBanks(fmt); img->screenSize = img->chWidth * img->chHeight; img->bitmapSize = img->screenSize * 8; img->charmemSize = C64_MAX_CHARS * C64_CHR_SIZE; // Allocate banks if ((img->color = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->bitmap = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->screen = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL || (img->charmem = dmCalloc(img->nbanks, sizeof(Uint8 *))) == NULL) goto err; for (int i = 0; i < img->nbanks; i++) { if ((img->color[i] = dmMalloc0(img->screenSize)) == NULL) goto err; if ((img->bitmap[i] = dmMalloc0(img->bitmapSize)) == NULL) goto err; if ((img->screen[i] = dmMalloc0(img->screenSize)) == NULL) goto err; if ((img->charmem[i] = dmMalloc0(img->charmemSize)) == NULL) goto err; } return img; err: dmC64ImageFree(img); return NULL; } void dmC64ImageFree(DMC64Image *img) { if (img != NULL) { // Free the allocated areas for (int i = 0; i < img->nbanks; i++) { dmFree(img->color[i]); dmFree(img->bitmap[i]); dmFree(img->screen[i]); dmFree(img->charmem[i]); } // Free the pointers to the areas dmFree(img->color); dmFree(img->bitmap); dmFree(img->screen); dmFree(img->charmem); // Extra data .. for (int i = 0; i < C64_MAX_EXTRA_DATA; i++) dmFree(img->extraData[i]); dmMemset(img, 0, sizeof(DMC64Image)); dmFree(img); } } int dmC64ConvertCSDataToImage(DMImage *img, int xoffs, int yoffs, const Uint8 *buf, int width, int height, BOOL multicolor, int *colors) { int yc, widthpx = width * 8; Uint8 *dp; if (img == NULL) return DMERR_NULLPTR; if (xoffs < 0 || yoffs < 0 || xoffs > img->width - widthpx || yoffs > img->height - height) return DMERR_INVALID_ARGS; dp = img->data + (yoffs * img->pitch) + xoffs; if (multicolor) { for (yc = 0; yc < height; yc++) { const int offs = yc * width; int xc; Uint8 *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 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 *d = dp; for (xc = 0; xc < widthpx; xc++) { const int b = buf[offs + (xc / 8)]; const int v = 7 - (xc & 7); const Uint8 c = colors[(b >> v) & 1]; *d++ = c; } dp += img->pitch; } } return DMERR_OK; } void dmGenericRLEAnalyze(const DMGrowBuf *buf, Uint8 *rleMarker, const int rleType) { #define DM_STAT_MAX 256 size_t *stats; // Allocate statistics counts buffer if ((stats = dmMalloc0(DM_STAT_MAX * sizeof(size_t))) == NULL) return; // Get statistics on the data for (size_t offs = 0; offs < buf->len; offs++) stats[buf->data[offs]]++; // According to compression type .. if (rleType == DM_COMP_RLE_MARKER) { size_t selected = 0, smallest = buf->len; // Find least used byte value for (size_t n = 0; n < DM_STAT_MAX; n++) { if (stats[n] < smallest) { selected = n; smallest = stats[n]; } } *rleMarker = selected; } else *rleMarker = 0xC0; dmFree(stats); } int dmDecodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg) { int res; // Perform RLE decode while (src < srcEnd) { Uint8 data = *src++; int count = 1; switch (cfg->type) { case DM_COMP_RLE_MARKER: // A simple marker byte RLE variant: [Marker] [count] [data] if (data == cfg->rleMarker) { if (srcEnd - src < 2) { res = DMERR_INVALID_DATA; goto err; } count = *src++; data = *src++; } break; case DM_COMP_RLE_MASK: // Mask marker RLE: usually high bit(s) of byte mark RLE sequence // and the lower bits contain the count: [Mask + count] [data] if ((data & cfg->rleMask1) == cfg->rleMarker) { if (srcEnd - src < 1) { res = DMERR_INVALID_DATA; goto err; } count = data & cfg->rleMask2; data = *src++; } break; } while (count--) { if (!dmGrowBufPutU8(dst, data)) { res = DMERR_MALLOC; goto err; } } } res = DMERR_OK; err: return res; } int dmDecodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg) { int res; if ((res = dmGrowBufAlloc(dst, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK) return res; return dmDecodeGenericRLE(dst, src, srcEnd, cfg); } static BOOL dmEncodeGenericRLESequence(DMGrowBuf *dst, const Uint8 data, Uint8 count, const DMCompParams *cfg) { BOOL copyOnly = FALSE; switch (cfg->type) { case DM_COMP_RLE_MARKER: if (count >= cfg->rleMinCount || data == cfg->rleMarker) { // A simple marker byte RLE variant: [Marker] [count] [data] if (!dmGrowBufPutU8(dst, cfg->rleMarker) || !dmGrowBufPutU8(dst, count) || !dmGrowBufPutU8(dst, data)) return FALSE; } else copyOnly = TRUE; break; case DM_COMP_RLE_MASK: if (count >= cfg->rleMinCount || (data & cfg->rleMarker) == cfg->rleMarker) { // Mask marker RLE: usually high bit(s) of byte mark RLE sequence // and the lower bits contain the count: [Mask + count] [data] if (!dmGrowBufPutU8(dst, cfg->rleMarker | count) || !dmGrowBufPutU8(dst, data)) return FALSE; } else copyOnly = TRUE; break; } if (copyOnly) { while (count--) { if (!dmGrowBufPutU8(dst, data)) return FALSE; } } return TRUE; } int dmEncodeGenericRLE(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg) { // Perform RLE encoding int count = 0, prev = -1; while (src < srcEnd) { Uint8 data = *src++; if (data != prev || count >= cfg->rleMaxCount) { if (!dmEncodeGenericRLESequence(dst, prev, count, cfg)) goto err; count = 1; } else count++; prev = data; } if (!dmEncodeGenericRLESequence(dst, prev, count, cfg)) goto err; return DMERR_OK; err: return dmError(DMERR_MALLOC, "Could reallocate memory for RLE encoding buffer.\n"); } int dmEncodeGenericRLEAlloc(DMGrowBuf *dst, const Uint8 *src, const Uint8 *srcEnd, const DMCompParams *cfg) { int res; if ((res = dmGrowBufAlloc(dst, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK) return res; return dmEncodeGenericRLE(dst, src, srcEnd, cfg); } // Perform probing of the given data buffer, trying to determine // if it contains a supported "C64" image format. Returns the // "probe score", see libgfx.h for list of values. If a match // is found, pointer to format description is set to *pfmt. int dmC64ProbeBMP(const Uint8 *buf, const size_t len, const DMC64ImageFormat **pfmt) { int scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1; for (int i = 0; i < ndmC64ImageFormats; i++) { const DMC64ImageFormat *fmt = &dmC64ImageFormats[i]; int score = DM_PROBE_SCORE_FALSE; if (fmt->probe == NULL && fmt->size > 0 && fmt->addr > 0) { // Generic probe just checks matching size and load address if (len == fmt->size && dmCompareAddr16(buf, 0, fmt->addr)) score = DM_PROBE_SCORE_GOOD; } else if (fmt->probe != NULL) score = fmt->probe(buf, len, fmt); if (score > scoreMax) { scoreMax = score; scoreIndex = i; } } if (scoreIndex >= 0) { *pfmt = &dmC64ImageFormats[scoreIndex]; return scoreMax; } else return DM_PROBE_SCORE_FALSE; } int dmC64SanityCheckEncDecOp(const int i, const DMC64EncDecOp *op, const DMC64Image *img) { switch (op->type) { case DT_COLOR_RAM: case DT_BITMAP: case DT_SCREEN_RAM: case DT_CHAR_DATA: if (op->bank < 0 || op->bank > img->nbanks) { return dmError(DMERR_INTERNAL, "Invalid bank %d / %d definition in generic encode/decode operator %d @ #%d.\n", op->bank, img->nbanks, op->type, i); } break; case DT_EXTRA_DATA: if (op->bank < 0 || op->bank >= C64_MAX_EXTRA_DATA) { return dmError(DMERR_INTERNAL, "Invalid bank %d definition in generic encode/decode operator %d @ #%d.\n", op->bank, op->type, i); } break; } if (op->type < 0 || op->type >= DT_LAST) { return dmError(DMERR_INTERNAL, "Invalid encode/decode operator type %d @ #%d.\n", op->type, i); } return DMERR_OK; } BOOL dmC64GetOpSize(const DMC64EncDecOp *op, const DMC64ImageFormat *fmt, size_t *size) { switch (op->type) { case DT_SCREEN_RAM: case DT_COLOR_RAM: *size = fmt->chHeight * fmt->chWidth; break; case DT_BITMAP: *size = fmt->chHeight * fmt->chWidth * 8; break; case DT_CHAR_DATA: *size = C64_MAX_CHARS * C64_CHR_SIZE; break; case DT_COLOR_REG: *size = 1; break; case DT_DEC_FUNCTION: case DT_ENC_FUNCTION: case DT_EXTRA_DATA: *size = op->size; break; default: *size = 0; } return TRUE; } int dmC64DecodeGenericBMP(DMC64Image *img, const Uint8 *buf, const size_t len, const DMC64ImageFormat *fmt) { int res = DMERR_OK; if (buf == NULL || img == NULL || fmt == NULL) return DMERR_NULLPTR; // Clear the image structure, set basics img->type = fmt->type; img->width = fmt->width; img->height = fmt->height; img->chWidth = fmt->chWidth; img->chHeight = fmt->chHeight; img->nbanks = dmC64ImageGetNumBanks(fmt); // Perform decoding for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; const Uint8 *src; size_t size; // Check for last operator if (op->type == DT_LAST) break; // Check operation validity if ((res = dmC64SanityCheckEncDecOp(i, op, img)) != DMERR_OK) return res; // Check size if (!dmC64GetOpSize(op, fmt, &size)) { return dmError(DMERR_INVALID_DATA, "Decode op SIZE out of bounds, op #%d type=%d, offs=%d ($%04x), " "bank=%d, size=%d ($%04x) vs. allocated %d ($%04x)\n", i, op->type, op->offs, op->offs, op->bank, size, size, op->size, op->size); } // Do we need to reallocate some more space? if (op->offs + size > len + 1) { return dmError(DMERR_INVALID_DATA, "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); } src = buf + op->offs; // Perform operation 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_CHAR_DATA: memcpy(img->charmem[op->bank], src, size); break; case DT_EXTRA_DATA: if (img->extraData[op->bank] != NULL) { return dmError(DMERR_INTERNAL, "Extra data block already allocated and used! " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, len, len); } if ((img->extraData[op->bank] = dmMalloc0(size)) == NULL) { return dmError(DMERR_MALLOC, "Could not allocate extradata block! " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, len, len); } img->extraDataSizes[op->bank] = size; memcpy(img->extraData[op->bank], src, size); break; case DT_COLOR_REG: switch (op->size) { case DC_D020: img->d020 = *src; break; case DC_BGCOL: case DC_D021: img->bgcolor = *src; break; case DC_D022: img->d022 = *src; break; case DC_D023: img->d023 = *src; break; case DC_D024: img->d024 = *src; break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_REG mode %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, len, len); } break; case DT_COLOR_SET: switch (op->size) { case DC_D020: img->d020 = op->offs; break; case DC_BGCOL: case DC_D021: img->bgcolor = op->offs; break; case DC_D022: img->d022 = op->offs; break; case DC_D023: img->d023 = op->offs; break; case DC_D024: img->d024 = op->offs; break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_SET mode %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, len, len); } break; case DT_CHAR_CONFIG: switch (op->offs) { case D64_CHCFG_SCREEN: break; case D64_CHCFG_LINEAR: { for (int bank = 0; bank < img->nbanks; bank++) for (int offs = 0; offs < fmt->chHeight * fmt->chWidth; offs++) img->screen[bank][offs] = offs & 0xff; } break; default: return dmError(DMERR_INTERNAL, "Unhandled DT_CHAR_CONFIG mode %d in ", "op #%d, bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->offs, i, op->bank, size, size, len, len); } break; case DT_DEC_FUNCTION: if (op->decFunction == NULL) { return dmError(DMERR_INTERNAL, "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); } if (!op->decFunction(img, op, buf, len, fmt)) { return dmError(DMERR_INTERNAL, "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); } break; } } return DMERR_OK; } int dmC64EncodeGenericBMP(const BOOL allocate, DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res = DMERR_OK; if (img == NULL || fmt == NULL) return DMERR_NULLPTR; // Allocate the output buffer if requested if (allocate && (res = dmGrowBufAlloc(buf, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK) { dmError(res, "Could not allocate %d bytes of memory for C64 image encoding buffer.\n", fmt->size); goto err; } dmGrowBufPush(buf); // Perform encoding for (int i = 0; i < D64_MAX_ENCDEC_OPS; i++) { const DMC64EncDecOp *op = &fmt->encdecOps[i]; size_t size, chksize; // Check for last operator if (op->type == DT_LAST) break; // Check operation validity if ((res = dmC64SanityCheckEncDecOp(i, op, img)) != DMERR_OK) goto err; // Check size if (!dmC64GetOpSize(op, fmt, &size)) { res = dmError(DMERR_INVALID_DATA, "Encode op SIZE out of bounds, op #%d type=%d, offs=%d ($%04x), " "bank=%d, size=%d ($%04x) vs. allocated %d ($%04x)\n", i, op->type, op->offs, op->offs, op->bank, size, size, op->size, op->size); goto err; } // Do we need to reallocate some more space? chksize = op->offs + size; if (!dmGrowBufCheckGrow(buf, chksize)) { res = dmError(DMERR_MALLOC, "Could not re-allocate %d bytes of memory for C64 image encoding buffer.\n", chksize); goto err; } if (chksize > buf->len) buf->len = chksize; // Perform operation Uint8 *dst = buf->data + op->offs; switch (op->type) { case DT_COLOR_RAM: memcpy(dst, img->color[op->bank], size); break; case DT_BITMAP: memcpy(dst, img->bitmap[op->bank], size); break; case DT_SCREEN_RAM: memcpy(dst, img->screen[op->bank], size); break; case DT_CHAR_DATA: memcpy(dst, img->charmem[op->bank], size); break; case DT_EXTRA_DATA: if (img->extraData[op->bank] == NULL) { res = dmError(DMERR_NULLPTR, "DT_EXTRA_DATA block is NULL in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len); goto err; } if (size > img->extraDataSizes[op->bank]) { res = dmError(DMERR_INTERNAL, "DT_EXTRA_DATA size mismatch %d <> %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, img->extraDataSizes[op->bank], i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len); goto err; } memcpy(dst, img->extraData[op->bank], size); break; case DT_COLOR_REG: switch (op->size) { case DC_D020: *dst = img->d020; break; case DC_BGCOL: case DC_D021: *dst = img->bgcolor; break; case DC_D022: *dst = img->d022; break; case DC_D023: *dst = img->d023; break; case DC_D024: *dst = img->d024; break; default: res = dmError(DMERR_INTERNAL, "Unhandled DT_COLOR_REG mode %d in " "op #%d, offs=%d ($%04x), bank=%d, size=%d ($%04x) @ %d ($%04x)\n", op->size, i, op->offs, op->offs, op->bank, size, size, buf->len, buf->len); goto err; } break; case DT_ENC_FUNCTION: if (op->encFunction == NULL) { res = dmError(DMERR_INTERNAL, "Encode 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, buf->len, buf->len); goto err; } if (!op->encFunction(op, buf, img, fmt)) { res = dmError(DMERR_INTERNAL, "Encode 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, buf->len, buf->len); goto err; } break; } } dmGrowBufPop(buf); res = DMERR_OK; err: return res; } // Convert a generic "C64" format bitmap in DMC64Image struct to // a indexed/paletted bitmap image. int dmC64ConvertGenericBMP2Image(DMImage *dst, const DMC64Image *src, const DMC64ImageFormat *fmt) { DMC64GetPixelFunc getPixel; // Sanity check arguments if (dst == NULL || src == NULL) return DMERR_NULLPTR; if (dst->width < src->width || dst->height < src->height) { return dmError(DMERR_INVALID_DATA, "Invalid src vs. dst width/height %d x %d <-> %d x %d\n", src->width, src->height, dst->width, dst->height); } dmMemset(dst->data, 0, dst->size); // Check pixel getter function if (fmt->getPixel != NULL) getPixel = fmt->getPixel; else getPixel = (fmt->type & D64_FMT_MC) ? fmtGetGenericMCPixel : fmtGetGenericSCPixel; // Resolution interlaced pics need to halve the source width int rwidth = src->width; if ((src->type & D64_FMT_ILACE) && src->laceType == D64_ILACE_RES) rwidth /= 2; // Perform conversion Uint8 *dp = dst->data; for (int yc = 0; yc < src->height; yc++) { Uint8 *d = dp; const int y = yc / 8, yb = yc & 7; const int scroffsy = y * src->chWidth; int xc; if (src->type & D64_FMT_CHAR) { // Charmode conversion if ((src->type & D64_FMT_MC) == D64_FMT_HIRES) // Hi-res charmap for (xc = 0; xc < rwidth; xc++) { const int x = xc / 8; const int scroffs = scroffsy + x; const int chr = src->screen[0][scroffs]; const int v = 7 - (xc & 7); if ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 1) *d++ = src->color[0][scroffs]; else *d++ = src->bgcolor; } else // Multicolor variants for (xc = 0; xc < rwidth; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int chr = src->screen[0][scroffs]; const int col = src->color[0][scroffs] & 15; if (col & 8) { const int v = 6 - ((xc * 2) & 6); switch ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 3) { case 0: *d++ = src->bgcolor; break; case 1: *d++ = src->d022; break; case 2: *d++ = src->d023; break; case 3: *d++ = col; } } else { const int v = 7 - (xc & 7); if ((src->charmem[0][chr * C64_CHR_SIZE + yb] >> v) & 1) *d++ = src->color[0][scroffs]; else *d++ = src->bgcolor; } } } else { // Perform generic BITMAP conversion const int bmoffsy = y * src->chWidth * 8 + yb; if ((src->type & D64_FMT_MC) == D64_FMT_HIRES) // Hi-res bitmap for (xc = 0; xc < rwidth; xc++) { const int x = xc / 8; const int scroffs = scroffsy + x; const int bmoffs = bmoffsy + (x * 8); const int vshift = 7 - (xc & 7); *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); } else // Multicolor bitmap and variants for (xc = 0; xc < rwidth; xc++) { const int x = xc / 4; const int scroffs = scroffsy + x; const int bmoffs = bmoffsy + (x * 8); const int vshift = 6 - ((xc * 2) & 6); if (src->type & D64_FMT_ILACE) { switch (src->laceType) { case D64_ILACE_RES: *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); *d++ = getPixel(src, bmoffs, scroffs, vshift, 1, yc); break; default: return DMERR_NOT_SUPPORTED; } } else { *d++ = getPixel(src, bmoffs, scroffs, vshift, 0, yc); } } } dp += dst->pitch; } return DMERR_OK; } int dmC64ConvertBMP2Image(DMImage **pdst, const DMC64Image *src, const DMC64ImageFormat *fmt) { int res; DMImage *dst; if (pdst == NULL || src == NULL) return DMERR_NULLPTR; // Allocate image structure if ((*pdst = dst = dmImageAlloc(src->width, src->height, DM_IFMT_PALETTE, -1)) == NULL) return DMERR_MALLOC; // Set partial palette information dst->ncolors = C64_NCOLORS; dst->constpal = TRUE; // Convert if (fmt->convertFrom != NULL) res = fmt->convertFrom(dst, src, fmt); else res = dmC64ConvertGenericBMP2Image(dst, src, fmt); return res; } int dmC64DecodeBMP(DMC64Image **img, const Uint8 *buf, const size_t len, const size_t probeOffs, const size_t loadOffs, const DMC64ImageFormat **fmt, const DMC64ImageFormat *forced) { if (img == NULL) return DMERR_NULLPTR; // Check for forced format if (forced != NULL) *fmt = forced; else { // Nope, perform a generic probe if (probeOffs >= len) return DMERR_OUT_OF_DATA; if (dmC64ProbeBMP(buf + probeOffs, len - probeOffs, fmt) == DM_PROBE_SCORE_FALSE) return DMERR_NOT_SUPPORTED; } if (loadOffs >= len) return DMERR_INVALID_ARGS; if (*fmt == NULL) return DMERR_INVALID_DATA; // Allocate memory if ((*img = dmC64ImageAlloc(*fmt)) == NULL) return DMERR_MALLOC; // Decode the bitmap to memory layout if ((*fmt)->decode != NULL) return (*fmt)->decode(*img, buf + loadOffs, len - loadOffs, *fmt); else return dmC64DecodeGenericBMP(*img, buf + loadOffs, len - loadOffs, *fmt); } // Convert a generic bitmap image to DMC64Image int dmC64ConvertGenericImage2BMP(DMC64Image *dst, const DMImage *src, const DMC64ImageFormat *fmt) { if (dst == NULL || src == NULL || fmt == NULL) return DMERR_NULLPTR; return DMERR_OK; } int dmC64ConvertImage2BMP(DMC64Image **pdst, const DMImage *src, const DMC64ImageFormat *fmt) { int res; DMC64Image *dst; if (pdst == NULL || src == NULL) return DMERR_NULLPTR; // Allocate the basic C64 bitmap image structure if ((*pdst = dst = dmC64ImageAlloc(fmt)) == NULL) return DMERR_MALLOC; // Convert if (fmt->convertTo != NULL) res = fmt->convertTo(dst, src, fmt); else res = dmC64ConvertGenericImage2BMP(dst, src, fmt); return res; } int dmC64EncodeBMP(DMGrowBuf *buf, const DMC64Image *img, const DMC64ImageFormat *fmt) { int res; if (img == NULL || fmt == NULL) return DMERR_NULLPTR; // Allocate a buffer if ((res = dmGrowBufAlloc(buf, BUF_SIZE_INITIAL, BUF_SIZE_GROW)) != DMERR_OK) goto err; // Add the loading address if (!dmGrowBufPutU8(buf, DM_GET_ADDR_LO(fmt->addr)) || !dmGrowBufPutU8(buf, DM_GET_ADDR_HI(fmt->addr))) goto err; // Attempt to encode the image to a buffer if (fmt->encode != NULL) res = fmt->encode(buf, img, fmt); else res = dmC64EncodeGenericBMP(FALSE, buf, img, fmt); if (res != DMERR_OK) goto err; return DMERR_OK; err: dmGrowBufFree(buf); return res; }