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