IMG_xcf.c
author Ryan C. Gordon <icculus@icculus.org>
Wed, 07 Feb 2018 16:18:54 -0500
changeset 568 c5f9cbb5d2bb
parent 562 d0142861559c
child 569 fb643e371806
permissions -rwxr-xr-x
xcf: Prevent infinite loop and/or buffer overflow on bogus data.
     1 /*
     2   SDL_image:  An example image loading library for use with SDL
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     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.
     8 
     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:
    12 
    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.
    20 */
    21 
    22 /* This is a XCF image file loading framework */
    23 
    24 #include "SDL_endian.h"
    25 #include "SDL_image.h"
    26 
    27 #ifdef LOAD_XCF
    28 
    29 #if DEBUG
    30 static char prop_names [][30] = {
    31   "end",
    32   "colormap",
    33   "active_layer",
    34   "active_channel",
    35   "selection",
    36   "floating_selection",
    37   "opacity",
    38   "mode",
    39   "visible",
    40   "linked",
    41   "preserve_transparency",
    42   "apply_mask",
    43   "edit_mask",
    44   "show_mask",
    45   "show_masked",
    46   "offsets",
    47   "color",
    48   "compression",
    49   "guides",
    50   "resolution",
    51   "tattoo",
    52   "parasites",
    53   "unit",
    54   "paths",
    55   "user_unit"
    56 };
    57 #endif
    58 
    59 
    60 typedef enum
    61 {
    62   PROP_END = 0,
    63   PROP_COLORMAP = 1,
    64   PROP_ACTIVE_LAYER = 2,
    65   PROP_ACTIVE_CHANNEL = 3,
    66   PROP_SELECTION = 4,
    67   PROP_FLOATING_SELECTION = 5,
    68   PROP_OPACITY = 6,
    69   PROP_MODE = 7,
    70   PROP_VISIBLE = 8,
    71   PROP_LINKED = 9,
    72   PROP_PRESERVE_TRANSPARENCY = 10,
    73   PROP_APPLY_MASK = 11,
    74   PROP_EDIT_MASK = 12,
    75   PROP_SHOW_MASK = 13,
    76   PROP_SHOW_MASKED = 14,
    77   PROP_OFFSETS = 15,
    78   PROP_COLOR = 16,
    79   PROP_COMPRESSION = 17,
    80   PROP_GUIDES = 18,
    81   PROP_RESOLUTION = 19,
    82   PROP_TATTOO = 20,
    83   PROP_PARASITES = 21,
    84   PROP_UNIT = 22,
    85   PROP_PATHS = 23,
    86   PROP_USER_UNIT = 24
    87 } xcf_prop_type;
    88 
    89 typedef enum {
    90   COMPR_NONE    = 0,
    91   COMPR_RLE     = 1,
    92   COMPR_ZLIB    = 2,
    93   COMPR_FRACTAL = 3
    94 } xcf_compr_type;
    95 
    96 typedef enum {
    97   IMAGE_RGB       = 0,
    98   IMAGE_GREYSCALE = 1,
    99   IMAGE_INDEXED   = 2
   100 } xcf_image_type;
   101 
   102 typedef struct {
   103   Uint32 id;
   104   Uint32 length;
   105   union {
   106     struct {
   107       Uint32 num;
   108       char * cmap;
   109     } colormap; // 1
   110     struct {
   111       Uint32 drawable_offset;
   112     } floating_selection; // 5
   113     Sint32 opacity;
   114     Sint32 mode;
   115     int    visible;
   116     int    linked;
   117     int    preserve_transparency;
   118     int    apply_mask;
   119     int    show_mask;
   120     struct {
   121       Sint32 x;
   122       Sint32 y;
   123     } offset;
   124     unsigned char color [3];
   125     Uint8 compression;
   126     struct {
   127       Sint32 x;
   128       Sint32 y;
   129     } resolution;
   130     struct {
   131       char * name;
   132       Uint32 flags;
   133       Uint32 size;
   134       char * data;
   135     } parasite;
   136   } data;
   137 } xcf_prop;
   138 
   139 typedef struct {
   140   char   sign [14];
   141   Uint32 width;
   142   Uint32 height;
   143   Sint32 image_type;
   144   xcf_prop * properties;
   145 
   146   Uint32 * layer_file_offsets;
   147   Uint32 * channel_file_offsets;
   148 
   149   xcf_compr_type compr;
   150   Uint32         cm_num;
   151   unsigned char * cm_map;
   152 } xcf_header;
   153 
   154 typedef struct {
   155   Uint32 width;
   156   Uint32 height;
   157   Sint32 layer_type;
   158   char * name;
   159   xcf_prop * properties;
   160 
   161   Uint32 hierarchy_file_offset;
   162   Uint32 layer_mask_offset;
   163 
   164   Uint32 offset_x;
   165   Uint32 offset_y;
   166   int visible;
   167 } xcf_layer;
   168 
   169 typedef struct {
   170   Uint32 width;
   171   Uint32 height;
   172   char * name;
   173   xcf_prop * properties;
   174 
   175   Uint32 hierarchy_file_offset;
   176 
   177   Uint32 color;
   178   Uint32 opacity;
   179   int selection;
   180   int visible;
   181 } xcf_channel;
   182 
   183 typedef struct {
   184   Uint32 width;
   185   Uint32 height;
   186   Uint32 bpp;
   187 
   188   Uint32 * level_file_offsets;
   189 } xcf_hierarchy;
   190 
   191 typedef struct {
   192   Uint32 width;
   193   Uint32 height;
   194 
   195   Uint32 * tile_file_offsets;
   196 } xcf_level;
   197 
   198 typedef unsigned char * xcf_tile;
   199 
   200 typedef unsigned char * (* load_tile_type) (SDL_RWops *, Uint32, int, int, int);
   201 
   202 
   203 /* See if an image is contained in a data source */
   204 int IMG_isXCF(SDL_RWops *src)
   205 {
   206     Sint64 start;
   207     int is_XCF;
   208     char magic[14];
   209 
   210     if ( !src )
   211         return 0;
   212     start = SDL_RWtell(src);
   213     is_XCF = 0;
   214     if ( SDL_RWread(src, magic, sizeof(magic), 1) ) {
   215         if (SDL_strncmp(magic, "gimp xcf ", 9) == 0) {
   216             is_XCF = 1;
   217         }
   218     }
   219     SDL_RWseek(src, start, RW_SEEK_SET);
   220     return(is_XCF);
   221 }
   222 
   223 static char * read_string (SDL_RWops * src) {
   224   Uint32 tmp;
   225   char * data;
   226 
   227   tmp = SDL_ReadBE32 (src);
   228   if (tmp > 0) {
   229     data = (char *) SDL_malloc (sizeof (char) * tmp);
   230     SDL_RWread (src, data, tmp, 1);
   231   }
   232   else {
   233     data = NULL;
   234   }
   235 
   236   return data;
   237 }
   238 
   239 
   240 static Uint32 Swap32 (Uint32 v) {
   241   return
   242     ((v & 0x000000FF) << 16)
   243     |  ((v & 0x0000FF00))
   244     |  ((v & 0x00FF0000) >> 16)
   245     |  ((v & 0xFF000000));
   246 }
   247 
   248 static void xcf_read_property (SDL_RWops * src, xcf_prop * prop) {
   249   Uint32 len;
   250   prop->id = SDL_ReadBE32 (src);
   251   prop->length = SDL_ReadBE32 (src);
   252 
   253 #if DEBUG
   254   printf ("%.8X: %s: %d\n", SDL_RWtell (src), prop->id < 25 ? prop_names [prop->id] : "unknown", prop->length);
   255 #endif
   256 
   257   switch (prop->id) {
   258   case PROP_COLORMAP:
   259     prop->data.colormap.num = SDL_ReadBE32 (src);
   260     prop->data.colormap.cmap = (char *) SDL_malloc (sizeof (char) * prop->data.colormap.num * 3);
   261     SDL_RWread (src, prop->data.colormap.cmap, prop->data.colormap.num*3, 1);
   262     break;
   263 
   264   case PROP_OFFSETS:
   265     prop->data.offset.x = SDL_ReadBE32 (src);
   266     prop->data.offset.y = SDL_ReadBE32 (src);
   267     break;
   268   case PROP_OPACITY:
   269     prop->data.opacity = SDL_ReadBE32 (src);
   270     break;
   271   case PROP_COMPRESSION:
   272   case PROP_COLOR:
   273     if (prop->length > sizeof(prop->data)) {
   274         len = sizeof(prop->data);
   275     } else {
   276         len = prop->length;
   277     }
   278     SDL_RWread(src, &prop->data, len, 1);
   279     break;
   280   case PROP_VISIBLE:
   281     prop->data.visible = SDL_ReadBE32 (src);
   282     break;
   283   default:
   284     //    SDL_RWread (src, &prop->data, prop->length, 1);
   285     SDL_RWseek (src, prop->length, RW_SEEK_CUR);
   286   }
   287 }
   288 
   289 static void free_xcf_header (xcf_header * h) {
   290   if (h->cm_num)
   291     SDL_free (h->cm_map);
   292   if (h->layer_file_offsets)
   293     SDL_free (h->layer_file_offsets);
   294   SDL_free (h);
   295 }
   296 
   297 static xcf_header * read_xcf_header (SDL_RWops * src) {
   298   xcf_header * h;
   299   xcf_prop prop;
   300 
   301   h = (xcf_header *) SDL_malloc (sizeof (xcf_header));
   302   if (!h) {
   303     return NULL;
   304   }
   305   SDL_RWread (src, h->sign, 14, 1);
   306   h->width       = SDL_ReadBE32 (src);
   307   h->height      = SDL_ReadBE32 (src);
   308   h->image_type  = SDL_ReadBE32 (src);
   309 
   310   h->properties = NULL;
   311   h->layer_file_offsets = NULL;
   312   h->compr      = COMPR_NONE;
   313   h->cm_num = 0;
   314   h->cm_map = NULL;
   315 
   316   // Just read, don't save
   317   do {
   318     xcf_read_property (src, &prop);
   319     if (prop.id == PROP_COMPRESSION)
   320       h->compr = (xcf_compr_type)prop.data.compression;
   321     else if (prop.id == PROP_COLORMAP) {
   322       // unused var: int i;
   323       Uint32 cm_num;
   324       unsigned char *cm_map;
   325 
   326       cm_num = prop.data.colormap.num;
   327       cm_map = (unsigned char *) SDL_realloc(h->cm_map, sizeof (unsigned char) * 3 * cm_num);
   328       if (cm_map) {
   329         h->cm_num = cm_num;
   330         h->cm_map = cm_map;
   331         SDL_memcpy (h->cm_map, prop.data.colormap.cmap, 3*sizeof (char)*h->cm_num);
   332       }
   333       SDL_free (prop.data.colormap.cmap);
   334 
   335       if (!cm_map) {
   336         free_xcf_header(h);
   337         return NULL;
   338       }
   339     }
   340   } while (prop.id != PROP_END);
   341 
   342   return h;
   343 }
   344 
   345 static void free_xcf_layer (xcf_layer * l) {
   346   SDL_free (l->name);
   347   SDL_free (l);
   348 }
   349 
   350 static xcf_layer * read_xcf_layer (SDL_RWops * src) {
   351   xcf_layer * l;
   352   xcf_prop    prop;
   353 
   354   l = (xcf_layer *) SDL_malloc (sizeof (xcf_layer));
   355   l->width  = SDL_ReadBE32 (src);
   356   l->height = SDL_ReadBE32 (src);
   357   l->layer_type = SDL_ReadBE32 (src);
   358 
   359   l->name = read_string (src);
   360 
   361   do {
   362     xcf_read_property (src, &prop);
   363     if (prop.id == PROP_OFFSETS) {
   364       l->offset_x = prop.data.offset.x;
   365       l->offset_y = prop.data.offset.y;
   366     } else if (prop.id == PROP_VISIBLE) {
   367       l->visible = prop.data.visible ? 1 : 0;
   368     }
   369   } while (prop.id != PROP_END);
   370 
   371   l->hierarchy_file_offset = SDL_ReadBE32 (src);
   372   l->layer_mask_offset     = SDL_ReadBE32 (src);
   373 
   374   return l;
   375 }
   376 
   377 static void free_xcf_channel (xcf_channel * c) {
   378   SDL_free (c->name);
   379   SDL_free (c);
   380 }
   381 
   382 static xcf_channel * read_xcf_channel (SDL_RWops * src) {
   383   xcf_channel * l;
   384   xcf_prop    prop;
   385 
   386   l = (xcf_channel *) SDL_malloc (sizeof (xcf_channel));
   387   l->width  = SDL_ReadBE32 (src);
   388   l->height = SDL_ReadBE32 (src);
   389 
   390   l->name = read_string (src);
   391 
   392   l->selection = 0;
   393   do {
   394     xcf_read_property (src, &prop);
   395     switch (prop.id) {
   396     case PROP_OPACITY:
   397       l->opacity = prop.data.opacity << 24;
   398       break;
   399     case PROP_COLOR:
   400       l->color = ((Uint32) prop.data.color[0] << 16)
   401     | ((Uint32) prop.data.color[1] << 8)
   402     | ((Uint32) prop.data.color[2]);
   403       break;
   404     case PROP_SELECTION:
   405       l->selection = 1;
   406       break;
   407     case PROP_VISIBLE:
   408       l->visible = prop.data.visible ? 1 : 0;
   409       break;
   410     default:
   411         ;
   412     }
   413   } while (prop.id != PROP_END);
   414 
   415   l->hierarchy_file_offset = SDL_ReadBE32 (src);
   416 
   417   return l;
   418 }
   419 
   420 static void free_xcf_hierarchy (xcf_hierarchy * h) {
   421   SDL_free (h->level_file_offsets);
   422   SDL_free (h);
   423 }
   424 
   425 static xcf_hierarchy * read_xcf_hierarchy (SDL_RWops * src) {
   426   xcf_hierarchy * h;
   427   int i;
   428 
   429   h = (xcf_hierarchy *) SDL_malloc (sizeof (xcf_hierarchy));
   430   h->width  = SDL_ReadBE32 (src);
   431   h->height = SDL_ReadBE32 (src);
   432   h->bpp    = SDL_ReadBE32 (src);
   433 
   434   h->level_file_offsets = NULL;
   435   i = 0;
   436   do {
   437     h->level_file_offsets = (Uint32 *) SDL_realloc (h->level_file_offsets, sizeof (Uint32) * (i+1));
   438     h->level_file_offsets [i] = SDL_ReadBE32 (src);
   439   } while (h->level_file_offsets [i++]);
   440 
   441   return h;
   442 }
   443 
   444 static void free_xcf_level (xcf_level * l) {
   445   SDL_free (l->tile_file_offsets);
   446   SDL_free (l);
   447 }
   448 
   449 static xcf_level * read_xcf_level (SDL_RWops * src) {
   450   xcf_level * l;
   451   int i;
   452 
   453   l = (xcf_level *) SDL_malloc (sizeof (xcf_level));
   454   l->width  = SDL_ReadBE32 (src);
   455   l->height = SDL_ReadBE32 (src);
   456 
   457   l->tile_file_offsets = NULL;
   458   i = 0;
   459   do {
   460     l->tile_file_offsets = (Uint32 *) SDL_realloc (l->tile_file_offsets, sizeof (Uint32) * (i+1));
   461     l->tile_file_offsets [i] = SDL_ReadBE32 (src);
   462   } while (l->tile_file_offsets [i++]);
   463 
   464   return l;
   465 }
   466 
   467 static void free_xcf_tile (unsigned char * t) {
   468   SDL_free (t);
   469 }
   470 
   471 static unsigned char * load_xcf_tile_none (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
   472   unsigned char * load;
   473 
   474   load = (unsigned char *) SDL_malloc (len); // expect this is okay
   475   SDL_RWread (src, load, len, 1);
   476 
   477   return load;
   478 }
   479 
   480 static unsigned char * load_xcf_tile_rle (SDL_RWops * src, Uint32 len, int bpp, int x, int y) {
   481   unsigned char * load, * t, * data, * d;
   482   Uint32 reallen;
   483   int i, size, count, j, length;
   484   unsigned char val;
   485 
   486   if (len == 0) {  /* probably bogus data. */
   487     return NULL;
   488   }
   489 
   490   t = load = (unsigned char *) SDL_malloc (len);
   491   reallen = SDL_RWread (src, t, 1, len);
   492 
   493   data = (unsigned char *) SDL_calloc (1, x*y*bpp);
   494   for (i = 0; i < bpp; i++) {
   495     d    = data + i;
   496     size = x*y;
   497     count = 0;
   498 
   499     while (size > 0) {
   500       val = *t++;
   501 
   502       length = val;
   503       if (length >= 128) {
   504         length = 255 - (length - 1);
   505         if (length == 128) {
   506           length = (*t << 8) + t[1];
   507           t += 2;
   508         }
   509 
   510         if (((size_t) (t - load) + length) >= len) {
   511           break;  /* bogus data */
   512         } else if (length > size) {
   513           break;  /* bogus data */
   514         }
   515 
   516         count += length;
   517         size -= length;
   518 
   519         while (length-- > 0) {
   520           *d = *t++;
   521           d += bpp;
   522         }
   523       } else {
   524         length += 1;
   525         if (length == 128) {
   526           length = (*t << 8) + t[1];
   527           t += 2;
   528         }
   529 
   530         if (((size_t) (t - load)) >= len) {
   531           break;  /* bogus data */
   532         } else if (length > size) {
   533           break;  /* bogus data */
   534         }
   535 
   536         count += length;
   537         size -= length;
   538 
   539         val = *t++;
   540 
   541         for (j = 0; j < length; j++) {
   542           *d = val;
   543           d += bpp;
   544         }
   545       }
   546     }
   547 
   548     if (size > 0) {
   549       break;  /* just drop out, untouched data initialized to zero. */
   550     }
   551 
   552   }
   553 
   554   SDL_free (load);
   555   return (data);
   556 }
   557 
   558 static Uint32 rgb2grey (Uint32 a) {
   559   Uint8 l;
   560   l = (Uint8)(0.2990 * ((a & 0x00FF0000) >> 16)
   561     + 0.5870 * ((a & 0x0000FF00) >>  8)
   562     + 0.1140 * ((a & 0x000000FF)));
   563 
   564   return (l << 16) | (l << 8) | l;
   565 }
   566 
   567 static void create_channel_surface (SDL_Surface * surf, xcf_image_type itype, Uint32 color, Uint32 opacity) {
   568   Uint32 c = 0;
   569 
   570   switch (itype) {
   571   case IMAGE_RGB:
   572   case IMAGE_INDEXED:
   573     c = opacity | color;
   574     break;
   575   case IMAGE_GREYSCALE:
   576     c = opacity | rgb2grey (color);
   577     break;
   578   }
   579   SDL_FillRect (surf, NULL, c);
   580 }
   581 
   582 static int 
   583 do_layer_surface(SDL_Surface * surface, SDL_RWops * src, xcf_header * head, xcf_layer * layer, load_tile_type load_tile)
   584 {
   585     xcf_hierarchy  *hierarchy;
   586     xcf_level      *level;
   587     unsigned char  *tile;
   588     Uint8          *p8;
   589     Uint16         *p16;
   590     Uint32         *p;
   591     int            i, j;
   592     Uint32         x, y, tx, ty, ox, oy;
   593     Uint32         *row;
   594 
   595     SDL_RWseek(src, layer->hierarchy_file_offset, RW_SEEK_SET);
   596     hierarchy = read_xcf_hierarchy(src);
   597 
   598     level = NULL;
   599     for (i = 0; hierarchy->level_file_offsets[i]; i++) {
   600         SDL_RWseek(src, hierarchy->level_file_offsets[i], RW_SEEK_SET);
   601         level = read_xcf_level(src);
   602 
   603         ty = tx = 0;
   604         for (j = 0; level->tile_file_offsets[j]; j++) {
   605             SDL_RWseek(src, level->tile_file_offsets[j], RW_SEEK_SET);
   606             ox = tx + 64 > level->width ? level->width % 64 : 64;
   607             oy = ty + 64 > level->height ? level->height % 64 : 64;
   608 
   609             if (level->tile_file_offsets[j + 1]) {
   610                 tile = load_tile(src, level->tile_file_offsets[j + 1] - level->tile_file_offsets[j], hierarchy->bpp, ox, oy);
   611             } else {
   612                 tile = load_tile(src, ox * oy * 6, hierarchy->bpp, ox, oy);
   613             }
   614 
   615             if (!tile) {
   616                 if (hierarchy) {
   617                     free_xcf_hierarchy(hierarchy);
   618                 }
   619                 if (level) {
   620                     free_xcf_level(level);
   621                 }
   622                 return 1;
   623             }
   624 
   625             p8 = tile;
   626             p16 = (Uint16 *) p8;
   627             p = (Uint32 *) p8;
   628             for (y = ty; y < ty + oy; y++) {
   629                 row = (Uint32 *) ((Uint8 *) surface->pixels + y * surface->pitch + tx * 4);
   630                 switch (hierarchy->bpp) {
   631                 case 4:
   632                     for (x = tx; x < tx + ox; x++)
   633                         *row++ = Swap32(*p++);
   634                     break;
   635                 case 3:
   636                     for (x = tx; x < tx + ox; x++) {
   637                         *row = 0xFF000000;
   638                         *row |= ((Uint32)*p8++ << 16);
   639                         *row |= ((Uint32)*p8++ << 8);
   640                         *row |= ((Uint32)*p8++ << 0);
   641                         row++;
   642                     }
   643                     break;
   644                 case 2:
   645                     /* Indexed / Greyscale + Alpha */
   646                     switch (head->image_type) {
   647                     case IMAGE_INDEXED:
   648                         for (x = tx; x < tx + ox; x++) {
   649                             *row = ((Uint32)(head->cm_map[*p8 * 3]) << 16);
   650                             *row |= ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8);
   651                             *row |= ((Uint32)(head->cm_map[*p8++ * 3 + 2]) << 0);
   652                             *row |= ((Uint32)*p8++ << 24);
   653                             row++;
   654                         }
   655                         break;
   656                     case IMAGE_GREYSCALE:
   657                         for (x = tx; x < tx + ox; x++) {
   658                             *row = ((Uint32)*p8 << 16);
   659                             *row |= ((Uint32)*p8 << 8);
   660                             *row |= ((Uint32)*p8++ << 0);
   661                             *row |= ((Uint32)*p8++ << 24);
   662                             row++;
   663                         }
   664                         break;
   665                     default:
   666                         SDL_Log("Unknown Gimp image type (%d)\n", head->image_type);
   667                         if (hierarchy) {
   668                             free_xcf_hierarchy(hierarchy);
   669                         }
   670                         if (level)
   671                             free_xcf_level(level);
   672                         return 1;
   673                     }
   674                     break;
   675                 case 1:
   676                     /* Indexed / Greyscale */
   677                     switch (head->image_type) {
   678                     case IMAGE_INDEXED:
   679                         for (x = tx; x < tx + ox; x++) {
   680                             *row++ = 0xFF000000
   681                                 | ((Uint32)(head->cm_map[*p8 * 3]) << 16)
   682                                 | ((Uint32)(head->cm_map[*p8 * 3 + 1]) << 8)
   683                                 | ((Uint32)(head->cm_map[*p8 * 3 + 2]) << 0);
   684                             p8++;
   685                         }
   686                         break;
   687                     case IMAGE_GREYSCALE:
   688                         for (x = tx; x < tx + ox; x++) {
   689                             *row++ = 0xFF000000
   690                                 | (((Uint32)(*p8)) << 16)
   691                                 | (((Uint32)(*p8)) << 8)
   692                                 | (((Uint32)(*p8)) << 0);
   693                             ++p8;
   694                         }
   695                         break;
   696                     default:
   697                         SDL_Log("Unknown Gimp image type (%d)\n", head->image_type);
   698                         if (tile)
   699                             free_xcf_tile(tile);
   700                         if (level)
   701                             free_xcf_level(level);
   702                         if (hierarchy)
   703                             free_xcf_hierarchy(hierarchy);
   704                         return 1;
   705                     }
   706                     break;
   707                 }
   708             }
   709             free_xcf_tile(tile);
   710 
   711             tx += 64;
   712             if (tx >= level->width) {
   713                 tx = 0;
   714                 ty += 64;
   715             }
   716             if (ty >= level->height) {
   717                 break;
   718             }
   719         }
   720         free_xcf_level(level);
   721     }
   722 
   723     free_xcf_hierarchy(hierarchy);
   724 
   725     return 0;
   726 }
   727 
   728 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
   729 {
   730   Sint64 start;
   731   const char *error = NULL;
   732   SDL_Surface *surface, *lays;
   733   xcf_header * head;
   734   xcf_layer  * layer;
   735   xcf_channel ** channel;
   736   int chnls, i, offsets;
   737   Sint64 offset, fp;
   738 
   739   unsigned char * (* load_tile) (SDL_RWops *, Uint32, int, int, int);
   740 
   741   if (!src) {
   742     /* The error message has been set in SDL_RWFromFile */
   743     return NULL;
   744   }
   745   start = SDL_RWtell(src);
   746 
   747   /* Initialize the data we will clean up when we're done */
   748   surface = NULL;
   749 
   750   head = read_xcf_header(src);
   751   if (!head) {
   752     return NULL;
   753   }
   754 
   755   switch (head->compr) {
   756   case COMPR_NONE:
   757     load_tile = load_xcf_tile_none;
   758     break;
   759   case COMPR_RLE:
   760     load_tile = load_xcf_tile_rle;
   761     break;
   762   default:
   763     SDL_Log("Unsupported Compression.\n");
   764     free_xcf_header (head);
   765     return NULL;
   766   }
   767 
   768   /* Create the surface of the appropriate type */
   769   surface = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
   770                  0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
   771 
   772   if ( surface == NULL ) {
   773     error = "Out of memory";
   774     goto done;
   775   }
   776 
   777   offsets = 0;
   778 
   779   while ((offset = SDL_ReadBE32 (src))) {
   780     head->layer_file_offsets = (Uint32 *) SDL_realloc (head->layer_file_offsets, sizeof (Uint32) * (offsets+1));
   781     head->layer_file_offsets [offsets] = (Uint32)offset;
   782     offsets++;
   783   }
   784   fp = SDL_RWtell (src);
   785 
   786   lays = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
   787               0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
   788 
   789   if ( lays == NULL ) {
   790     error = "Out of memory";
   791     goto done;
   792   }
   793 
   794   // Blit layers backwards, because Gimp saves them highest first
   795   for (i = offsets; i > 0; i--) {
   796     SDL_Rect rs, rd;
   797     SDL_RWseek (src, head->layer_file_offsets [i-1], RW_SEEK_SET);
   798 
   799     layer = read_xcf_layer (src);
   800     do_layer_surface (lays, src, head, layer, load_tile);
   801     rs.x = 0;
   802     rs.y = 0;
   803     rs.w = layer->width;
   804     rs.h = layer->height;
   805     rd.x = layer->offset_x;
   806     rd.y = layer->offset_y;
   807     rd.w = layer->width;
   808     rd.h = layer->height;
   809 
   810     if (layer->visible)
   811       SDL_BlitSurface (lays, &rs, surface, &rd);
   812     free_xcf_layer (layer);
   813   }
   814 
   815   SDL_FreeSurface (lays);
   816 
   817   SDL_RWseek (src, fp, RW_SEEK_SET);
   818 
   819   // read channels
   820   channel = NULL;
   821   chnls   = 0;
   822   while ((offset = SDL_ReadBE32 (src))) {
   823     channel = (xcf_channel **) SDL_realloc (channel, sizeof (xcf_channel *) * (chnls+1));
   824     fp = SDL_RWtell (src);
   825     SDL_RWseek (src, offset, RW_SEEK_SET);
   826     channel [chnls++] = (read_xcf_channel (src));
   827     SDL_RWseek (src, fp, RW_SEEK_SET);
   828   }
   829 
   830   if (chnls) {
   831     SDL_Surface * chs;
   832 
   833     chs = SDL_CreateRGBSurface(SDL_SWSURFACE, head->width, head->height, 32,
   834                0x00FF0000,0x0000FF00,0x000000FF,0xFF000000);
   835 
   836     if (chs == NULL) {
   837       error = "Out of memory";
   838       goto done;
   839     }
   840     for (i = 0; i < chnls; i++) {
   841       //      printf ("CNLBLT %i\n", i);
   842       if (!channel [i]->selection && channel [i]->visible) {
   843     create_channel_surface (chs, (xcf_image_type)head->image_type, channel [i]->color, channel [i]->opacity);
   844     SDL_BlitSurface (chs, NULL, surface, NULL);
   845       }
   846       free_xcf_channel (channel [i]);
   847     }
   848 
   849     SDL_FreeSurface (chs);
   850   }
   851 
   852 done:
   853   free_xcf_header (head);
   854   if ( error ) {
   855     SDL_RWseek(src, start, RW_SEEK_SET);
   856     if ( surface ) {
   857       SDL_FreeSurface(surface);
   858       surface = NULL;
   859     }
   860     IMG_SetError("%s", error);
   861   }
   862 
   863   return(surface);
   864 }
   865 
   866 #else
   867 
   868 /* See if an image is contained in a data source */
   869 int IMG_isXCF(SDL_RWops *src)
   870 {
   871   return(0);
   872 }
   873 
   874 /* Load a XCF type image from an SDL datasource */
   875 SDL_Surface *IMG_LoadXCF_RW(SDL_RWops *src)
   876 {
   877   return(NULL);
   878 }
   879 
   880 #endif /* LOAD_XCF */