diff --git a/.gitattributes b/.gitattributes index 790f11abb13c27768dcfaf30d807d3c8332fd21f..98ebc1e4da5261649dbeb185152fa6118383bccc 100644 --- a/.gitattributes +++ b/.gitattributes @@ -191,6 +191,7 @@ testfont binary /src/modules/Image/encodings/tga.c foreign_ident /src/modules/Image/encodings/x.c foreign_ident /src/modules/Image/encodings/xbm.c foreign_ident +/src/modules/Image/encodings/xcf.c foreign_ident /src/modules/Image/encodings/xwd.c foreign_ident /src/modules/Image/font.c foreign_ident /src/modules/Image/illustration.pike foreign_ident diff --git a/lib/modules/_Image_XCF.pmod b/lib/modules/_Image_XCF.pmod new file mode 100644 index 0000000000000000000000000000000000000000..f5f7506a87af8d36250fa35b63bf3e329643a2c2 --- /dev/null +++ b/lib/modules/_Image_XCF.pmod @@ -0,0 +1,387 @@ +inherit Image._XCF; + +class PathPoint +{ + int type; + float x; + float y; +} + +class Path +{ + string name; + int ptype; + int tattoo; + int closed; + int state; + int locked; + array (PathPoint) points = ({}); +} + +class Guide +{ + int pos; + int vertical; + void create(string data) + { + sscanf(data, "%4c%c", pos,vertical); + } +} + +class Parasite +{ + string name; + int flags; + string data; + + void create( string _n, int _f, string _d ) + { + name = _n; + data = _d; + flags = _f; + } +} + +array(Parasite) decode_parasites( string data ) +{ + array res = ({}); + while(strlen(data)) + { + int slen, flags; + string value, name; + sscanf(data, "%4c", slen); + name = data[..slen-2]; + data = data[slen..]; + sscanf(data, "%4c%4c", flags, slen); + res += ({ Parasite( name,flags,data[8..slen+8-1] ) }); + data = data[slen+8..]; + } + return res; +} + +#define FLAG(X,Y) case PROP_##X: sscanf(p->data, "%4c", flags->Y); break; +#define INT(X,Y) case PROP_##X: sscanf(p->data, "%4c", Y); break; + +class Hierarchy +{ + Image.image img; + Image.image alpha; + int width; + int height; + int bpp; + + void create( int x, int y, int bp, array tiles, int compression, + Image.colortable cmap) + { + width = x; + height = y; + bpp = bp; + img = Image.image( x, y, 0,0,0 ); + alpha = Image.image( x, y, 255,255,255 ); + switch(compression) + { + case COMPRESS_NONE: + case COMPRESS_RLE: + _decode_tiles( img,alpha,tiles,compression,bpp,cmap ); + break; + default: + error("Image tile compression type not supported\n"); + } + } +} + +int iid; +Hierarchy decode_image_data( mapping what, object i ) +{ + Hierarchy h = + Hierarchy( what->width, what->height, what->bpp, + what->tiles, i->compression, i->colormap ); + + +#if 1 + object bg = Image.image( what->width, what->height )->test(); + bg = bg->paste_mask( h->img, h->alpha ); + rm("/tmp/xcftest_"+iid); + Stdio.write_file( "/tmp/xcftest_"+iid++, Image.PNM.encode( bg )); +#endif + return h; +} + +class Channel +{ + string name; + int width; + int height; + int opacity; + int r, g, b; + int tattoo; + Hierarchy image_data; + object parent; + mapping flags = ([]); + array (Parasite) parasites; + + void decode_properties( array properties ) + { + foreach(properties, mapping p) + { + switch(p->type) + { + case PROP_ACTIVE_CHANNEL: + parent->active_channel = this_object(); + break; + case PROP_SELECTION: + parent->selection = this_object(); + break; + INT(OPACITY,opacity); + FLAG(VISIBLE,visible); + FLAG(LINKED,linked); + FLAG(PRESERVE_TRANSPARENCY,preserve_transparency); + FLAG(EDIT_MASK,edit_mask); + FLAG(SHOW_MASKED,show_masked); + INT(TATTOO,tattoo); + case PROP_COLOR: + sscanf( p->data, "%c%c%c", r, g, b); + break; + + case PROP_PARASITES: + parasites = decode_parasites( p->data ); + break; + } + } + } + + + void create( mapping d, object p ) + { + + parent = p; + name = d->name; + width = d->width; + height = d->height; + image_data = decode_image_data( d->image_data, parent ); + if(d->properties) decode_properties( d->properties ); + } +} + + +class LayerMask +{ + inherit Channel; +} + +class Layer +{ + string name; + int opacity; + int type; + int mode; + int tattoo; + mapping flags = ([]); + int width, height; + int xoffset, yoffset; + array (Parasite) parasites; + LayerMask mask; + Hierarchy image; + + object parent; + + void decode_properties( array properties ) + { + foreach( properties, mapping p) + { + switch(p->type) + { + case PROP_ACTIVE_LAYER: + parent->active_layer = this_object(); + break; + case PROP_SELECTION: + parent->selection = this_object(); + break; + INT(OPACITY,opacity); + FLAG(VISIBLE,visible); + FLAG(SHOW_MASKED,show_masked); + INT(TATTOO,tattoo); + case PROP_PARASITES: + parasites = decode_parasites( p->data ); + break; + } + } + } + + void create( mapping data, object pa ) + { + parent = pa; + name = data->name; + type = data->type; + width = data->width; + height = data->height; + decode_properties( data->properties ); + image = decode_image_data( data->image_data, pa ); + if(data->mask) + mask = LayerMask( data->mask, pa ); + } +} + + + +class GimpImage +{ + int width; + int height; + int compression; + int type; + int tattoo_state; + float xres = 72.0; + float yres = 72.0; + int res_unit; + Image.colortable colormap; + Image.colortable meta_colormap; + array(Layer) layers = ({}); + array(Channel) channels = ({}); + array(Guide) guides = ({}); + array(Parasite) parasites = ({}); + array(Path) paths = ({}); + + Layer active_layer; + Channel active_channel; + Channel selection; + + + static string read_point_bz1( string data, Path path ) + { + object p = PathPoint( ); + int x, y; + sscanf(data, "%4c%4c%4c%s", p->type, x, y); + p->x = (float)x; + p->y = (float)y; + return data; + } + + static string read_point_bz2( string data, Path path ) + { + object p = PathPoint( ); + sscanf(data, "%4c%4F%4F%s", p->type, p->x, p->y); + return data; + } + + static string decode_one_path( string data, Path path ) + { + int nlen, version, num_points; + sscanf(data, "%4c", nlen ); + path->name = data[..nlen-2]; + data = data[nlen..]; + sscanf(data, "%4c%4c%4c%4c%4c", + path->locked, path->state, path->closed, num_points, version); + switch(version) + { + case 1: + while(num_points--) + data = read_point_bz1( data, path ); + break; + case 2: + sscanf(data, "%4c%s", path->ptype, data ); + while(num_points--) + data = read_point_bz2( data, path ); + break; + case 3: + sscanf(data, "%4%4cc%s", path->ptype, path->tattoo, data ); + while(num_points--) + data = read_point_bz2( data, path ); + break; + default: + data =""; + } + return data; + } + + array(Path) decode_paths( string data ) + { + int last_selected_row; + int num_paths; + array res = ({}); + sscanf( data, "%4c%4c%s", last_selected_row, num_paths, data ); + while(num_paths--) + { + Path path = Path(); + data = decode_one_path( data, path ); + res += ({ path }); + } + return res; + } + + + + void decode_properties(array props) + { + foreach( props, mapping p) + { + switch(p->type) + { + case PROP_COLORMAP: + if(type == INDEXED) + meta_colormap = colormap = Image.colortable( p->data ); + else + meta_colormap = Image.colortable( p->data ); + break; + case PROP_COMPRESSION: compression = p->data[0]; break; + case PROP_GUIDES: + guides = Array.map(p->data/5,Guide); + break; + case PROP_RESOLUTION: + sscanf( p->data, "%4f%4f", xres,yres); + if (xres < 1e-5 || xres> 1e+5 || yres<1e-5 || yres>1e+5) + xres = yres = 72.0; + break; + case PROP_TATTOO: + sscanf(p->data, "%4c", tattoo_state ); + break; + case PROP_PARASITES: + parasites = decode_parasites( p->data ); + break; + case PROP_UNIT: + sscanf(p->data, "%4c", res_unit ); + break; + case PROP_PATHS: + paths = decode_paths( p->data ); + break; + case PROP_USER_UNIT: + /* NYI */ + break; + } + } + } + + void create( mapping data ) + { + type = data->type; + decode_properties( data->properties ); + width = data->width; + height = data->height; + foreach(data->layers, mapping l ) + layers += ({ Layer( l, this_object() ) }); + foreach(data->channels, mapping c ) + channels += ({ Channel( c, this_object() ) }); + } +} + + + +GimpImage __decode( string|mapping what ) +{ + if(stringp(what)) + what = ___decode( what ); + return GimpImage(what); +} + + +mapping _decode( string|mapping what ) +{ + GimpImage data = __decode( what ); + /* This is rather non-trivial.. */ +} + + +object decode( string what ) +{ + return _decode( what )->image; +} diff --git a/src/modules/Image/encodings/xcf.c b/src/modules/Image/encodings/xcf.c new file mode 100644 index 0000000000000000000000000000000000000000..d601c3e5c2fc3185c9c2e6730dd8202b534cca46 --- /dev/null +++ b/src/modules/Image/encodings/xcf.c @@ -0,0 +1,1257 @@ +#include "global.h" +RCSID("$Id: xcf.c,v 1.1 1999/04/11 05:38:22 per Exp $"); + +#include "config.h" + +#include "pike_macros.h" +#include "object.h" +#include "constants.h" +#include "module_support.h" +#include "interpret.h" +#include "object.h" +#include "svalue.h" +#include "threads.h" +#include "array.h" +#include "interpret.h" +#include "svalue.h" +#include "mapping.h" +#include "error.h" +#include "stralloc.h" +#include "builtin_functions.h" +#include "operators.h" +#include "dynamic_buffer.h" + +#include "image.h" +#include "colortable.h" + +extern struct program *image_colortable_program; +extern struct program *image_program; + +/* +**! module Image +**! submodule XCF +**! +*/ + +#define STRING(X) static struct pike_string *s_##X; +#include "xcf_constant_strings.h" +#undef STRING + + +struct buffer +{ + unsigned int len; + unsigned char *str; +}; + + +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 +} property_type; + + +typedef enum +{ + RGB = 0, + GRAY = 1, + INDEXED = 2, +} image_type; + +typedef enum +{ + Red, + Green, + Blue, + Gray, + Indexed, + Auxillary +} channel_type; + +typedef enum +{ + RGB_GIMAGE, + RGBA_GIMAGE, + GRAY_GIMAGE, + GRAYA_GIMAGE, + INDEXED_GIMAGE, + INDEXEDA_GIMAGE +} layer_type; + +typedef enum +{ + COMPRESS_NONE = 0, + COMPRESS_RLE = 1, + COMPRESS_ZLIB = 2, + COMPRESS_FRACTAL = 3, +} compression_type; + +struct property +{ + int type; + struct buffer data; + struct property *next; +}; + +static unsigned int read_uint( struct buffer *from ) +{ + unsigned int res; + if(from->len < 4) + error("Not enough space for 4 bytes (uint32)\n"); + res = from->str[0]<<24|from->str[1]<<16|from->str[2]<<8|from->str[3]; + from->str += 4; + from->len -= 4; + return res; +} + +static int read_int( struct buffer *from ) +{ + return (int)read_uint( from ); +} + +static char *read_data( struct buffer * from, unsigned int len ) +{ + char *res; + if( from->len < len ) + error("Not enough space for %ud bytes\n", len); + res = from->str; + from->str += len; + from->len -= len; + return res; +} + +static struct buffer read_string( struct buffer *data ) +{ + struct buffer res; + res.len = read_int( data ); + res.str = read_data( data, res.len ); + if(res.len > 0) res.len--; /* len includes ending \0 */ + if(!res.str) + error("String read failed\n"); + return res; +} + +static struct property read_property( struct buffer * data ) +{ + int i; + struct property res; + res.type = read_uint( data ); + if(res.type == PROP_COLORMAP) + { + unsigned int foo; + read_uint(data); /* bogus 'len'... */ + foo = read_uint( data ); + res.data.len = foo*3; + res.data.str = read_data( data,foo*3 ); + } else { + res.data.len = read_uint( data ); + res.data.str = read_data( data,res.data.len ); + } + res.next = NULL; + return res; +} + + +struct tile +{ + struct tile *next; + struct buffer data; +}; + +struct level +{ + unsigned int width; + unsigned int height; + + struct tile *first_tile; +}; + +struct hierarchy +{ + unsigned int width; + unsigned int height; + unsigned int bpp; + struct level level; +}; + + +struct channel +{ + struct channel *next; + unsigned int width; + unsigned int height; + struct buffer name; + struct hierarchy image_data; + struct property *first_property; +}; + +struct layer_mask +{ + unsigned int width; + unsigned int height; + struct buffer name; + struct hierarchy image_data; + struct property *first_property; +}; + +struct layer +{ + struct layer *next; + unsigned int width; + unsigned int height; + int type; + struct buffer name; + struct hierarchy image_data; + struct property *first_property; + struct layer_mask *mask; +}; + +struct gimp_image +{ + unsigned int width; + unsigned int height; + int type; + struct property *first_property; + struct layer *first_layer; /* OBS: In reverse order! */ + struct channel *first_channel; /* OBS: In reverse order! */ +}; + + + +static void free_level( struct level *l ) +{ + struct tile *t; + while( (t = l->first_tile) ) + { + struct tile *n; + n = t->next; + free(t); + l->first_tile = n; + } +} + +static void free_layer_mask( struct layer_mask *l ) +{ + struct property *p; + + while( (p = l->first_property) ) + { + struct property *n; + n = p->next; + free(p); + l->first_property = n; + } + free_level( &l->image_data.level ); +} + + +static void free_layer( struct layer *l ) +{ + struct property *p; + + while( (p = l->first_property) ) + { + struct property *n; + n = p->next; + free(p); + l->first_property = n; + } + if(l->mask) + { + free_layer_mask( l->mask ); + free( l->mask ); + } + free_level( &l->image_data.level ); +} + +static void free_channel( struct channel *l ) +{ + struct property *p; + + while( (p = l->first_property) ) + { + struct property *n; + n = p->next; + free(p); + l->first_property = n; + } + free_level( &l->image_data.level ); +} + + +static void free_image( struct gimp_image *i ) +{ + struct property *p; + struct layer *l; + while( (p = i->first_property) ) + { + struct property *n; + n = p->next; + free(p); + i->first_property = n; + } + while( (l = i->first_layer) ) + { + struct layer *n; + n = l->next; + free_layer( l ); + free( l ); + i->first_layer = n; + } +} + +static struct level read_level( struct buffer *buff, + struct buffer *initial ) +{ + struct level res; + ONERROR err; + int offset; + struct tile *last_tile = NULL; + int all_tiles_eq = 0; + MEMSET(&res, 0, sizeof(res)); + res.width = read_uint( buff ); + res.height = read_uint( buff ); + + SET_ONERROR( err, free_level, &res ); + offset = read_uint( buff ); + while(offset) + { + struct buffer ob = *initial; + int offset2 = read_uint( buff ); + struct tile *tile = (struct tile *)xalloc(sizeof(struct tile)); + read_data( &ob, offset ); + if(last_tile) + last_tile->next = tile; + last_tile = tile; + if(!res.first_tile) + { + res.first_tile = tile; + if(offset2) + all_tiles_eq = offset2-offset; + } + if(all_tiles_eq && offset2 && offset2-offset != all_tiles_eq) + all_tiles_eq = 0; + if(all_tiles_eq) + ob.len = all_tiles_eq; + else if(offset2) + ob.len = offset2-offset; + tile->data = ob; + tile->next = NULL; +/* fprintf(stderr, "tile, o=%d; o2=%d; l=%d\n", offset, offset2,ob.len); */ + offset = offset2; + } + UNSET_ONERROR( err ); + return res; +} + +static struct hierarchy read_hierarchy( struct buffer *buff, + struct buffer *initial ) +{ + struct hierarchy res; + unsigned int offset; + struct buffer ob; + + MEMSET(&res, 0, sizeof(res)); + res.width = read_uint( buff ); + res.height = read_uint( buff ); + res.bpp = read_uint( buff ); + offset = read_uint( buff ); + ob = *initial; + read_data( &ob, offset ); + res.level = read_level( &ob, initial ); + return res; +} + +static struct layer_mask read_layer_mask( struct buffer *buff, + struct buffer *initial ) +{ + struct layer_mask res; + ONERROR err; + int offset; + struct property tmp; + struct buffer ob; + + MEMSET(&res, 0, sizeof(res)); + res.width = read_uint( buff ); + res.height = read_uint( buff ); + res.name = read_string( buff ); + + SET_ONERROR( err, free_layer_mask, &res ); + do + { + tmp = read_property( buff ); + if(tmp.type) + { + struct property *s = (struct property *)xalloc(sizeof(struct property)); + *s = tmp; + s->next = res.first_property; + res.first_property = s; + } + } while(tmp.type); + offset = read_uint( buff ); + + if(offset) + { + struct buffer ob = *initial; + read_data( &ob, offset ); + res.image_data = read_hierarchy( &ob, initial ); + } + UNSET_ONERROR( err ); + return res; +} + +static struct channel read_channel( struct buffer *buff, + struct buffer *initial ) +{ + struct channel res; + ONERROR err; + int offset; + struct property tmp; + struct buffer ob; + + MEMSET(&res, 0, sizeof(res)); + res.width = read_uint( buff ); + res.height = read_uint( buff ); + res.name = read_string( buff ); + + SET_ONERROR( err, free_channel, &res ); + do + { + tmp = read_property( buff ); + if(tmp.type) + { + struct property *s =(struct property *)xalloc( sizeof(struct property )); + *s = tmp; + s->next = res.first_property; + res.first_property = s; + } + } while( tmp.type ); + offset = read_uint( buff ); + + if(offset) + { + struct buffer ob = *initial; + read_data( &ob, offset ); + res.image_data = read_hierarchy( &ob, initial ); + } + UNSET_ONERROR( err ); + return res; +} + +static struct layer read_layer( struct buffer *buff, struct buffer *initial ) +{ + struct layer res; + struct property tmp; + int lm_offset; + int h_offset; + ONERROR err; + + MEMSET(&res, 0, sizeof(res)); + SET_ONERROR( err, free_layer, &res ); + res.width = read_uint( buff ); + res.height = read_uint( buff ); + res.type = read_int( buff ); + res.name = read_string( buff ); + + + do + { + tmp = read_property( buff ); + if(tmp.type) + { + struct property *s=(struct property *)xalloc( sizeof(struct property )); + *s = tmp; + s->next = res.first_property; + res.first_property = s; + } + } while( tmp.type ); + + h_offset = read_int( buff ); + lm_offset = read_int( buff ); + + if(lm_offset) + { + struct buffer loffset = *initial; + struct layer_mask *m=(struct layer_mask *)xalloc(sizeof(struct layer_mask)); + res.mask = m; + read_data( &loffset, lm_offset ); + MEMSET(m, 0, sizeof( struct layer_mask )); + *m = read_layer_mask( &loffset, initial ); + } + + if(h_offset) + { + struct buffer loffset = *initial; + read_data( &loffset, h_offset ); + res.image_data = read_hierarchy( &loffset, initial ); + } + + UNSET_ONERROR( err ); + return res; +} + + +static struct gimp_image read_image( struct buffer * data ) +{ + struct gimp_image res; + struct property tmp; + struct buffer initial; + unsigned int offset; + ONERROR err; + + MEMSET(&res, 0, sizeof(res)); + initial = *data; + if(data->len < 34) + error("This is not an xcf file (to little data)\n"); + if(!(data->str[0] == 'g' && + data->str[1] == 'i' && + data->str[2] == 'm' && + data->str[3] == 'p' && + data->str[4] == ' ')) + { + if(strlen(data->str) == 13) + error("This is not an xcf file (%s)\n", data->str); + else + error("This is not an xcf file\n"); + } + data->str+=14; data->len-=14; + + res.width = read_uint( data ); + res.height = read_uint( data ); + res.type = read_int( data ); + + SET_ONERROR( err, free_image, &res ); + + do + { + tmp = read_property( data ); + if(tmp.type) + { + struct property *s= (struct property *)xalloc( sizeof(struct property )); + *s = tmp; + s->next = res.first_property; + res.first_property = s; + } + } while( tmp.type ); + + while( (offset = read_uint( data )) ) + { + struct buffer layer_data = initial; + struct layer tmp; + read_data( &layer_data, offset ); + tmp = read_layer( &layer_data, &initial ); + if( tmp.width && tmp.height ) + { + struct layer *s = (struct layer *)xalloc( sizeof(struct layer)); + *s = tmp; + s->next = res.first_layer; + res.first_layer = s; + } + } + + while( (offset = read_uint( data )) ) + { + struct buffer channel_data = initial; + struct channel tmp; + read_data( &channel_data, offset ); + tmp = read_channel( &channel_data, &initial ); + if( tmp.width && tmp.height ) + { + struct channel *s = (struct channel *)xalloc( sizeof(struct channel)); + *s = tmp; + s->next = res.first_channel; + res.first_channel = s; + } + } + UNSET_ONERROR( err ); + return res; +} + +static void push_buffer( struct buffer *b ) +{ + push_string( make_shared_binary_string( b->str, b->len ) ); +} + +static void push_properties( struct property *p ) +{ + struct svalue *osp = sp; + while(p) + { + ref_push_string( s_type ); push_int( p->type ); + ref_push_string( s_data ); push_buffer( &p->data ); + f_aggregate_mapping( 4 ); + p = p->next; + } + f_aggregate( sp-osp ); +} + +static void push_tile( struct tile *t ) +{ + push_buffer( &t->data ); +} + +static void push_hierarchy( struct hierarchy * h ) +{ + struct tile *t = h->level.first_tile; + struct svalue *osp = sp, *tsp; + if(h->level.width != h->width || + h->level.height != h->height) + error("Illegal hierarchy level sizes!\n"); + + ref_push_string( s_width ); push_int( h->width ); + ref_push_string( s_height ); push_int( h->height ); + ref_push_string( s_bpp ); push_int( h->bpp ); + + ref_push_string( s_tiles ); + tsp = sp; + while(t) + { + push_tile( t ); + t=t->next; + } + f_aggregate( sp-tsp ); + f_aggregate_mapping( sp-osp ); +} + +static void push_layer_mask(struct layer_mask *i) +{ + struct svalue *osp = sp; + ref_push_string( s_width ); push_int( i->width ); + ref_push_string( s_height ); push_int( i->height ); + ref_push_string( s_properties ); + push_properties( i->first_property ); + ref_push_string( s_name ); push_buffer( &i->name ); + ref_push_string( s_image_data ); + push_hierarchy( &i->image_data ); + f_aggregate_mapping( sp-osp ); +} + +static void push_channel(struct channel *i) +{ + struct svalue *osp = sp; + ref_push_string( s_width ); push_int( i->width ); + ref_push_string( s_height ); push_int( i->height ); + ref_push_string( s_properties ); + push_properties( i->first_property ); + ref_push_string( s_name ); push_buffer( &i->name ); + ref_push_string( s_image_data ); + push_hierarchy( &i->image_data ); + f_aggregate_mapping( sp-osp ); +} + +static void push_layer(struct layer *i) +{ + struct svalue *osp = sp; + ref_push_string( s_width ); push_int( i->width ); + ref_push_string( s_height ); push_int( i->height ); + ref_push_string( s_type ); push_int( i->type ); + ref_push_string( s_properties ); + push_properties( i->first_property ); + ref_push_string( s_name ); push_buffer( &i->name ); + ref_push_string( s_image_data ); + push_hierarchy( &i->image_data ); + if( i->mask ) + { + ref_push_string( s_mask ); + push_layer_mask( i->mask ); + } + f_aggregate_mapping( sp-osp ); +} + + +static void push_image( struct gimp_image *i ) +{ + struct layer *l; + struct channel *c; + int nitems = 0; + struct svalue *osp = sp; + ref_push_string( s_width ); push_int( i->width ); + ref_push_string( s_height ); push_int( i->height ); + ref_push_string( s_type ); push_int( i->type ); + ref_push_string( s_properties ); push_properties( i->first_property ); + + ref_push_string( s_layers ); + l = i->first_layer; + while(l) + { + nitems++; + push_layer( l ); + l = l->next; + } + f_aggregate( nitems ); + f_reverse(1); + + ref_push_string( s_channels ); + nitems=0; + c = i->first_channel; + while(c) + { + nitems++; + push_channel( c ); + c = c->next; + } + f_aggregate( nitems ); + f_reverse(1); + f_aggregate_mapping( sp-osp ); +} + + +static void image_xcf____decode( INT32 args ) +{ + struct pike_string *s; + struct buffer b; + struct gimp_image res; + ONERROR err; + get_all_args( "___decode", args, "%S", &s ); + if(args > 1) + error("Too many arguments to Image.XCF.___decode()\n"); + + b.str = s->str; b.len = s->len; + res = read_image( &b ); + SET_ONERROR( err, free_image, &res ); + push_image( &res ); + UNSET_ONERROR( err ); + free_image( &res ); + stack_swap(); + pop_stack(); +} + + +#define TILE_WIDTH 64 +#define TILE_HEIGHT 64 + +unsigned char read_char( struct buffer *from ) +{ + unsigned char res = 0; + if(from->len) + { + res = from->str[0]; + from->str++; + from->len--; + } + return res; +} + + +void image_xcf_f__rle_decode( INT32 args ) +{ + struct pike_string *t; + struct buffer s; + struct buffer od, d; + int bpp, xsize, ysize, i; + get_all_args( "_rle_decode", args, "%S%d%d%d", &t, &bpp, &xsize, &ysize); + + s.len = t->len; + s.str = t->str; + od.len = xsize*ysize*bpp; + od.str = xalloc( xsize*ysize*bpp ); /* Max and only size, really */ + d = od; + for(i=0; i<bpp; i++) + { + int nelems = xsize*ysize; + int length; + while(nelems) + { + unsigned char val = read_char( &s ); + if(!s.len) + { + break; /* Hm. This is actually rather fatal */ + } + length = val; + if( length >= 128 ) + { + length = 255-(length-1); + if (length == 128) + length = (read_char( &s )<<8) + read_char( &s ); + nelems -= length; + while(length--) + { + if(d.len < 1) + break; + d.str[0] = read_char( &s ); + d.str++; + d.len--; + } + } else { + length++; + if(length == 128) + length = (read_char( &s )<<8) + read_char( &s ); + nelems -= length; + val = read_char( &s ); + while(length--) + { + if(d.len < 1) + break; + d.str[0] = val; + d.str++; + d.len--; + } + } + } + } + + pop_n_elems(args); + /* fprintf(stderr, "%d bytes of source data used out of %d bytes\n" */ + /* "%d bytes decoded data generated\n", */ + /* t->len-s.len,t->len,od.len); */ + push_string(make_shared_binary_string(od.str,od.len)); + free(od.str); +} + + +/* +**! method object decode(string data) +**! Decodes a XCF image to a single image object. +**! +**! note +**! Throws upon error in data, you will loose quite a lot of +**! information by doing this. See Image.XCF._decode and Image.XCF.__decode +*/ + + +/* +**! method mapping _decode(string|object data) +**! Decodes a XCF image to a mapping, with at least an +**! 'image' and an 'alpha' object. Data is either a XCF image, or +**! a XCF.GimpImage object structure (as received from __decode) +**! +**! note +**! Throws upon error in data. For more information, see Image.XCF.__decode +*/ + +/* +**! method object __decode(string|mapping data) +**! Decodes a XCF image to a Image.XCF.GimpImage object. +**! +**! <pre>Structure reference +**! +**! class GimpImage +**! { +**! int width; +**! int height; +**! int compression; +**! int type; +**! int tattoo_state; +**! float xres = 72.0; +**! float yres = 72.0; +**! int res_unit; +**! Image.colortable colormap; +**! Image.colortable meta_colormap; +**! array(Layer) layers = ({}); +**! array(Channel) channels = ({}); +**! array(Guide) guides = ({}); +**! array(Parasite) parasites = ({}); +**! array(Path) paths = ({}); +**! +**! Layer active_layer; +**! Channel active_channel; +**! Channel selection; +**! } +**! +**! class Layer +**! { +**! string name; +**! int opacity; +**! int type; +**! int mode; +**! int tattoo; +**! mapping flags = ([]); +**! int width, height; +**! int xoffset, yoffset; +**! array (Parasite) parasites; +**! LayerMask mask; +**! Hierarchy image; +**! } +**! +**! class Channel +**! { +**! string name; +**! int width; +**! int height; +**! int opacity; +**! int r, g, b; +**! int tattoo; +**! Hierarchy image_data; +**! object parent; +**! mapping flags = ([]); +**! array (Parasite) parasites; +**! } +**! +**! class Hierarchy +**! { +**! Image.image img; +**! Image.image alpha; +**! int width; +**! int height; +**! int bpp; +**! } +**! +**! class Parasite +**! { +**! string name; +**! int flags; +**! string data; +**! } +**! +**! +**! class Guide +**! { +**! int pos; +**! int vertical; +**! } +**! +**! class Path +**! { +**! string name; +**! int ptype; +**! int tattoo; +**! int closed; +**! int state; +**! int locked; +**! array (PathPoint) points = ({}); +**! } +**! +**! class PathPoint +**! { +**! int type; +**! float x; +**! float y; +**! } +**! +**! </pre> +*/ + +/* +**! method object ___decode(string|mapping data) +**! Decodes a XCF image to a mapping. +**! +**! <pre>Structure reference +**! +**! ([ +**! "width":int, +**! "height":int, +**! "type":int, +**! "properties":({ +**! ([ +**! "type":int, +**! "data":string, +**! ]), +**! ... +**! }), +**! "layers":({ +**! ([ +**! "name":string, +**! "width":int, +**! "height":int, +**! "type":type, +**! "properties":({ +**! ([ +**! "type":int, +**! "data":string, +**! ]), +**! ... +**! }), +**! "mask":0 || ([ +**! "name":string, +**! "width":int, +**! "height":int, +**! "properties":({ +**! ([ +**! "type":int, +**! "data":string, +**! ]), +**! ... +**! }), +**! "image_data":([ +**! "bpp":int, +**! "width":int, +**! "height":int, +**! "tiles":({ +**! string, +**! ... +**! }), +**! ]), +**! ]), +**! "image_data":([ +**! "bpp":int, +**! "width":int, +**! "height":int, +**! "tiles":({ +**! string, +**! ... +**! }), +**! ]), +**! ]), +**! ... +**! }), +**! "channels":({ +**! "name":string, +**! "width":int, +**! "height":int, +**! "properties":({ +**! ([ +**! "type":int, +**! "data":string, +**! ]), +**! ... +**! }), +**! "image_data":([ +**! "bpp":int, +**! "width":int, +**! "height":int, +**! "tiles":({ +**! string, +**! ... +**! }), +**! ]), +**! }), +**! ]) +**!</pre> +*/ + +#define MIN(X,Y) ((X)<(Y)?(X):(Y)) +void image_xcf_f__decode_tiles( INT32 args ) +{ + struct object *io,*ao, *cmapo; + struct array *tiles; + struct image *i, *a; + struct neo_colortable *cmap = NULL; + rgb_group *colortable=NULL; + int rle, bpp, span; + unsigned int l, x=0, y=0, cx, cy; + get_all_args( "_decode_tiles", args, "%o%o%a%i%i%O", + &io, &ao, &tiles, &rle, &bpp, &cmapo); + if( !(i = (struct image *)get_storage( io, image_program ))) + error("Wrong type object argument 1 (image)\n"); + if( !(a = (struct image *)get_storage( ao, image_program ))) + error("Wrong type object argument 2 (image)\n"); + if( cmapo && + !(cmap=(struct neo_colortable *)get_storage(cmapo, + image_colortable_program))) + error("Wrong type object argument 4 (colortable)\n"); + for(l=0; l<(unsigned int)tiles->size; l++) + if(tiles->item[l].type != T_STRING) + error("Wrong type array argument 3 (tiles)\n"); + if(i->xsize != a->xsize ||i->ysize != a->ysize) + error("Image and alpha objects are not identically sized.\n"); + + if(cmap) + { + colortable = malloc(sizeof(rgb_group)*image_colortable_size( cmap )); + image_colortable_write_rgb( cmap, (unsigned char *)colortable ); + } + + + x=y=0; + for(l=0; l<(unsigned)tiles->size; l++) + { + struct pike_string *tile = tiles->item[l].u.string; + unsigned int eheight, ewidth; + unsigned char *s; + ewidth = MIN(TILE_WIDTH, i->xsize-x); + eheight = MIN(TILE_HEIGHT, i->ysize-y); + tile->refs++; + + fprintf(stderr, " tile %d/%d [%dx%d] %dbpp \n", + l+1, tiles->size, ewidth, eheight,bpp); + + if(rle) + { + push_string( tile ); + push_int( bpp ); + push_int( ewidth ); + push_int( eheight ); + image_xcf_f__rle_decode( 4 ); + tile = sp[-1].u.string; + if(sp[-1].type != T_STRING) + fatal("Internal disaster in XCF module\n"); + sp--; + } + + if( (unsigned)(tile->len) < (unsigned)(eheight * ewidth * bpp )) + error("Too small tile, was %d bytes, I really need %d\n", + tile->len, eheight*ewidth * bpp); + + s = tile->str; + + check_signals(); /* Allow ^C */ + + for(cy=0; cy<eheight; cy++) + { + for(cx=0; cx<ewidth; cx++) + { + rgb_group pix; + rgb_group apix = {255,255,255}; + int ind = (cx+cy*ewidth); + + if(rle) + span = ewidth*eheight; + else + span = 1; + if(cx+x > i->xsize) continue; + if(cy+y > i->ysize) continue; + + switch( bpp ) + { + case 1: /* indexed or grey */ + if(colortable) + pix = colortable[s[ind]]; + else + pix.r = pix.g = pix.b = s[ind]; + break; + case 2: /* indexed or grey with alpha */ + if(colortable) + pix = colortable[s[ind]]; + else + pix.r = pix.g = pix.b = s[ind]; + apix.r = apix.g = apix.b = s[ind+span]; + break; + case 3: /* rgb */ + pix.r = s[ind]; + pix.g = s[ind+span]; + pix.b = s[ind+span*2]; + break; + case 4: /* rgb with alpha */ + pix.r = s[ind]; + pix.g = s[ind+span*1]; + pix.b = s[ind+span*2]; + apix.r = apix.g = apix.b = s[ind+span*3]; + break; + } + ind = i->xsize*(cy+y)+(cx+x); + i->img[ind] = pix; + a->img[ind] = apix; + } + } + x += TILE_WIDTH; + if( x >= (unsigned)i->xsize ) + { + x = 0; + y += TILE_HEIGHT; + } + if(y>=(unsigned)i->ysize) + { + free_string(tile); + if(colortable) free( colortable ); + return; + } + free_string(tile); + } + if(colortable) free( colortable ); + + pop_n_elems(args); + push_int(0); +} + + +static struct program *image_encoding_xcf_program=NULL; +void init_image_xcf() +{ + start_new_program(); + add_function( "___decode", image_xcf____decode, + "function(string:mapping)", 0); + + add_function( "_decode_tiles", image_xcf_f__decode_tiles, "mixed", 0); + + add_integer_constant( "PROP_END", PROP_END,0 ); + add_integer_constant( "PROP_COLORMAP", PROP_COLORMAP, 0 ); + add_integer_constant( "PROP_ACTIVE_LAYER", PROP_ACTIVE_LAYER, 0 ); + add_integer_constant( "PROP_ACTIVE_CHANNEL", PROP_ACTIVE_CHANNEL, 0 ); + add_integer_constant( "PROP_SELECTION", PROP_SELECTION, 0 ); + add_integer_constant( "PROP_FLOATING_SELECTION", PROP_FLOATING_SELECTION, 0 ); + add_integer_constant( "PROP_OPACITY", PROP_OPACITY, 0 ); + add_integer_constant( "PROP_MODE", PROP_MODE, 0 ); + add_integer_constant( "PROP_VISIBLE", PROP_VISIBLE, 0 ); + add_integer_constant( "PROP_LINKED", PROP_LINKED, 0 ); + add_integer_constant( "PROP_PRESERVE_TRANSPARENCY", + PROP_PRESERVE_TRANSPARENCY, 0); + add_integer_constant( "PROP_APPLY_MASK", PROP_APPLY_MASK, 0 ); + add_integer_constant( "PROP_EDIT_MASK", PROP_EDIT_MASK, 0 ); + add_integer_constant( "PROP_SHOW_MASK", PROP_SHOW_MASK, 0 ); + add_integer_constant( "PROP_SHOW_MASKED", PROP_SHOW_MASKED, 0 ); + add_integer_constant( "PROP_OFFSETS", PROP_OFFSETS, 0 ); + add_integer_constant( "PROP_COLOR", PROP_COLOR, 0 ); + add_integer_constant( "PROP_COMPRESSION", PROP_COMPRESSION, 0 ); + add_integer_constant( "PROP_GUIDES", PROP_GUIDES, 0 ); + add_integer_constant( "PROP_RESOLUTION", PROP_RESOLUTION, 0 ); + add_integer_constant( "PROP_TATTOO", PROP_TATTOO, 0 ); + add_integer_constant( "PROP_PARASITES", PROP_PARASITES, 0 ); + add_integer_constant( "PROP_UNIT", PROP_UNIT, 0 ); + add_integer_constant( "PROP_PATHS", PROP_PATHS, 0 ); + add_integer_constant( "PROP_USER_UNIT", PROP_USER_UNIT, 0 ); + + add_integer_constant( "RGB", RGB, 0 ); + add_integer_constant( "GRAY", GRAY, 0 ); + add_integer_constant( "INDEXED", INDEXED, 0 ); + + add_integer_constant( "COMPRESS_NONE", COMPRESS_NONE, 0 ); + add_integer_constant( "COMPRESS_RLE", COMPRESS_RLE, 0 ); + add_integer_constant( "COMPRESS_ZLIB", COMPRESS_ZLIB, 0 ); + add_integer_constant( "COMPRESS_FRACTAL", COMPRESS_FRACTAL, 0 ); + + add_integer_constant( "Red", Red, 0 ); + add_integer_constant( "Green", Green, 0 ); + add_integer_constant( "Blue", Blue, 0 ); + add_integer_constant( "Gray", Gray, 0 ); + add_integer_constant( "Indexed", Indexed, 0 ); + add_integer_constant( "Auxillary", Auxillary, 0 ); + + add_integer_constant( "RGB_GIMAGE", RGB_GIMAGE, 0 ); + add_integer_constant( "RGBA_GIMAGE", RGBA_GIMAGE, 0 ); + add_integer_constant( "GRAY_GIMAGE", GRAY_GIMAGE, 0 ); + add_integer_constant( "GRAYA_GIMAGE", GRAYA_GIMAGE, 0 ); + add_integer_constant( "INDEXED_GIMAGE", INDEXED_GIMAGE, 0 ); + add_integer_constant( "INDEXEDA_GIMAGE", INDEXEDA_GIMAGE, 0 ); + + image_encoding_xcf_program=end_program(); + + + push_object(clone_object(image_encoding_xcf_program,0)); + + { + struct pike_string *s=make_shared_string("_XCF"); + add_constant(s,sp-1,0); + free_string(s); + } +#define STRING(X) s_##X = make_shared_binary_string(#X,sizeof( #X )-sizeof("")); +#include "xcf_constant_strings.h" +#undef STRING +} + + +void exit_image_xcf() +{ + if(image_encoding_xcf_program) + { + free_program(image_encoding_xcf_program); + image_encoding_xcf_program=0; +#define STRING(X) free_string(s_##X) +#include "xcf_constant_strings.h" +#undef STRING + } +} + diff --git a/src/modules/Image/encodings/xcf_constant_strings.h b/src/modules/Image/encodings/xcf_constant_strings.h new file mode 100644 index 0000000000000000000000000000000000000000..0ae969fcabbfd5796a61313267cead0340df2cab --- /dev/null +++ b/src/modules/Image/encodings/xcf_constant_strings.h @@ -0,0 +1,12 @@ +STRING(bpp); +STRING(channels); +STRING(data); +STRING(height); +STRING(image_data); +STRING(layers); +STRING(mask); +STRING(name); +STRING(properties); +STRING(tiles); +STRING(type); +STRING(width);