efl/src/generic/evas/xcf/main.c

1742 lines
52 KiB
C
Raw Normal View History

/*
2011-04-30 08:20:52 -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
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 <Eina.h>
#include "common.h"
#include "shmfile.h"
#include "timeout.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
{
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.
--cK.
*/
struct _Layer
2011-04-30 08:20:52 -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
{
void *file;
char *filename;
long cp;
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 ------------ */
typedef struct _File File;
typedef struct _Chunk Chunk;
#define FBUF 1
#define CHUNK_SIZE (32 * 1024)
struct _Chunk
{
int size;
unsigned char data[CHUNK_SIZE];
};
struct _File
{
int fd;
gzFile fp;
long pos, size;
int chunk_num;
Chunk **chunk;
};
static File *
f_open(const char *file)
{
File *f;
f = calloc(1, sizeof(File));
if (!f) return NULL;
f->fd = open(file, O_RDONLY);
if (f->fd < 0)
{
D("open of %s failed\n", file);
free(f);
return NULL;
}
f->fp = gzdopen(f->fd, "r");
if (!f->fp)
{
D("gzdopen of %i failed\n", f->fd);
close(f->fd);
free(f);
return NULL;
}
return f;
}
static void
f_close(File *f)
{
// FIXME: free chunks
gzclose(f->fp);
free(f);
}
#ifdef FBUF
static void
_f_read_pos(File *f, long pos, long bytes)
{
long i, cnum;
Chunk **cks;
if (f->size > 0) return;
cnum = ((pos + bytes) / CHUNK_SIZE) + 1;
if (f->chunk_num >= cnum) return;
D("FFFF: go up to %li + %li, chunks %li\n", pos, bytes, cnum);
cks = realloc(f->chunk, sizeof(Chunk *) * cnum);
if (!cks) return;
f->chunk = cks;
for (i = f->chunk_num; i < cnum; i++)
{
if (f->size != 0)
{
f->chunk[i] = NULL;
continue;
}
f->chunk[i] = malloc(sizeof(Chunk));
if (f->chunk[i])
{
f->chunk[i]->size = gzread(f->fp, f->chunk[i]->data, CHUNK_SIZE);
D("FFFF: go %i\n", f->chunk[i]->size);
if (f->chunk[i]->size < CHUNK_SIZE)
{
f->size = (i * CHUNK_SIZE) + f->chunk[i]->size;
}
}
}
f->chunk_num = cnum;
}
#endif
static long
f_read(File *f, unsigned char *dest, long bytes)
{
#ifdef FBUF
long done = 0, off = 0;
int c;
unsigned char *p;
_f_read_pos(f, f->pos, bytes);
c = f->pos / CHUNK_SIZE;
off = f->pos - (c * CHUNK_SIZE);
p = dest;
while ((done < bytes) && (c < f->chunk_num))
{
long amount = bytes - done;
if (!f->chunk[c]) break;
if (amount > (f->chunk[c]->size - off))
amount = (f->chunk[c]->size - off);
if (amount < 1) return 0;
memcpy(p, f->chunk[c]->data + off, amount);
p += amount;
off = 0;
done += amount;
c++;
}
f->pos += done;
return done;
#else
long done = gzread(f->fp, dest, bytes);
f->pos += done;
return done;
#endif
}
static void
f_seek(File *f, long pos)
{
#ifdef FBUF
if (f->size > 0)
{
if (pos >= f->size) pos = f->size -1;
}
#endif
if (f->pos == pos) return;
f->pos = pos;
#ifdef FBUF
_f_read_pos(f, f->pos, 1);
#else
gzseek(f->fp, f->pos, SEEK_SET);
#endif
}
/* stuff that was adapted from xcf.c */
static void xcf_seek_pos(int pos);
static int xcf_read_int32(void *fp, DATA32 *data, int count);
static int xcf_read_int8(void *fp, DATA8 *data, int count);
static int xcf_read_string(void *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);
/* ---------------------------------------------------------------------------- globals ------------ */
/* This makes using the Gimp sources easier */
2015-11-10 06:48:09 -08:00
static struct _GimpImage * image = &_image;
/* ------------------------------------------------------------------------------- code ------------ */
static void
xcf_seek_pos(int pos)
{
if (image->cp != pos)
{
image->cp = pos;
f_seek(image->file, image->cp);
}
}
static int
xcf_read_int32(void *fp,
DATA32 *data,
int count)
{
int total;
2011-04-30 08:20:52 -07:00
total = count;
if (count > 0)
{
xcf_read_int8(fp, (DATA8*) data, count * 4);
while (count--)
{
*data = (DATA32)eina_ntohl(*data);
data++;
}
}
return total * 4;
}
static int
xcf_read_int8(void *fp,
DATA8 *data,
int count)
{
int total;
int bytes;
2011-04-30 08:20:52 -07:00
total = count;
while (count > 0)
{
bytes = f_read(fp, data, count);
if (bytes <= 0) /* something bad happened */
break;
count -= bytes;
data += bytes;
}
return total;
}
static int
xcf_read_string(void *fp,
char **data,
int count)
{
DATA32 tmp;
int total;
int i;
2011-04-30 08:20:52 -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->file, (DATA32 *)prop_type, 1);
image->cp += xcf_read_int32(image->file, (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
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)
{
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->file, (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++)
{
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
{
D("Loading colormap.\n");
image->cp += xcf_read_int32(image->file, (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->file, (DATA8 *)image->cmap, image->num_cols * 3);
}
}
break;
case PROP_COMPRESSION:
{
char compression = 0;
2011-04-30 08:20:52 -07:00
image->cp += xcf_read_int8(image->file, (DATA8 *)&compression, 1);
2011-04-30 08:20:52 -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
D("Image compression type: %i\n", compression);
2011-04-30 08:20:52 -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
D("Skipping unexpected/unknown image property: %d\n", prop_type);
2011-04-30 08:20:52 -07:00
while (prop_size > 0)
{
amount = (16 < prop_size ? 16 : prop_size);
image->cp += xcf_read_int8(image->file, buf, amount);
prop_size -= 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
/* read the image properties */
if (!xcf_load_image_props()) goto hard_error;
2011-04-30 08:20:52 -07:00
while (1)
{
/* read in the offset of the next layer */
image->cp += xcf_read_int32(image->file, &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:
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
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->file, (DATA32 *)&image->floating_sel_offset, 1);
break;
case PROP_OPACITY:
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->opacity, 1);
break;
case PROP_VISIBLE:
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->visible, 1);
break;
case PROP_PRESERVE_TRANSPARENCY:
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->preserve_trans, 1);
break;
case PROP_OFFSETS:
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->offset_x, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->offset_y, 1);
break;
case PROP_MODE:
image->cp += xcf_read_int32(image->file, (DATA32 *)&layer->mode, 1);
break;
2011-04-30 08:20:52 -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
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->file, buf, amount);
prop_size -= 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
D("Loading one layer ...\n");
/* read in the layer width, height and type */
image->cp += xcf_read_int32(image->file, (DATA32 *)&width, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&height, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&type, 1);
image->cp += xcf_read_string(image->file, &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
D("Loading opacity: %i \n", layer->opacity);
if (!layer->visible) return layer;
2011-04-30 08:20:52 -07:00
/* read the hierarchy and layer mask offsets */
image->cp += xcf_read_int32(image->file, &hierarchy_offset, 1);
image->cp += xcf_read_int32(image->file, &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),
&(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
layer_mask = xcf_load_channel();
if (!layer_mask) goto error;
2011-04-30 08:20:52 -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
return layer;
2011-04-30 08:20:52 -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
if (tiles)
{
if (*data_p) FREE(*data_p);
2011-04-30 08:20:52 -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
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
t = &tiles[(tile_y * num_cols) + tile_x];
2011-04-30 08:20:52 -07:00
ptr2 = &(t->data[(offset_y * t->ewidth * bpp) +
(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
D("Loading channel ...\n");
/* read in the layer width, height and name */
image->cp += xcf_read_int32(image->file, (DATA32 *)&width, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&height, 1);
image->cp += xcf_read_string(image->file, &name, 1);
2011-04-30 08:20:52 -07:00
/* Yeah, still ugly :) */
FREE(name);
2011-04-30 08:20:52 -07:00
/* create a new channel */
2013-05-15 07:12:03 -07:00
layer = new_layer(width, height, GRAY_GIMAGE, 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->file, &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
D("Channel loaded successfully.\n");
return layer;
2011-04-30 08:20:52 -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
while (1)
{
if (!xcf_load_prop(&prop_type, &prop_size)) return 0;
2011-04-30 08:20:52 -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->file, (DATA32 *)&layer->opacity, 1);
break;
case PROP_VISIBLE:
image->cp += xcf_read_int32(image->file, (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
D("Skipping unexpected/unknown/unneeded channel property: %d\n", prop_type);
2011-04-30 08:20:52 -07:00
while (prop_size > 0)
{
amount = (16 < prop_size ? 16 : prop_size);
image->cp += xcf_read_int8(image->file, buf, amount);
prop_size -= 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
image->cp += xcf_read_int32(image->file, (DATA32 *)&width, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&height, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)bpp, 1);
image->cp += xcf_read_int32(image->file, &offset, 1); /* top level */
2011-04-30 08:20:52 -07:00
D("Loading hierarchy: width %i, height %i, bpp %i\n", width, height, *bpp);
2011-04-30 08:20:52 -07:00
/* discard offsets for layers below first, if any. */
2011-04-30 08:20:52 -07:00
do
{
image->cp += xcf_read_int32(image->file, &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,
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
image->cp += xcf_read_int32(image->file, (DATA32*) &width, 1);
image->cp += xcf_read_int32(image->file, (DATA32*) &height, 1);
2011-04-30 08:20:52 -07:00
if ((width != hierarchy_width) || (height != hierarchy_height)) return 0;
2011-04-30 08:20:52 -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
image->cp += xcf_read_int32(image->file, &offset, 1);
if (offset == 0) return 1;
2011-04-30 08:20:52 -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
if (offset == 0)
{
D("Not enough tiles found in level\n");
return 0;
}
2011-04-30 08:20:52 -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
/* 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->file, &offset2, 1);
2011-04-30 08:20:52 -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
/* seek to the tile offset */
xcf_seek_pos(offset);
2011-04-30 08:20:52 -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)
{
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->file, &offset, 1);
}
2011-04-30 08:20:52 -07:00
if (offset != 0)
{
D("encountered garbage after reading level: %d\n", offset);
return 0;
}
2011-04-30 08:20:52 -07:00
D("Loaded level successfully.\n");
return 1;
}
static char
xcf_load_tile(Tile *tile)
{
image->cp += xcf_read_int8(image->file, tile->data,
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
data = tile->data;
bpp = tile->bpp;
2011-04-30 08:20:52 -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
xcfdata = xcfodata = malloc(sizeof(DATA8) * data_length);
if (!xcfdata) return 0;
2011-04-30 08:20:52 -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 = f_read(image->file, xcfdata, data_length);
image->cp += nmemb_read_successfully;
2011-04-30 08:20:52 -07:00
xcfdatalimit = &xcfodata[nmemb_read_successfully - 1];
2011-04-30 08:20:52 -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
while (size > 0)
{
if (xcfdata > xcfdatalimit) goto bogus_rle;
2011-04-30 08:20:52 -07:00
val = *xcfdata++;
2011-04-30 08:20:52 -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
length = (*xcfdata << 8) + xcfdata[1];
xcfdata += 2;
}
2011-04-30 08:20:52 -07:00
count += length;
size -= length;
2011-04-30 08:20:52 -07:00
if (size < 0) goto bogus_rle;
if (&xcfdata[length-1] > xcfdatalimit) goto bogus_rle;
2011-04-30 08:20:52 -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
length = (*xcfdata << 8) + xcfdata[1];
xcfdata += 2;
}
2011-04-30 08:20:52 -07:00
count += length;
size -= length;
2011-04-30 08:20:52 -07:00
if (size < 0) goto bogus_rle;
if (xcfdata > xcfdatalimit) goto bogus_rle;
2011-04-30 08:20:52 -07:00
val = *xcfdata++;
2011-04-30 08:20:52 -07:00
for (j = 0; j < length; j++)
{
*data = val;
data += bpp;
}
}
}
}
FREE(xcfodata);
return 1;
2011-04-30 08:20:52 -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
layer = calloc(1, sizeof(Layer));
if (!layer)
{
D("Couldn't allocate layer.\n");
return NULL;
}
2011-04-30 08:20:52 -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
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 *
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
(*num_rows) = (height + TILE_HEIGHT - 1) / TILE_HEIGHT;
(*num_cols) = (width + TILE_WIDTH - 1) / TILE_WIDTH;
2011-04-30 08:20:52 -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
right_tile = width - (((*num_cols) - 1) * TILE_WIDTH);
bottom_tile = height - (((*num_rows) - 1) * TILE_HEIGHT);
2011-04-30 08:20:52 -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
free_tiles(Tile *tiles, int num_tiles)
{
int i;
2011-04-30 08:20:52 -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
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
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
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
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
layer_index = 0;
2011-04-30 08:20:52 -07:00
while (l)
2011-04-30 08:20:52 -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) ||
(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");
/* Fall through */
2011-04-30 08:20:52 -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
default:
D("Unknown layer mode: %i. Skipping.\n", l->mode);
}
}
2011-04-30 08:20:52 -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
l = lp;
layer_index++;
}
2011-04-30 08:20:52 -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];
int width;
int height;
int image_type;
2011-04-30 08:20:52 -07:00
image->single_layer_index = -1;
image->file = f_open(filename);
D("image->file = %p\n", image->file);
if (!image->file) return 0;
2011-04-30 08:20:52 -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
image->cp = 0;
2011-04-30 08:20:52 -07:00
image->cp += xcf_read_int8(image->file, (DATA8 *)id, 14);
if (strncmp(id, "gimp xcf ", 9))
{
success = 0;
f_close(image->file);
}
else if (!strcmp(id + 9, "file"))
{
image->file_version = 0;
2011-04-30 08:20:52 -07:00
}
else if (id[9] == 'v')
{
image->file_version = atoi(id + 10);
}
else
{
success = 0;
f_close(image->file);
}
if (success)
{
image->cp += xcf_read_int32(image->file, (DATA32 *)&width, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&height, 1);
image->cp += xcf_read_int32(image->file, (DATA32 *)&image_type, 1);
2011-04-30 08:20:52 -07:00
image->width = width;
image->height = height;
image->base_type = image_type;
2011-04-30 08:20:52 -07:00
D("Loading %ix%i image.\n", width, height);
}
2011-04-30 08:20:52 -07:00
return success;
}
static void
xcf_cleanup(void)
{
Layer *l, *lp;
2011-04-30 08:20:52 -07:00
if (image->file) f_close(image->file);
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
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
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;
int w, h, i;
int head_only = 0;
if (argc < 2) return -1;
// file is ALWAYS first arg, other options come after
file = argv[1];
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]);
}
}
timeout_init(8);
D("xcf_file_init\n");
if (!xcf_file_init(file)) return -1;
D("size %i %i\n", image->width, image->height);
if (!head_only)
{
xcf_load_image();
premul_image();
}
w = image->width;
h = image->height;
printf("size %i %i\n", w, h);
printf("alpha 1\n");
if (!head_only)
{
#ifdef _WIN32
if (shm_fd) printf("shmfile %s\n", shmfile);
#else
if (shm_fd >= 0) printf("shmfile %s\n", shmfile);
#endif
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
printf("data\n");
if (fwrite(image->data, w * h * sizeof(DATA32), 1, stdout) != 1)
{
// nothing much to do, the receiver will simply ignore the
// data as it's too short
D("fwrite failed (%d bytes): %m\n", w * h * sizeof(DATA32));
}
}
shm_free();
}
else
printf("done\n");
xcf_cleanup();
fflush(stdout);
return 0;
}