/* * Copyright (c) 2002-2007 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Loader for Gimp XCF image format. * * TODO Add support for Gimp 2.x XCF images. */ #include #include #include "view.h" #include "load_xcf.h" #include enum xcf_compression { XCF_COMPRESSION_NONE, XCF_COMPRESSION_RLE, XCF_COMPRESSION_ZLIB, /* Unimplemented */ XCF_COMPRESSION_FRACTAL /* Unimplemented */ }; enum xcf_base_type { XCF_IMAGE_RGB, XCF_IMAGE_GREYSCALE, XCF_IMAGE_INDEXED }; struct xcf_guide { Uint32 position; Uint8 orientation; SLIST_ENTRY(xcf_guide) guides; }; struct xcf_prop { Uint32 id; Uint32 length; union { enum xcf_compression compression; /* Tile compression */ struct { Uint32 size; /* Number of RGB triplets */ char *data; /* RGB triplet array */ } colormap; struct { Sint32 position; /* Guide coordinates */ Sint8 orientation; /* Guide orientation */ } guide; struct { char *name; Uint32 flags; Uint32 size; Uint8 *data; } parasite; Uint32 tattoo_state; /* Tattoo state */ Uint32 unit; /* Measurement unit */ struct { Uint32 drawable_offset; /* Floating selection offset */ } floating_sel; Uint32 opacity; /* Layer opacity */ Uint32 mode; /* Application mode */ struct { Sint32 x, y; /* Layer offset in image */ } offset; Uint8 color[3]; /* RGB triplet for color */ #if defined(HAVE_IEEE754) struct { float x, y; /* Resolution */ } resolution; #endif } data; }; struct xcf_header { Uint32 w, h; /* Geometry in pixels */ enum xcf_base_type base_type; /* Type of image */ Uint32 *layer_offstable; Uint32 *channel_offstable; Uint8 compression; struct { Uint32 size; Uint8 *data; } colormap; }; struct xcf_layer { char *name; /* Identifier */ Uint32 w, h; /* Geometry in pixels */ Uint32 layer_type; /* Image type information */ Uint32 offset_x, offset_y; /* Offset of layer in image */ Uint32 opacity; /* Layer opacity */ Uint32 mode; /* Application mode */ Uint32 hierarchy_offset; /* Offset of xcf_hierarchy */ Uint32 mask_offset; /* Offset of mask xcf_layer */ }; struct xcf_hierarchy { Uint32 w, h; Uint32 bpp; Uint32 *level_offsets; int nlevel_offsets; int maxlevel_offsets; }; struct xcf_level { Uint32 w, h; Uint32 *tile_offsets; int ntile_offsets; int maxtile_offsets; }; enum { PROP_END, PROP_COLORMAP, PROP_ACTIVE_LAYER, PROP_ACTIVE_CHANNEL, PROP_SELECTION, AG_PROP_FLOATING_SELECTION, PROP_OPACITY, PROP_MODE, PROP_VISIBLE, PROP_LINKED, PROP_PRESERVE_TRANSPARENCY, PROP_APPLY_MASK, PROP_EDIT_MASK, PROP_SHOW_MASK, PROP_SHOW_MASKED, PROP_OFFSETS, PROP_COLOR, PROP_COMPRESSION, PROP_GUIDES, PROP_RESOLUTION, PROP_TATTOO, PROP_PARASITE, PROP_UNIT, PROP_PATHS, PROP_USER_UNIT }; enum { LEVEL_OFFSETS_INIT = 2, LEVEL_OFFSETS_GROW = 4, TILE_OFFSETS_INIT = 16, TILE_OFFSETS_GROW = 8 }; #define XCF_WIDTH_MAX 65536 #define XCF_HEIGHT_MAX 65536 static void ReadProp(AG_DataSource *buf, struct xcf_prop *prop) { Uint32 i; Uint8 c; prop->id = AG_ReadUint32(buf); prop->length = AG_ReadUint32(buf); switch (prop->id) { case PROP_COLORMAP: /* Colormap for indexed images */ prop->data.colormap.size = AG_ReadUint32(buf); prop->data.colormap.data = Malloc(prop->data.colormap.size*3); if (AG_Read(buf, prop->data.colormap.data, prop->data.colormap.size, 3) != 0) { AG_FatalError(NULL); } break; case PROP_OFFSETS: /* Offset of layer in image */ prop->data.offset.x = AG_ReadSint32(buf); prop->data.offset.y = AG_ReadSint32(buf); break; case PROP_OPACITY: /* Layer opacity */ prop->data.opacity = AG_ReadUint32(buf); break; case PROP_MODE: /* Application mode */ prop->data.mode = AG_ReadUint32(buf); break; case PROP_COMPRESSION: /* Tile compression mode */ c = AG_ReadUint8(buf); prop->data.compression = (enum xcf_compression)c; break; case PROP_COLOR: /* Color of a channel */ if (AG_Read(buf, &prop->data.color, sizeof(Uint8), 3) != 0) AG_FatalError(NULL); break; case PROP_GUIDES: /* Guides */ for (i = 0; i < prop->length / 5; i++) { prop->data.guide.position = AG_ReadSint32(buf); prop->data.guide.orientation = AG_ReadSint8(buf); } break; #if defined(HAVE_IEEE754) case PROP_RESOLUTION: /* Image resolution */ prop->data.resolution.x = AG_ReadFloat(buf); prop->data.resolution.y = AG_ReadFloat(buf); break; #endif case PROP_TATTOO: /* Tattoo */ prop->data.tattoo_state = AG_ReadUint32(buf); break; case PROP_PARASITE: /* Parasite */ prop->data.parasite.name = AG_ReadNulString(buf); prop->data.parasite.flags = AG_ReadUint32(buf); prop->data.parasite.size = AG_ReadUint32(buf); prop->data.parasite.data = Malloc(prop->data.parasite.size); if (AG_Read(buf, prop->data.parasite.data, prop->data.parasite.size, 1) != 0) { AG_FatalError(NULL); } Free(prop->data.parasite.name); Free(prop->data.parasite.data); break; case PROP_UNIT: prop->data.unit = AG_ReadUint32(buf); break; case PROP_USER_UNIT: case PROP_PATHS: /* XXX ... */ break; default: AG_Seek(buf, prop->length, AG_SEEK_CUR); } } static Uint8 * ReadTileFlat(AG_DataSource *buf, Uint32 len, int bpp, int x, int y) { Uint8 *load; load = Malloc(len); if (AG_Read(buf, load, len, 1) != 0) { Free(load); return (NULL); } return (load); } static Uint8 * ReadTileRLE(AG_DataSource *buf, Uint32 len, int Bpp, int x, int y) { int i, size, count, j; Uint8 *tilep, *tile, *data; tilep = tile = Malloc(len); if (AG_Read(buf, tile, len, 1) != AG_IO_SUCCESS) { AG_SetError("XCF Tile: Read error"); return (NULL); } if ((data = malloc(x*y*Bpp)) == NULL) { AG_SetError("XCF Tile: Out of memory"); return (NULL); } for (i = 0; i < Bpp; i++) { Uint8 *d = &data[i]; size = x*y; count = 0; while (size > 0) { Uint8 val = *tile++; int length = val; if (length >= 128) { length = 255 - (length - 1); if (length == 128) { length = (*tile << 8) + tile[1]; tile += 2; } count += length; size -= length; while (length-- > 0) { *d = *tile++; d += Bpp; } } else { length += 1; if (length == 128) { length = (*tile << 8) + tile[1]; tile += 2; } count += length; size -= length; val = *tile++; for (j = 0; j < length; j++) { *d = val; d += Bpp; } } } } Free(tilep); return (data); } static Uint8 * ReadTile(struct xcf_header *head, AG_DataSource *buf, Uint32 len, int Bpp, int x, int y) { switch (head->compression) { case XCF_COMPRESSION_NONE: return ReadTileFlat(buf, len, Bpp, x, y); case XCF_COMPRESSION_RLE: return ReadTileRLE(buf, len, Bpp, x, y); } AG_SetError(_("Unknown XCF compression mode: %d"), head->compression); return (NULL); } #define XCF_ALPHA_TRANSPARENT 0x01 /* Contains a transparent pixel */ #define XCF_ALPHA_ALPHA 0x02 /* Contains an alpha pixel */ #define XCF_ALPHA_OPAQUE 0x04 /* Contains an opaque pixel */ static int ConvertLevel(AG_DataSource *buf, Uint32 xcfoffs, struct xcf_hierarchy *hier, struct xcf_header *head, struct xcf_level *level, AG_Surface *su, int *aflags) { int tx = 0, ty = 0; int ox, oy; int j; for (j = 0; j < level->ntile_offsets; j++) { Uint8 *tile, *p; int y; AG_Seek(buf, xcfoffs+level->tile_offsets[j], AG_SEEK_SET); ox = (tx+64 > (int)level->w) ? (level->w % 64) : 64; oy = (ty+64 > (int)level->h) ? (level->h % 64) : 64; if (level->tile_offsets[j+1] != 0) { tile = ReadTile(head, buf, level->tile_offsets[j+1] - level->tile_offsets[j], hier->bpp, ox, oy); } else { tile = ReadTile(head, buf, ox*oy*6, hier->bpp, ox, oy); } if (tile == NULL) { /* return (-1); */ continue; } p = tile; for (y = ty; y < ty+oy; y++) { Uint8 *dst = (Uint8 *)su->pixels + y*su->pitch + tx*su->format->BytesPerPixel; Uint32 color; Uint8 r, g, b, a; int x; for (x = tx; x < tx+ox; x++) { switch (hier->bpp) { case 4: #if AG_BYTEORDER == AG_BIG_ENDIAN r = (*(Uint32 *)p & 0xff000000)>>24; g = (*(Uint32 *)p & 0x00ff0000)>>16; b = (*(Uint32 *)p & 0x0000ff00)>>8; a = (*(Uint32 *)p & 0x000000ff); #else r = (*(Uint32 *)p & 0x000000ff); g = (*(Uint32 *)p & 0x0000ff00)>>8; b = (*(Uint32 *)p & 0x00ff0000)>>16; a = (*(Uint32 *)p & 0xff000000)>>24; #endif break; case 3: r = p[2]; g = p[1]; b = p[0]; a = 255; break; default: r = 0; g = 0; b = 0; a = 255; break; } color = AG_MapRGBA(su->format, r,g,b,a); switch (su->format->BytesPerPixel) { case 4: *(Uint32 *)dst = color; break; case 3: #if AG_BYTEORDER == AG_BIG_ENDIAN dst[0] = (color>>16) & 0xff; dst[1] = (color>>8) & 0xff; dst[2] = color & 0xff; #else dst[2] = (color>>16) & 0xff; dst[1] = (color>>8) & 0xff; dst[0] = color & 0xff; #endif break; case 2: *(Uint16 *)dst = color; break; case 1: *dst = color; break; } dst += su->format->BytesPerPixel; p += hier->bpp; switch (a) { case 0: *aflags |= XCF_ALPHA_TRANSPARENT; break; case 255: *aflags |= XCF_ALPHA_OPAQUE; break; default: *aflags |= XCF_ALPHA_ALPHA; break; } } } tx += 64; if (tx >= (int)level->w) { tx = 0; ty += 64; } if (ty >= (int)level->h) { Free(tile); break; } Free(tile); } return (0); } static AG_Surface * ConvertLayer(AG_DataSource *buf, Uint32 xcfoffs, struct xcf_header *head, struct xcf_layer *layer) { struct xcf_hierarchy *hier; AG_Surface *su; int aflags = 0; int i; su = AG_SurfaceRGBA(head->w, head->h, 32, 0, #if AG_BYTEORDER == AG_BIG_ENDIAN 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff #else 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 #endif ); if (su == NULL) return (NULL); /* Read the hierarchy. */ AG_Seek(buf, xcfoffs+layer->hierarchy_offset, AG_SEEK_SET); hier = Malloc(sizeof(struct xcf_hierarchy)); hier->w = AG_ReadUint32(buf); hier->h = AG_ReadUint32(buf); hier->bpp = AG_ReadUint32(buf); if (hier->bpp != 4 && hier->bpp != 3) { AG_SetError(_("Cannot handle %dBpp XCF data"), (int)hier->bpp); Free(hier); return (NULL); } /* Read the level offsets. */ hier->level_offsets = Malloc(LEVEL_OFFSETS_INIT*sizeof(Uint32)); hier->maxlevel_offsets = LEVEL_OFFSETS_INIT; hier->nlevel_offsets = 0; i = 0; for (;;) { if (hier->nlevel_offsets+1 >= hier->maxlevel_offsets) { hier->maxlevel_offsets += LEVEL_OFFSETS_GROW; hier->level_offsets = Realloc(hier->level_offsets, hier->maxlevel_offsets*sizeof(Uint32)); } if ((hier->level_offsets[hier->nlevel_offsets] = AG_ReadUint32(buf)) == 0) { break; } hier->nlevel_offsets++; } /* Read the levels. */ for (i = 0; i < hier->nlevel_offsets; i++) { struct xcf_level *level; AG_Seek(buf, xcfoffs+hier->level_offsets[i], AG_SEEK_SET); level = Malloc(sizeof(struct xcf_level)); level->w = AG_ReadUint32(buf); level->h = AG_ReadUint32(buf); level->tile_offsets = Malloc(TILE_OFFSETS_INIT*sizeof(Uint32)); level->maxtile_offsets = TILE_OFFSETS_INIT; level->ntile_offsets = 0; for (;;) { if (level->ntile_offsets+1 >= level->maxtile_offsets) { level->maxtile_offsets += TILE_OFFSETS_GROW; level->tile_offsets = Realloc( level->tile_offsets, level->maxtile_offsets*sizeof(Uint32)); } if ((level->tile_offsets[level->ntile_offsets] = AG_ReadUint32(buf)) == 0) { break; } level->ntile_offsets++; } if (ConvertLevel(buf, xcfoffs, hier, head, level, su, &aflags) == -1) { Free(hier->level_offsets); Free(hier); Free(level->tile_offsets); Free(level); AG_SurfaceFree(su); return (NULL); /* LEAK */ } Free(level->tile_offsets); Free(level); } Free(hier->level_offsets); Free(hier); /* Adjust the alpha/colorkey properties of the surface. */ { Uint8 oldalpha = su->format->alpha; AG_SetAlpha(su, 0, 0); AG_SetColorKey(su, 0, 0); if (aflags & (XCF_ALPHA_ALPHA|XCF_ALPHA_TRANSPARENT)) AG_SetAlpha(su, AG_SRCALPHA, oldalpha); } return (su); } /* * Load a set of surfaces from an XCF file. The add_layer_fn callback function * is passed the converted surface for each layer in the file. */ int AG_XCFLoad(AG_DataSource *buf, off_t xcf_offs, void (*add_layer_fn)(AG_Surface *, const char *, void *), void *arg) { char magic[14]; struct xcf_header *head; int i, offsets; Uint32 offset; struct xcf_prop prop; AG_Seek(buf, xcf_offs, AG_SEEK_SET); if (AG_Read(buf, magic, sizeof(magic), 1) != 0 || strncmp(magic, "gimp xcf ", 9) != 0) { AG_SetError(_("Not a Gimp XCF file")); return (-1); } /* Read the XCF header. */ head = Malloc(sizeof(struct xcf_header)); head->w = AG_ReadUint32(buf); head->h = AG_ReadUint32(buf); if (head->w > XCF_WIDTH_MAX || head->h > XCF_HEIGHT_MAX) { AG_SetError(_("Bad XCF geometry: %ux%u"), (unsigned int)head->w, (unsigned int)head->h); Free(head); return (-1); } head->base_type = AG_ReadUint32(buf); switch (head->base_type) { case XCF_IMAGE_RGB: case XCF_IMAGE_GREYSCALE: case XCF_IMAGE_INDEXED: break; default: AG_SetError(_("Unknown base image type: %u."), head->base_type); Free(head); return (-1); } head->compression = XCF_COMPRESSION_NONE; head->colormap.size = 0; head->colormap.data = NULL; /* Read the image properties. */ do { ReadProp(buf, &prop); switch (prop.id) { case PROP_COMPRESSION: head->compression = prop.data.compression; break; case PROP_COLORMAP: head->colormap.size = prop.data.colormap.size; head->colormap.data = Malloc(3*head->colormap.size); memcpy(head->colormap.data, prop.data.colormap.data, 3*head->colormap.size); break; case PROP_RESOLUTION: break; } } while (prop.id != PROP_END); /* Reader the layer offsets. */ head->layer_offstable = NULL; offsets = 0; for (offsets = 0; (offset = AG_ReadUint32(buf)) != 0; offsets++) { /* XXX inefficient */ head->layer_offstable = Realloc(head->layer_offstable, (offsets+1)*sizeof(Uint32)); head->layer_offstable[offsets] = offset; } /* Read the XCF layers. */ for (i = offsets; i > 0; i--) { struct xcf_layer *layer; struct xcf_prop prop; AG_Surface *su; AG_Seek(buf, xcf_offs+head->layer_offstable[i-1], AG_SEEK_SET); layer = Malloc(sizeof(struct xcf_layer)); layer->w = AG_ReadUint32(buf); layer->h = AG_ReadUint32(buf); layer->layer_type = AG_ReadUint32(buf); layer->name = AG_ReadNulString(buf); /* Read the layer properties. */ do { ReadProp(buf, &prop); switch (prop.id) { case PROP_OFFSETS: layer->offset_x = prop.data.offset.x; layer->offset_y = prop.data.offset.y; break; case PROP_OPACITY: layer->opacity = prop.data.opacity; break; case PROP_MODE: layer->mode = prop.data.mode; break; } } while (prop.id != PROP_END); layer->hierarchy_offset = AG_ReadUint32(buf); layer->mask_offset = AG_ReadUint32(buf); /* Convert this layer to a SDL surface. */ if ((su = ConvertLayer(buf, xcf_offs, head, layer)) == NULL) { Free(layer->name); Free(layer); Free(head); return (-1); } add_layer_fn(su, layer->name, arg); Free(layer->name); Free(layer); } Free(head->colormap.data); Free(head->layer_offstable); Free(head); return (0); }