2011-04-25 23:28:29 -07:00
|
|
|
/*
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
-----------------------------[ XCF Loader ]-----------------------------
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
|
|
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
------------------------------------------------------------------------
|
|
|
|
|
|
|
|
There's a quick overview of the XCF file structure in Gimp's source
|
|
|
|
tree, in docs/xcf.doc. However it's brief, so here's a more verbose
|
|
|
|
overview based on my understanding of XCF. All image characteristics
|
|
|
|
are defined in "properties". In the data stream properties are defined
|
|
|
|
through a 4 bit index number (see enum below), followed by a 4 byte
|
|
|
|
length. The property data directly follows this information. A list of
|
|
|
|
properties ends when PROP_END is encountered. There is a properties
|
|
|
|
block at the beginning of the file, as well as at the beginning of each
|
|
|
|
layer and channel. Layers and channels are read at offsets in the file,
|
|
|
|
the list of layers (resp. channels) is exhausted when the next offset
|
|
|
|
read in is zero.
|
|
|
|
|
|
|
|
The actual image data is stored in tiles, which are by default 64x64 in
|
|
|
|
size, likely with smaller ones on the right and bottom edges.
|
|
|
|
|
|
|
|
The position of the tiles on the image is left->right, row by row. The
|
|
|
|
actual DATA8* data is contained in a "level" which is contained in a
|
|
|
|
"hierarchy". I've not really understood the purpose of the hierarchy, as
|
|
|
|
it seems to always contain only one level anyway.
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
Layer masks are stored as channels (basically grayscale layers with a
|
|
|
|
single color definition. For the purpose of this loader I replaced the
|
|
|
|
concept of a channel with a layer, since it doesn't really matter.
|
|
|
|
|
|
|
|
Ok, hope this helps with understanding XCF. -- cK.
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
#define FREE(X) { free(X); X = NULL; }
|
|
|
|
|
|
|
|
#define TILE_WIDTH 64
|
|
|
|
#define TILE_HEIGHT 64
|
|
|
|
|
|
|
|
/* --------------------------------------------------------------------------- typedefs ------------ */
|
|
|
|
|
|
|
|
typedef struct _Layer Layer;
|
|
|
|
typedef struct _Tile Tile;
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------ enums ------------ */
|
|
|
|
|
|
|
|
|
|
|
|
/* These are all the properties that a layer or channel can have.
|
|
|
|
Only some of them are actually used. */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
PROP_END = 0,
|
|
|
|
PROP_COLORMAP = 1,
|
|
|
|
PROP_ACTIVE_LAYER = 2,
|
|
|
|
PROP_ACTIVE_CHANNEL = 3,
|
|
|
|
PROP_SELECTION = 4,
|
|
|
|
PROP_FLOATING_SELECTION = 5,
|
|
|
|
PROP_OPACITY = 6,
|
|
|
|
PROP_MODE = 7,
|
|
|
|
PROP_VISIBLE = 8,
|
|
|
|
PROP_LINKED = 9,
|
|
|
|
PROP_PRESERVE_TRANSPARENCY = 10,
|
|
|
|
PROP_APPLY_MASK = 11,
|
|
|
|
PROP_EDIT_MASK = 12,
|
|
|
|
PROP_SHOW_MASK = 13,
|
|
|
|
PROP_SHOW_MASKED = 14,
|
|
|
|
PROP_OFFSETS = 15,
|
|
|
|
PROP_COLOR = 16,
|
|
|
|
PROP_COMPRESSION = 17,
|
|
|
|
PROP_GUIDES = 18,
|
|
|
|
PROP_RESOLUTION = 19,
|
|
|
|
PROP_TATTOO = 20,
|
|
|
|
PROP_PARASITES = 21,
|
|
|
|
PROP_UNIT = 22,
|
|
|
|
PROP_PATHS = 23,
|
|
|
|
PROP_USER_UNIT = 24
|
|
|
|
}
|
|
|
|
PropType;
|
|
|
|
|
|
|
|
/* The tiles can be stored in an encrypted fashion, defined as follows: */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
COMPRESS_NONE = 0,
|
|
|
|
COMPRESS_RLE = 1,
|
|
|
|
COMPRESS_ZLIB = 2,
|
|
|
|
COMPRESS_FRACTAL = 3 /* Unused. */
|
|
|
|
} CompressionType;
|
|
|
|
|
|
|
|
/* Layer modes (*SIGH*) */
|
2011-04-30 08:20:52 -07:00
|
|
|
typedef enum
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
NORMAL_MODE,
|
|
|
|
DISSOLVE_MODE,
|
|
|
|
BEHIND_MODE,
|
|
|
|
MULTIPLY_MODE,
|
|
|
|
SCREEN_MODE,
|
|
|
|
OVERLAY_MODE,
|
|
|
|
DIFFERENCE_MODE,
|
|
|
|
ADDITION_MODE,
|
|
|
|
SUBTRACT_MODE,
|
|
|
|
DARKEN_ONLY_MODE,
|
|
|
|
LIGHTEN_ONLY_MODE,
|
|
|
|
HUE_MODE,
|
|
|
|
SATURATION_MODE,
|
|
|
|
COLOR_MODE,
|
|
|
|
VALUE_MODE,
|
|
|
|
DIVIDE_MODE,
|
|
|
|
ERASE_MODE, /*< skip >*/
|
|
|
|
REPLACE_MODE, /*< skip >*/
|
|
|
|
ANTI_ERASE_MODE /*< skip >*/
|
|
|
|
}
|
|
|
|
LayerModeEffects;
|
|
|
|
|
|
|
|
/* Base image types */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
RGB,
|
|
|
|
GRAY,
|
|
|
|
INDEXED
|
|
|
|
} GimpImageBaseType;
|
|
|
|
|
|
|
|
/* Image types */
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
RGB_GIMAGE,
|
|
|
|
RGBA_GIMAGE,
|
|
|
|
GRAY_GIMAGE,
|
|
|
|
GRAYA_GIMAGE,
|
|
|
|
INDEXED_GIMAGE,
|
|
|
|
INDEXEDA_GIMAGE
|
|
|
|
} GimpImageType;
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------- structs ------------ */
|
|
|
|
|
|
|
|
/* Ok, this is what's left of Gimp's layer abstraction. I kicked out
|
|
|
|
all the stuff that's unnecessary and added the necessary stuff
|
|
|
|
from the Gimp drawable superclass. This one also serves as a
|
2011-04-30 08:20:52 -07:00
|
|
|
Channel, e.g. for use as a layer mask.
|
2011-04-25 23:28:29 -07:00
|
|
|
--cK.
|
|
|
|
*/
|
|
|
|
struct _Layer
|
2011-04-30 08:20:52 -07:00
|
|
|
{
|
2011-04-25 23:28:29 -07:00
|
|
|
int visible; /* controls visibility */
|
|
|
|
int width, height; /* size of drawable */
|
|
|
|
int bpp; /* depth */
|
|
|
|
int offset_x, offset_y; /* offset of layer in image */
|
|
|
|
|
|
|
|
int ID; /* provides a unique ID */
|
|
|
|
GimpImageType type; /* type of drawable */
|
|
|
|
char has_alpha; /* drawable has alpha */
|
|
|
|
|
|
|
|
int preserve_trans; /* preserve transparency */
|
|
|
|
|
|
|
|
Layer *mask; /* possible layer mask */
|
|
|
|
|
|
|
|
int opacity; /* layer opacity */
|
|
|
|
LayerModeEffects mode; /* layer combination mode */
|
|
|
|
|
|
|
|
|
|
|
|
/* XCF stores the actual image data as tiles. A Layer is covered with
|
|
|
|
tiles, the tiles on the right and bottom will (usually) be a bit
|
|
|
|
smaller. num_rows and num_cols gives the number of tile rows and
|
|
|
|
columns.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Tile* tiles; /* tiles for drawable data */
|
|
|
|
int num_rows;
|
|
|
|
int num_cols;
|
|
|
|
|
|
|
|
/* After the tiles are read in, they're serialized int an array
|
|
|
|
of DATA8's, that will always contain 4 bpp data. Not optimal,
|
|
|
|
I know, but makes life easier
|
|
|
|
*/
|
|
|
|
|
|
|
|
DATA8* data;
|
|
|
|
|
|
|
|
/* Layers are stored as a linked list. */
|
|
|
|
struct _Layer* next;
|
|
|
|
struct _Layer* prev;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* The tile structure:
|
|
|
|
*/
|
|
|
|
struct _Tile
|
|
|
|
{
|
|
|
|
unsigned char bpp; /* the bytes per pixel (1, 2, 3 or 4) */
|
|
|
|
unsigned short ewidth; /* the effective width of the tile */
|
|
|
|
unsigned short eheight; /* the effective height of the tile */
|
|
|
|
|
|
|
|
/* a tile's effective width and height may be smaller
|
|
|
|
* (but not larger) than TILE_WIDTH and TILE_HEIGHT.
|
|
|
|
* This is to handle edge tiles of a drawable.
|
|
|
|
*/
|
|
|
|
|
|
|
|
DATA8 *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* This struct simply contains everything that didn't fit elsewhere,
|
|
|
|
based on GimpImage :)
|
|
|
|
*/
|
|
|
|
struct _GimpImage
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
gzFile fp;
|
|
|
|
char* filename;
|
2011-04-30 08:20:52 -07:00
|
|
|
int cp; /* file stream pointer */
|
2011-04-25 23:28:29 -07:00
|
|
|
int compression; /* file compression mode */
|
|
|
|
int file_version;
|
|
|
|
|
|
|
|
int width, height; /* width and height attributes */
|
|
|
|
GimpImageBaseType base_type; /* base gimp_image type */
|
|
|
|
|
|
|
|
DATA32 floating_sel_offset;
|
|
|
|
|
|
|
|
DATA8* cmap; /* colormap--for indexed */
|
|
|
|
int num_cols; /* number of colors in map */
|
|
|
|
|
|
|
|
/* If a layer number was passed to the loader, it goes here: */
|
|
|
|
int single_layer_index;
|
|
|
|
|
|
|
|
/* Tadaa -- the final image data. Layers get pasted
|
|
|
|
onto this one, bottom-up.
|
|
|
|
*/
|
|
|
|
DATA8* data;
|
|
|
|
|
|
|
|
Layer* layers;
|
|
|
|
Layer* last_layer;
|
|
|
|
Layer* floating_sel;
|
|
|
|
}
|
|
|
|
_image;
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------- prototypes ------------ */
|
|
|
|
|
|
|
|
/* stuff that was adapted from xcf.c */
|
|
|
|
|
|
|
|
static void xcf_seek_pos(int pos);
|
|
|
|
static int xcf_read_int32(gzFile *fp, DATA32 *data, int count);
|
|
|
|
static int xcf_read_int8(gzFile *fp, DATA8 *data, int count);
|
|
|
|
static int xcf_read_string(gzFile *fp, char **data, int count);
|
|
|
|
static char xcf_load_prop(PropType *prop_type, DATA32 *prop_size);
|
|
|
|
static void xcf_load_image(void);
|
|
|
|
static char xcf_load_image_props(void);
|
|
|
|
|
|
|
|
static Layer* xcf_load_channel(void);
|
|
|
|
static char xcf_load_channel_props(Layer *layer);
|
|
|
|
static Layer* xcf_load_layer(void);
|
|
|
|
static char xcf_load_layer_props(Layer *layer);
|
|
|
|
static char xcf_load_hierarchy(Tile** tiles, int *num_rows, int *num_cols, int *bpp);
|
|
|
|
static char xcf_load_level(Tile** tiles, int hierarchy_width, int hierarchy_height, int bpp, int* num_rows, int *num_cols);
|
|
|
|
static char xcf_load_tile(Tile *tile);
|
|
|
|
static char xcf_load_tile_rle(Tile *tile, int data_length);
|
|
|
|
|
|
|
|
/* new stuff :) */
|
|
|
|
|
|
|
|
static Tile* allocate_tiles(int width, int height, int bpp, int* num_rows, int* num_cols);
|
|
|
|
static void free_tiles(Tile* tiles, int num_tiles);
|
|
|
|
static void init_tile(Tile* tile, int width, int height, int bpp);
|
|
|
|
static Layer* new_layer(int width, int height, GimpImageType type, int opacity, LayerModeEffects mode);
|
|
|
|
static void free_layer(Layer* layer);
|
|
|
|
static void add_layer_to_image(Layer* layer);
|
|
|
|
static void read_tiles_into_data(Tile* tiles, int num_cols, int width, int height, int bpp, DATA8** data, int use_cmap);
|
|
|
|
static void apply_layer_mask(Layer* layer);
|
|
|
|
static void set_layer_opacity(Layer* layer);
|
|
|
|
static void flatten_image(void);
|
|
|
|
|
|
|
|
static char xcf_file_init(char* filename);
|
|
|
|
static void xcf_cleanup(void);
|
|
|
|
|
|
|
|
/* Stuff for layer merging: */
|
|
|
|
extern void combine_pixels_normal (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_add (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_sub (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_diff (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_darken (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_lighten (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_mult (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_div (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_screen (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_overlay (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_hue (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_sat (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_val (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_col (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
extern void combine_pixels_diss (DATA8* src, int src_w, int src_h, DATA8* dest, int dest_w, int dest_h, int dest_x, int dest_y);
|
|
|
|
|
|
|
|
static int shm_fd = -1;
|
|
|
|
static int shm_size = 0;
|
|
|
|
static void *shm_addr = NULL;
|
|
|
|
static char shmfile[1024] = "";
|
|
|
|
|
|
|
|
static void
|
|
|
|
shm_alloc(int dsize)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_SHM_OPEN
|
|
|
|
srand(time(NULL));
|
|
|
|
do
|
|
|
|
{
|
2011-04-30 08:20:52 -07:00
|
|
|
snprintf(shmfile, sizeof(shmfile), "/evas-loader-xcf.%i.%i",
|
2011-04-25 23:28:29 -07:00
|
|
|
(int)getpid(), (int)rand());
|
|
|
|
shm_fd = shm_open(shmfile, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
|
|
|
|
}
|
|
|
|
while (shm_fd < 0);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (ftruncate(shm_fd, dsize) < 0)
|
|
|
|
{
|
|
|
|
close(shm_fd);
|
|
|
|
shm_unlink(shmfile);
|
|
|
|
shm_fd = -1;
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
shm_addr = mmap(NULL, dsize, PROT_READ | PROT_WRITE, MAP_SHARED, shm_fd, 0);
|
|
|
|
if (shm_addr == MAP_FAILED)
|
|
|
|
{
|
|
|
|
close(shm_fd);
|
|
|
|
shm_unlink(shmfile);
|
|
|
|
shm_fd = -1;
|
|
|
|
goto failed;
|
|
|
|
}
|
|
|
|
shm_size = dsize;
|
|
|
|
return;
|
|
|
|
failed:
|
|
|
|
#endif
|
|
|
|
shm_addr = malloc(dsize);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
shm_free(void)
|
|
|
|
{
|
|
|
|
#ifdef HAVE_SHM_OPEN
|
|
|
|
if (shm_fd >= 0)
|
|
|
|
{
|
|
|
|
munmap(shm_addr, shm_size);
|
|
|
|
close(shm_fd);
|
|
|
|
shm_fd = -1;
|
|
|
|
shm_addr = NULL;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
free(shm_addr);
|
|
|
|
shm_addr = NULL;
|
|
|
|
shm_fd = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------------- globals ------------ */
|
|
|
|
|
|
|
|
/* This makes using the Gimp sources easier */
|
|
|
|
struct _GimpImage * image = &_image;
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------------- code ------------ */
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_seek_pos(int pos)
|
|
|
|
{
|
|
|
|
if (image->cp != pos)
|
|
|
|
{
|
|
|
|
image->cp = pos;
|
|
|
|
gzseek(image->fp, image->cp, SEEK_SET);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
xcf_read_int32(gzFile *fp,
|
|
|
|
DATA32 *data,
|
|
|
|
int count)
|
|
|
|
{
|
|
|
|
int total;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
total = count;
|
|
|
|
if (count > 0)
|
|
|
|
{
|
|
|
|
xcf_read_int8(fp, (DATA8*) data, count * 4);
|
|
|
|
while (count--)
|
|
|
|
{
|
|
|
|
*data = (DATA32) ntohl (*data);
|
|
|
|
data++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return total * 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
xcf_read_int8(gzFile *fp,
|
|
|
|
DATA8 *data,
|
|
|
|
int count)
|
|
|
|
{
|
|
|
|
int total;
|
|
|
|
int bytes;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
total = count;
|
|
|
|
while (count > 0)
|
|
|
|
{
|
|
|
|
bytes = gzread(fp, data, count);
|
|
|
|
if (bytes <= 0) /* something bad happened */
|
|
|
|
break;
|
|
|
|
count -= bytes;
|
|
|
|
data += bytes;
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
xcf_read_string(gzFile *fp,
|
|
|
|
char **data,
|
|
|
|
int count)
|
|
|
|
{
|
|
|
|
DATA32 tmp;
|
|
|
|
int total;
|
|
|
|
int i;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
total = 0;
|
|
|
|
for (i = 0; i < count; i++)
|
|
|
|
{
|
|
|
|
total += xcf_read_int32(fp, &tmp, 1);
|
|
|
|
if (tmp > 0)
|
|
|
|
{
|
|
|
|
data[i] = malloc(sizeof(DATA8) * tmp);
|
|
|
|
if (data[i]) total += xcf_read_int8(fp, (DATA8 *)data[i], tmp);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
data[i] = NULL;
|
|
|
|
}
|
|
|
|
return total;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_prop(PropType *prop_type,
|
|
|
|
DATA32 *prop_size)
|
|
|
|
{
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)prop_type, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)prop_size, 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_image_props(void)
|
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
DATA32 prop_size;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (!xcf_load_prop (&prop_type, &prop_size)) return 0;
|
|
|
|
switch (prop_type)
|
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
{
|
|
|
|
D("Finished reading image properties.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case PROP_COLORMAP:
|
|
|
|
{
|
2011-04-30 08:20:52 -07:00
|
|
|
if (image->file_version == 0)
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
fprintf (stderr,
|
|
|
|
"XCF warning: version 0 of XCF file format\n"
|
|
|
|
"did not save indexed colormaps correctly.\n"
|
|
|
|
"Substituting grayscale map.\n");
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&image->num_cols, 1);
|
|
|
|
image->cmap = malloc(sizeof(DATA8) * image->num_cols * 3);
|
|
|
|
if (!image->cmap) return 0;
|
|
|
|
xcf_seek_pos (image->cp + image->num_cols);
|
2011-04-30 08:20:52 -07:00
|
|
|
for (i = 0; i < image->num_cols; i++)
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
image->cmap[(i * 3) + 0] = i;
|
|
|
|
image->cmap[(i * 3) + 1] = i;
|
|
|
|
image->cmap[(i * 3) + 2] = i;
|
|
|
|
}
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
else
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
D("Loading colormap.\n");
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&image->num_cols, 1);
|
|
|
|
image->cmap = malloc(sizeof(DATA8) * image->num_cols * 3);
|
|
|
|
if (!image->cmap) return 0;
|
|
|
|
image->cp += xcf_read_int8(image->fp, (DATA8 *)image->cmap, image->num_cols * 3);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case PROP_COMPRESSION:
|
|
|
|
{
|
|
|
|
char compression;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp += xcf_read_int8(image->fp, (DATA8 *)&compression, 1);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if ((compression != COMPRESS_NONE) &&
|
|
|
|
(compression != COMPRESS_RLE) &&
|
|
|
|
(compression != COMPRESS_ZLIB) &&
|
|
|
|
(compression != COMPRESS_FRACTAL))
|
|
|
|
{
|
|
|
|
fprintf (stderr, "unknown xcf compression type: %d\n", (int) compression);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Image compression type: %i\n", compression);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->compression = compression;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
/* I threw out all of the following: --cK */
|
|
|
|
case PROP_TATTOO:
|
|
|
|
case PROP_PARASITES:
|
|
|
|
case PROP_UNIT:
|
|
|
|
case PROP_PATHS:
|
|
|
|
case PROP_USER_UNIT:
|
|
|
|
case PROP_GUIDES:
|
|
|
|
case PROP_RESOLUTION:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
DATA8 buf[16];
|
|
|
|
int amount;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Skipping unexpected/unknown image property: %d\n", prop_type);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (prop_size > 0)
|
|
|
|
{
|
|
|
|
amount = (16 < prop_size ? 16 : prop_size);
|
|
|
|
image->cp += xcf_read_int8 (image->fp, buf, amount);
|
|
|
|
prop_size -= (16 < amount ? 16 : amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_load_image(void)
|
|
|
|
{
|
|
|
|
Layer *layer;
|
|
|
|
DATA32 saved_pos;
|
|
|
|
DATA32 offset;
|
|
|
|
int num_successful_elements = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* read the image properties */
|
|
|
|
if (!xcf_load_image_props()) goto hard_error;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
/* read in the offset of the next layer */
|
|
|
|
image->cp += xcf_read_int32(image->fp, &offset, 1);
|
|
|
|
/* if the offset is 0 then we are at the end
|
|
|
|
* of the layer list. */
|
|
|
|
if (offset == 0) break;
|
|
|
|
/* save the current position as it is where the
|
|
|
|
* next layer offset is stored. */
|
|
|
|
saved_pos = image->cp;
|
|
|
|
/* seek to the layer offset */
|
|
|
|
xcf_seek_pos(offset);
|
|
|
|
/* read in the layer */
|
|
|
|
layer = xcf_load_layer();
|
|
|
|
if (!layer) goto error;
|
|
|
|
num_successful_elements++;
|
|
|
|
/* add the layer to the image if it's visible */
|
|
|
|
if (layer->visible) add_layer_to_image(layer);
|
|
|
|
else free_layer(layer);
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset. */
|
|
|
|
xcf_seek_pos (saved_pos);
|
|
|
|
}
|
|
|
|
/* If we were a Gimp we would now load the user-defined channels here ... */
|
|
|
|
/* Flat-o-rama now :) */
|
|
|
|
flatten_image();
|
|
|
|
return;
|
|
|
|
error:
|
|
|
|
if (num_successful_elements == 0) goto hard_error;
|
|
|
|
fprintf(stderr, "XCF: This file is corrupt! I have loaded as much\nof it as I can, but it is incomplete.\n");
|
|
|
|
return;
|
2011-04-30 08:20:52 -07:00
|
|
|
hard_error:
|
2011-04-25 23:28:29 -07:00
|
|
|
fprintf(stderr, "XCF: This file is corrupt! I could not even\nsalvage any partial image data from it.\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_layer_props(Layer *layer)
|
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
DATA32 prop_size;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (!xcf_load_prop(&prop_type, &prop_size)) return 0;
|
|
|
|
switch (prop_type)
|
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
{
|
|
|
|
D("Finished reading layer properties.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case PROP_FLOATING_SELECTION:
|
|
|
|
D("Loading floating selection.\n");
|
|
|
|
image->floating_sel = layer;
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&image->floating_sel_offset, 1);
|
|
|
|
break;
|
|
|
|
case PROP_OPACITY:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->opacity, 1);
|
|
|
|
break;
|
|
|
|
case PROP_VISIBLE:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->visible, 1);
|
|
|
|
break;
|
|
|
|
case PROP_PRESERVE_TRANSPARENCY:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->preserve_trans, 1);
|
|
|
|
break;
|
|
|
|
case PROP_OFFSETS:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->offset_x, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->offset_y, 1);
|
|
|
|
break;
|
|
|
|
case PROP_MODE:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->mode, 1);
|
|
|
|
break;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* I threw out all of the following: --cK */
|
|
|
|
case PROP_LINKED:
|
|
|
|
case PROP_ACTIVE_LAYER:
|
|
|
|
case PROP_TATTOO:
|
|
|
|
case PROP_APPLY_MASK:
|
|
|
|
case PROP_EDIT_MASK:
|
|
|
|
case PROP_SHOW_MASK:
|
|
|
|
case PROP_PARASITES:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
DATA8 buf[16];
|
|
|
|
int amount;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Skipping unexpected/unknown/unneeded channel property: %d\n", prop_type);
|
|
|
|
while (prop_size > 0)
|
|
|
|
{
|
|
|
|
amount = (16 < prop_size ? 16 : prop_size);
|
|
|
|
image->cp += xcf_read_int8 (image->fp, buf, amount);
|
|
|
|
prop_size -= (16 < amount ? 16 : amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static Layer *
|
|
|
|
xcf_load_layer(void)
|
|
|
|
{
|
|
|
|
Layer *layer;
|
|
|
|
Layer *layer_mask;
|
|
|
|
DATA32 hierarchy_offset;
|
|
|
|
DATA32 layer_mask_offset;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int type;
|
|
|
|
char *name;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loading one layer ...\n");
|
|
|
|
/* read in the layer width, height and type */
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&width, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&height, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&type, 1);
|
|
|
|
image->cp += xcf_read_string(image->fp, &name, 1);
|
|
|
|
/* ugly, I know */
|
|
|
|
FREE(name);
|
|
|
|
|
|
|
|
/* create a new layer */
|
|
|
|
layer = new_layer(width, height, type, 255, NORMAL_MODE);
|
|
|
|
if (!layer) return NULL;
|
|
|
|
|
|
|
|
/* read in the layer properties */
|
|
|
|
if (!xcf_load_layer_props(layer)) goto error;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loading opacity: %i \n", layer->opacity);
|
|
|
|
if (!layer->visible) return layer;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* read the hierarchy and layer mask offsets */
|
|
|
|
image->cp += xcf_read_int32(image->fp, &hierarchy_offset, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, &layer_mask_offset, 1);
|
|
|
|
/* read in the hierarchy */
|
|
|
|
xcf_seek_pos(hierarchy_offset);
|
2011-04-30 08:20:52 -07:00
|
|
|
if (!xcf_load_hierarchy(&(layer->tiles), &(layer->num_rows),
|
2011-04-25 23:28:29 -07:00
|
|
|
&(layer->num_cols), &(layer->bpp)))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* read in the layer mask */
|
|
|
|
if (layer_mask_offset != 0)
|
|
|
|
{
|
|
|
|
D("Loading layer mask.\n");
|
|
|
|
xcf_seek_pos(layer_mask_offset);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
layer_mask = xcf_load_channel();
|
|
|
|
if (!layer_mask) goto error;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* set the offsets of the layer_mask */
|
|
|
|
layer_mask->offset_x = layer->offset_x;
|
|
|
|
layer_mask->offset_y = layer->offset_y;
|
|
|
|
layer->mask = layer_mask;
|
|
|
|
}
|
|
|
|
read_tiles_into_data(layer->tiles, layer->num_cols,
|
|
|
|
layer->width, layer->height,
|
|
|
|
layer->bpp, &(layer->data),
|
|
|
|
1);
|
|
|
|
free_tiles(layer->tiles, layer->num_rows * layer->num_cols);
|
|
|
|
layer->tiles = NULL;
|
|
|
|
set_layer_opacity(layer);
|
|
|
|
if (layer->mask) apply_layer_mask(layer);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
return layer;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
error:
|
|
|
|
free_layer(layer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
read_tiles_into_data(Tile *tiles, int num_cols, int width,
|
|
|
|
int height, int bpp, DATA8 **data_p, int use_cmap)
|
|
|
|
{
|
|
|
|
int tile_x, tile_y, x, y, offset_x, offset_y;
|
|
|
|
DATA8 *data;
|
|
|
|
DATA8 *ptr;
|
|
|
|
DATA8 *ptr2;
|
|
|
|
Tile *t;
|
|
|
|
int warned = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (tiles)
|
|
|
|
{
|
|
|
|
if (*data_p) FREE(*data_p);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* Always allocate the data as 4 bytes per pixel */
|
|
|
|
data = (*data_p) = malloc(sizeof(DATA32) * width * height);
|
|
|
|
if (!data) return;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
ptr = data;
|
|
|
|
for (y = 0; y < height; y++)
|
|
|
|
{
|
|
|
|
for (x = 0; x < width; x++)
|
|
|
|
{
|
|
|
|
tile_x = x / TILE_WIDTH;
|
|
|
|
tile_y = y / TILE_HEIGHT;
|
|
|
|
offset_x = x % TILE_WIDTH;
|
|
|
|
offset_y = y % TILE_HEIGHT;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
t = &tiles[(tile_y * num_cols) + tile_x];
|
2011-04-30 08:20:52 -07:00
|
|
|
ptr2 = &(t->data[(offset_y * t->ewidth * bpp) +
|
2011-04-25 23:28:29 -07:00
|
|
|
(offset_x * bpp)]);
|
|
|
|
switch (bpp)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
/* use colormap if the image has one */
|
|
|
|
if (image->cmap && use_cmap)
|
|
|
|
{
|
|
|
|
R_VAL(ptr) = image->cmap[(*(ptr2) * 3)];
|
|
|
|
G_VAL(ptr) = image->cmap[(*(ptr2) * 3) + 1];
|
|
|
|
B_VAL(ptr) = image->cmap[(*(ptr2) * 3) + 2];
|
|
|
|
A_VAL(ptr) = 255;
|
|
|
|
}
|
|
|
|
/* else use colors themselves */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
R_VAL(ptr) = *(ptr2);
|
|
|
|
G_VAL(ptr) = *(ptr2);
|
|
|
|
B_VAL(ptr) = *(ptr2);
|
|
|
|
A_VAL(ptr) = 255;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 2:
|
|
|
|
{
|
|
|
|
/* use colormap if the image has one */
|
|
|
|
if (image->cmap && use_cmap)
|
|
|
|
{
|
|
|
|
R_VAL(ptr) = image->cmap[(*(ptr2) * 3)];
|
|
|
|
G_VAL(ptr) = image->cmap[(*(ptr2) * 3) + 1];
|
|
|
|
B_VAL(ptr) = image->cmap[(*(ptr2) * 3) + 2];
|
|
|
|
A_VAL(ptr) = *(ptr2 + 1);
|
|
|
|
}
|
|
|
|
/* else use colors themselves */
|
|
|
|
else if (warned == 0)
|
|
|
|
{
|
|
|
|
warned++;
|
|
|
|
fprintf (stderr, "There's nothing to see here. 2 bpp without colormap not implemented yet.\n");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 3:
|
|
|
|
{
|
|
|
|
if (image->cmap)
|
|
|
|
{
|
|
|
|
if (warned == 0)
|
|
|
|
{
|
|
|
|
warned++;
|
|
|
|
fprintf (stderr, "There's nothing to see here. 3 bpp with colormap not implemented yet.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
R_VAL(ptr) = *(ptr2);
|
|
|
|
G_VAL(ptr) = *(ptr2 + 1);
|
|
|
|
B_VAL(ptr) = *(ptr2 + 2);
|
|
|
|
A_VAL(ptr) = 255;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
R_VAL(ptr) = *(ptr2);
|
|
|
|
G_VAL(ptr) = *(ptr2 + 1);
|
|
|
|
B_VAL(ptr) = *(ptr2 + 2);
|
|
|
|
A_VAL(ptr) = *(ptr2 + 3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ptr += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static Layer *
|
|
|
|
xcf_load_channel(void)
|
|
|
|
{
|
|
|
|
Layer *layer;
|
|
|
|
DATA32 hierarchy_offset;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
char *name;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loading channel ...\n");
|
|
|
|
/* read in the layer width, height and name */
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&width, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&height, 1);
|
|
|
|
image->cp += xcf_read_string(image->fp, &name, 1);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* Yeah, still ugly :) */
|
|
|
|
FREE(name);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* create a new channel */
|
|
|
|
layer = new_layer(width, height, GRAY, 255, NORMAL_MODE);
|
|
|
|
if (!layer) return NULL;
|
|
|
|
/* read in the channel properties */
|
|
|
|
if (!xcf_load_channel_props(layer)) goto error;
|
|
|
|
/* read the hierarchy and layer mask offsets */
|
|
|
|
image->cp += xcf_read_int32(image->fp, &hierarchy_offset, 1);
|
|
|
|
/* read in the hierarchy */
|
|
|
|
xcf_seek_pos(hierarchy_offset);
|
|
|
|
if (!xcf_load_hierarchy(&(layer->tiles), &(layer->num_rows), &(layer->num_cols), &(layer->bpp)))
|
|
|
|
goto error;
|
|
|
|
read_tiles_into_data(layer->tiles, layer->num_cols, layer->width,
|
|
|
|
layer->height, layer->bpp, &(layer->data), 0);
|
|
|
|
free_tiles(layer->tiles, layer->num_rows * layer->num_cols);
|
|
|
|
layer->tiles = NULL;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Channel loaded successfully.\n");
|
|
|
|
return layer;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
error:
|
|
|
|
free_layer(layer);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_channel_props(Layer *layer)
|
|
|
|
{
|
|
|
|
PropType prop_type;
|
|
|
|
DATA32 prop_size;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (!xcf_load_prop(&prop_type, &prop_size)) return 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
switch (prop_type)
|
|
|
|
{
|
|
|
|
case PROP_END:
|
|
|
|
{
|
|
|
|
D("Finished loading channel props.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
case PROP_OPACITY:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->opacity, 1);
|
|
|
|
break;
|
|
|
|
case PROP_VISIBLE:
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&layer->visible, 1);
|
|
|
|
break;
|
|
|
|
case PROP_ACTIVE_CHANNEL:
|
|
|
|
case PROP_SHOW_MASKED:
|
|
|
|
case PROP_SELECTION:
|
|
|
|
case PROP_COLOR:
|
|
|
|
case PROP_TATTOO:
|
|
|
|
case PROP_PARASITES:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
DATA8 buf[16];
|
|
|
|
int amount;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Skipping unexpected/unknown/unneeded channel property: %d\n", prop_type);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (prop_size > 0)
|
|
|
|
{
|
|
|
|
amount = (16 < prop_size ? 16 : prop_size);
|
|
|
|
image->cp += xcf_read_int8(image->fp, buf, amount);
|
|
|
|
prop_size -= (16 < amount ? 16 : amount);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_hierarchy(Tile **tiles, int *num_rows, int *num_cols, int *bpp)
|
|
|
|
{
|
|
|
|
DATA32 saved_pos;
|
|
|
|
DATA32 offset;
|
|
|
|
DATA32 junk;
|
|
|
|
int width;
|
|
|
|
int height;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&width, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&height, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)bpp, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, &offset, 1); /* top level */
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loading hierarchy: width %i, height %i, bpp %i\n", width, height, *bpp);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* discard offsets for layers below first, if any. */
|
2011-04-30 08:20:52 -07:00
|
|
|
do
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
image->cp += xcf_read_int32(image->fp, &junk, 1);
|
|
|
|
}
|
|
|
|
while (junk != 0);
|
|
|
|
/* save the current position as it is where the
|
|
|
|
* next level offset is stored. */
|
|
|
|
saved_pos = image->cp;
|
|
|
|
/* seek to the level offset */
|
|
|
|
xcf_seek_pos(offset);
|
|
|
|
/* read in the level */
|
|
|
|
if (!xcf_load_level(tiles, width, height, *bpp, num_rows, num_cols))
|
|
|
|
return 0;
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset. */
|
|
|
|
xcf_seek_pos (saved_pos);
|
|
|
|
D("Loaded hierarchy successfully.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
2011-04-30 08:20:52 -07:00
|
|
|
xcf_load_level(Tile **tiles_p, int hierarchy_width, int hierarchy_height,
|
2011-04-25 23:28:29 -07:00
|
|
|
int bpp, int *num_rows, int *num_cols)
|
|
|
|
{
|
|
|
|
DATA32 saved_pos;
|
|
|
|
DATA32 offset, offset2;
|
|
|
|
int ntiles;
|
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int i;
|
|
|
|
int fail;
|
|
|
|
Tile *tiles;
|
|
|
|
Tile *current_tile;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32*) &width, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32*) &height, 1);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if ((width != hierarchy_width) || (height != hierarchy_height)) return 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loading level of size %ix%i.\n", width, height);
|
|
|
|
(*tiles_p) = allocate_tiles(width, height, bpp, num_rows, num_cols);
|
|
|
|
tiles = (*tiles_p);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp += xcf_read_int32(image->fp, &offset, 1);
|
|
|
|
if (offset == 0) return 1;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
ntiles = (*num_rows) * (*num_cols);
|
|
|
|
for (i = 0; i < ntiles; i++)
|
|
|
|
{
|
|
|
|
current_tile = &(tiles[i]);
|
|
|
|
fail = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (offset == 0)
|
|
|
|
{
|
|
|
|
D("Not enough tiles found in level\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* save the current position as it is where the
|
|
|
|
* next tile offset is stored.
|
|
|
|
*/
|
|
|
|
saved_pos = image->cp;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* read in the offset of the next tile so we can calculate the amount
|
|
|
|
of data needed for this tile*/
|
|
|
|
image->cp += xcf_read_int32(image->fp, &offset2, 1);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* if the offset is 0 then we need to read in the maximum possible
|
|
|
|
allowing for negative compression */
|
|
|
|
if (offset2 == 0)
|
|
|
|
offset2 = offset + (TILE_WIDTH * TILE_WIDTH * 4 * 1.5);
|
|
|
|
/* 1.5 is probably more than we need to allow */
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* seek to the tile offset */
|
|
|
|
xcf_seek_pos(offset);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* read in the current_tile */
|
|
|
|
switch (image->compression)
|
|
|
|
{
|
|
|
|
case COMPRESS_NONE:
|
|
|
|
if (!xcf_load_tile(current_tile)) fail = 1;
|
|
|
|
break;
|
|
|
|
case COMPRESS_RLE:
|
|
|
|
if (!xcf_load_tile_rle(current_tile, offset2 - offset)) fail = 1;
|
|
|
|
break;
|
|
|
|
case COMPRESS_ZLIB:
|
|
|
|
fprintf (stderr, "xcf: zlib compression unimplemented\n");
|
|
|
|
fail = 1;
|
|
|
|
break;
|
|
|
|
case COMPRESS_FRACTAL:
|
|
|
|
fprintf (stderr, "xcf: fractal compression unimplemented\n");
|
|
|
|
fail = 1;
|
|
|
|
break;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
|
|
|
if (fail)
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
D("Couldn't load tiles.\n");
|
|
|
|
free_tiles(tiles, (*num_rows) * (*num_cols));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* restore the saved position so we'll be ready to
|
|
|
|
* read the next offset.
|
|
|
|
*/
|
|
|
|
xcf_seek_pos(saved_pos);
|
|
|
|
/* read in the offset of the next tile */
|
|
|
|
image->cp += xcf_read_int32(image->fp, &offset, 1);
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (offset != 0)
|
|
|
|
{
|
|
|
|
D("encountered garbage after reading level: %d\n", offset);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Loaded level successfully.\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_tile(Tile *tile)
|
|
|
|
{
|
2011-04-30 08:20:52 -07:00
|
|
|
image->cp += xcf_read_int8(image->fp, tile->data,
|
2011-04-25 23:28:29 -07:00
|
|
|
tile->ewidth * tile->eheight * tile->bpp);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_load_tile_rle(Tile *tile,
|
|
|
|
int data_length)
|
|
|
|
{
|
|
|
|
DATA8 *data;
|
|
|
|
DATA8 val;
|
|
|
|
int size;
|
|
|
|
int count;
|
|
|
|
int length;
|
|
|
|
int bpp;
|
|
|
|
int i, j;
|
|
|
|
int nmemb_read_successfully;
|
|
|
|
DATA8 *xcfdata, *xcfodata, *xcfdatalimit;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
data = tile->data;
|
|
|
|
bpp = tile->bpp;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/*printf ("Reading encrypted tile %ix%ix%i, data_length %i\n", tile->ewidth, tile->eheight, tile->bpp, data_length);*/
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
xcfdata = xcfodata = malloc(sizeof(DATA8) * data_length);
|
|
|
|
if (!xcfdata) return 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* we have to use fread instead of xcf_read_* because we may be
|
|
|
|
reading past the end of the file here */
|
|
|
|
nmemb_read_successfully = gzread(image->fp, xcfdata, data_length);
|
|
|
|
image->cp += nmemb_read_successfully;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
xcfdatalimit = &xcfodata[nmemb_read_successfully - 1];
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
for (i = 0; i < bpp; i++)
|
|
|
|
{
|
|
|
|
data = (tile->data) + i;
|
|
|
|
size = tile->ewidth * tile->eheight;
|
|
|
|
count = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (size > 0)
|
|
|
|
{
|
|
|
|
if (xcfdata > xcfdatalimit) goto bogus_rle;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
val = *xcfdata++;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
length = val;
|
|
|
|
if (length >= 128)
|
|
|
|
{
|
|
|
|
length = 255 - (length - 1);
|
|
|
|
if (length == 128)
|
|
|
|
{
|
|
|
|
if (xcfdata >= xcfdatalimit) goto bogus_rle;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
length = (*xcfdata << 8) + xcfdata[1];
|
|
|
|
xcfdata += 2;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
count += length;
|
|
|
|
size -= length;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (size < 0) goto bogus_rle;
|
|
|
|
if (&xcfdata[length-1] > xcfdatalimit) goto bogus_rle;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (length-- > 0)
|
|
|
|
{
|
|
|
|
*data = *xcfdata++;
|
|
|
|
data += bpp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
length += 1;
|
|
|
|
if (length == 128)
|
|
|
|
{
|
|
|
|
if (xcfdata >= xcfdatalimit) goto bogus_rle;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
length = (*xcfdata << 8) + xcfdata[1];
|
|
|
|
xcfdata += 2;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
count += length;
|
|
|
|
size -= length;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (size < 0) goto bogus_rle;
|
|
|
|
if (xcfdata > xcfdatalimit) goto bogus_rle;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
val = *xcfdata++;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
for (j = 0; j < length; j++)
|
|
|
|
{
|
|
|
|
*data = val;
|
|
|
|
data += bpp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FREE(xcfodata);
|
|
|
|
return 1;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
bogus_rle:
|
|
|
|
fprintf(stderr, "WHOOOOOP -- bogus rle? Highly unlikely, blame cK for this one :) \n");
|
|
|
|
if (xcfodata) FREE(xcfodata);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Layer *
|
|
|
|
new_layer(int width, int height, GimpImageType type, int opacity, LayerModeEffects mode)
|
|
|
|
{
|
|
|
|
Layer *layer;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
layer = calloc(1, sizeof(Layer));
|
|
|
|
if (!layer)
|
|
|
|
{
|
|
|
|
D("Couldn't allocate layer.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
layer->width = width;
|
|
|
|
layer->height = height;
|
|
|
|
layer->type = type;
|
|
|
|
layer->opacity = opacity;
|
|
|
|
layer->mode = mode;
|
|
|
|
layer->tiles = NULL;
|
|
|
|
layer->next = NULL;
|
|
|
|
layer->mask = NULL;
|
|
|
|
return layer;
|
|
|
|
}
|
|
|
|
|
2011-04-30 08:20:52 -07:00
|
|
|
static void
|
2011-04-25 23:28:29 -07:00
|
|
|
free_layer(Layer *layer)
|
|
|
|
{
|
|
|
|
if (layer)
|
|
|
|
{
|
|
|
|
if (layer->tiles)
|
|
|
|
free_tiles(layer->tiles, layer->num_rows * layer->num_cols);
|
|
|
|
if (layer->mask) free_layer(layer->mask);
|
|
|
|
if (layer->data) FREE(layer->data);
|
|
|
|
FREE(layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-30 08:20:52 -07:00
|
|
|
static Tile *
|
2011-04-25 23:28:29 -07:00
|
|
|
allocate_tiles(int width, int height, int bpp, int* num_rows, int* num_cols)
|
|
|
|
{
|
|
|
|
Tile* tiles;
|
|
|
|
int i, j, k, right_tile, bottom_tile;
|
|
|
|
int tile_width, tile_height;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
(*num_rows) = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
|
|
|
|
(*num_cols) = (width + TILE_WIDTH - 1) / TILE_WIDTH;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
tiles = malloc(sizeof(Tile) * (*num_rows) * (*num_cols));
|
|
|
|
if (!tiles)
|
|
|
|
{
|
|
|
|
D("Couldn't allocate tiles.\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
right_tile = width - (((*num_cols) - 1) * TILE_WIDTH);
|
|
|
|
bottom_tile = height - (((*num_rows) - 1) * TILE_HEIGHT);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
for (i = 0, k = 0; i < (*num_rows); i++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < (*num_cols); j++, k++)
|
|
|
|
{
|
|
|
|
tile_width = ((j == (*num_cols) - 1) ? right_tile : TILE_WIDTH);
|
|
|
|
tile_height = ((i == (*num_rows) - 1) ? bottom_tile : TILE_HEIGHT);
|
|
|
|
init_tile(&(tiles[k]), tile_width, tile_height, bpp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
D("Allocated %ix%i tiles.\n", (*num_cols), (*num_rows));
|
|
|
|
return tiles;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
init_tile(Tile *tile, int width, int height, int bpp)
|
|
|
|
{
|
|
|
|
if (tile)
|
|
|
|
{
|
|
|
|
tile->bpp = bpp;
|
|
|
|
tile->ewidth = width;
|
|
|
|
tile->eheight = height;
|
|
|
|
tile->data = malloc(sizeof(DATA8) * width * height * bpp);
|
|
|
|
if (!tile->data)
|
|
|
|
{
|
|
|
|
D("Couldn't allocate tile.\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-30 08:20:52 -07:00
|
|
|
static void
|
2011-04-25 23:28:29 -07:00
|
|
|
free_tiles(Tile *tiles, int num_tiles)
|
|
|
|
{
|
|
|
|
int i;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
for (i = 0; i < num_tiles; i++)
|
|
|
|
{
|
|
|
|
if (tiles[i].data) FREE(tiles[i].data);
|
|
|
|
}
|
|
|
|
FREE(tiles);
|
|
|
|
}
|
|
|
|
|
2011-04-30 08:20:52 -07:00
|
|
|
static void
|
2011-04-25 23:28:29 -07:00
|
|
|
add_layer_to_image(Layer *layer)
|
|
|
|
{
|
|
|
|
if (layer)
|
|
|
|
{
|
|
|
|
if (image->last_layer)
|
|
|
|
{
|
|
|
|
image->last_layer->next = layer;
|
|
|
|
layer->prev = image->last_layer;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
image->layers = layer;
|
|
|
|
layer->prev = NULL;
|
|
|
|
}
|
|
|
|
layer->next = NULL;
|
|
|
|
image->last_layer = layer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
set_layer_opacity(Layer *layer)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
DATA8* ptr;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (layer)
|
|
|
|
{
|
|
|
|
if (layer->opacity != 255)
|
|
|
|
{
|
|
|
|
for (i = 0, ptr = layer->data; i < layer->width * layer->height; i++, ptr += 4)
|
|
|
|
{
|
|
|
|
*(ptr + 3) = (*(ptr + 3) * layer->opacity) >> 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
apply_layer_mask(Layer *layer)
|
|
|
|
{
|
|
|
|
DATA8* ptr1;
|
|
|
|
DATA8* ptr2;
|
|
|
|
int i, tmp;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
D("Applying layer mask.\n");
|
|
|
|
if (layer)
|
|
|
|
{
|
|
|
|
if (layer->mask)
|
|
|
|
{
|
|
|
|
ptr1 = layer->data;
|
|
|
|
ptr2 = layer->mask->data;
|
|
|
|
for (i = 0; i < layer->width * layer->height; i++)
|
|
|
|
{
|
|
|
|
tmp = (*(ptr1 + 3) * *(ptr2)) / 256;
|
|
|
|
if (tmp > 255) tmp = 255;
|
|
|
|
*(ptr1 + 3) = (DATA8)tmp;
|
|
|
|
ptr1 += 4;
|
|
|
|
ptr2 += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
flatten_image(void)
|
|
|
|
{
|
|
|
|
Layer* l = image->last_layer;
|
|
|
|
Layer* lp;
|
|
|
|
int layer_index;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
shm_alloc(image->width * image->height * sizeof(DATA32));
|
|
|
|
if (!shm_addr) return;
|
|
|
|
image->data = shm_addr;
|
|
|
|
memset(image->data, 0, image->width * image->height * sizeof(DATA32));
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
layer_index = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
while (l)
|
2011-04-30 08:20:52 -07:00
|
|
|
{
|
2011-04-25 23:28:29 -07:00
|
|
|
/* Ok, paste each layer on top of the image, using the mode's merging type.
|
|
|
|
We're moving upward through the layer stack.
|
|
|
|
--cK.
|
|
|
|
*/
|
2011-04-30 08:20:52 -07:00
|
|
|
if ((image->single_layer_index < 0) ||
|
2011-04-25 23:28:29 -07:00
|
|
|
(layer_index == image->single_layer_index))
|
|
|
|
{
|
|
|
|
// FIXME: not all modes are implemented right
|
|
|
|
// xcf's may not render right :)
|
|
|
|
switch (l->mode)
|
|
|
|
{
|
|
|
|
case MULTIPLY_MODE:
|
|
|
|
D("MULTIPLY\n");
|
|
|
|
combine_pixels_mult(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case DIVIDE_MODE:
|
|
|
|
D("DIVIDE\n");
|
|
|
|
combine_pixels_div(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case SCREEN_MODE:
|
|
|
|
D("SCREEN\n");
|
|
|
|
combine_pixels_screen(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case OVERLAY_MODE:
|
|
|
|
D("OVERLAY\n");
|
|
|
|
combine_pixels_overlay(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case DIFFERENCE_MODE:
|
|
|
|
D("DIFF\n");
|
|
|
|
combine_pixels_diff(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case ADDITION_MODE:
|
|
|
|
D("ADD\n");
|
|
|
|
combine_pixels_add(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case SUBTRACT_MODE:
|
|
|
|
D("SUB\n");
|
|
|
|
combine_pixels_sub(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case DARKEN_ONLY_MODE:
|
|
|
|
D("DARKEN\n");
|
|
|
|
combine_pixels_darken(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case LIGHTEN_ONLY_MODE:
|
|
|
|
D("LIGHTEN\n");
|
|
|
|
combine_pixels_lighten(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case HUE_MODE:
|
|
|
|
D("HUE\n");
|
|
|
|
combine_pixels_hue(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case SATURATION_MODE:
|
|
|
|
D("SATURATION\n");
|
|
|
|
combine_pixels_sat(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case VALUE_MODE:
|
|
|
|
D("VALUE\n");
|
|
|
|
combine_pixels_val(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case COLOR_MODE:
|
|
|
|
D("COLOR\n");
|
|
|
|
combine_pixels_col(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
case DISSOLVE_MODE:
|
|
|
|
D("DISSOLVE\n");
|
|
|
|
combine_pixels_diss(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
|
|
|
/* None of the following is actually valid for layer blending, fall through: */
|
|
|
|
case BEHIND_MODE:
|
|
|
|
case REPLACE_MODE:
|
|
|
|
case ERASE_MODE:
|
|
|
|
case ANTI_ERASE_MODE:
|
|
|
|
D("EEEEEK -- this mode shouldn't be here\n");
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
case NORMAL_MODE:
|
|
|
|
D("NORMAL\n");
|
|
|
|
combine_pixels_normal(l->data, l->width, l->height,
|
|
|
|
image->data, image->width, image->height,
|
|
|
|
l->offset_x, l->offset_y);
|
|
|
|
break;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
default:
|
|
|
|
D("Unknown layer mode: %i. Skipping.\n", l->mode);
|
|
|
|
}
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
lp = l->prev;
|
|
|
|
/* free the layer now, since it's not needed anymore */
|
|
|
|
free_layer(l);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
l = lp;
|
|
|
|
layer_index++;
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
/* We've used up all the layers now, so set them to NULL in the image: */
|
|
|
|
image->layers = NULL;
|
|
|
|
image->last_layer = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char
|
|
|
|
xcf_file_init(char *filename)
|
|
|
|
{
|
|
|
|
char success = 1;
|
|
|
|
char id[14];
|
2011-04-26 02:41:27 -07:00
|
|
|
int width;
|
|
|
|
int height;
|
|
|
|
int image_type;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->single_layer_index = -1;
|
|
|
|
image->fd = open(filename, O_RDONLY);
|
|
|
|
if (image->fd < 0) return 0;
|
|
|
|
image->fp = gzdopen(image->fd, "r");
|
|
|
|
if (!image->fp) return 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->filename = filename;
|
|
|
|
image->layers = NULL;
|
|
|
|
image->last_layer = NULL;
|
|
|
|
image->cmap = NULL;
|
|
|
|
image->num_cols = 0;
|
|
|
|
image->data = NULL;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
image->cp += xcf_read_int8(image->fp, (DATA8 *)id, 14);
|
|
|
|
if (strncmp(id, "gimp xcf ", 9))
|
|
|
|
{
|
|
|
|
success = 0;
|
|
|
|
gzclose(image->fp);
|
|
|
|
close(image->fd);
|
|
|
|
}
|
|
|
|
else if (!strcmp(id + 9, "file"))
|
|
|
|
{
|
|
|
|
image->file_version = 0;
|
2011-04-30 08:20:52 -07:00
|
|
|
}
|
|
|
|
else if (id[9] == 'v')
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
|
|
|
image->file_version = atoi(id + 10);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
success = 0;
|
|
|
|
gzclose(image->fp);
|
|
|
|
close(image->fd);
|
|
|
|
}
|
2011-04-26 02:41:27 -07:00
|
|
|
|
|
|
|
if (success)
|
|
|
|
{
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&width, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&height, 1);
|
|
|
|
image->cp += xcf_read_int32(image->fp, (DATA32 *)&image_type, 1);
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-26 02:41:27 -07:00
|
|
|
image->width = width;
|
|
|
|
image->height = height;
|
|
|
|
image->base_type = image_type;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-26 02:41:27 -07:00
|
|
|
D("Loading %ix%i image.\n", width, height);
|
|
|
|
}
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xcf_cleanup(void)
|
|
|
|
{
|
|
|
|
Layer *l, *lp;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
if (image->fp) gzclose(image->fp);
|
|
|
|
if (image->fd >= 0) close(image->fd);
|
|
|
|
for (l = image->last_layer; l; l = lp)
|
|
|
|
{
|
|
|
|
lp = l->prev;
|
|
|
|
free_layer(l);
|
|
|
|
}
|
|
|
|
if (image->cmap) FREE(image->cmap);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
premul_image(void)
|
|
|
|
{
|
|
|
|
DATA32 *p, *end;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
end = (DATA32 *)image->data + (image->width * image->height);
|
|
|
|
for (p = (DATA32 *)image->data; p < end; p++)
|
|
|
|
{
|
|
|
|
unsigned int r, g, b, a;
|
2011-04-30 08:20:52 -07:00
|
|
|
|
2011-04-25 23:28:29 -07:00
|
|
|
a = A_VAL(p);
|
|
|
|
r = (R_VAL(p) * a) / 255;
|
|
|
|
R_VAL(p) = r;
|
|
|
|
g = (G_VAL(p) * a) / 255;
|
|
|
|
G_VAL(p) = g;
|
|
|
|
b = (B_VAL(p) * a) / 255;
|
|
|
|
B_VAL(p) = b;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
char *file;
|
2011-04-26 02:41:27 -07:00
|
|
|
int w, h, i;
|
|
|
|
int head_only = 0;
|
2011-04-25 23:28:29 -07:00
|
|
|
|
|
|
|
if (argc < 2) return -1;
|
2011-04-26 02:41:27 -07:00
|
|
|
// file is ALWAYS first arg, other options come after
|
2011-04-25 23:28:29 -07:00
|
|
|
file = argv[1];
|
2011-04-26 02:41:27 -07:00
|
|
|
for (i = 2; i < argc; i++)
|
|
|
|
{
|
|
|
|
if (!strcmp(argv[i], "-head"))
|
|
|
|
// asked to only load header, not body/data
|
|
|
|
head_only = 1;
|
|
|
|
else if (!strcmp(argv[i], "-key"))
|
|
|
|
{ // not used by xcf loader
|
|
|
|
i++;
|
|
|
|
// const char *key = argv[i];
|
|
|
|
}
|
|
|
|
else if (!strcmp(argv[i], "-opt-scale-down-by"))
|
|
|
|
{ // not used by xcf loader
|
|
|
|
i++;
|
|
|
|
// int scale_down = atoi(argv[i]);
|
|
|
|
}
|
|
|
|
else if (!strcmp(argv[i], "-opt-dpi"))
|
|
|
|
{ // not used by xcf loader
|
|
|
|
i++;
|
|
|
|
// double dpi = ((double)atoi(argv[i])) / 1000.0;
|
|
|
|
}
|
|
|
|
else if (!strcmp(argv[i], "-opt-size"))
|
|
|
|
{ // not used by xcf loader
|
|
|
|
i++;
|
|
|
|
// int size_w = atoi(argv[i]);
|
|
|
|
i++;
|
|
|
|
// int size_h = atoi(argv[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
D("xcf_file_init\n");
|
2011-04-25 23:28:29 -07:00
|
|
|
if (!xcf_file_init(file)) return -1;
|
2011-04-26 02:41:27 -07:00
|
|
|
D("size %i %i\n", image->width, image->height);
|
|
|
|
if (!head_only)
|
|
|
|
{
|
|
|
|
xcf_load_image();
|
|
|
|
premul_image();
|
|
|
|
}
|
2011-04-25 23:28:29 -07:00
|
|
|
w = image->width;
|
|
|
|
h = image->height;
|
|
|
|
printf("size %i %i\n", w, h);
|
|
|
|
printf("alpha 1\n");
|
2011-04-26 02:41:27 -07:00
|
|
|
if (!head_only)
|
2011-04-25 23:28:29 -07:00
|
|
|
{
|
2011-04-26 02:41:27 -07:00
|
|
|
if (shm_fd >= 0) printf("shmfile %s\n", shmfile);
|
|
|
|
else
|
|
|
|
{
|
2011-04-26 02:42:25 -07:00
|
|
|
// could also to "tmpfile %s\n" like shmfile but just
|
|
|
|
// a mmaped tmp file on the system
|
2011-04-26 02:41:27 -07:00
|
|
|
printf("data\n");
|
|
|
|
fwrite(image->data, w * h * sizeof(DATA32), 1, stdout);
|
|
|
|
}
|
|
|
|
shm_free();
|
2011-04-25 23:28:29 -07:00
|
|
|
}
|
2011-04-26 02:41:27 -07:00
|
|
|
else
|
|
|
|
printf("done");
|
2011-04-25 23:28:29 -07:00
|
|
|
xcf_cleanup();
|
|
|
|
return 0;
|
|
|
|
}
|