Mercurial > hg > dmlib
view libgfx.c @ 448:f1aab48a76fe
Add transp mask support to IFF ILBM loader.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 04 Nov 2012 08:41:15 +0200 |
parents | 0e27860ddcfe |
children | 117f94b253af |
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 "libgfx.h" #include "dmfile.h" #include "dmbstr.h" #ifdef DM_USE_LIBPNG #include <png.h> #endif 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)); 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_PLANE : case DM_IFMT_RGB : return 3; case DM_IFMT_RGBA : return 4; default: return 0; } } int dmWriteImageData(DMImage *img, void *cbdata, BOOL (*writeRowCB)(void *, Uint8 *, size_t), const DMImageSpec *spec) { int x, y, yscale, xscale, res = 0, rowSize, rowWidth; Uint8 *row = NULL; // Allocate memory for row buffer rowWidth = img->width * spec->scale; rowSize = rowWidth * dmImageGetBytesPerPixel(spec->format); if ((row = dmMalloc(rowSize + 16)) == NULL) { res = DMERR_MALLOC; goto done; } // Generate the image for (y = 0; y < img->height; y++) { Uint8 *ptr1 = row, *ptr2 = ptr1 + rowWidth, *ptr3 = ptr2 + rowWidth; for (x = 0; x < img->width; x++) { Uint8 c = img->data[(y * img->pitch) + x], qr, qg, qb, qa; switch (spec->format) { case DM_IFMT_PALETTE: for (xscale = 0; xscale < spec->scale; xscale++) *ptr1++ = 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 < spec->scale; xscale++) { *ptr1++ = qr; *ptr1++ = qg; *ptr1++ = qb; *ptr1++ = 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 < spec->scale; xscale++) { *ptr1++ = qr; *ptr1++ = qg; *ptr1++ = 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 < spec->scale; xscale++) { *ptr1++ = qr; *ptr2++ = qg; *ptr3++ = qb; } break; } } for (yscale = 0; yscale < spec->scale; yscale++) { if (!writeRowCB(cbdata, row, rowSize)) { res = DMERR_FWRITE; 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); } fclose(fp); return 0; } int dmWriteIFFMasterRAWImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec) { int xc, yc, plane, res; DMBitStream bs; if ((res = dmInitBitStream(&bs, fp)) != DMERR_OK) return res; if (spec->interleave) { // Output bitplanes in interleaved format (each plane of line sequentially) for (yc = 0; yc < img->height; yc++) { for (plane = 0; plane < spec->nplanes; plane++) { Uint8 *sp = img->data + yc * img->pitch; for (xc = 0; xc < img->width; xc++) { if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1)) return DMERR_FWRITE; } } } } else { // Output each bitplane in sequence for (plane = 0; plane < spec->nplanes; plane++) { for (yc = 0; yc < img->height; yc++) { Uint8 *sp = img->data + yc * img->pitch; for (xc = 0; xc < img->width; xc++) { if (!dmPutBits(&bs, (sp[xc] & (1 << plane)) ? 1 : 0, 1)) return DMERR_FWRITE; } } } } return dmFlushBitStream(&bs); } int dmWriteIFFMasterRAWImage(const char *filename, DMImage *img, DMImageSpec *spec) { FILE *fp; int res; if ((fp = fopen(filename, "wb")) == NULL) { dmError("IFFMasterRAW: Could not open file '%s' for writing.\n", filename); return DMERR_FOPEN; } res = dmWriteIFFMasterRAWImageFILE(fp, img, spec); fclose(fp); return res; } static BOOL dmWritePPMRow(void *cbdata, Uint8 *row, size_t len) { return fwrite(row, sizeof(Uint8), len, (FILE *) cbdata) == len; } int dmWritePPMImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec) { // Write PPM header fprintf(fp, "P6\n%d %d\n255\n", img->width * spec->scale, img->height * spec->scale); // Write image data spec->format = DM_IFMT_RGB; return dmWriteImageData(img, (void *) fp, dmWritePPMRow, spec); } int dmWritePPMImage(const char *filename, DMImage *img, DMImageSpec *spec) { 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 DMERR_FOPEN; } res = dmWritePPMImageFILE(fp, img, spec); fclose(fp); return res; } #ifdef DM_USE_LIBPNG static BOOL dmWritePNGRow(void *cbdata, Uint8 *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, DMImageSpec *spec) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_colorp palette = NULL; int fmt, res = DMERR_OK; // 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"); res = DMERR_MALLOC; 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); res = DMERR_INIT_FAIL; goto error; } if (setjmp(png_jmpbuf(png_ptr))) { dmError("PNG: Error during image writing..\n"); res = DMERR_INIT_FAIL; goto error; } png_init_io(png_ptr, fp); // Write PNG header info switch (spec->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", spec->format); goto error; } png_set_IHDR(png_ptr, info_ptr, img->width * spec->scale, img->height * spec->scale, 8, /* bits per component */ fmt, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Palette if (spec->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, spec); // Write footer png_write_end(png_ptr, NULL); error: png_free(png_ptr, palette); palette = NULL; if (png_ptr && info_ptr) png_destroy_write_struct(&png_ptr, &info_ptr); return res; } int dmWritePNGImage(const char *filename, DMImage *img, DMImageSpec *spec) { int res; FILE *fp; if ((fp = fopen(filename, "wb")) == NULL) { dmError("PNG: could not open file '%s' for writing.\n", filename); return DMERR_FOPEN; } res = dmWritePNGImageFILE(fp, img, spec); fclose(fp); return res; } #endif typedef struct { Uint8 r,g,b; } DMPCXColor; typedef struct { Uint8 manufacturer, version, encoding, bpp; Uint16 xmin, ymin, xmax, ymax; Uint16 hres, vres; DMPCXColor colormap[16]; Uint8 reserved; Uint8 nplanes; Uint16 bpl; Uint16 palinfo; Uint8 filler[58]; } DMPCXHeader; typedef struct { DMPCXHeader *header; Uint8 *buf; size_t bufLen, bufOffs; int format; FILE *fp; } DMPCXData; static inline Uint8 dmPCXGetByte(Uint8 *row, const size_t len, const size_t soffs) { return (soffs < len) ? row[soffs] : 0; } static BOOL dmPCXFlush(DMPCXData *pcx) { BOOL ret = TRUE; if (pcx->bufOffs > 0) ret = fwrite(pcx->buf, sizeof(Uint8), pcx->bufOffs, pcx->fp) == pcx->bufOffs; pcx->bufOffs = 0; return ret; } static inline BOOL dmPCXPutByte(DMPCXData *pcx, const Uint8 val) { if (pcx->bufOffs < pcx->bufLen) { pcx->buf[pcx->bufOffs++] = val; return TRUE; } else return dmPCXFlush(pcx); } static BOOL dmWritePCXRow(void *cbdata, Uint8 *row, size_t len) { DMPCXData *pcx = (DMPCXData *) cbdata; int plane; size_t soffs = 0; // fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl); pcx->bufOffs = 0; for (plane = 0; plane < pcx->header->nplanes; plane++) { Uint8 data = dmPCXGetByte(row, len, soffs++), count = 1; // size_t blen = pcx->header->bpl * pcx->header->nplanes; size_t blen = pcx->header->bpl; while (soffs < blen) { 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 dmWritePCXImageFILE(FILE *fp, DMImage *img, DMImageSpec *spec) { DMPCXData pcx; DMPCXHeader hdr; int res; // Create output file pcx.buf = NULL; pcx.format = spec->paletted ? DM_IFMT_PALETTE : DM_IFMT_RGB_PLANE; pcx.header = &hdr; pcx.fp = fp; // Create PCX header memset(&hdr, 0, sizeof(hdr)); if (spec->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 * spec->scale; hdr.vres = img->height * spec->scale; hdr.xmin = hdr.ymin = 0; hdr.xmax = hdr.hres - 1; hdr.ymax = hdr.vres - 1; hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); hdr.palinfo = 1; res = (img->width * spec->scale); hdr.bpl = res / 2; if (res % 2) hdr.bpl++; hdr.bpl *= 2; dmMsg(2, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", spec->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 = DMERR_MALLOC; 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 = DMERR_FWRITE; 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 = DMERR_FWRITE; goto error; } if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap))) { dmError("PCX: Could not write colormap.\n"); res = DMERR_FWRITE; 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 *) &hdr.filler, sizeof(hdr.filler))) { dmError("PCX: Could not write header remainder.\n"); res = DMERR_FWRITE; goto error; } // Write image data res = dmWriteImageData(img, (void *) &pcx, dmWritePCXRow, spec); // Write VGA palette if (spec->paletted) { int i; dm_fwrite_byte(pcx.fp, 0x0C); dmMsg(2, "PCX: Writing palette of %d active entries.\n", img->ncolors); 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: dmFree(pcx.buf); return res; } int dmWritePCXImage(const char *filename, DMImage *img, DMImageSpec *spec) { FILE *fp; int res; if ((fp = fopen(filename, "wb")) == NULL) { dmError("PCX: Could not open file '%s' for writing.\n", filename); return DMERR_FOPEN; } res = dmWritePCXImageFILE(fp, img, spec); fclose(fp); return res; } static BOOL dmPCXDecodeRLERow(FILE *fp, Uint8 *buf, const size_t bufLen) { size_t offs = 0; do { int count; Uint8 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 *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 = DMERR_FREAD; goto error; } if (hdr.manufacturer != 10 || hdr.version != 5 || hdr.encoding != 1 || hdr.bpp != 8) { dmError("PCX: Not a PCX file, or unsupported variant.\n"); res = DMERR_FREAD; 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 = DMERR_FREAD; goto error; } if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap))) { dmError("PCX: Could not read colormap.\n"); res = DMERR_FREAD; 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 *) &hdr.filler, sizeof(hdr.filler))) { dmError("PCX: Could not read header remainder.\n"); res = DMERR_FREAD; goto error; } if (hdr.nplanes != 3 && hdr.nplanes != 1) { dmError("PCX: Unsupported number of bitplanes %d.\n", hdr.nplanes); res = DMERR_FREAD; 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 = DMERR_MALLOC; 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 = DMERR_MALLOC; 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 = DMERR_INVALID_DATA; goto error; } // Decode bitplanes switch (hdr.nplanes) { case 1: memcpy(dp, pcx.buf, img->width); break; case 3: { Uint8 *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 tmpb; BOOL read; if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) { read = FALSE; img->ncolors = 16; } else { read = TRUE; img->ncolors = 256; } if ((img->pal = dmCalloc(img->ncolors, sizeof(DMColor))) == NULL) { dmError("PCX: Could not allocate palette data!\n"); res = DMERR_MALLOC; goto error; } if (read) { for (i = 0; i < img->ncolors; i++) { Uint8 tmpR, tmpG, tmpB; if (!dm_fread_byte(fp, &tmpR) || !dm_fread_byte(fp, &tmpG) || !dm_fread_byte(fp, &tmpB)) goto error; img->pal[i].r = tmpR; img->pal[i].g = tmpG; img->pal[i].b = tmpB; } } else { for (i = 0; i < img->ncolors; i++) { if (i < 16) { img->pal[i].r = hdr.colormap[i].r; img->pal[i].g = hdr.colormap[i].g; img->pal[i].b = hdr.colormap[i].b; } } } } 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; } #define IFF_ID_FORM 0x464F524D // "FORM" #define IFF_ID_ILBM 0x494C424D // "ILBM" #define IFF_ID_BMHD 0x424D4844 // "BMHD" #define IFF_ID_CMAP 0x434D4150 // "CMAP" #define IFF_ID_BODY 0x424F4459 // "BODY" #define IFF_MASK_NONE 0 #define IFF_MASK_HAS_MASK 1 #define IFF_MASK_TRANSP 2 #define IFF_MASK_LASSO 3 #define IFF_COMP_NONE 0 #define IFF_COMP_BYTERUN1 1 typedef struct { Uint32 id; Uint32 size; int count; char str[6]; } DMIFFChunk; typedef struct { Uint16 w, h; Sint16 x, y; Uint8 nplanes; Uint8 masking; Uint8 compression; Uint8 pad1; Uint16 transp; Uint8 xasp, yasp; Sint16 pagew, pageh; } DMIFFBMHD; typedef struct { DMIFFChunk chBMHD, chCMAP, chBODY; DMIFFBMHD bmhd; int ncolors; DMColor *pal; BOOL paletted; } DMIFF; static BOOL dmReadIFFChunk(FILE *fp, DMIFFChunk *chunk) { if (!dm_fread_be32(fp, &chunk->id) || !dm_fread_be32(fp, &chunk->size)) { dmError("ILBM: Could not read IFF chunk header.\n"); return FALSE; } else return TRUE; } static char * dmGetIFFChunkID(DMIFFChunk *chunk) { chunk->str[0] = (chunk->id >> 24) & 0xff; chunk->str[1] = (chunk->id >> 16) & 0xff; chunk->str[2] = (chunk->id >> 8) & 0xff; chunk->str[3] = (chunk->id) & 0xff; chunk->str[4] = 0; return chunk->str; } static BOOL dmSkipIFFChunkRest(FILE *fp, const DMIFFChunk *chunk, const Uint32 used) { if (chunk->size > used) { dmMsg(3, "ILBM: Skipping %d bytes (%d of %d consumed)\n", chunk->size - used, used, chunk->size); return fseeko(fp, chunk->size - used, SEEK_CUR) == 0; } else return TRUE; } static BOOL dmCheckIFFChunk(DMIFFChunk *dest, DMIFFChunk *chunk, const BOOL multi, const Uint32 minSize) { if (dest->count > 0 && !multi) { dmError("ILBM: Multiple instances of chunk %s found.\n", dmGetIFFChunkID(chunk)); return FALSE; } dest->count++; if (chunk->size < minSize) return FALSE; return TRUE; } static BOOL dmIFFDecodeByteRun1Row(FILE *fp, Uint8 *buf, const size_t bufLen) { size_t offs = 0; do { Sint8 dcount; Uint8 data; if (!dm_fread_byte(fp, (Uint8 *) &dcount)) return FALSE; if (dcount == -128) { if (!dm_fread_byte(fp, &data)) return FALSE; } else if (dcount < 0) { int count = (-dcount) + 1; if (!dm_fread_byte(fp, &data)) return FALSE; while (count-- && offs < bufLen) buf[offs++] = data; } else { int count = dcount + 1; while (count-- && offs < bufLen) { if (!dm_fread_byte(fp, &data)) return FALSE; buf[offs++] = data; } } } while (offs < bufLen); return TRUE; } static BOOL dmIFFReadOneRow(FILE *fp, DMIFF *iff, Uint8 *buf, const size_t bufLen) { if (iff->bmhd.compression == IFF_COMP_BYTERUN1) return dmIFFDecodeByteRun1Row(fp, buf, bufLen); else return dm_fread_str(fp, buf, bufLen); } void dmDecodeBitPlane(Uint8 *dp, Uint8 *src, const int width, const int nplane) { int xc; for (xc = 0; xc < width; xc++) { const Uint8 data = (src[xc / 8] >> (7 - (xc & 7))) & 1; dp[xc] |= (data << nplane); } } int dmDecodeILBMBody(FILE *fp, DMIFF *iff, DMImage **pimg, Uint32 *read) { DMImage *img; Uint8 *buf; size_t bufLen; int yc, res = DMERR_OK; *read = 0; // Allocate image if ((*pimg = img = dmImageAlloc(iff->bmhd.w, iff->bmhd.h)) == NULL) return DMERR_MALLOC; // Allocate planar decoding buffer bufLen = ((img->width + 15) / 16) * 2; if ((buf = dmMalloc(bufLen)) == NULL) return DMERR_MALLOC; dmMsg(2, "ILBM: plane row size %d bytes.\n", bufLen); // Decode the chunk for (yc = 0; yc < img->height; yc++) { int plane; const int nplanes = iff->bmhd.nplanes; Uint8 *dp = img->data + (yc * img->pitch); memset(dp, 0, img->pitch); for (plane = 0; plane < nplanes; plane++) { // Decompress or read data if (!dmIFFReadOneRow(fp, iff, buf, bufLen)) { dmError("ILBM: Error in reading image plane #%d.\n", plane); res = DMERR_FREAD; goto error; } // Decode bitplane dmDecodeBitPlane(dp, buf, img->width, plane); *read += bufLen; } // Read mask data if (iff->bmhd.masking == IFF_MASK_HAS_MASK) { int xc; // Decompress or read data if (!dmIFFReadOneRow(fp, iff, buf, bufLen)) { dmError("ILBM: Error in reading mask plane.\n"); res = DMERR_FREAD; goto error; } // Decode mask for (xc = 0; xc < img->width; xc++) { const Uint8 data = (buf[xc / 8] >> (7 - (xc & 7))) & 1; // Black out any pixels with mask bit 0 if (!data) dp[xc] = 0; } *read += bufLen; } } error: dmFree(buf); return res; } int dmReadILBMImageFILE(FILE *fp, DMImage **pimg) { Uint32 idILBM; DMIFFChunk chunk; DMIFF iff; Uint32 read; BOOL parsed = FALSE; int i, res = DMERR_OK; memset(&iff, 0, sizeof(iff)); // Read IFF FORM header if (!dmReadIFFChunk(fp, &chunk) || chunk.id != IFF_ID_FORM || chunk.size < 32) { dmError("ILBM: Not a IFF file.\n"); return DMERR_FREAD; } // Check IFF ILBM signature if (!dm_fread_be32(fp, &idILBM) || idILBM != IFF_ID_ILBM) { dmError("ILBM: Not a ILBM file.\n"); return DMERR_INVALID_DATA; } while (!parsed && !feof(fp)) { if (!dmReadIFFChunk(fp, &chunk)) { dmError("ILBM: Error reading IFF ILBM data.\n"); return DMERR_FREAD; } switch (chunk.id) { case IFF_ID_BMHD: // Check for multiple occurences of BMHD if (!dmCheckIFFChunk(&iff.chBMHD, &chunk, FALSE, sizeof(iff.bmhd))) return DMERR_FREAD; // Read BMHD data if (!dm_fread_be16(fp, &iff.bmhd.w) || !dm_fread_be16(fp, &iff.bmhd.h) || !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.x) || !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.y) || !dm_fread_byte(fp, &iff.bmhd.nplanes) || !dm_fread_byte(fp, &iff.bmhd.masking) || !dm_fread_byte(fp, &iff.bmhd.compression) || !dm_fread_byte(fp, &iff.bmhd.pad1) || !dm_fread_be16(fp, &iff.bmhd.transp) || !dm_fread_byte(fp, &iff.bmhd.xasp) || !dm_fread_byte(fp, &iff.bmhd.yasp) || !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.pagew) || !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.pageh)) { dmError("ILBM: Error reading BMHD chunk.\n"); return DMERR_FREAD; } dmMsg(2, "ILBM: BMHD %d x %d @ %d, %d : nplanes=%d, comp=%d, mask=%d\n", iff.bmhd.w, iff.bmhd.h, iff.bmhd.x, iff.bmhd.y, iff.bmhd.nplanes, iff.bmhd.compression, iff.bmhd.masking); // Sanity check if (iff.bmhd.nplanes < 1 || iff.bmhd.nplanes > 8 || (iff.bmhd.compression != IFF_COMP_NONE && iff.bmhd.compression != IFF_COMP_BYTERUN1) || (iff.bmhd.masking != IFF_MASK_NONE && iff.bmhd.masking != IFF_MASK_HAS_MASK && iff.bmhd.masking != IFF_MASK_TRANSP)) { dmError("ILBM: Unsupported features, refusing to load.\n"); return DMERR_INVALID_DATA; } if (!dmSkipIFFChunkRest(fp, &chunk, sizeof(iff.bmhd))) return DMERR_FREAD; break; case IFF_ID_CMAP: // Check for multiple occurences of CMAP if (!dmCheckIFFChunk(&iff.chCMAP, &chunk, FALSE, 3)) return DMERR_FREAD; // Check for sanity if (chunk.size % 3 != 0) dmError("ILBM: CMAP chunk size not divisible by 3, possibly broken file.\n"); iff.ncolors = chunk.size / 3; dmMsg(2, "ILBM: CMAP %d entries (%d bytes)\n", iff.ncolors, chunk.size, 1 << iff.bmhd.nplanes); if (iff.bmhd.nplanes > 0 && iff.ncolors != 1 << iff.bmhd.nplanes) dmMsg(2, "ILBM: Expected %d entries in CMAP.\n", 1 << iff.bmhd.nplanes); if (iff.ncolors == 0) break; // Allocate palette if ((iff.pal = dmMalloc(sizeof(DMColor) * iff.ncolors)) == NULL) { dmError("ILBM: Could not allocate memory for palette.\n"); return DMERR_MALLOC; } // Read palette for (i = 0; i < iff.ncolors; i++) { Uint8 colR, colG, colB; if (!dm_fread_byte(fp, &colR) || !dm_fread_byte(fp, &colG) || !dm_fread_byte(fp, &colB)) { dmError("ILBM: Error reading CMAP entry #%d, broken file.\n", i); return DMERR_FREAD; } iff.pal[i].r = colR; iff.pal[i].g = colG; iff.pal[i].b = colB; } if (iff.chBMHD.count && iff.chBODY.count) parsed = TRUE; break; case IFF_ID_BODY: // Check for multiple occurences of CMAP if (!dmCheckIFFChunk(&iff.chBODY, &chunk, FALSE, 1)) return DMERR_FREAD; // Check for sanity if (!iff.chBMHD.count) { dmError("ILBM: BODY chunk before BMHD?\n"); return DMERR_INVALID_DATA; } dmMsg(2, "ILBM: BODY chunk size %d bytes\n", chunk.size); // Decode the body if ((res = dmDecodeILBMBody(fp, &iff, pimg, &read)) != DMERR_OK) return res; if (!dmSkipIFFChunkRest(fp, &chunk, read)) return DMERR_FREAD; if (iff.chCMAP.count) parsed = TRUE; break; default: { dmMsg(3, "Unknown chunk ID '%s', size %d\n", dmGetIFFChunkID(&chunk), chunk.size); if (fseeko(fp, chunk.size, SEEK_CUR) != 0) { dmError("ILBM: Error skipping in file."); return DMERR_FREAD; } } break; } if (chunk.size & 1) fgetc(fp); } // Set colormap after finishing if (iff.pal != NULL && iff.ncolors > 0 && *pimg != NULL) { (*pimg)->ncolors = iff.ncolors; (*pimg)->pal = iff.pal; } return res; } int dmReadILBMImage(const char *filename, DMImage **pimg) { FILE *fp; int res; if ((fp = fopen(filename, "rb")) == NULL) { dmError("ILBM: Could not open file '%s' for reading.\n", filename); return DMERR_FOPEN; } res = dmReadILBMImageFILE(fp, pimg); fclose(fp); return res; } static int fmtProbePNG(const Uint8 *buf, const size_t len) { if (len > 64 && buf[0] == 0x89 && buf[1] == 'P' && buf[2] == 'N' && buf[3] == 'G' && buf[4] == 0x0d && buf[5] == 0x0a) { if (buf[12] == 'I' && buf[13] == 'H' && buf[14] == 'D' && buf[15] == 'R') return DM_PROBE_SCORE_MAX; else return DM_PROBE_SCORE_GOOD; } return DM_PROBE_SCORE_FALSE; } static int fmtProbePCX(const Uint8 *buf, const size_t len) { if (len > 128 + 32 && buf[0] == 10 && (buf[1] == 5 || buf[1] == 2 || buf[1] == 3) && buf[2] == 1 && (buf[3] == 8 || buf[3] == 4 || buf[3] == 3 || buf[3] == 1) && buf[65] >= 1 && buf[65] <= 4) return DM_PROBE_SCORE_GOOD; return DM_PROBE_SCORE_FALSE; } static int fmtProbeILBM(const Uint8 *buf, const size_t len) { if (len > 32 && buf[ 0] == 'F' && buf[ 1] == 'O' && buf[ 2] == 'R' && buf[ 3] == 'M' && buf[ 8] == 'I' && buf[ 9] == 'L' && buf[10] == 'B' && buf[11] == 'M') { if (buf[12] == 'B' && buf[13] == 'M' && buf[14] == 'H' && buf[15] == 'D') return DM_PROBE_SCORE_MAX; else return DM_PROBE_SCORE_GOOD; } return DM_PROBE_SCORE_FALSE; } DMImageFormat dmImageFormatList[IMGFMT_LAST] = { { "PNG", "Portable Network Graphics", fmtProbePNG, NULL, NULL, #ifdef DM_USE_LIBPNG dmWritePNGImage, dmWritePNGImageFILE, #else NULL, NULL, #endif }, { "PPM", "Portable PixMap", NULL, NULL, NULL, dmWritePPMImage, dmWritePPMImageFILE, }, { "PCX", "Z-Soft Paintbrush", fmtProbePCX, dmReadPCXImage, dmReadPCXImageFILE, dmWritePCXImage, dmWritePCXImageFILE, }, { "ILBM", "IFF ILBM", fmtProbeILBM, dmReadILBMImage, dmReadILBMImageFILE, NULL, NULL, }, { "ARAW", "IFFMaster Amiga RAW", NULL, NULL, NULL, dmWriteIFFMasterRAWImage, dmWriteIFFMasterRAWImageFILE, } }; int dmImageProbeGeneric(const Uint8 *buf, const size_t len, DMImageFormat **pfmt, int *index) { int i, scoreMax = DM_PROBE_SCORE_FALSE, scoreIndex = -1; for (i = 0; i < IMGFMT_LAST; i++) { DMImageFormat *fmt = &dmImageFormatList[i]; if (fmt->probe != NULL) { int score = fmt->probe(buf, len); if (score > scoreMax) { scoreMax = score; scoreIndex = i; } } } if (scoreIndex >= 0) { *pfmt = &dmImageFormatList[scoreIndex]; *index = scoreIndex; return scoreMax; } else return DM_PROBE_SCORE_FALSE; }