2 SDL_image: An example image loading library for use with SDL
3 Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
22 /* This is a XCF image file loading framework */
29 #include "SDL_endian.h"
30 #include "SDL_image.h"
35 static char prop_names [][30] = {
46 "preserve_transparency",
69 PROP_ACTIVE_LAYER = 2,
70 PROP_ACTIVE_CHANNEL = 3,
72 PROP_FLOATING_SELECTION = 5,
77 PROP_PRESERVE_TRANSPARENCY = 10,
81 PROP_SHOW_MASKED = 14,
84 PROP_COMPRESSION = 17,
116 Uint32 drawable_offset;
117 } floating_selection; // 5
122 int preserve_transparency;
129 unsigned char color [3];
149 xcf_prop * properties;
151 Uint32 * layer_file_offsets;
152 Uint32 * channel_file_offsets;
154 xcf_compr_type compr;
156 unsigned char * cm_map;
164 xcf_prop * properties;
166 Uint32 hierarchy_file_offset;
167 Uint32 layer_mask_offset;
178 xcf_prop * properties;
180 Uint32 hierarchy_file_offset;
193 Uint32 * level_file_offsets;
200 Uint32 * tile_file_offsets;
203 typedef unsigned char * xcf_tile;
205 typedef unsigned char * (* load_tile_type) (SDL_RWops *, Uint32, int, int, int);
208 /* See if an image is contained in a data source */
209 int IMG_isXCF(SDL_RWops *src)
217 start = SDL_RWtell(src);
219 if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
220 if (SDL_strncmp(magic, "gimp xcf ", 9) == 0) {
224 SDL_RWseek(src, start, RW_SEEK_SET);
228 static char * read_string (SDL_RWops * src) {
232 tmp = SDL_ReadBE32 (src);
234 data = (char *) SDL_malloc (sizeof (char) * tmp);
235 SDL_RWread (src, data, tmp, 1);
245 static Uint32 Swap32 (Uint32 v) {
247 ((v & 0x000000FF) << 16)
249 | ((v & 0x00FF0000) >> 16)
250 | ((v & 0xFF000000));
253 static void xcf_read_property (SDL_RWops * src, xcf_prop * prop) {
254 prop->id = SDL_ReadBE32 (src);
255 prop->length = SDL_ReadBE32 (src);
258 printf ("%.8X: %s: %d\n", SDL_RWtell (src), prop->id < 25 ? prop_names [prop->id] : "unknown", prop->length);
263 prop->data.colormap.num = SDL_ReadBE32 (src);
264 prop->data.colormap.cmap = (char *) SDL_malloc (sizeof (char) * prop->data.colormap.num * 3);
265 SDL_RWread (src, prop->data.colormap.cmap, prop->data.colormap.num*3, 1);
269 prop->data.offset.x = SDL_ReadBE32 (src);
270 prop->data.offset.y = SDL_ReadBE32 (src);
273 prop->data.opacity = SDL_ReadBE32 (src);
275 case PROP_COMPRESSION:
277 SDL_RWread (src, &prop->data, prop->length, 1);
280 prop->data.visible = SDL_ReadBE32 (src);
283 // SDL_RWread (src, &prop->data, prop->length, 1);
284 SDL_RWseek (src, prop->length, RW_SEEK_CUR);
288 static void free_xcf_header (xcf_header * h) {
290 SDL_free (h->cm_map);
291 if (h->layer_file_offsets)
292 SDL_free (h->layer_file_offsets);
296 static xcf_header * read_xcf_header (SDL_RWops * src) {
300 h = (xcf_header *) SDL_malloc (sizeof (xcf_header));
301 SDL_RWread (src, h->sign, 14, 1);
302 h->width = SDL_ReadBE32 (src);
303 h->height = SDL_ReadBE32 (src);
304 h->image_type = SDL_ReadBE32 (src);
306 h->properties = NULL;
307 h->layer_file_offsets = NULL;
308 h->compr = COMPR_NONE;
312 // Just read, don't save
314 xcf_read_property (src, &prop);
315 if (prop.id == PROP_COMPRESSION)
316 h->compr = (xcf_compr_type)prop.data.compression;
317 else if (prop.id == PROP_COLORMAP) {
318 // unused var: int i;
320 h->cm_num = prop.data.colormap.num;
321 h->cm_map = (unsigned char *) SDL_malloc (sizeof (unsigned char) * 3 * h->cm_num);
322 SDL_memcpy (h->cm_map, prop.data.colormap.cmap, 3*sizeof (char)*h->cm_num);
323 SDL_free (prop.data.colormap.cmap);
325 } while (prop.id != PROP_END);
330 static void free_xcf_layer (xcf_layer * l) {
335 static xcf_layer * read_xcf_layer (SDL_RWops * src) {
339 l = (xcf_layer *) SDL_malloc (sizeof (xcf_layer));
340 l->width = SDL_ReadBE32 (src);
341 l->height = SDL_ReadBE32 (src);
342 l->layer_type = SDL_ReadBE32 (src);
344 l->name = read_string (src);
347 xcf_read_property (src, &prop);
348 if (prop.id == PROP_OFFSETS) {
349 l->offset_x = prop.data.offset.x;
350 l->offset_y = prop.data.offset.y;
351 } else if (prop.id == PROP_VISIBLE) {
352 l->visible = prop.data.visible ? 1 : 0;
354 } while (prop.id != PROP_END);
356 l->hierarchy_file_offset = SDL_ReadBE32 (src);
357 l->layer_mask_offset = SDL_ReadBE32 (src);
362 static void free_xcf_channel (xcf_channel * c) {
367 static xcf_channel * read_xcf_channel (SDL_RWops * src) {
371 l = (xcf_channel *) SDL_malloc (sizeof (xcf_channel));
372 l->width = SDL_ReadBE32 (src);
373 l->height = SDL_ReadBE32 (src);
375 l->name = read_string (src);
379 xcf_read_property (src, &prop);
382 l->opacity = prop.data.opacity << 24;
385 l->color = ((Uint32) prop.data.color[0] << 16)
386 | ((Uint32) prop.data.color[1] << 8)
387 | ((Uint32) prop.data.color[2]);
393 l->visible = prop.data.visible ? 1 : 0;
398 } while (prop.id != PROP_END);
400 l->hierarchy_file_offset = SDL_ReadBE32 (src);
405 static void free_xcf_hierarchy (xcf_hierarchy * h) {
406 SDL_free (h->level_file_offsets);
410 static xcf_hierarchy * read_xcf_hierarchy (SDL_RWops * src) {
414 h = (xcf_hierarchy *) SDL_malloc (sizeof (xcf_hierarchy));
415 h->width = SDL_ReadBE32 (src);
416 h->height = SDL_ReadBE32 (src);
417 h->bpp = SDL_ReadBE32 (src);
419 h->level_file_offsets = NULL;
422 h->level_file_offsets = (Uint32 *) SDL_realloc (h->level_file_offsets, sizeof (Uint32) * (i+1));
423 h->level_file_offsets [i] = SDL_ReadBE32 (src);
424 } while (h->level_file_offsets [i++]);
429 static void free_xcf_level (xcf_level * l) {
430 SDL_free (l->tile_file_offsets);
434 static xcf_level * read_xcf_level (SDL_RWops * src) {
438 l = (xcf_level *) SDL_malloc (sizeof (xcf_level));
439 l->width = SDL_ReadBE32 (src);
440 l->height = SDL_ReadBE32 (src);
442 l->tile_file_offsets = NULL;
445 l->tile_file_offsets = (Uint32 *) SDL_realloc (l->tile_file_offsets, sizeof (Uint32) * (i+1));
446 l->tile_file_offsets [i] = SDL_ReadBE32 (src);
447 } while (l->tile_file_offsets [i++]);
452 static void free_xcf_tile (unsigned char * t) {
456 static unsigned char * load_xcf_tile_none (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
457 unsigned char * load;
459 load = (unsigned char *) SDL_malloc (len); // expect this is okay
460 SDL_RWread (src, load, len, 1);
465 static unsigned char * load_xcf_tile_rle (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
466 unsigned char * load, * t, * data, * d;
468 int i, size, count, j, length;
471 t = load = (unsigned char *) SDL_malloc (len);
472 reallen = SDL_RWread (src, t, 1, len);
474 data = (unsigned char *) SDL_malloc (x*y*bpp);
475 for (i = 0; i < bpp; i++) {
485 length = 255 - (length - 1);
487 length = (*t << 8) + t[1];
494 while (length-- > 0) {
502 length = (*t << 8) + t[1];
511 for (j = 0; j < length; j++) {
523 static Uint32 rgb2grey (Uint32 a) {
525 l = (Uint8)(0.2990 * ((a & 0x00FF0000) >> 16)
526 + 0.5870 * ((a & 0x0000FF00) >> 8)
527 + 0.1140 * ((a & 0x000000FF)));
529 return (l << 16) | (l << 8) | l;
532 static void create_channel_surface (SDL_Surface * surf, xcf_image_type itype, Uint32 color, Uint32 opacity) {
540 case IMAGE_GREYSCALE:
541 c = opacity | rgb2grey (color);
544 SDL_FillRect (surf, NULL, c);
547 static int do_layer_surface (SDL_Surface * surface, SDL_RWops * src, xcf_header * head, xcf_layer * layer, load_tile_type load_tile) {
548 xcf_hierarchy * hierarchy;
550 unsigned char * tile;
555 Uint32 x, y, tx, ty, ox, oy;
558 SDL_RWseek (src, layer->hierarchy_file_offset, RW_SEEK_SET);
559 hierarchy = read_xcf_hierarchy (src);
562 for (i = 0; hierarchy->level_file_offsets [i]; i++) {
563 SDL_RWseek (src, hierarchy->level_file_offsets [i], RW_SEEK_SET);
564 level = read_xcf_level (src);
567 for (j = 0; level->tile_file_offsets [j]; j++) {
568 SDL_RWseek (src, level->tile_file_offsets [j], RW_SEEK_SET);
569 ox = tx+64 > level->width ? level->width % 64 : 64;
570 oy = ty+64 > level->height ? level->height % 64 : 64;
572 if (level->tile_file_offsets [j+1]) {
575 level->tile_file_offsets [j+1] - level->tile_file_offsets [j],
590 for (y=ty; y < ty+oy; y++) {
591 row = (Uint32 *)((Uint8 *)surface->pixels + y*surface->pitch + tx*4);
592 switch (hierarchy->bpp) {
594 for (x=tx; x < tx+ox; x++)
595 *row++ = Swap32 (*p++);
598 for (x=tx; x < tx+ox; x++) {
600 *row |= ((Uint32) *(p8++) << 16);
601 *row |= ((Uint32) *(p8++) << 8);
602 *row |= ((Uint32) *(p8++) << 0);
606 case 2: // Indexed/Greyscale + Alpha
607 switch (head->image_type) {
609 for (x=tx; x < tx+ox; x++) {
610 *row = ((Uint32) (head->cm_map [*p8*3]) << 16);
611 *row |= ((Uint32) (head->cm_map [*p8*3+1]) << 8);
612 *row |= ((Uint32) (head->cm_map [*p8++*3+2]) << 0);
613 *row |= ((Uint32) *p8++ << 24);;
617 case IMAGE_GREYSCALE:
618 for (x=tx; x < tx+ox; x++) {
619 *row = ((Uint32) *p8 << 16);
620 *row |= ((Uint32) *p8 << 8);
621 *row |= ((Uint32) *p8++ << 0);
622 *row |= ((Uint32) *p8++ << 24);;
627 fprintf (stderr, "Unknown Gimp image type (%d)\n", head->image_type);
630 if (hierarchy->level_file_offsets)
631 SDL_free(hierarchy->level_file_offsets);
633 free_xcf_hierarchy(hierarchy);
636 free_xcf_level (level);
640 case 1: // Indexed/Greyscale
641 switch (head->image_type) {
643 for (x = tx; x < tx+ox; x++) {
645 | ((Uint32) (head->cm_map [*p8*3]) << 16)
646 | ((Uint32) (head->cm_map [*p8*3+1]) << 8)
647 | ((Uint32) (head->cm_map [*p8*3+2]) << 0);
651 case IMAGE_GREYSCALE:
652 for (x=tx; x < tx+ox; x++) {
654 | (((Uint32) (*p8)) << 16)
655 | (((Uint32) (*p8)) << 8)
656 | (((Uint32) (*p8)) << 0);
661 fprintf (stderr, "Unknown Gimp image type (%d)\n", head->image_type);
663 free_xcf_tile (tile);
665 free_xcf_level (level);
667 free_xcf_hierarchy (hierarchy);
674 if (tx >= level->width) {
678 if (ty >= level->height) {
682 free_xcf_tile (tile);
684 free_xcf_level (level);
687 free_xcf_hierarchy (hierarchy);
692 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
695 const char *error = NULL;
696 SDL_Surface *surface, *lays;
699 xcf_channel ** channel;
700 int chnls, i, offsets;
703 unsigned char * (* load_tile) (SDL_RWops *, Uint32, int, int, int);
706 /* The error message has been set in SDL_RWFromFile */
709 start = SDL_RWtell(src);
711 /* Initialize the data we will clean up when we're done */
714 head = read_xcf_header (src);
716 switch (head->compr) {
718 load_tile = load_xcf_tile_none;
721 load_tile = load_xcf_tile_rle;
724 fprintf (stderr, "Unsupported Compression.\n");
725 free_xcf_header (head);
729 /* Create the surface of the appropriate type */
730 surface = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
731 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
733 if ( surface == NULL ) {
734 error = "Out of memory";
740 while ((offset = SDL_ReadBE32 (src))) {
741 head->layer_file_offsets = (Uint32 *) SDL_realloc (head->layer_file_offsets, sizeof (Uint32) * (offsets+1));
742 head->layer_file_offsets [offsets] = (Uint32)offset;
745 fp = SDL_RWtell (src);
747 lays = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
748 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
750 if ( lays == NULL ) {
751 error = "Out of memory";
755 // Blit layers backwards, because Gimp saves them highest first
756 for (i = offsets; i > 0; i--) {
758 SDL_RWseek (src, head->layer_file_offsets [i-1], RW_SEEK_SET);
760 layer = read_xcf_layer (src);
761 do_layer_surface (lays, src, head, layer, load_tile);
765 rs.h = layer->height;
766 rd.x = layer->offset_x;
767 rd.y = layer->offset_y;
769 rd.h = layer->height;
772 SDL_BlitSurface (lays, &rs, surface, &rd);
773 free_xcf_layer (layer);
776 SDL_FreeSurface (lays);
778 SDL_RWseek (src, fp, RW_SEEK_SET);
783 while ((offset = SDL_ReadBE32 (src))) {
784 channel = (xcf_channel **) SDL_realloc (channel, sizeof (xcf_channel *) * (chnls+1));
785 fp = SDL_RWtell (src);
786 SDL_RWseek (src, offset, RW_SEEK_SET);
787 channel [chnls++] = (read_xcf_channel (src));
788 SDL_RWseek (src, fp, RW_SEEK_SET);
794 chs = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
795 0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
798 error = "Out of memory";
801 for (i = 0; i < chnls; i++) {
802 // printf ("CNLBLT %i\n", i);
803 if (!channel [i]->selection && channel [i]->visible) {
804 create_channel_surface (chs, (xcf_image_type)head->image_type, channel [i]->color, channel [i]->opacity);
805 SDL_BlitSurface (chs, NULL, surface, NULL);
807 free_xcf_channel (channel [i]);
810 SDL_FreeSurface (chs);
814 free_xcf_header (head);
816 SDL_RWseek(src, start, RW_SEEK_SET);
818 SDL_FreeSurface(surface);
829 /* See if an image is contained in a data source */
830 int IMG_isXCF(SDL_RWops *src)
835 /* Load a XCF type image from an SDL datasource */
836 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
841 #endif /* LOAD_XCF */