diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 17:43:19 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-02-24 17:43:19 +0000 |
commit | 0292059f4a16434600564cfa3f0ad2309a508a54 (patch) | |
tree | d95953cd53011917c4df679b96aedca39401b54f /kernel/kls_xcf/xcf2pnm | |
download | libksquirrel-0292059f4a16434600564cfa3f0ad2309a508a54.tar.gz libksquirrel-0292059f4a16434600564cfa3f0ad2309a508a54.zip |
Added libksquirrel for KDE3
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/libraries/libksquirrel@1095624 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kernel/kls_xcf/xcf2pnm')
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/Makefile.am | 5 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/enums.c | 116 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/enums.h | 98 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/flatspec.c | 370 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/flatten.c | 689 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/flatten.h | 77 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/io-unix.c | 176 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/options.i | 410 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/pixels.c | 487 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/pixels.h | 128 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/scaletab.c | 42 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/table.c | 263 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/utils.c | 164 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/xcf-general.c | 287 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/xcf2pnm.c | 269 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/xcf2pnm.oi | 41 | ||||
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/xcftools.h | 182 |
17 files changed, 3804 insertions, 0 deletions
diff --git a/kernel/kls_xcf/xcf2pnm/Makefile.am b/kernel/kls_xcf/xcf2pnm/Makefile.am new file mode 100644 index 0000000..c81ca26 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/Makefile.am @@ -0,0 +1,5 @@ +bin_PROGRAMS = ksquirrel-libs-xcf2pnm + +ksquirrel_libs_xcf2pnm_SOURCES = enums.c flatspec.c flatten.c io-unix.c pixels.c scaletab.c table.c utils.c xcf2pnm.c xcf-general.c + +EXTRA_DIST = xcf2pnm.oi
\ No newline at end of file diff --git a/kernel/kls_xcf/xcf2pnm/enums.c b/kernel/kls_xcf/xcf2pnm/enums.c new file mode 100644 index 0000000..88f68e7 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/enums.c @@ -0,0 +1,116 @@ +/* Autogenerated from enums.h */ +#include "enums.h" +#define N_ +#include <stdio.h> +const char* +showGimpLayerModeEffects(GimpLayerModeEffects x) +{ + static char buf[35]; + switch(x) { + case GIMP_NORMAL_MODE: return N_("Normal"); + case GIMP_DISSOLVE_MODE: return N_("Dissolve"); + case GIMP_BEHIND_MODE: return N_("Behind"); + case GIMP_MULTIPLY_MODE: return N_("Multiply"); + case GIMP_SCREEN_MODE: return N_("Screen"); + case GIMP_OVERLAY_MODE: return N_("Overlay"); + case GIMP_DIFFERENCE_MODE: return N_("Difference"); + case GIMP_ADDITION_MODE: return N_("Addition"); + case GIMP_SUBTRACT_MODE: return N_("Subtract"); + case GIMP_DARKEN_ONLY_MODE: return N_("DarkenOnly"); + case GIMP_LIGHTEN_ONLY_MODE: return N_("LightenOnly"); + case GIMP_HUE_MODE: return N_("Hue"); + case GIMP_SATURATION_MODE: return N_("Saturation"); + case GIMP_COLOR_MODE: return N_("Color"); + case GIMP_VALUE_MODE: return N_("Value"); + case GIMP_DIVIDE_MODE: return N_("Divide"); + case GIMP_DODGE_MODE: return N_("Dodge"); + case GIMP_BURN_MODE: return N_("Burn"); + case GIMP_HARDLIGHT_MODE: return N_("Hardlight"); + case GIMP_SOFTLIGHT_MODE: return N_("Softlight"); + case GIMP_GRAIN_EXTRACT_MODE: return N_("GrainExtract"); + case GIMP_GRAIN_MERGE_MODE: return N_("GrainMerge"); + case GIMP_COLOR_ERASE_MODE: return N_("ColorErase"); + case GIMP_ERASE_MODE: return N_("Erase"); + case GIMP_REPLACE_MODE: return N_("Replace"); + case GIMP_ANTI_ERASE_MODE: return N_("AntiErase"); + case GIMP_NORMAL_NOPARTIAL_MODE: return N_("NormalNopartial"); + default: sprintf(buf,"(GimpLayerModeEffects:%d)",(int)x); + return buf; + } +} +const char* +showGimpImageBaseType(GimpImageBaseType x) +{ + static char buf[32]; + switch(x) { + case GIMP_RGB: return N_("RGB color"); + case GIMP_GRAY: return N_("Grayscale"); + case GIMP_INDEXED: return N_("Indexed color"); + default: sprintf(buf,"(GimpImageBaseType:%d)",(int)x); + return buf; + } +} +const char* +showGimpImageType(GimpImageType x) +{ + static char buf[28]; + switch(x) { + case GIMP_RGB_IMAGE: return N_("RGB"); + case GIMP_RGBA_IMAGE: return N_("RGB-alpha"); + case GIMP_GRAY_IMAGE: return N_("Grayscale"); + case GIMP_GRAYA_IMAGE: return N_("Grayscale-alpha"); + case GIMP_INDEXED_IMAGE: return N_("Indexed"); + case GIMP_INDEXEDA_IMAGE: return N_("Indexed-alpha"); + default: sprintf(buf,"(GimpImageType:%d)",(int)x); + return buf; + } +} +const char* +showPropType(PropType x) +{ + static char buf[23]; + switch(x) { + case PROP_END: return ("End"); + case PROP_COLORMAP: return ("Colormap"); + case PROP_ACTIVE_LAYER: return ("ActiveLayer"); + case PROP_ACTIVE_CHANNEL: return ("ActiveChannel"); + case PROP_SELECTION: return ("Selection"); + case PROP_FLOATING_SELECTION: return ("FloatingSelection"); + case PROP_OPACITY: return ("Opacity"); + case PROP_MODE: return ("Mode"); + case PROP_VISIBLE: return ("Visible"); + case PROP_LINKED: return ("Linked"); + case PROP_PRESERVE_TRANSPARENCY: return ("PreserveTransparency"); + case PROP_APPLY_MASK: return ("ApplyMask"); + case PROP_EDIT_MASK: return ("EditMask"); + case PROP_SHOW_MASK: return ("ShowMask"); + case PROP_SHOW_MASKED: return ("ShowMasked"); + case PROP_OFFSETS: return ("Offsets"); + case PROP_COLOR: return ("Color"); + case PROP_COMPRESSION: return ("Compression"); + case PROP_GUIDES: return ("Guides"); + case PROP_RESOLUTION: return ("Resolution"); + case PROP_TATTOO: return ("Tattoo"); + case PROP_PARASITES: return ("Parasites"); + case PROP_UNIT: return ("Unit"); + case PROP_PATHS: return ("Paths"); + case PROP_USER_UNIT: return ("UserUnit"); + case PROP_VECTORS: return ("Vectors"); + case PROP_TEXT_LAYER_FLAGS: return ("TextLayerFlags"); + default: sprintf(buf,"(PropType:%d)",(int)x); + return buf; + } +} +const char* +showXcfCompressionType(XcfCompressionType x) +{ + static char buf[33]; + switch(x) { + case COMPRESS_NONE: return N_("None"); + case COMPRESS_RLE: return N_("RLE"); + case COMPRESS_ZLIB: return N_("Zlib"); + case COMPRESS_FRACTAL: return N_("Fractal"); + default: sprintf(buf,"(XcfCompressionType:%d)",(int)x); + return buf; + } +} diff --git a/kernel/kls_xcf/xcf2pnm/enums.h b/kernel/kls_xcf/xcf2pnm/enums.h new file mode 100644 index 0000000..81746ef --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/enums.h @@ -0,0 +1,98 @@ +/* Extracted from + * gimp/base-enums.h + * gimp/gimpbaseenums.h + * gimp/xcf-private.h + * by mkenumsh.pl + */ +typedef enum +{ + GIMP_NORMAL_MODE, + GIMP_DISSOLVE_MODE, + GIMP_BEHIND_MODE, + GIMP_MULTIPLY_MODE, + GIMP_SCREEN_MODE, + GIMP_OVERLAY_MODE, + GIMP_DIFFERENCE_MODE, + GIMP_ADDITION_MODE, + GIMP_SUBTRACT_MODE, + GIMP_DARKEN_ONLY_MODE, + GIMP_LIGHTEN_ONLY_MODE, + GIMP_HUE_MODE, + GIMP_SATURATION_MODE, + GIMP_COLOR_MODE, + GIMP_VALUE_MODE, + GIMP_DIVIDE_MODE, + GIMP_DODGE_MODE, + GIMP_BURN_MODE, + GIMP_HARDLIGHT_MODE, + GIMP_SOFTLIGHT_MODE, + GIMP_GRAIN_EXTRACT_MODE, + GIMP_GRAIN_MERGE_MODE, + GIMP_COLOR_ERASE_MODE, + GIMP_ERASE_MODE, /*< pdb-skip, skip >*/ + GIMP_REPLACE_MODE, /*< pdb-skip, skip >*/ + GIMP_ANTI_ERASE_MODE /*< pdb-skip, skip >*/ + ,GIMP_NORMAL_NOPARTIAL_MODE=-1 +} GimpLayerModeEffects; +const char *showGimpLayerModeEffects(GimpLayerModeEffects); +#define GimpLayerModeEffects_LAST GIMP_ANTI_ERASE_MODE +typedef enum +{ + GIMP_RGB, /*< desc="RGB color" >*/ + GIMP_GRAY, /*< desc="Grayscale" >*/ + GIMP_INDEXED /*< desc="Indexed color" >*/ +} GimpImageBaseType; +const char *showGimpImageBaseType(GimpImageBaseType); +#define GimpImageBaseType_LAST GIMP_INDEXED +typedef enum +{ + GIMP_RGB_IMAGE, /*< desc="RGB" >*/ + GIMP_RGBA_IMAGE, /*< desc="RGB-alpha" >*/ + GIMP_GRAY_IMAGE, /*< desc="Grayscale" >*/ + GIMP_GRAYA_IMAGE, /*< desc="Grayscale-alpha" >*/ + GIMP_INDEXED_IMAGE, /*< desc="Indexed" >*/ + GIMP_INDEXEDA_IMAGE /*< desc="Indexed-alpha" >*/ +} GimpImageType; +const char *showGimpImageType(GimpImageType); +#define GimpImageType_LAST GIMP_INDEXEDA_IMAGE +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, + PROP_VECTORS = 25, + PROP_TEXT_LAYER_FLAGS = 26 +} PropType; +const char *showPropType(PropType); +#define PropType_LAST PROP_TEXT_LAYER_FLAGS +typedef enum +{ + COMPRESS_NONE = 0, + COMPRESS_RLE = 1, + COMPRESS_ZLIB = 2, /* unused */ + COMPRESS_FRACTAL = 3 /* unused */ +} XcfCompressionType; +const char *showXcfCompressionType(XcfCompressionType); +#define XcfCompressionType_LAST COMPRESS_FRACTAL diff --git a/kernel/kls_xcf/xcf2pnm/flatspec.c b/kernel/kls_xcf/xcf2pnm/flatspec.c new file mode 100644 index 0000000..75827ff --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/flatspec.c @@ -0,0 +1,370 @@ +/* Flattening selections function for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include "flatten.h" +#include <string.h> +#include <stdlib.h> + +void +init_flatspec(struct FlattenSpec *spec) +{ + spec->window_mode = USE_CANVAS ; + spec->default_pixel = PERHAPS_ALPHA_CHANNEL ; + spec->numLayers = 0 ; + spec->layers = NULL ; + spec->transmap_filename = NULL ; + spec->output_filename = "-" ; + spec->out_color_mode = COLOR_BY_CONTENTS ; + spec->partial_transparency_mode = ALLOW_PARTIAL_TRANSPARENCY ; + spec->process_in_memory = 0 ; + spec->gimpish_indexed = 1 ; +} + +void +add_layer_request(struct FlattenSpec *spec, const char *layer) +{ + spec->layers = realloc(spec->layers, + sizeof(struct xcfLayer) * (1+spec->numLayers)); + if( spec->layers == NULL ) + FatalUnexpected(_("Out of memory")); + spec->layers[spec->numLayers].name = layer ; + spec->layers[spec->numLayers].mode = (GimpLayerModeEffects)-1 ; + spec->layers[spec->numLayers].opacity = 9999 ; + spec->layers[spec->numLayers].hasMask = -1 ; + spec->numLayers++ ; +} + +struct xcfLayer * +lastlayerspec(struct FlattenSpec *spec,const char *option) +{ + if( spec->numLayers == 0 ) + FatalGeneric(20,_("The %s option must follow a layer name on the " + "command line"),option); + return spec->layers + (spec->numLayers-1) ; +} + +static int +typeHasTransparency(GimpImageType type) +{ + switch( type ) { + case GIMP_RGB_IMAGE: + case GIMP_GRAY_IMAGE: + case GIMP_INDEXED_IMAGE: + return 0 ; + case GIMP_RGBA_IMAGE: + case GIMP_GRAYA_IMAGE: + case GIMP_INDEXEDA_IMAGE: + return 1 ; + } + return 1 ; +} + +static enum out_color_mode +color_by_layers(struct FlattenSpec *spec) +{ + int colormap_is_colored = 0 ; + enum out_color_mode grayish = COLOR_MONO ; + int i ; + + if( spec->default_pixel == CHECKERED_BACKGROUND ) + grayish = COLOR_GRAY ; + else if( degrayPixel(spec->default_pixel) < 0 ) + return COLOR_RGB ; + for( i=0; i<colormapLength; i++ ) { + if( colormap[i] == NEWALPHA(0,0) || colormap[i] == NEWALPHA(-1,0) ) + continue ; + if( degrayPixel(colormap[i]) == -1 ) { + colormap_is_colored = 1 ; + break ; + } else { + grayish = COLOR_GRAY ; + } + } + for( i=0; i<spec->numLayers; i++ ) + switch( spec->layers[i].type ) { + case GIMP_RGB_IMAGE: + case GIMP_RGBA_IMAGE: + return COLOR_RGB ; + case GIMP_GRAY_IMAGE: + case GIMP_GRAYA_IMAGE: + grayish = COLOR_GRAY ; + break ; + case GIMP_INDEXED_IMAGE: + case GIMP_INDEXEDA_IMAGE: + if( colormap_is_colored ) return COLOR_RGB ; + break ; + } + return grayish ; +} + +void +complete_flatspec(struct FlattenSpec *spec, guesser guess_callback) +{ + unsigned i ; + int anyPartial ; + + /* Find the layers to convert. + */ + if( spec->numLayers == 0 ) { + spec->layers = XCF.layers ; + spec->numLayers = XCF.numLayers ; + } else { + for( i=0; i<spec->numLayers; i++ ) { + GimpLayerModeEffects mode ; + int opacity, hasMask ; + unsigned j ; + + for( j=0; ; j++ ) { + if( j == XCF.numLayers ) + FatalGeneric(22,_("The image has no layer called '%s'"), + spec->layers[i].name); + if( strcmp(spec->layers[i].name,XCF.layers[j].name) == 0 ) + break ; + } + mode = spec->layers[i].mode == (GimpLayerModeEffects)-1 ? + XCF.layers[j].mode : spec->layers[i].mode ; + opacity = spec->layers[i].opacity == 9999 ? + XCF.layers[j].opacity : spec->layers[i].opacity ; + hasMask = spec->layers[i].hasMask == -1 ? + XCF.layers[j].hasMask : spec->layers[i].hasMask ; + if( hasMask && !XCF.layers[j].hasMask && + XCF.layers[j].mask.hierarchy == 0 ) + FatalGeneric(22,_("Layer '%s' has no layer mask to enable"), + spec->layers[i].name); + spec->layers[i] = XCF.layers[j] ; + spec->layers[i].mode = mode ; + spec->layers[i].opacity = opacity ; + spec->layers[i].hasMask = hasMask ; + spec->layers[i].isVisible = 1 ; + } + } + + /* Force the mode of the lowest visible layer to be Normal or Dissolve. + * That may not be logical, but the Gimp does it + */ + for( i=0; i < spec->numLayers; i++ ) { + if( spec->layers[i].isVisible ) { + if( spec->layers[i].mode != GIMP_DISSOLVE_MODE ) + spec->layers[i].mode = GIMP_NORMAL_MODE ; + break ; + } + } + + /* Mimic the Gimp's behavior on indexed layers */ + if( XCF.type == GIMP_INDEXED && spec->gimpish_indexed ) { + for( i=0; i<spec->numLayers; i++ ) + if( spec->layers[i].mode != GIMP_DISSOLVE_MODE ) + spec->layers[i].mode = GIMP_NORMAL_NOPARTIAL_MODE ; + } else + spec->gimpish_indexed = 0 ; + + /* compute dimensions of the window */ + if( spec->window_mode == AUTOCROP ) { + int first = 1 ; + for( i=0; i<spec->numLayers; i++ ) + if( spec->layers[i].isVisible ) { + computeDimensions(&spec->layers[i].dim) ; + if( first ) { + spec->dim = spec->layers[i].dim ; + first = 0 ; + } else { + if( spec->dim.c.l < spec->layers[i].dim.c.l ) + spec->dim.c.l = spec->layers[i].dim.c.l ; + if( spec->dim.c.r > spec->layers[i].dim.c.r ) + spec->dim.c.r = spec->layers[i].dim.c.r ; + if( spec->dim.c.t < spec->layers[i].dim.c.t ) + spec->dim.c.t = spec->layers[i].dim.c.t ; + if( spec->dim.c.b > spec->layers[i].dim.c.b ) + spec->dim.c.b = spec->layers[i].dim.c.b ; + } + } + if( first ) { + spec->window_mode = USE_CANVAS ; + } else { + spec->dim.width = spec->dim.c.r - spec->dim.c.l ; + spec->dim.height = spec->dim.c.b - spec->dim.c.t ; + } + } + if( spec->window_mode != AUTOCROP ) { + if( (spec->window_mode & MANUAL_OFFSET) == 0 ) + spec->dim.c.t = spec->dim.c.l = 0 ; + if( (spec->window_mode & MANUAL_CROP) == 0 ) { + spec->dim.height = XCF.height ; + spec->dim.width = XCF.width ; + } + } + computeDimensions(&spec->dim); + + /* Turn off layers that we don't hit at all */ + for( i=0; i<spec->numLayers; i++ ) + if( spec->layers[i].isVisible && + disjointRects(spec->dim.c,spec->layers[i].dim.c) ) + spec->layers[i].isVisible = 0 ; + + /* See if there is a completely covering layer somewhere in the stack */ + /* Also check if partial transparency is possible */ + anyPartial = 0 ; + for( i=spec->numLayers; i-- ; ) { + if( !spec->layers[i].isVisible ) + continue ; + if( typeHasTransparency(spec->layers[i].type) ) { + if( spec->layers[i].mode == GIMP_NORMAL_MODE ) + anyPartial = 1; + } else if( isSubrect(spec->dim.c,spec->layers[i].dim.c) && + (spec->layers[i].mode == GIMP_NORMAL_MODE || + spec->layers[i].mode == GIMP_NORMAL_NOPARTIAL_MODE || + spec->layers[i].mode == GIMP_DISSOLVE_MODE) ) { + /* This layer fills out the entire image. + * Turn off anly lower layers, and note that we cannot have + * transparency at all. + */ + while(i) spec->layers[--i].isVisible = 0 ; + if( spec->default_pixel != FORCE_ALPHA_CHANNEL ) + spec->default_pixel = NEWALPHA(colormap[0],255); + anyPartial = 0 ; + break ; + } + } + if( spec->partial_transparency_mode == ALLOW_PARTIAL_TRANSPARENCY && + (!anyPartial || ALPHA(spec->default_pixel) >= 128) ) + spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ; + + /* Initialize layers and print overview if we're verbose */ + for( i=spec->numLayers; i--; ) + if( spec->layers[i].isVisible ) { + initLayer(&spec->layers[i]) ; + if( verboseFlag ) { + fprintf(stderr,"%dx%d%+d%+d %s %s", + spec->layers[i].dim.width, spec->layers[i].dim.height, + spec->layers[i].dim.c.l - spec->dim.c.l, + spec->layers[i].dim.c.t - spec->dim.c.t, + _(showGimpImageType(spec->layers[i].type)), + _(showGimpLayerModeEffects(spec->layers[i].mode))); + if( spec->layers[i].opacity < 255 ) + fprintf(stderr,"/%02d%%",spec->layers[i].opacity * 100 / 255); + if( XCF.layers[i].hasMask ) + fprintf(stderr,_("/mask")); + fprintf(stderr," %s\n",spec->layers[i].name); + } + } + + /* Resolve color mode unless we wait until we have the entire image */ + if( spec->out_color_mode == COLOR_BY_CONTENTS && + !spec->process_in_memory ) { + if( guess_callback ) + spec->out_color_mode = guess_callback(spec,NULL); + if( spec->out_color_mode == COLOR_BY_CONTENTS ) + spec->out_color_mode = color_by_layers(spec) ; + } +} + +void +analyse_colormode(struct FlattenSpec *spec,rgba **allPixels, + guesser guess_callback) +{ + unsigned x,y ; + int status ; + /* 8 - looking for any transparency + * 4 - looking for partially transparent pixels + * 2 - looking for pixels other than black and white + * 1 - looking for colored pixels + */ + int known_absent = 0 ; + int assume_present = 0 ; + + if( spec->out_color_mode == COLOR_BY_CONTENTS && guess_callback ) + spec->out_color_mode = guess_callback(spec,allPixels) ; + + if( spec->out_color_mode == COLOR_RGB ) assume_present |= 3 ; + if( spec->out_color_mode == COLOR_INDEXED ) assume_present |= 3 ; + if( spec->out_color_mode == COLOR_GRAY ) assume_present |= 2 ; + switch( color_by_layers(spec) ) { + case COLOR_GRAY: known_absent |= 1 ; break ; + case COLOR_MONO: known_absent |= 3 ; break ; + default: break ; + } + if( spec->partial_transparency_mode == DISSOLVE_PARTIAL_TRANSPARENCY || + spec->partial_transparency_mode == PARTIAL_TRANSPARENCY_IMPOSSIBLE ) + known_absent |= 4 ; + if( ALPHA(spec->default_pixel) >= 128 ) known_absent |= 12 ; + else if( spec->default_pixel == FORCE_ALPHA_CHANNEL ) assume_present |= 8 ; + + status = 15 - (known_absent | assume_present) ; + + for( y=0; status && y<spec->dim.height; y++ ) { + rgba *row = allPixels[y] ; + if( (status & 3) != 0 ) { + /* We're still interested in color */ + for( x=0; status && x<spec->dim.width; x++ ) { + if( NULLALPHA(row[x]) ) + status &= ~8 ; + else { + rgba full = row[x] | (255 << ALPHA_SHIFT) ; + if( !FULLALPHA(row[x]) ) status &= ~12 ; + if( full == NEWALPHA(0,255) || full == NEWALPHA(-1,255) ) + /* Black or white */ ; + else if( degrayPixel(row[x]) != -1 ) + status &= ~2 ; /* gray */ + else + status &= ~3 ; /* color */ + } + } + } else { + /* Not interested in color */ + for( x=0; status && x<spec->dim.width; x++ ) { + if( NULLALPHA(row[x]) ) + status &= ~8 ; + else if( !FULLALPHA(row[x]) ) + status &= ~12 ; + } + } + } + + status |= known_absent ; + + switch( spec->out_color_mode ) { + case COLOR_INDEXED: /* The caller takes responsibility */ + case COLOR_RGB: /* Everything is fine. */ + break ; + case COLOR_GRAY: + if( (status & 1) == 0 ) + FatalGeneric(103, + _("Grayscale output selected, but colored pixel(s) found")); + break ; + case COLOR_MONO: + if( (status & 2) == 0 ) + FatalGeneric(103,_("Monochrome output selected, but not all pixels " + "are black or white")); + break ; + case COLOR_BY_FILENAME: /* Should not happen ... */ + case COLOR_BY_CONTENTS: + if( (status & 1) == 0 ) + spec->out_color_mode = COLOR_RGB ; + else if( (status & 2) == 0 ) + spec->out_color_mode = COLOR_GRAY ; + else + spec->out_color_mode = COLOR_MONO ; + break ; + } + + if( (status & 12) == 12 ) /* No transparency found */ + spec->default_pixel = NEWALPHA(colormap[0],255); + else if( (status & 12) == 4 ) + spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ; +} diff --git a/kernel/kls_xcf/xcf2pnm/flatten.c b/kernel/kls_xcf/xcf2pnm/flatten.c new file mode 100644 index 0000000..c564ddd --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/flatten.c @@ -0,0 +1,689 @@ +/* Flattning functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include "flatten.h" +#include "pixels.h" +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +static rgba __ATTRIBUTE__((noinline,const)) +composite_one(rgba bot,rgba top) +{ + unsigned tfrac, alpha ; + + tfrac = ALPHA(top) ; + alpha = 255 ; + if( !FULLALPHA(bot) ) { + alpha = 255 ^ scaletable[255-ALPHA(bot)][255-ALPHA(top)] ; + /* This peculiar combination of ^ and - makes the GCC code + * generator for i386 particularly happy. + */ + tfrac = (256*ALPHA(top) - 1) / alpha ; + /* Tfrac is the fraction of the coposited pixel's covered area + * that comes from the top pixel. + * For mathematical accuracy we ought to scale by 255 and + * subtract alpha/2, but this is faster, and never misses the + * true value by more than one 1/255. This effect is completely + * overshadowed by the linear interpolation in the first place. + * (I.e. gamma is ignored when combining intensities). + * [In any case, complete fairness is not possible: if the + * bottom pixel had alpha=170 and the top has alpha=102, + * each should contribute equally to the color of the + * resulting alpha=204 pixel, which is not possible in general] + * Subtracting one helps the topfrac never be 256, which would + * be bad. + * On the other hand it means that we would get tfrac=-1 if the + * top pixel is completely transparent, and we get a division + * by zero if _both_ pixels are fully transparent. These cases + * must be handled by all callers. + * More snooping in the Gimp sources reveal that it uses + * floating-point for its equivalent of tfrac when the + * bottom layer has an alpha channel. (alphify() macro + * in paint-funcs.c). What gives? + */ + } + return (alpha << ALPHA_SHIFT) + + ((uint32_t)scaletable[ tfrac ][255&(top>>RED_SHIFT )] << RED_SHIFT ) + + ((uint32_t)scaletable[ tfrac ][255&(top>>GREEN_SHIFT)] << GREEN_SHIFT ) + + ((uint32_t)scaletable[ tfrac ][255&(top>>BLUE_SHIFT )] << BLUE_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>RED_SHIFT )] << RED_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>GREEN_SHIFT)] << GREEN_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>BLUE_SHIFT )] << BLUE_SHIFT ) + ; +} + +/* merge_normal() takes ownership of bot. + * merge_normal() will share ownership of top. + * Return: may be shared. + */ +static struct Tile * __ATTRIBUTE__((noinline)) +merge_normal(struct Tile *bot, struct Tile *top) +{ + unsigned i ; + assertTileCompatibility(bot,top); + + /* See if there is an easy winner */ + if( (bot->summary & TILESUMMARY_ALLNULL) || + (top->summary & TILESUMMARY_ALLFULL) ) { + freeTile(bot); + return top ; + } + if( top->summary & TILESUMMARY_ALLNULL ) { + freeTile(top); + return bot ; + } + + /* Try hard to make top win */ + for( i=0; ; i++ ) { + if( i == top->count ) { + freeTile(bot); + return top ; + } + if( !(NULLALPHA(bot->pixels[i]) || FULLALPHA(top->pixels[i])) ) + break ; + } + + INIT_SCALETABLE_IF( !(top->summary & TILESUMMARY_CRISP) ); + + /* Otherwise bot wins, but is forever changed ... */ + if( (top->summary & TILESUMMARY_ALLNULL) == 0 ) { + unsigned i ; + invalidateSummary(bot,0); + for( i=0 ; i < top->count ; i++ ) { + if( !NULLALPHA(top->pixels[i]) ) { + if( FULLALPHA(top->pixels[i]) || NULLALPHA(bot->pixels[i]) ) + bot->pixels[i] = top->pixels[i] ; + else + bot->pixels[i] = composite_one(bot->pixels[i],top->pixels[i]); + } + } + } + freeTile(top); + return bot ; +} + +#define exotic_combinator static inline unsigned __ATTRIBUTE__((const)) + + + +exotic_combinator +ucombine_ADDITION(uint8_t bot,uint8_t top) +{ + return bot+top > 255 ? 255 : bot+top ; +} + +exotic_combinator +ucombine_SUBTRACT(uint8_t bot,uint8_t top) +{ + return top>bot ? 0 : bot-top ; +} + +exotic_combinator +ucombine_LIGHTEN_ONLY(uint8_t bot,uint8_t top) +{ + return top > bot ? top : bot ; +} + +exotic_combinator +ucombine_DARKEN_ONLY(uint8_t bot,uint8_t top) +{ + return top < bot ? top : bot ; +} + +exotic_combinator +ucombine_DIFFERENCE(uint8_t bot,uint8_t top) +{ + return top > bot ? top-bot : bot-top ; +} + +exotic_combinator +ucombine_MULTIPLY(uint8_t bot,uint8_t top) +{ + return scaletable[bot][top] ; +} + +exotic_combinator +ucombine_DIVIDE(uint8_t bot,uint8_t top) +{ + int result = (int)bot*256 / (1+top) ; + return result >= 256 ? 255 : result ; +} + +exotic_combinator +ucombine_SCREEN(uint8_t bot,uint8_t top) +{ + /* An inverted version of "multiply" */ + return 255 ^ scaletable[255-bot][255-top] ; +} + +exotic_combinator +ucombine_OVERLAY(uint8_t bot,uint8_t top) +{ + return scaletable[bot][bot] + + 2*scaletable[top][scaletable[bot][255-bot]] ; + /* This strange formula is equivalent to + * (1-top)*(bot^2) + top*(1-(1-top)^2) + * that is, the top value is used to interpolate between + * the self-multiply and the self-screen of the bottom. + */ + /* Note: This is exactly what the "Soft light" effect also + * does, though with different code in the Gimp. + */ +} + +exotic_combinator +ucombine_DODGE(uint8_t bot,uint8_t top) +{ + return ucombine_DIVIDE(bot,255-top); +} + +exotic_combinator +ucombine_BURN(uint8_t bot,uint8_t top) +{ + return 255 - ucombine_DIVIDE(255-bot,top); +} + +exotic_combinator +ucombine_HARDLIGHT(uint8_t bot,uint8_t top) +{ + if( top >= 128 ) + return 255 ^ scaletable[255-bot][2*(255-top)] ; + else + return scaletable[bot][2*top]; + /* The code that implements "hardlight" in Gimp 2.2.10 has some + * rounding errors, but this is undoubtedly what is meant. + */ +} + +exotic_combinator +ucombine_GRAIN_EXTRACT(uint8_t bot,uint8_t top) +{ + int temp = (int)bot - (int)top + 128 ; + return temp < 0 ? 0 : temp >= 256 ? 255 : temp ; +} + +exotic_combinator +ucombine_GRAIN_MERGE(uint8_t bot,uint8_t top) +{ + int temp = (int)bot + (int)top - 128 ; + return temp < 0 ? 0 : temp >= 256 ? 255 : temp ; +} + +struct HSV { + enum { HUE_RED_GREEN_BLUE,HUE_RED_BLUE_GREEN,HUE_BLUE_RED_GREEN, + HUE_BLUE_GREEN_RED,HUE_GREEN_BLUE_RED,HUE_GREEN_RED_BLUE } hue; + unsigned ch1, ch2, ch3 ; +}; + +static void +RGBtoHSV(rgba rgb,struct HSV *hsv) +{ + unsigned RED = (uint8_t)(rgb >> RED_SHIFT); + unsigned GREEN = (uint8_t)(rgb >> GREEN_SHIFT); + unsigned BLUE = (uint8_t)(rgb >> BLUE_SHIFT) ; + #define HEXTANT(b,m,t) hsv->ch1 = b, hsv->ch2 = m, hsv->ch3 = t, \ + hsv->hue = HUE_ ## b ## _ ## m ## _ ## t + if( GREEN <= RED ) + if( BLUE <= RED ) + if( GREEN <= BLUE ) + HEXTANT(GREEN,BLUE,RED); + else + HEXTANT(BLUE,GREEN,RED); + else + HEXTANT(GREEN,RED,BLUE); + else if( BLUE <= RED ) + HEXTANT(BLUE,RED,GREEN); + else if( BLUE <= GREEN ) + HEXTANT(RED,BLUE,GREEN); + else + HEXTANT(RED,GREEN,BLUE); + #undef HEXTANT +} + +/* merge_exotic() destructively updates bot. + * merge_exotic() reads but does not free top. + */ +static void __ATTRIBUTE__((noinline)) +merge_exotic(struct Tile *bot, const struct Tile *top, + GimpLayerModeEffects mode) +{ + unsigned i ; + assertTileCompatibility(bot,top); + if( (bot->summary & TILESUMMARY_ALLNULL) != 0 ) return ; + if( (top->summary & TILESUMMARY_ALLNULL) != 0 ) return ; + assert( bot->refcount == 1 ); + /* The transparency status of bot never changes */ + + INIT_SCALETABLE_IF(1); + + for( i=0; i < top->count ; i++ ) { + uint32_t RED, GREEN, BLUE ; + if( NULLALPHA(bot->pixels[i]) || NULLALPHA(top->pixels[i]) ) + continue ; +#define UNIFORM(mode) case GIMP_ ## mode ## _MODE: \ + RED = ucombine_ ## mode (bot->pixels[i]>>RED_SHIFT , \ + top->pixels[i]>>RED_SHIFT ); \ + GREEN = ucombine_ ## mode (bot->pixels[i]>>GREEN_SHIFT, \ + top->pixels[i]>>GREEN_SHIFT); \ + BLUE = ucombine_ ## mode (bot->pixels[i]>>BLUE_SHIFT , \ + top->pixels[i]>>BLUE_SHIFT ); \ + break ; + switch( mode ) { + case GIMP_NORMAL_MODE: + case GIMP_DISSOLVE_MODE: + FatalUnexpected("Normal and Dissolve mode can't happen here!"); + UNIFORM(ADDITION); + UNIFORM(SUBTRACT); + UNIFORM(LIGHTEN_ONLY); + UNIFORM(DARKEN_ONLY); + UNIFORM(DIFFERENCE); + UNIFORM(MULTIPLY); + UNIFORM(DIVIDE); + UNIFORM(SCREEN); + case GIMP_SOFTLIGHT_MODE: /* A synonym for "overlay"! */ + UNIFORM(OVERLAY); + UNIFORM(DODGE); + UNIFORM(BURN); + UNIFORM(HARDLIGHT); + UNIFORM(GRAIN_EXTRACT); + UNIFORM(GRAIN_MERGE); + case GIMP_HUE_MODE: + case GIMP_SATURATION_MODE: + case GIMP_VALUE_MODE: + case GIMP_COLOR_MODE: + { + static struct HSV hsvTop, hsvBot ; + RGBtoHSV(top->pixels[i],&hsvTop); + if( mode == GIMP_HUE_MODE && hsvTop.ch1 == hsvTop.ch3 ) + continue ; + RGBtoHSV(bot->pixels[i],&hsvBot); + if( mode == GIMP_VALUE_MODE ) { + if( hsvBot.ch3 ) { + hsvBot.ch1 = (hsvBot.ch1*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3; + hsvBot.ch2 = (hsvBot.ch2*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3; + hsvBot.ch3 = hsvTop.ch3 ; + } else { + hsvBot.ch1 = hsvBot.ch2 = hsvBot.ch3 = hsvTop.ch3 ; + } + } else { + unsigned mfNum, mfDenom ; + if( mode == GIMP_HUE_MODE || mode == GIMP_COLOR_MODE ) { + mfNum = hsvTop.ch2-hsvTop.ch1 ; + mfDenom = hsvTop.ch3-hsvTop.ch1 ; + hsvBot.hue = hsvTop.hue ; + } else { + mfNum = hsvBot.ch2-hsvBot.ch1 ; + mfDenom = hsvBot.ch3-hsvBot.ch1 ; + } + if( mode == GIMP_SATURATION_MODE ) { + if( hsvTop.ch3 == 0 ) + hsvBot.ch1 = hsvBot.ch3 ; /* Black has no saturation */ + else + hsvBot.ch1 = (hsvTop.ch1*hsvBot.ch3 + hsvTop.ch3/2) / hsvTop.ch3; + } else if( mode == GIMP_COLOR_MODE ) { + /* GIMP_COLOR_MODE works in HSL space instead of HSV. We must + * transfer H and S, keeping the L = ch1+ch3 of the bottom pixel, + * but the S we transfer works differently from the S in HSV. + */ + unsigned L = hsvTop.ch1 + hsvTop.ch3 ; + unsigned sNum = hsvTop.ch3 - hsvTop.ch1 ; + unsigned sDenom = L < 256 ? L : 510-L ; + if( sDenom == 0 ) sDenom = 1 ; /* sNum will be 0 */ + L = hsvBot.ch1 + hsvBot.ch3 ; + if( L < 256 ) { + /* Ideally we want to compute L/2 * (1-sNum/sDenom) + * But shuffle this a bit so we can use integer arithmetic. + * The "-1" in the rounding prevents us from ending up with + * ch1 > ch3. + */ + hsvBot.ch1 = (L*(sDenom-sNum)+sDenom-1)/(2*sDenom); + hsvBot.ch3 = L - hsvBot.ch1 ; + } else { + /* Here our goal is 255 - (510-L)/2 * (1-sNum/sDenom) */ + hsvBot.ch3 = 255 - ((510-L)*(sDenom-sNum)+sDenom-1)/(2*sDenom); + hsvBot.ch1 = L - hsvBot.ch3 ; + } + assert(hsvBot.ch3 <= 255); + assert(hsvBot.ch3 >= hsvBot.ch1); + } + if( mfDenom == 0 ) + hsvBot.ch2 = hsvBot.ch1 ; + else + hsvBot.ch2 = hsvBot.ch1 + + (mfNum*(hsvBot.ch3-hsvBot.ch1) + mfDenom/2) / mfDenom ; + } + switch( hsvBot.hue ) { + #define HEXTANT(b,m,t) case HUE_ ## b ## _ ## m ## _ ## t : \ + b = hsvBot.ch1; m = hsvBot.ch2; t = hsvBot.ch3; break; + HEXTANT(RED,GREEN,BLUE); + HEXTANT(RED,BLUE,GREEN); + HEXTANT(BLUE,RED,GREEN); + HEXTANT(BLUE,GREEN,RED); + HEXTANT(GREEN,BLUE,RED); + HEXTANT(GREEN,RED,BLUE); + #undef HEXTANT + } + break ; + } + default: + FatalUnsupportedXCF(_("'%s' layer mode"), + _(showGimpLayerModeEffects(mode))); + } + if( FULLALPHA(bot->pixels[i] & top->pixels[i]) ) + bot->pixels[i] = (bot->pixels[i] & (255 << ALPHA_SHIFT)) + + (RED << RED_SHIFT) + + (GREEN << GREEN_SHIFT) + + (BLUE << BLUE_SHIFT) ; + else { + rgba bp = bot->pixels[i] ; + /* In a sane world, the alpha of the top pixel would simply be + * used to interpolate linearly between the bottom pixel's base + * color and the effect-computed color. + * But no! What the Gimp actually does is empirically + * described by the following (which borrows code from + * composite_one() that makes no theoretical sense here): + */ + unsigned tfrac = ALPHA(top->pixels[i]) ; + if( !FULLALPHA(bp) ) { + unsigned pseudotop = (tfrac < ALPHA(bp) ? tfrac : ALPHA(bp)); + unsigned alpha = 255 ^ scaletable[255-ALPHA(bp)][255-pseudotop] ; + tfrac = (256*pseudotop - 1) / alpha ; + } + bot->pixels[i] = (bp & (255 << ALPHA_SHIFT)) + + ((rgba)scaletable[ tfrac ][ RED ] << RED_SHIFT ) + + ((rgba)scaletable[ tfrac ][ GREEN ] << GREEN_SHIFT) + + ((rgba)scaletable[ tfrac ][ BLUE ] << BLUE_SHIFT ) + + ((rgba)scaletable[255^tfrac][255&(bp>>RED_SHIFT )] << RED_SHIFT ) + + ((rgba)scaletable[255^tfrac][255&(bp>>GREEN_SHIFT)] << GREEN_SHIFT) + + ((rgba)scaletable[255^tfrac][255&(bp>>BLUE_SHIFT )] << BLUE_SHIFT ) ; + } + } + return ; +} + +static void +dissolveTile(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + assert( tile->refcount == 1 ); + if( (tile->summary & TILESUMMARY_CRISP) ) + return ; + summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i = 0 ; i < tile->count ; i++ ) { + if( FULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLNULL ; + else if ( NULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLFULL ; + else if( ALPHA(tile->pixels[i]) > rand() % 0xFF ) { + tile->pixels[i] |= 255 << ALPHA_SHIFT ; + summary &= ~TILESUMMARY_ALLNULL ; + } else { + tile->pixels[i] = 0 ; + summary &= ~TILESUMMARY_ALLFULL ; + } + } + tile->summary = summary ; +} + +static void +roundAlpha(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + assert( tile->refcount == 1 ); + if( (tile->summary & TILESUMMARY_CRISP) ) + return ; + summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i = 0 ; i < tile->count ; i++ ) { + if( ALPHA(tile->pixels[i]) >= 128 ) { + tile->pixels[i] |= 255 << ALPHA_SHIFT ; + summary &= ~TILESUMMARY_ALLNULL ; + } else { + tile->pixels[i] = 0 ; + summary &= ~TILESUMMARY_ALLFULL ; + } + } + tile->summary = summary ; +} + +/* flattenTopdown() shares ownership of top. + * The return value may be a shared tile. + */ +static struct Tile * +flattenTopdown(struct FlattenSpec *spec, struct Tile *top, + unsigned nlayers, const struct rect *where) +{ + struct Tile *tile; + + while( nlayers-- ) { + if( tileSummary(top) & TILESUMMARY_ALLFULL ) + return top ; + if( !spec->layers[nlayers].isVisible ) + continue ; + + tile = getLayerTile(&spec->layers[nlayers],where); + + if( tile->summary & TILESUMMARY_ALLNULL ) + continue ; /* Simulate a tail call */ + + switch( spec->layers[nlayers].mode ) { + case GIMP_NORMAL_NOPARTIAL_MODE: + roundAlpha(tile) ; + /* fall through */ + if(0) { + case GIMP_DISSOLVE_MODE: + dissolveTile(tile); + /* fall through */ + } + case GIMP_NORMAL_MODE: + top = merge_normal(tile,top); + break ; + default: + { + struct Tile *below, *above ; + unsigned i ; + if( !(top->summary & TILESUMMARY_ALLNULL) ) { + rgba tile_or = 0 ; + invalidateSummary(tile,0); + for( i=0; i<top->count; i++ ) + if( FULLALPHA(top->pixels[i]) ) + tile->pixels[i] = 0 ; + else + tile_or |= tile->pixels[i] ; + /* If the tile only has pixels that will be covered by 'top' anyway, + * forget it anyway. + */ + if( ALPHA(tile_or) == 0 ) { + freeTile(tile); + break ; /* from the switch, which will continue the while */ + } + } + /* Create a dummy top for the layers below this */ + if( top->summary & TILESUMMARY_CRISP ) { + above = forkTile(top); + } else { + summary_t summary = TILESUMMARY_ALLNULL ; + above = newTile(*where); + for( i=0; i<top->count; i++ ) + if( FULLALPHA(top->pixels[i]) ) { + above->pixels[i] = -1 ; + summary = 0 ; + } else + above->pixels[i] = 0 ; + above->summary = TILESUMMARY_UPTODATE + TILESUMMARY_CRISP + summary; + } + below = flattenTopdown(spec, above, nlayers, where); + if( below->refcount > 1 ) { + assert( below == top ); + /* This can only happen if 'below' is a copy of 'top' + * THROUGH 'above', which in turn means that none of all + * this is visible after all. So just free it and return 'top'. + */ + freeTile(below); + return top ; + } + merge_exotic(below,tile,spec->layers[nlayers].mode); + freeTile(tile); + top = merge_normal(below,top); + return top ; + } + } + } + return top ; +} + +static void +addBackground(struct FlattenSpec *spec, struct Tile *tile, unsigned ncols) +{ + unsigned i ; + + if( tileSummary(tile) & TILESUMMARY_ALLFULL ) + return ; + + switch( spec->partial_transparency_mode ) { + case FORBID_PARTIAL_TRANSPARENCY: + if( !(tileSummary(tile) & TILESUMMARY_CRISP) ) + FatalGeneric(102,_("Flattened image has partially transparent pixels")); + break ; + case DISSOLVE_PARTIAL_TRANSPARENCY: + dissolveTile(tile); + break ; + case ALLOW_PARTIAL_TRANSPARENCY: + case PARTIAL_TRANSPARENCY_IMPOSSIBLE: + break ; + } + + if( spec->default_pixel == CHECKERED_BACKGROUND ) { + INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP ) ); + for( i=0; i<tile->count; i++ ) + if( !FULLALPHA(tile->pixels[i]) ) { + rgba fillwith = ((i/ncols)^(i%ncols))&8 ? 0x66 : 0x99 ; + fillwith = graytable[fillwith] + (255 << ALPHA_SHIFT) ; + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = fillwith ; + else + tile->pixels[i] = composite_one(fillwith,tile->pixels[i]); + } + tile->summary = TILESUMMARY_UPTODATE + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + return ; + } + if( !FULLALPHA(spec->default_pixel) ) return ; + if( tileSummary(tile) & TILESUMMARY_ALLNULL ) { + fillTile(tile,spec->default_pixel); + } else { + INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP) ); + for( i=0; i<tile->count; i++ ) + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = spec->default_pixel ; + else if( FULLALPHA(tile->pixels[i]) ) + ; + else + tile->pixels[i] = composite_one(spec->default_pixel,tile->pixels[i]); + + tile->summary = TILESUMMARY_UPTODATE + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + } +} + +void +flattenIncrementally(struct FlattenSpec *spec,lineCallback callback) +{ + rgba *rows[TILE_HEIGHT] ; + unsigned i, y, nrows, ncols ; + struct rect where ; + struct Tile *tile ; + static struct Tile toptile ; + + toptile.count = TILE_HEIGHT * TILE_WIDTH ; + fillTile(&toptile,0); + + for( where.t = spec->dim.c.t; where.t < spec->dim.c.b; where.t=where.b ) { + where.b = (where.t+TILE_HEIGHT) - where.t % TILE_HEIGHT ; + if( where.b > spec->dim.c.b ) where.b = spec->dim.c.b ; + nrows = where.b - where.t ; + for( y = 0; y < nrows ; y++ ) + rows[y] = xcfmalloc(4*(spec->dim.c.r-spec->dim.c.l)); + + for( where.l = spec->dim.c.l; where.l < spec->dim.c.r; where.l=where.r ) { + where.r = (where.l+TILE_WIDTH) - where.l % TILE_WIDTH ; + if( where.r > spec->dim.c.r ) where.r = spec->dim.c.r ; + ncols = where.r - where.l ; + + toptile.count = ncols * nrows ; + toptile.refcount = 2 ; /* For bug checking */ + assert( toptile.summary == TILESUMMARY_UPTODATE + + TILESUMMARY_ALLNULL + TILESUMMARY_CRISP ); + tile = flattenTopdown(spec,&toptile,spec->numLayers,&where) ; + toptile.refcount-- ; /* addBackground may change destructively */ + addBackground(spec,tile,ncols); + + for( i = 0 ; i < tile->count ; i++ ) + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = 0 ; + for( y = 0 ; y < nrows ; y++ ) + memcpy(rows[y] + (where.l - spec->dim.c.l), + tile->pixels + y * ncols, ncols*4); + + if( tile == &toptile ) { + fillTile(&toptile,0); + } else { + freeTile(tile); + } + } + for( y = 0 ; y < nrows ; y++ ) + callback(spec->dim.width,rows[y]); + } +} + +static rgba **collectPointer ; + +static void +collector(unsigned num,rgba *row) +{ + *collectPointer++ = row ; +} + +rgba ** +flattenAll(struct FlattenSpec *spec) +{ + rgba **rows = xcfmalloc(spec->dim.height * sizeof(rgba*)); + if( verboseFlag ) + fprintf(stderr,_("Flattening image ...")); + collectPointer = rows ; + flattenIncrementally(spec,collector); + if( verboseFlag ) + fprintf(stderr,"\n"); + return rows ; +} + +void +shipoutWithCallback(struct FlattenSpec *spec, rgba **pixels, + lineCallback callback) +{ + unsigned i ; + for( i = 0; i < spec->dim.height; i++ ) { + callback(spec->dim.width,pixels[i]); + } + xcffree(pixels); +} diff --git a/kernel/kls_xcf/xcf2pnm/flatten.h b/kernel/kls_xcf/xcf2pnm/flatten.h new file mode 100644 index 0000000..c252175 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/flatten.h @@ -0,0 +1,77 @@ +/* Flattning functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef FLATTEN_H +#define FLATTEN_H + +#include "xcftools.h" +#include "pixels.h" + +#define PERHAPS_ALPHA_CHANNEL (NEWALPHA(0,1)) +#define FORCE_ALPHA_CHANNEL (NEWALPHA(0,2)) +#define CHECKERED_BACKGROUND (NEWALPHA(0,200)) +struct FlattenSpec { + struct tileDimensions dim ; + rgba default_pixel ; + int numLayers ; + struct xcfLayer *layers ; + + const char * transmap_filename ; + const char * output_filename ; + enum out_color_mode { + COLOR_BY_FILENAME, + COLOR_BY_CONTENTS, + COLOR_INDEXED, + COLOR_RGB, + COLOR_GRAY, + COLOR_MONO + } out_color_mode ; + enum { ALLOW_PARTIAL_TRANSPARENCY, + DISSOLVE_PARTIAL_TRANSPARENCY, + FORBID_PARTIAL_TRANSPARENCY, + PARTIAL_TRANSPARENCY_IMPOSSIBLE + } partial_transparency_mode ; + enum { USE_CANVAS = 0, + MANUAL_OFFSET = 1, + MANUAL_CROP = 2, + AUTOCROP = 4 } window_mode ; + int process_in_memory ; + int gimpish_indexed ; +}; + +/* From flatspec.c */ + +void init_flatspec(struct FlattenSpec *); + +void add_layer_request(struct FlattenSpec *,const char *name); +struct xcfLayer *lastlayerspec(struct FlattenSpec *,const char *option); + +typedef enum out_color_mode (*guesser) (struct FlattenSpec *,rgba **); + +/* Call this after processing options, and after opening the XCF file */ +void complete_flatspec(struct FlattenSpec *,guesser); +void analyse_colormode(struct FlattenSpec *,rgba **allPixels,guesser); + +/* From flatten.c */ + +typedef void (*lineCallback)(unsigned num,rgba *pixels); +void flattenIncrementally(struct FlattenSpec *,lineCallback); +rgba **flattenAll(struct FlattenSpec*); +void shipoutWithCallback(struct FlattenSpec *,rgba **pixels,lineCallback); + +#endif /* FLATTEN_H */ diff --git a/kernel/kls_xcf/xcf2pnm/io-unix.c b/kernel/kls_xcf/xcf2pnm/io-unix.c new file mode 100644 index 0000000..38dc4e4 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/io-unix.c @@ -0,0 +1,176 @@ +/* OS-specific IO functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#if HAVE_MMAP +#include <sys/mman.h> +#endif + +static FILE *xcfstream = 0 ; + +void +free_or_close_xcf(void) +{ + if( xcf_file ) { + if( xcfstream ) { + munmap(xcf_file,xcf_length) ; + fclose(xcfstream); + xcf_file = 0 ; + xcfstream = 0 ; + } else { + free(xcf_file) ; + xcf_file = 0 ; + } + } +} + +void +read_or_mmap_xcf(const char *filename,const char *unzipper) +{ + struct stat statbuf ; + + free_or_close_xcf() ; + + if( strcmp(filename,"-") != 0 ) { + if( access(filename,R_OK) != 0 ) + FatalGeneric(21,"!%s",filename); + } + + if( !unzipper ) { + const char *pc ; + pc = filename + strlen(filename) ; + if( pc-filename > 2 && strcmp(pc-2,"gz") == 0 ) + unzipper = "zcat" ; + else if ( pc-filename > 3 && strcmp(pc-3,"bz2") == 0 ) + unzipper = "bzcat" ; + else + unzipper = "" ; + } else if( strcmp(unzipper,"cat") == 0 ) + unzipper = "" ; + + if( *unzipper ) { + int pid, status, outfd ; +#if HAVE_MMAP + xcfstream = tmpfile() ; + if( !xcfstream ) + FatalUnexpected(_("!Cannot create temporary unzipped file")); + outfd = fileno(xcfstream) ; +#else + int fh[2] ; + if( pipe(fh) < 0 ) + FatalUnexpected("!Cannot create pipe for %s",unzipper); + xcfstream = fdopen(fh[1],"rb") ; + if( !xcfstream ) + FatalUnexpected("!Cannot fdopen() unzipper pipe"); + outfd = fh[0] ; +#endif + if( (pid = fork()) == 0 ) { + /* We're the child */ + if( dup2(outfd,1) < 0 ) { + perror("Cannot dup2 in unzip process"); + exit(127) ; + } + fclose(xcfstream) ; + execlp(unzipper,unzipper,filename,NULL) ; + fprintf(stderr,_("Cannot execute ")); + perror(unzipper); + exit(126) ; + } +#if HAVE_MMAP + while( wait(&status) != pid ) + ; + if( WIFEXITED(status) ) { + status = WEXITSTATUS(status) ; + if( status > 0 ) { + fclose(xcfstream) ; + xcfstream = 0 ; + FatalGeneric(status,NULL); + } + } else { + fclose(xcfstream) ; + xcfstream = 0 ; + FatalGeneric(126,_("%s terminated abnormally"),unzipper); + } +#else + close(fh[0]) ; +#endif + } else if( strcmp(filename,"-") == 0 ) { + xcfstream = fdopen(dup(0),"rb") ; + if( !xcfstream ) + FatalUnexpected("!Cannot dup stdin for input") ; + } else { + xcfstream = fopen(filename,"rb") ; + if( !xcfstream ) + FatalGeneric(21,_("!Cannot open %s"),filename); + } + /* OK, now we have an open stream ... */ + if( fstat(fileno(xcfstream),&statbuf) == 0 && + (statbuf.st_mode & S_IFMT) == S_IFREG ) { + xcf_length = statbuf.st_size ; +#if HAVE_MMAP + xcf_file = mmap(0,xcf_length,PROT_READ,MAP_SHARED,fileno(xcfstream),0); + if( xcf_file != (void*)-1 ) + return ; + if( errno != ENODEV ) { + int saved = errno ; + fclose(xcfstream) ; + xcf_file = 0 ; + errno = saved ; + FatalUnexpected("!Could not mmap input"); + } +#endif + xcf_file = malloc(xcf_length); + if( xcf_file == 0 ) + FatalUnexpected(_("Out of memory for xcf data")); + if( fread(xcf_file,1,xcf_length,xcfstream) != xcf_length ) { + if( feof(xcfstream) ) + FatalUnexpected(_("XCF file shrunk while reading it")); + else + FatalUnexpected(_("!Could not read xcf data")); + } + fclose(xcfstream) ; + xcfstream = 0 ; + } else { + size_t blocksize = 0x80000 ; /* 512 KB */ + xcf_length = 0 ; + xcf_file = 0 ; + while(1) { + xcf_file = realloc(xcf_file,blocksize) ; + if( xcf_file == 0 ) + FatalUnexpected(_("Out of memory for xcf data")); + size_t actual = fread(xcf_file+xcf_length,1,blocksize-xcf_length, + xcfstream) ; + xcf_length += actual ; + if( feof(xcfstream) ) + break ; + if( xcf_length < blocksize ) { + FatalUnexpected(_("!Could not read xcf data")) ; + } + blocksize += (blocksize >> 1) & ~(size_t)0x3FFF ; /* 16 KB granularity */ + } + fclose(xcfstream) ; + xcfstream = 0 ; + } +} diff --git a/kernel/kls_xcf/xcf2pnm/options.i b/kernel/kls_xcf/xcf2pnm/options.i new file mode 100644 index 0000000..f891767 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/options.i @@ -0,0 +1,410 @@ +/* Option processing for xcftools -*- C -*- + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +OPTIONGROUP(1i,General options); + +OPTION('h',--help,show this message, + (Print an option summery to standard output and exit with a + return code of 0. + )); +exit(0); + +OPTION('V',--version,show version, + (Print the version numer of + .B xcftools + to standard output and exit with a return code of 0. + )); +printf(OPTI_TARGET " - " PACKAGE_STRING "\n"); +exit(0); + +OPTION('v',--verbose,show progress messages, + (Print progress messages about the conversion to standard error. + )); +verboseFlag = 1 ; +break ; + +OPTION('j',--bzip,input is bzip2 compressed, + (Equivalent to + .BR "\-Z bzcat" . + Default if the filename ends with + .BR bz2 . + )); +unzipper = "bzcat" ; +break ; + +OPTION('z',--gzip,input is gzip compressed, + (Equivalent to + .BR "\-Z zcat" . + Default if the filename ends with + .BR gz . + )); +unzipper = "zcat" ; +break ; + +OPTION('Z',--unpack,(command) use 'command' to decompress input, + (Specify a command that the input file is filtered through + before being interpreted as an XCF file. The command is invoked as + .I command filename + and must produce output to its standard output. + Note that it is not possible to specify arguments as part of + .IR command . + An uncompressor is selected automatically if the filename ends + with + .B gz + or + .BR bz2 ; + to suppress this, use + .B \-Z cat + (which is implemented without actually starting a + .BR cat (1) + process). + )); +unzipper = optarg ; +break ; + +#ifdef XCF2FOO + +OPTION('o',--output,(filename) name output file, + (Write the converted picture to + .I filename + instead of to standard output. + )); +flatspec.output_filename = optarg ; +break ; + +#ifdef XCF2PNM +OPTION('a',--alpha,(filename) write transparency map, + (Output a transparency mask for the flattened image to + .I filename + as a + .BR pgm (5) + file, in addition to the ordinary output. + If the flattened image is completely opaque, this will produce an + error message and exit status 101; + use + .B \-A + to suppress this. + )); +flatspec.transmap_filename = optarg ; +break ; +#endif + +OPTION('b',--background,(color) select background color, + (Use this color for transparent pixels in the image. + The color can be given as + .B #rrggbb + or + .B #rgb + hexadecimal values, + or as an X11 color name + (which will only work if a color name database can be found + in one of a number of standard locations). + )); +{ + unsigned r,g,b ; + unsigned long hex ; + int met = 0 ; + if( *optarg == '#' ) + sscanf(optarg+1,"%lx%n",&hex,&met); + if( met == 3 && strlen(optarg) == 4 ) { + r = ((hex >> 8) & 0xF) * 0x11 ; + g = ((hex >> 4) & 0xF) * 0x11 ; + b = ((hex >> 0) & 0xF) * 0x11 ; + } else if( met == 6 && strlen(optarg) == 7 ) { + r = ((hex >> 16) & 0xFF) ; + g = ((hex >> 8) & 0xFF) ; + b = ((hex >> 0) & 0xFF) ; + } else if( strcasecmp(optarg,"black") == 0 ) + r = g = b = 0 ; + else if( strcasecmp(optarg,"white") == 0 ) + r = g = b = 255 ; + else { + const char *filenames[] = { "/etc/X11/rgb.txt", + "/usr/lib/X11/rgb.txt", + "/usr/share/X11/rgb.txt", + NULL }; + const char **fnp ; + r = (unsigned)-1 ; + int any = 0 ; + for( fnp = filenames; r == (unsigned)-1 && fnp && *fnp; fnp++ ) { + FILE *colortable = fopen(*fnp,"rt"); + if( colortable ) { + any = 1 ; + int clen ; + char colorbuf[80] ; + do { + if( !fgets(colorbuf,sizeof colorbuf,colortable) ) { + r = (unsigned)-1 ; + break ; + } + clen = strlen(colorbuf); + while( clen && isspace(colorbuf[clen-1]) ) + clen-- ; + colorbuf[clen] = '\0' ; + clen = 0 ; + sscanf(colorbuf," %u %u %u %n",&r,&g,&b,&clen); + } while( clen == 0 || strcasecmp(colorbuf+clen,optarg) != 0 ); + fclose(colortable) ; + } + } + if( !any ) { + fprintf(stderr,_("Could not find X11 color database\n")); + } + } + if( r == (unsigned)-1 ) + FatalGeneric(20,_("Unknown background color '%s'"),optarg); + flatspec.default_pixel = ((rgba)255 << ALPHA_SHIFT) + + ((rgba)r << RED_SHIFT) + + ((rgba)g << GREEN_SHIFT) + + ((rgba)b << BLUE_SHIFT); + break ; +} + +OPTION('A',--force-alpha,force alpha channel in output, + (Invent a trivial alpha channel even if the flattened image is + completely opaque. + )); +flatspec.default_pixel = FORCE_ALPHA_CHANNEL ; +break ; + +OPTION('c',--color --colour,select color output, + (Force the output to use RGB color space even if it there are + more compact alternatives. +#ifdef XCF2PNM + This will be selected automatically if the output file''s name + ends with + .BR .ppm . +#endif + )); +flatspec.out_color_mode = COLOR_RGB ; +break ; + +OPTION('g',--gray --grey,select grayscale output, + (Force the output to be a grayscale image even if it may be monochrome. + If any colored pixels are encountered, exit with status 103. + This will be selected automatically if the output file''s name + ends with + .BR .pgm . + )); +flatspec.out_color_mode = COLOR_GRAY ; +break ; + +#ifdef XCF2PNM +OPTION('m',--mono,select monochrome output, + (Force the output to be a monochrome image. + If any colors except black and white are encountered, exit with + status 103. + This will be selected automatically if the output file''s name + ends with + .BR .pbm . + )); +flatspec.out_color_mode = COLOR_MONO ; +break ; +#endif + +#ifdef XCF2PNM +OPTION('n',--pnm,select -c/-g/-m by image contents, + (Suppress the automatic choice of + .BR \-c , + .BR \-g , + or + .BR \-m + based on output filename, and instead select the output format + based on image contents. + This is the default if the filename is not recognized, and + when writing to stdout. + )); +flatspec.out_color_mode = COLOR_BY_CONTENTS ; +break ; +#endif + +OPTION('T',--truecolor,treat indexed images as RGB for flattening, + (Use standard RGB compositing for flattening indexed layers. + Without this option, + .B \*p + will mimic the Gimp''s current strategy of rounding each + alpha value to either full transparency or full opacity, + and interpret all layer modes as + .BR Normal . + )); +flatspec.gimpish_indexed = 0 ; +break ; + +OPTION('G',--for-gif,disallow partial transparency, + (Assert that the flattened image will have no partial transparency + (allowing a more compact representation of the alpha output). + Exit with status 102 if the flattened image has any partial + transparency. + If + .B \-b + is also given, this tests whether there there is partial + transparency before applying the background color. + )); +flatspec.partial_transparency_mode = FORBID_PARTIAL_TRANSPARENCY ; +break ; + +OPTION('D',--dissolve,dissolve partial transparency, + (Do a "dissolve" step to eliminate partial transparency after + flattening. + If + .B \-b + is also given, this happens before the background color is applied. + )); +flatspec.partial_transparency_mode = DISSOLVE_PARTIAL_TRANSPARENCY ; +break ; + +OPTION('f',--full-image,flatten to memory; then analyse, + (First flatten the entire image to a memory buffer before writing + output. Then analyse the image to decide on the details of the + output format (e.g., whether a grayscale output is sufficient). + Without this option, the program flattens only a singe row of "tiles" + (height 64) at a time. + )); +flatspec.process_in_memory = 1 ; +break ; + +OPTION('S',--size,(w"x"h) crop image while converting, + (Crop the converted image to width \fIw\fP and height \fIh\fP. + )); +{ + unsigned w,h ; + int n = 0 ; + sscanf(optarg,"%ux%u%n",&w,&h,&n) ; + if( n && n == strlen(optarg) ) { + if( flatspec.window_mode == AUTOCROP ) flatspec.window_mode = USE_CANVAS ; + flatspec.window_mode |= MANUAL_CROP ; + flatspec.dim.width = w ; + flatspec.dim.height = h ; + } else + FatalGeneric(20,_("-S option must have an argument of the form wxh")); + break ; +} + +OPTION('O',--offset,(x","y) translate converted part of image, + (Offset the converted part of the image from the top-left corner + of the XCF canvas. Usually used with + .BR \-S . + )); +{ + int x,y ; + int n = 0 ; + sscanf(optarg,"%d,%d%n",&x,&y,&n) ; + if( n && n == strlen(optarg) ) { + if( flatspec.window_mode == AUTOCROP ) flatspec.window_mode = USE_CANVAS ; + flatspec.window_mode |= MANUAL_OFFSET ; + flatspec.dim.c.l = x ; + flatspec.dim.c.t = y ; + } else + FatalGeneric(20,_("-O option must have an argument of the form x,y")); + break ; +} + +OPTION('C',--autocrop,autocrop to visible layer boundaries, + (Crop and offset the converted part of the image to just include + the boundaries of the visible (or selected) layers. + (Note that the + .I contents + of the layers is not taken into account when autocropping). + .IP + In the absence of options that specify otherwise, the converted + image will cover the entire XCF canvas. + )); +flatspec.window_mode = AUTOCROP ; +break ; + +#ifndef XCFVIEW +OPTIONGROUP(1il,Layer-selection options); +#endif + +OPTION(300,--mode,(mode) set layer mode, + (Set the layer mode (e.g., + .B Normal + or + .BR Multiply ). + )); +{ + GimpLayerModeEffects m ; + #ifdef ENABLE_NLS + for( m = 0; m < GimpLayerModeEffects_LAST; m++ ) + if( strcmp(optarg,_(showGimpLayerModeEffects(m))) == 0 ) + goto found_localized ; + #endif + + for( m = 0; strcmp(optarg,showGimpLayerModeEffects(m)) != 0 ; m++ ) { + if( m > GimpLayerModeEffects_LAST ) + FatalGeneric(20,_("Layer mode '%s' is unknown"),optarg); + } + found_localized: + lastlayerspec(&flatspec,"--mode")->mode = m ; + break ; +} + +OPTION(301,--percent,(n) set opacity in percent, + (Set the opacity on a scale from 0 to 100 + (as in the Gimp user interface). + )); +{ + unsigned pct ; + int n ; + sscanf(optarg,"%u%n",&pct,&n) ; + if( n != strlen(optarg) || pct > 100 ) + FatalGeneric(20,_("The argument to --percent is not a percentage")); + lastlayerspec(&flatspec,"--percent")->opacity = pct * 255 / 100 ; + break ; +} + +OPTION(302,--opacity,(n) set opacity in 1/255 units, + (Set the opacity on a scale from 0 to 255 (as used internally) + )); +{ + unsigned alpha ; + int n ; + sscanf(optarg,"%u%n",&alpha,&n) ; + if( n != strlen(optarg) || alpha > 255 ) + FatalGeneric(20,_("The argument to --opacity is not a number " + "between 0 and 255")); + lastlayerspec(&flatspec,"--percent")->opacity = alpha ; + break ; +} + +OPTION(303,--mask,enable layer mask, + (Enable the layer mask. + )); +lastlayerspec(&flatspec,"--mask")->hasMask = 1 ; +break ; + +OPTION(304,--nomask,disable layer mask, + (Disable the layer mask. + )); +lastlayerspec(&flatspec,"--nomask")->hasMask = 0 ; +break ; + +#endif /* XCF2FOO */ + +OPTIONGROUP(1i,) + +#if HAVE_ICONV +OPTION('u',--utf8,use UTF-8 for layer names, + (Use the raw UTF-8 representation from the XCF file to compare + and display layer names. + Ordinarily, layer names will be converted to the character set + of the current locale. + )); +use_utf8 = 1 ; +break ; +#endif diff --git a/kernel/kls_xcf/xcf2pnm/pixels.c b/kernel/kls_xcf/xcf2pnm/pixels.c new file mode 100644 index 0000000..65891f9 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/pixels.c @@ -0,0 +1,487 @@ +/* Pixel and tile functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#define DEBUG +#include "xcftools.h" +#include "pixels.h" +#include <assert.h> +#include <string.h> + +rgba colormap[256] ; +unsigned colormapLength=0 ; + +int +degrayPixel(rgba pixel) +{ + if( ((pixel >> RED_SHIFT) & 255) == ((pixel >> GREEN_SHIFT) & 255) && + ((pixel >> RED_SHIFT) & 255) == ((pixel >> BLUE_SHIFT) & 255) ) + return (pixel >> RED_SHIFT) & 255 ; + return -1 ; +} + +/* ****************************************************************** */ + +typedef const struct _convertParams { + int bpp ; + int shift[4] ; + uint32_t base_pixel ; + const rgba *lookup ; +} convertParams ; +#define RGB_SHIFT RED_SHIFT, GREEN_SHIFT, BLUE_SHIFT +#define OPAQUE (255 << ALPHA_SHIFT) +static convertParams convertRGB = { 3, {RGB_SHIFT}, OPAQUE, 0 }; +static convertParams convertRGBA = { 4, {RGB_SHIFT, ALPHA_SHIFT}, 0,0 }; +static convertParams convertGRAY = { 1, {-1}, OPAQUE, graytable }; +static convertParams convertGRAYA = { 2, {-1,ALPHA_SHIFT}, 0, graytable }; +static convertParams convertINDEXED = { 1, {-1}, OPAQUE, colormap }; +static convertParams convertINDEXEDA = { 2, {-1,ALPHA_SHIFT}, 0, colormap }; + +static convertParams convertColormap = { 3, {RGB_SHIFT}, 0, 0 }; +static convertParams convertChannel = { 1, {ALPHA_SHIFT}, 0, 0 }; + +/* ****************************************************************** */ + +static inline int +tileDirectoryOneLevel(struct tileDimensions *dim,uint32_t ptr) +{ + if( ptr == 0 ) + return 0 ; + if( xcfL(ptr ) != dim->c.r - dim->c.l || + xcfL(ptr+4) != dim->c.b - dim->c.t ) + FatalBadXCF("Drawable size mismatch at %" PRIX32, ptr); + return ptr += 8 ; +} + +static void +initTileDirectory(struct tileDimensions *dim,struct xcfTiles *tiles, + const char *type) +{ + uint32_t ptr ; + uint32_t data ; + + ptr = tiles->hierarchy ; + tiles->hierarchy = 0 ; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + if( tiles->params == &convertChannel ) { + /* A layer mask is a channel. + * Skip a name and a property list. + */ + xcfString(ptr,&ptr); + while( xcfNextprop(&ptr,&data) != PROP_END ) + ; + ptr = xcfOffset(ptr,4*4); + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + } + /* The XCF format has a dummy "hierarchy" level which was + * once meant to mean something, but never happened. It contains + * the bpp value and a list of "level" pointers; but only the + * first level actually contains data. + */ + data = xcfL(ptr) ; + if( xcfL(ptr) != tiles->params->bpp ) + FatalBadXCF("%"PRIu32" bytes per pixel for %s drawable",xcfL(ptr),type); + ptr = xcfOffset(ptr+4,3*4) ; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + + xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr); + if( xcfL(ptr + dim->ntiles*4) != 0 ) + FatalBadXCF("Wrong sized tile directory at %" PRIX32,ptr); +#define REUSE_RAW_DATA tiles->tileptrs = (uint32_t*)(xcf_file + ptr) +#if defined(WORDS_BIGENDIAN) && defined(CAN_DO_UNALIGNED_WORDS) + REUSE_RAW_DATA; +#else +# if defined(WORDS_BIGENDIAN) + if( (ptr&3) == 0 ) REUSE_RAW_DATA; else +# endif + { + unsigned i ; + tiles->tileptrs = xcfmalloc(dim->ntiles * sizeof(uint32_t)) ; + for( i = 0 ; i < dim->ntiles ; i++ ) + tiles->tileptrs[i] = xcfL(ptr+i*4); + } +#endif +} + +void +initLayer(struct xcfLayer *layer) { + if( layer->dim.ntiles == 0 || + (layer->pixels.hierarchy == 0 && layer->mask.hierarchy == 0) ) + return ; + switch(layer->type) { +#define DEF(X) case GIMP_##X##_IMAGE: layer->pixels.params = &convert##X; break + DEF(RGB); + DEF(RGBA); + DEF(GRAY); + DEF(GRAYA); + DEF(INDEXED); + DEF(INDEXEDA); + default: + FatalUnsupportedXCF(_("Layer type %s"),_(showGimpImageType(layer->type))); + } + initTileDirectory(&layer->dim,&layer->pixels, + _(showGimpImageType(layer->type))); + layer->mask.params = &convertChannel ; + initTileDirectory(&layer->dim,&layer->mask,"layer mask"); +} +static void copyStraightPixels(rgba *dest,unsigned npixels, + uint32_t ptr,convertParams *params); +void +initColormap(void) { + uint32_t ncolors ; + if( XCF.colormapptr == 0 ) { + colormapLength = 0 ; + return ; + } + ncolors = xcfL(XCF.colormapptr) ; + if( ncolors > 256 ) + FatalUnsupportedXCF(_("Color map has more than 256 entries")); + copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap); + colormapLength = ncolors ; +#ifdef xDEBUG + { + unsigned j ; + fprintf(stderr,"Colormap decoding OK\n"); + for( j = 0 ; j < ncolors ; j++ ) { + if( j % 8 == 0 ) fprintf(stderr,"\n"); + fprintf(stderr," %08x",colormap[j]); + } + fprintf(stderr,"\n"); + } +#endif +} + +/* ****************************************************************** */ + +struct Tile * +newTile(struct rect r) +{ + unsigned npixels = (unsigned)(r.b-r.t) * (unsigned)(r.r-r.l) ; + struct Tile *data + = xcfmalloc(sizeof(struct Tile) - + sizeof(rgba)*(TILE_HEIGHT*TILE_WIDTH - npixels)) ; + data->count = npixels ; + data->refcount = 1 ; + data->summary = 0 ; + return data ; +} + +struct Tile * +forkTile(struct Tile* tile) +{ + if( ++tile->refcount <= 0 ) + FatalUnsupportedXCF(_("Unbelievably many layers?\n" + "More likely to be a bug in %s"),progname); + return tile ; +} + +void +freeTile(struct Tile* tile) +{ + if( --tile->refcount == 0 ) + xcffree(tile) ; +} + +summary_t +tileSummary(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + if( (tile->summary & TILESUMMARY_UPTODATE) != 0 ) + return tile->summary ; + summary = TILESUMMARY_ALLNULL + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i=0; summary && i<tile->count; i++ ) { + if( FULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLNULL ; + else if( NULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLFULL ; + else + summary = 0 ; + } + summary += TILESUMMARY_UPTODATE ; + tile->summary = summary ; + return summary ; +} + +void +fillTile(struct Tile *tile,rgba data) +{ + unsigned i ; + for( i = 0 ; i < tile->count ; i++ ) + tile->pixels[i] = data ; + if( FULLALPHA(data) ) + tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP; + else if (NULLALPHA(data) ) + tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLNULL+TILESUMMARY_CRISP; + else + tile->summary = TILESUMMARY_UPTODATE ; +} + +/* ****************************************************************** */ + +static void +copyStraightPixels(rgba *dest,unsigned npixels, + uint32_t ptr,convertParams *params) +{ + unsigned bpp = params->bpp; + const rgba *lookup = params->lookup; + rgba base_pixel = params->base_pixel ; + uint8_t *bp = xcf_file + ptr ; + xcfCheckspace(ptr,bpp*npixels, + "pixel array (%u x %d bpp) at %"PRIX32,npixels,bpp,ptr); + while( npixels-- ) { + rgba pixel = base_pixel ; + unsigned i ; + for( i = 0 ; i < bpp ; i++ ) { + if( params->shift[i] < 0 ) { + pixel += lookup[*bp++] ; + } else { + pixel += *bp++ << params->shift[i] ; + } + } + *dest++ = pixel ; + } +} + +static inline void +copyRLEpixels(rgba *dest,unsigned npixels,uint32_t ptr,convertParams *params) +{ + unsigned i,j ; + rgba base_pixel = params->base_pixel ; + +#ifdef xDEBUG + fprintf(stderr,"RLE stream at %x, want %u x %u pixels, base %x\n", + ptr,params->bpp,npixels,base_pixel); +#endif + + + /* This algorithm depends on the indexed byte always being the first one */ + if( params->shift[0] < -1 ) + base_pixel = 0 ; + for( j = npixels ; j-- ; ) + dest[j] = base_pixel ; + + for( i = 0 ; i < params->bpp ; i++ ) { + int shift = params->shift[i] ; + if( shift < 0 ) + shift = 0 ; + for( j = 0 ; j < npixels ; ) { + int countspec ; + unsigned count ; + xcfCheckspace(ptr,2,"RLE data stream"); + countspec = (int8_t) xcf_file[ptr++] ; + count = countspec >= 0 ? countspec+1 : -countspec ; + if( count == 128 ) { + xcfCheckspace(ptr,3,"RLE long count"); + count = xcf_file[ptr++] << 8 ; + count += xcf_file[ptr++] ; + } + if( j + count > npixels ) + FatalBadXCF("Overlong RLE run at %"PRIX32" (plane %u, %u left)", + ptr,i,npixels-j); + if( countspec >= 0 ) { + rgba data = (uint32_t) xcf_file[ptr++] << shift ; + while( count-- ) + dest[j++] += data ; + } else { + while( count-- ) + dest[j++] += (uint32_t) xcf_file[ptr++] << shift ; + } + } + if( i == 0 && params->shift[0] < 0 ) { + const rgba *lookup = params->lookup ; + base_pixel = params->base_pixel ; + for( j = npixels ; j-- ; ) { + dest[j] = lookup[dest[j]-base_pixel] + base_pixel ; + } + } + } +#ifdef xDEBUG + fprintf(stderr,"RLE decoding OK at %"PRIX32"\n",ptr); + /* + for( j = 0 ; j < npixels ; j++ ) { + if( j % 8 == 0 ) fprintf(stderr,"\n"); + fprintf(stderr," %8x",dest[j]); + } + fprintf(stderr,"\n"); + */ +#endif +} + +static inline void +copyTilePixels(struct Tile *dest, uint32_t ptr,convertParams *params) +{ + if( FULLALPHA(params->base_pixel) ) + dest->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP; + else + dest->summary = 0 ; + switch( XCF.compression ) { + case COMPRESS_NONE: + copyStraightPixels(dest->pixels,dest->count,ptr,params); + break ; + case COMPRESS_RLE: + copyRLEpixels(dest->pixels,dest->count,ptr,params); + break ; + default: + FatalUnsupportedXCF(_("%s compression"), + _(showXcfCompressionType(XCF.compression))); + } +} + +static struct Tile * +getMaskOrLayerTile(struct tileDimensions *dim, struct xcfTiles *tiles, + struct rect want) +{ + struct Tile *tile = newTile(want); + + assert( want.l < want.r && want.t < want.b ); + if( tiles->tileptrs == 0 ) { + fillTile(tile,0); + return tile ; + } + +#ifdef xDEBUG + fprintf(stderr,"getMaskOrLayer: (%d-%d),(%d-%d)\n",left,right,top,bottom); +#endif + + if( isSubrect(want,dim->c) && + (want.l - dim->c.l) % TILE_WIDTH == 0 && + (want.t - dim->c.t) % TILE_HEIGHT == 0 ) { + unsigned tx = (want.l - dim->c.l) / TILE_WIDTH ; + unsigned ty = (want.t - dim->c.t) / TILE_WIDTH ; + if( want.r == TILEXn(*dim,tx+1) && want.b == TILEYn(*dim,ty+1) ) { + /* The common case? An entire single tile from the layer */ + copyTilePixels(tile,tiles->tileptrs[tx + ty*dim->tilesx],tiles->params); + return tile ; + } + } + + /* OK, we must construct the wanted tile as a jigsaw */ + { + unsigned width = want.r-want.l ; + rgba *pixvert = tile->pixels ; + rgba *pixhoriz ; + unsigned y, ty, l0, l1, lstart, lnum ; + unsigned x, tx, c0, c1, cstart, cnum ; + + if( !isSubrect(want,dim->c) ) { + if( want.l < dim->c.l ) pixvert += (dim->c.l - want.l), + want.l = dim->c.l ; + if( want.r > dim->c.r ) want.r = dim->c.r ; + if( want.t < dim->c.t ) pixvert += (dim->c.t - want.t) * width, + want.t = dim->c.t ; + if( want.b > dim->c.b ) want.b = dim->c.b ; + fillTile(tile,0); + } else { + tile->summary = -1 ; /* I.e. whatever the jigsaw pieces say */ + } + +#ifdef xDEBUG + fprintf(stderr,"jig0 (%d-%d),(%d-%d)\n",left,right,top,bottom); +#endif + + for( y=want.t, ty=(want.t-dim->c.t)/TILE_HEIGHT, l0=TILEYn(*dim,ty); + y<want.b; + pixvert += lnum*width, ty++, y=l0=l1 ) { + l1 = TILEYn(*dim,ty+1) ; + lstart = y - l0 ; + lnum = (l1 > want.b ? want.b : l1) - y ; + + pixhoriz = pixvert ; + for( x=want.l, tx=(want.l-dim->c.l)/TILE_WIDTH, c0=TILEXn(*dim,tx); + x<want.r; + pixhoriz += cnum, tx++, x=c0=c1 ) { + c1 = TILEXn(*dim,tx+1); + cstart = x - c0 ; + cnum = (c1 > want.r ? want.r : c1) - x ; + + { + static struct Tile tmptile ; + unsigned dwidth = c1-c0 ; + unsigned i, j ; + tmptile.count = (c1-c0)*(l1-l0) ; +#ifdef xDEBUG + fprintf(stderr,"jig ty=%u(%u-%u-%u)(%u+%u) tx=%u(%u-%u-%u)(%u+%u)\n", + ty,l0,y,l1,lstart,lnum, + tx,c0,x,c1,cstart,cnum); +#endif + copyTilePixels(&tmptile, + tiles->tileptrs[tx+ty*dim->tilesx],tiles->params); + for(i=0; i<lnum; i++) + for(j=0; j<cnum; j++) + pixhoriz[i*width+j] + = tmptile.pixels[(i+lstart)*dwidth+(j+cstart)]; + tile->summary &= tmptile.summary ; + } + } + } + } + return tile ; +} + +void +applyMask(struct Tile *tile, struct Tile *mask) +{ + unsigned i ; + assertTileCompatibility(tile,mask); + assert( tile->count == mask->count ); + INIT_SCALETABLE_IF(1); + invalidateSummary(tile,0); + for( i=0; i < tile->count ;i++ ) + tile->pixels[i] = NEWALPHA(tile->pixels[i], + scaletable[mask->pixels[i]>>ALPHA_SHIFT] + [ALPHA(tile->pixels[i])]); + freeTile(mask); +} + +struct Tile * +getLayerTile(struct xcfLayer *layer,const struct rect *where) +{ + struct Tile *data ; + +#ifdef xDEBUG + fprintf(stderr,"getLayerTile(%s): (%d-%d),(%d-%d)\n", + layer->name,where->l,where->r,where->t,where->b); +#endif + + if( disjointRects(*where,layer->dim.c) || + layer->opacity == 0 ) { + data = newTile(*where); + fillTile(data,0); + return data ; + } + + data = getMaskOrLayerTile(&layer->dim,&layer->pixels,*where); + if( (data->summary & TILESUMMARY_ALLNULL) != 0 ) + return data ; + if( layer->hasMask ) { + struct Tile *mask = getMaskOrLayerTile(&layer->dim,&layer->mask,*where); + applyMask(data,mask); + } + if( layer->opacity < 255 ) { + const uint8_t *ourtable ; + int i ; + invalidateSummary(data,~(TILESUMMARY_CRISP | TILESUMMARY_ALLFULL)); + INIT_SCALETABLE_IF(1); + ourtable = scaletable[layer->opacity] ; + for( i=0; i < data->count; i++ ) + data->pixels[i] + = NEWALPHA(data->pixels[i],ourtable[ALPHA(data->pixels[i])]) ; + } + return data ; +} + diff --git a/kernel/kls_xcf/xcf2pnm/pixels.h b/kernel/kls_xcf/xcf2pnm/pixels.h new file mode 100644 index 0000000..1ee5219 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/pixels.h @@ -0,0 +1,128 @@ +/* Pixel and tile functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PIXELS_H +#define PIXELS_H + +#include "xcftools.h" + +/* MACROS FOR INTERNAL PIXEL ORDERING HERE */ +/*=========================================*/ +/* In principle the internal representation of pixels may change. + * - this was supposed to allow an optimization where a layer could + * be represented as a pointer into the mmapped xcf file, if + * alignment, bpp, and endianness agreed (the point was that the + * pixel representation had to agree with the endianness). + * + * However, it turns out that the current Gimp _always_ saves images + * with RLE encoding of tiles, so such an effort would be in vain. + * + * Just for modularity, nevertheless try to isolate knowledge of + * the RGBA-to-machine-word packing in this section of the + * header file. Define new macros if necessary. + * + * Given that we don't have to agree with the uncompressed + * RLE format, we choose to have the alpha in the _least_ + * significant byte on all archs - it is tested and used more + * often than the visible channels. + */ +typedef uint32_t rgba ; + +#define ALPHA_SHIFT 0 +#define RED_SHIFT 8 +#define GREEN_SHIFT 16 +#define BLUE_SHIFT 24 + +#define ALPHA(rgba) ((uint8_t)(rgba)) +#define FULLALPHA(rgba) ((uint8_t)(rgba) == 255) +#define NULLALPHA(rgba) ((uint8_t)(rgba) == 0) +#define NEWALPHA(rgb,a) (((rgba)(rgb) & 0xFFFFFF00) + (a)) + +#ifdef PRECOMPUTED_SCALETABLE +extern const uint8_t scaletable[256][256] ; +#define INIT_SCALETABLE_IF(foo) ((void)0) +#else +extern uint8_t scaletable[256][256] ; +extern int ok_scaletable ; +void mk_scaletable(void); +#define INIT_SCALETABLE_IF(foo) \ + (ok_scaletable || !(foo) || (mk_scaletable(),0) ) +#endif + +extern const rgba graytable[256] ; +extern rgba colormap[256] ; +extern unsigned colormapLength ; +void initLayer(struct xcfLayer *); +void initColormap(); + +int degrayPixel(rgba); /* returns -1 for non-gray pixels */ + +/* ******************************************************* */ + +#define TILEXn(dim,tx) \ + ((tx)==(dim).tilesx ? (dim).c.r : (dim).c.l + ((tx)*TILE_WIDTH)) +#define TILEYn(dim,ty) \ + ((ty)==(dim).tilesy ? (dim).c.b : (dim).c.t + ((ty)*TILE_HEIGHT)) + +#if __i386__ +/* This is probably the only common architecture where small constants + * are more efficient for byte operations. + */ +typedef int8_t summary_t ; +typedef short int refcount_t ; +#else +typedef int summary_t ; +typedef int refcount_t ; +#endif + +#define TILESUMMARY_UPTODATE 8 +#define TILESUMMARY_ALLNULL 4 +#define TILESUMMARY_ALLFULL 2 +#define TILESUMMARY_CRISP 1 /* everyting either null or full */ +struct Tile { + refcount_t refcount ; + summary_t summary ; /* a combination of TIMESUMMARY_FOO constatns */ + unsigned count ; + rgba pixels[TILE_WIDTH * TILE_HEIGHT]; +}; +/* Actually, the Tile structures that get allocated many not have + * room for that many pixels. We subtract the space for those we don't + * use - which is Not Legal C, but ought to be portable. + * OTOH, one can also use a static struct Tile for temporary storage. + */ + + +#define assertTileCompatibility(t1,t2) assert((t1)->count==(t2)->count) + +struct Tile *newTile(struct rect); +struct Tile *forkTile(struct Tile*); +void freeTile(struct Tile*); +#define invalidateSummary(tile,mask) \ + do{ assert((tile)->refcount==1); (tile)->summary &= mask; } while(0) +summary_t __ATTRIBUTE__((pure)) tileSummary(struct Tile *); + +void fillTile(struct Tile*,rgba); + +/* applyMask() destructively changes tile, + * applyMask() gets ownership of mask + */ +void applyMask(struct Tile *tile, struct Tile *mask); + +struct Tile *getLayerTile(struct xcfLayer *,const struct rect *); + +#endif /* FLATTEN_H */ diff --git a/kernel/kls_xcf/xcf2pnm/scaletab.c b/kernel/kls_xcf/xcf2pnm/scaletab.c new file mode 100644 index 0000000..c09867e --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/scaletab.c @@ -0,0 +1,42 @@ +/* Run-time scaletable computation for Xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "pixels.h" +#ifndef PRECOMPUTED_SCALETABLE + +uint8_t scaletable[256][256] ; +int ok_scaletable = 0 ; + +void +mk_scaletable(void) +{ + unsigned p, q, r ; + if( ok_scaletable ) return ; + for( p = 0 ; p < 128 ; p++ ) + for( q = 0 ; q <= p ; q++ ) { + r = (p*q+127)/255 ; + scaletable[p][q] = scaletable[q][p] = r ; + scaletable[255-p][q] = scaletable[q][255-p] = q-r ; + scaletable[p][255-q] = scaletable[255-q][p] = p-r ; + scaletable[255-p][255-q] = scaletable[255-q][255-p] = (255-q)-(p-r) ; + } + ok_scaletable = 1 ; +} + +#endif + diff --git a/kernel/kls_xcf/xcf2pnm/table.c b/kernel/kls_xcf/xcf2pnm/table.c new file mode 100644 index 0000000..9e76923 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/table.c @@ -0,0 +1,263 @@ +/* Autogenerated by mktablec.pl */ +#include "pixels.h" +#ifdef PRECOMPUTED_SCALETABLE +#error PRECOMPUTED_SCALETABLE was not defined at generation time +#endif +const rgba graytable[256] = { + (rgba)0 << RED_SHIFT | (rgba)0 << GREEN_SHIFT | (rgba)0 << BLUE_SHIFT, + (rgba)1 << RED_SHIFT | (rgba)1 << GREEN_SHIFT | (rgba)1 << BLUE_SHIFT, + (rgba)2 << RED_SHIFT | (rgba)2 << GREEN_SHIFT | (rgba)2 << BLUE_SHIFT, + (rgba)3 << RED_SHIFT | (rgba)3 << GREEN_SHIFT | (rgba)3 << BLUE_SHIFT, + (rgba)4 << RED_SHIFT | (rgba)4 << GREEN_SHIFT | (rgba)4 << BLUE_SHIFT, + (rgba)5 << RED_SHIFT | (rgba)5 << GREEN_SHIFT | (rgba)5 << BLUE_SHIFT, + (rgba)6 << RED_SHIFT | (rgba)6 << GREEN_SHIFT | (rgba)6 << BLUE_SHIFT, + (rgba)7 << RED_SHIFT | (rgba)7 << GREEN_SHIFT | (rgba)7 << BLUE_SHIFT, + (rgba)8 << RED_SHIFT | (rgba)8 << GREEN_SHIFT | (rgba)8 << BLUE_SHIFT, + (rgba)9 << RED_SHIFT | (rgba)9 << GREEN_SHIFT | (rgba)9 << BLUE_SHIFT, + (rgba)10 << RED_SHIFT | (rgba)10 << GREEN_SHIFT | (rgba)10 << BLUE_SHIFT, + (rgba)11 << RED_SHIFT | (rgba)11 << GREEN_SHIFT | (rgba)11 << BLUE_SHIFT, + (rgba)12 << RED_SHIFT | (rgba)12 << GREEN_SHIFT | (rgba)12 << BLUE_SHIFT, + (rgba)13 << RED_SHIFT | (rgba)13 << GREEN_SHIFT | (rgba)13 << BLUE_SHIFT, + (rgba)14 << RED_SHIFT | (rgba)14 << GREEN_SHIFT | (rgba)14 << BLUE_SHIFT, + (rgba)15 << RED_SHIFT | (rgba)15 << GREEN_SHIFT | (rgba)15 << BLUE_SHIFT, + (rgba)16 << RED_SHIFT | (rgba)16 << GREEN_SHIFT | (rgba)16 << BLUE_SHIFT, + (rgba)17 << RED_SHIFT | (rgba)17 << GREEN_SHIFT | (rgba)17 << BLUE_SHIFT, + (rgba)18 << RED_SHIFT | (rgba)18 << GREEN_SHIFT | (rgba)18 << BLUE_SHIFT, + (rgba)19 << RED_SHIFT | (rgba)19 << GREEN_SHIFT | (rgba)19 << BLUE_SHIFT, + (rgba)20 << RED_SHIFT | (rgba)20 << GREEN_SHIFT | (rgba)20 << BLUE_SHIFT, + (rgba)21 << RED_SHIFT | (rgba)21 << GREEN_SHIFT | (rgba)21 << BLUE_SHIFT, + (rgba)22 << RED_SHIFT | (rgba)22 << GREEN_SHIFT | (rgba)22 << BLUE_SHIFT, + (rgba)23 << RED_SHIFT | (rgba)23 << GREEN_SHIFT | (rgba)23 << BLUE_SHIFT, + (rgba)24 << RED_SHIFT | (rgba)24 << GREEN_SHIFT | (rgba)24 << BLUE_SHIFT, + (rgba)25 << RED_SHIFT | (rgba)25 << GREEN_SHIFT | (rgba)25 << BLUE_SHIFT, + (rgba)26 << RED_SHIFT | (rgba)26 << GREEN_SHIFT | (rgba)26 << BLUE_SHIFT, + (rgba)27 << RED_SHIFT | (rgba)27 << GREEN_SHIFT | (rgba)27 << BLUE_SHIFT, + (rgba)28 << RED_SHIFT | (rgba)28 << GREEN_SHIFT | (rgba)28 << BLUE_SHIFT, + (rgba)29 << RED_SHIFT | (rgba)29 << GREEN_SHIFT | (rgba)29 << BLUE_SHIFT, + (rgba)30 << RED_SHIFT | (rgba)30 << GREEN_SHIFT | (rgba)30 << BLUE_SHIFT, + (rgba)31 << RED_SHIFT | (rgba)31 << GREEN_SHIFT | (rgba)31 << BLUE_SHIFT, + (rgba)32 << RED_SHIFT | (rgba)32 << GREEN_SHIFT | (rgba)32 << BLUE_SHIFT, + (rgba)33 << RED_SHIFT | (rgba)33 << GREEN_SHIFT | (rgba)33 << BLUE_SHIFT, + (rgba)34 << RED_SHIFT | (rgba)34 << GREEN_SHIFT | (rgba)34 << BLUE_SHIFT, + (rgba)35 << RED_SHIFT | (rgba)35 << GREEN_SHIFT | (rgba)35 << BLUE_SHIFT, + (rgba)36 << RED_SHIFT | (rgba)36 << GREEN_SHIFT | (rgba)36 << BLUE_SHIFT, + (rgba)37 << RED_SHIFT | (rgba)37 << GREEN_SHIFT | (rgba)37 << BLUE_SHIFT, + (rgba)38 << RED_SHIFT | (rgba)38 << GREEN_SHIFT | (rgba)38 << BLUE_SHIFT, + (rgba)39 << RED_SHIFT | (rgba)39 << GREEN_SHIFT | (rgba)39 << BLUE_SHIFT, + (rgba)40 << RED_SHIFT | (rgba)40 << GREEN_SHIFT | (rgba)40 << BLUE_SHIFT, + (rgba)41 << RED_SHIFT | (rgba)41 << GREEN_SHIFT | (rgba)41 << BLUE_SHIFT, + (rgba)42 << RED_SHIFT | (rgba)42 << GREEN_SHIFT | (rgba)42 << BLUE_SHIFT, + (rgba)43 << RED_SHIFT | (rgba)43 << GREEN_SHIFT | (rgba)43 << BLUE_SHIFT, + (rgba)44 << RED_SHIFT | (rgba)44 << GREEN_SHIFT | (rgba)44 << BLUE_SHIFT, + (rgba)45 << RED_SHIFT | (rgba)45 << GREEN_SHIFT | (rgba)45 << BLUE_SHIFT, + (rgba)46 << RED_SHIFT | (rgba)46 << GREEN_SHIFT | (rgba)46 << BLUE_SHIFT, + (rgba)47 << RED_SHIFT | (rgba)47 << GREEN_SHIFT | (rgba)47 << BLUE_SHIFT, + (rgba)48 << RED_SHIFT | (rgba)48 << GREEN_SHIFT | (rgba)48 << BLUE_SHIFT, + (rgba)49 << RED_SHIFT | (rgba)49 << GREEN_SHIFT | (rgba)49 << BLUE_SHIFT, + (rgba)50 << RED_SHIFT | (rgba)50 << GREEN_SHIFT | (rgba)50 << BLUE_SHIFT, + (rgba)51 << RED_SHIFT | (rgba)51 << GREEN_SHIFT | (rgba)51 << BLUE_SHIFT, + (rgba)52 << RED_SHIFT | (rgba)52 << GREEN_SHIFT | (rgba)52 << BLUE_SHIFT, + (rgba)53 << RED_SHIFT | (rgba)53 << GREEN_SHIFT | (rgba)53 << BLUE_SHIFT, + (rgba)54 << RED_SHIFT | (rgba)54 << GREEN_SHIFT | (rgba)54 << BLUE_SHIFT, + (rgba)55 << RED_SHIFT | (rgba)55 << GREEN_SHIFT | (rgba)55 << BLUE_SHIFT, + (rgba)56 << RED_SHIFT | (rgba)56 << GREEN_SHIFT | (rgba)56 << BLUE_SHIFT, + (rgba)57 << RED_SHIFT | (rgba)57 << GREEN_SHIFT | (rgba)57 << BLUE_SHIFT, + (rgba)58 << RED_SHIFT | (rgba)58 << GREEN_SHIFT | (rgba)58 << BLUE_SHIFT, + (rgba)59 << RED_SHIFT | (rgba)59 << GREEN_SHIFT | (rgba)59 << BLUE_SHIFT, + (rgba)60 << RED_SHIFT | (rgba)60 << GREEN_SHIFT | (rgba)60 << BLUE_SHIFT, + (rgba)61 << RED_SHIFT | (rgba)61 << GREEN_SHIFT | (rgba)61 << BLUE_SHIFT, + (rgba)62 << RED_SHIFT | (rgba)62 << GREEN_SHIFT | (rgba)62 << BLUE_SHIFT, + (rgba)63 << RED_SHIFT | (rgba)63 << GREEN_SHIFT | (rgba)63 << BLUE_SHIFT, + (rgba)64 << RED_SHIFT | (rgba)64 << GREEN_SHIFT | (rgba)64 << BLUE_SHIFT, + (rgba)65 << RED_SHIFT | (rgba)65 << GREEN_SHIFT | (rgba)65 << BLUE_SHIFT, + (rgba)66 << RED_SHIFT | (rgba)66 << GREEN_SHIFT | (rgba)66 << BLUE_SHIFT, + (rgba)67 << RED_SHIFT | (rgba)67 << GREEN_SHIFT | (rgba)67 << BLUE_SHIFT, + (rgba)68 << RED_SHIFT | (rgba)68 << GREEN_SHIFT | (rgba)68 << BLUE_SHIFT, + (rgba)69 << RED_SHIFT | (rgba)69 << GREEN_SHIFT | (rgba)69 << BLUE_SHIFT, + (rgba)70 << RED_SHIFT | (rgba)70 << GREEN_SHIFT | (rgba)70 << BLUE_SHIFT, + (rgba)71 << RED_SHIFT | (rgba)71 << GREEN_SHIFT | (rgba)71 << BLUE_SHIFT, + (rgba)72 << RED_SHIFT | (rgba)72 << GREEN_SHIFT | (rgba)72 << BLUE_SHIFT, + (rgba)73 << RED_SHIFT | (rgba)73 << GREEN_SHIFT | (rgba)73 << BLUE_SHIFT, + (rgba)74 << RED_SHIFT | (rgba)74 << GREEN_SHIFT | (rgba)74 << BLUE_SHIFT, + (rgba)75 << RED_SHIFT | (rgba)75 << GREEN_SHIFT | (rgba)75 << BLUE_SHIFT, + (rgba)76 << RED_SHIFT | (rgba)76 << GREEN_SHIFT | (rgba)76 << BLUE_SHIFT, + (rgba)77 << RED_SHIFT | (rgba)77 << GREEN_SHIFT | (rgba)77 << BLUE_SHIFT, + (rgba)78 << RED_SHIFT | (rgba)78 << GREEN_SHIFT | (rgba)78 << BLUE_SHIFT, + (rgba)79 << RED_SHIFT | (rgba)79 << GREEN_SHIFT | (rgba)79 << BLUE_SHIFT, + (rgba)80 << RED_SHIFT | (rgba)80 << GREEN_SHIFT | (rgba)80 << BLUE_SHIFT, + (rgba)81 << RED_SHIFT | (rgba)81 << GREEN_SHIFT | (rgba)81 << BLUE_SHIFT, + (rgba)82 << RED_SHIFT | (rgba)82 << GREEN_SHIFT | (rgba)82 << BLUE_SHIFT, + (rgba)83 << RED_SHIFT | (rgba)83 << GREEN_SHIFT | (rgba)83 << BLUE_SHIFT, + (rgba)84 << RED_SHIFT | (rgba)84 << GREEN_SHIFT | (rgba)84 << BLUE_SHIFT, + (rgba)85 << RED_SHIFT | (rgba)85 << GREEN_SHIFT | (rgba)85 << BLUE_SHIFT, + (rgba)86 << RED_SHIFT | (rgba)86 << GREEN_SHIFT | (rgba)86 << BLUE_SHIFT, + (rgba)87 << RED_SHIFT | (rgba)87 << GREEN_SHIFT | (rgba)87 << BLUE_SHIFT, + (rgba)88 << RED_SHIFT | (rgba)88 << GREEN_SHIFT | (rgba)88 << BLUE_SHIFT, + (rgba)89 << RED_SHIFT | (rgba)89 << GREEN_SHIFT | (rgba)89 << BLUE_SHIFT, + (rgba)90 << RED_SHIFT | (rgba)90 << GREEN_SHIFT | (rgba)90 << BLUE_SHIFT, + (rgba)91 << RED_SHIFT | (rgba)91 << GREEN_SHIFT | (rgba)91 << BLUE_SHIFT, + (rgba)92 << RED_SHIFT | (rgba)92 << GREEN_SHIFT | (rgba)92 << BLUE_SHIFT, + (rgba)93 << RED_SHIFT | (rgba)93 << GREEN_SHIFT | (rgba)93 << BLUE_SHIFT, + (rgba)94 << RED_SHIFT | (rgba)94 << GREEN_SHIFT | (rgba)94 << BLUE_SHIFT, + (rgba)95 << RED_SHIFT | (rgba)95 << GREEN_SHIFT | (rgba)95 << BLUE_SHIFT, + (rgba)96 << RED_SHIFT | (rgba)96 << GREEN_SHIFT | (rgba)96 << BLUE_SHIFT, + (rgba)97 << RED_SHIFT | (rgba)97 << GREEN_SHIFT | (rgba)97 << BLUE_SHIFT, + (rgba)98 << RED_SHIFT | (rgba)98 << GREEN_SHIFT | (rgba)98 << BLUE_SHIFT, + (rgba)99 << RED_SHIFT | (rgba)99 << GREEN_SHIFT | (rgba)99 << BLUE_SHIFT, + (rgba)100 << RED_SHIFT | (rgba)100 << GREEN_SHIFT | (rgba)100 << BLUE_SHIFT, + (rgba)101 << RED_SHIFT | (rgba)101 << GREEN_SHIFT | (rgba)101 << BLUE_SHIFT, + (rgba)102 << RED_SHIFT | (rgba)102 << GREEN_SHIFT | (rgba)102 << BLUE_SHIFT, + (rgba)103 << RED_SHIFT | (rgba)103 << GREEN_SHIFT | (rgba)103 << BLUE_SHIFT, + (rgba)104 << RED_SHIFT | (rgba)104 << GREEN_SHIFT | (rgba)104 << BLUE_SHIFT, + (rgba)105 << RED_SHIFT | (rgba)105 << GREEN_SHIFT | (rgba)105 << BLUE_SHIFT, + (rgba)106 << RED_SHIFT | (rgba)106 << GREEN_SHIFT | (rgba)106 << BLUE_SHIFT, + (rgba)107 << RED_SHIFT | (rgba)107 << GREEN_SHIFT | (rgba)107 << BLUE_SHIFT, + (rgba)108 << RED_SHIFT | (rgba)108 << GREEN_SHIFT | (rgba)108 << BLUE_SHIFT, + (rgba)109 << RED_SHIFT | (rgba)109 << GREEN_SHIFT | (rgba)109 << BLUE_SHIFT, + (rgba)110 << RED_SHIFT | (rgba)110 << GREEN_SHIFT | (rgba)110 << BLUE_SHIFT, + (rgba)111 << RED_SHIFT | (rgba)111 << GREEN_SHIFT | (rgba)111 << BLUE_SHIFT, + (rgba)112 << RED_SHIFT | (rgba)112 << GREEN_SHIFT | (rgba)112 << BLUE_SHIFT, + (rgba)113 << RED_SHIFT | (rgba)113 << GREEN_SHIFT | (rgba)113 << BLUE_SHIFT, + (rgba)114 << RED_SHIFT | (rgba)114 << GREEN_SHIFT | (rgba)114 << BLUE_SHIFT, + (rgba)115 << RED_SHIFT | (rgba)115 << GREEN_SHIFT | (rgba)115 << BLUE_SHIFT, + (rgba)116 << RED_SHIFT | (rgba)116 << GREEN_SHIFT | (rgba)116 << BLUE_SHIFT, + (rgba)117 << RED_SHIFT | (rgba)117 << GREEN_SHIFT | (rgba)117 << BLUE_SHIFT, + (rgba)118 << RED_SHIFT | (rgba)118 << GREEN_SHIFT | (rgba)118 << BLUE_SHIFT, + (rgba)119 << RED_SHIFT | (rgba)119 << GREEN_SHIFT | (rgba)119 << BLUE_SHIFT, + (rgba)120 << RED_SHIFT | (rgba)120 << GREEN_SHIFT | (rgba)120 << BLUE_SHIFT, + (rgba)121 << RED_SHIFT | (rgba)121 << GREEN_SHIFT | (rgba)121 << BLUE_SHIFT, + (rgba)122 << RED_SHIFT | (rgba)122 << GREEN_SHIFT | (rgba)122 << BLUE_SHIFT, + (rgba)123 << RED_SHIFT | (rgba)123 << GREEN_SHIFT | (rgba)123 << BLUE_SHIFT, + (rgba)124 << RED_SHIFT | (rgba)124 << GREEN_SHIFT | (rgba)124 << BLUE_SHIFT, + (rgba)125 << RED_SHIFT | (rgba)125 << GREEN_SHIFT | (rgba)125 << BLUE_SHIFT, + (rgba)126 << RED_SHIFT | (rgba)126 << GREEN_SHIFT | (rgba)126 << BLUE_SHIFT, + (rgba)127 << RED_SHIFT | (rgba)127 << GREEN_SHIFT | (rgba)127 << BLUE_SHIFT, + (rgba)128 << RED_SHIFT | (rgba)128 << GREEN_SHIFT | (rgba)128 << BLUE_SHIFT, + (rgba)129 << RED_SHIFT | (rgba)129 << GREEN_SHIFT | (rgba)129 << BLUE_SHIFT, + (rgba)130 << RED_SHIFT | (rgba)130 << GREEN_SHIFT | (rgba)130 << BLUE_SHIFT, + (rgba)131 << RED_SHIFT | (rgba)131 << GREEN_SHIFT | (rgba)131 << BLUE_SHIFT, + (rgba)132 << RED_SHIFT | (rgba)132 << GREEN_SHIFT | (rgba)132 << BLUE_SHIFT, + (rgba)133 << RED_SHIFT | (rgba)133 << GREEN_SHIFT | (rgba)133 << BLUE_SHIFT, + (rgba)134 << RED_SHIFT | (rgba)134 << GREEN_SHIFT | (rgba)134 << BLUE_SHIFT, + (rgba)135 << RED_SHIFT | (rgba)135 << GREEN_SHIFT | (rgba)135 << BLUE_SHIFT, + (rgba)136 << RED_SHIFT | (rgba)136 << GREEN_SHIFT | (rgba)136 << BLUE_SHIFT, + (rgba)137 << RED_SHIFT | (rgba)137 << GREEN_SHIFT | (rgba)137 << BLUE_SHIFT, + (rgba)138 << RED_SHIFT | (rgba)138 << GREEN_SHIFT | (rgba)138 << BLUE_SHIFT, + (rgba)139 << RED_SHIFT | (rgba)139 << GREEN_SHIFT | (rgba)139 << BLUE_SHIFT, + (rgba)140 << RED_SHIFT | (rgba)140 << GREEN_SHIFT | (rgba)140 << BLUE_SHIFT, + (rgba)141 << RED_SHIFT | (rgba)141 << GREEN_SHIFT | (rgba)141 << BLUE_SHIFT, + (rgba)142 << RED_SHIFT | (rgba)142 << GREEN_SHIFT | (rgba)142 << BLUE_SHIFT, + (rgba)143 << RED_SHIFT | (rgba)143 << GREEN_SHIFT | (rgba)143 << BLUE_SHIFT, + (rgba)144 << RED_SHIFT | (rgba)144 << GREEN_SHIFT | (rgba)144 << BLUE_SHIFT, + (rgba)145 << RED_SHIFT | (rgba)145 << GREEN_SHIFT | (rgba)145 << BLUE_SHIFT, + (rgba)146 << RED_SHIFT | (rgba)146 << GREEN_SHIFT | (rgba)146 << BLUE_SHIFT, + (rgba)147 << RED_SHIFT | (rgba)147 << GREEN_SHIFT | (rgba)147 << BLUE_SHIFT, + (rgba)148 << RED_SHIFT | (rgba)148 << GREEN_SHIFT | (rgba)148 << BLUE_SHIFT, + (rgba)149 << RED_SHIFT | (rgba)149 << GREEN_SHIFT | (rgba)149 << BLUE_SHIFT, + (rgba)150 << RED_SHIFT | (rgba)150 << GREEN_SHIFT | (rgba)150 << BLUE_SHIFT, + (rgba)151 << RED_SHIFT | (rgba)151 << GREEN_SHIFT | (rgba)151 << BLUE_SHIFT, + (rgba)152 << RED_SHIFT | (rgba)152 << GREEN_SHIFT | (rgba)152 << BLUE_SHIFT, + (rgba)153 << RED_SHIFT | (rgba)153 << GREEN_SHIFT | (rgba)153 << BLUE_SHIFT, + (rgba)154 << RED_SHIFT | (rgba)154 << GREEN_SHIFT | (rgba)154 << BLUE_SHIFT, + (rgba)155 << RED_SHIFT | (rgba)155 << GREEN_SHIFT | (rgba)155 << BLUE_SHIFT, + (rgba)156 << RED_SHIFT | (rgba)156 << GREEN_SHIFT | (rgba)156 << BLUE_SHIFT, + (rgba)157 << RED_SHIFT | (rgba)157 << GREEN_SHIFT | (rgba)157 << BLUE_SHIFT, + (rgba)158 << RED_SHIFT | (rgba)158 << GREEN_SHIFT | (rgba)158 << BLUE_SHIFT, + (rgba)159 << RED_SHIFT | (rgba)159 << GREEN_SHIFT | (rgba)159 << BLUE_SHIFT, + (rgba)160 << RED_SHIFT | (rgba)160 << GREEN_SHIFT | (rgba)160 << BLUE_SHIFT, + (rgba)161 << RED_SHIFT | (rgba)161 << GREEN_SHIFT | (rgba)161 << BLUE_SHIFT, + (rgba)162 << RED_SHIFT | (rgba)162 << GREEN_SHIFT | (rgba)162 << BLUE_SHIFT, + (rgba)163 << RED_SHIFT | (rgba)163 << GREEN_SHIFT | (rgba)163 << BLUE_SHIFT, + (rgba)164 << RED_SHIFT | (rgba)164 << GREEN_SHIFT | (rgba)164 << BLUE_SHIFT, + (rgba)165 << RED_SHIFT | (rgba)165 << GREEN_SHIFT | (rgba)165 << BLUE_SHIFT, + (rgba)166 << RED_SHIFT | (rgba)166 << GREEN_SHIFT | (rgba)166 << BLUE_SHIFT, + (rgba)167 << RED_SHIFT | (rgba)167 << GREEN_SHIFT | (rgba)167 << BLUE_SHIFT, + (rgba)168 << RED_SHIFT | (rgba)168 << GREEN_SHIFT | (rgba)168 << BLUE_SHIFT, + (rgba)169 << RED_SHIFT | (rgba)169 << GREEN_SHIFT | (rgba)169 << BLUE_SHIFT, + (rgba)170 << RED_SHIFT | (rgba)170 << GREEN_SHIFT | (rgba)170 << BLUE_SHIFT, + (rgba)171 << RED_SHIFT | (rgba)171 << GREEN_SHIFT | (rgba)171 << BLUE_SHIFT, + (rgba)172 << RED_SHIFT | (rgba)172 << GREEN_SHIFT | (rgba)172 << BLUE_SHIFT, + (rgba)173 << RED_SHIFT | (rgba)173 << GREEN_SHIFT | (rgba)173 << BLUE_SHIFT, + (rgba)174 << RED_SHIFT | (rgba)174 << GREEN_SHIFT | (rgba)174 << BLUE_SHIFT, + (rgba)175 << RED_SHIFT | (rgba)175 << GREEN_SHIFT | (rgba)175 << BLUE_SHIFT, + (rgba)176 << RED_SHIFT | (rgba)176 << GREEN_SHIFT | (rgba)176 << BLUE_SHIFT, + (rgba)177 << RED_SHIFT | (rgba)177 << GREEN_SHIFT | (rgba)177 << BLUE_SHIFT, + (rgba)178 << RED_SHIFT | (rgba)178 << GREEN_SHIFT | (rgba)178 << BLUE_SHIFT, + (rgba)179 << RED_SHIFT | (rgba)179 << GREEN_SHIFT | (rgba)179 << BLUE_SHIFT, + (rgba)180 << RED_SHIFT | (rgba)180 << GREEN_SHIFT | (rgba)180 << BLUE_SHIFT, + (rgba)181 << RED_SHIFT | (rgba)181 << GREEN_SHIFT | (rgba)181 << BLUE_SHIFT, + (rgba)182 << RED_SHIFT | (rgba)182 << GREEN_SHIFT | (rgba)182 << BLUE_SHIFT, + (rgba)183 << RED_SHIFT | (rgba)183 << GREEN_SHIFT | (rgba)183 << BLUE_SHIFT, + (rgba)184 << RED_SHIFT | (rgba)184 << GREEN_SHIFT | (rgba)184 << BLUE_SHIFT, + (rgba)185 << RED_SHIFT | (rgba)185 << GREEN_SHIFT | (rgba)185 << BLUE_SHIFT, + (rgba)186 << RED_SHIFT | (rgba)186 << GREEN_SHIFT | (rgba)186 << BLUE_SHIFT, + (rgba)187 << RED_SHIFT | (rgba)187 << GREEN_SHIFT | (rgba)187 << BLUE_SHIFT, + (rgba)188 << RED_SHIFT | (rgba)188 << GREEN_SHIFT | (rgba)188 << BLUE_SHIFT, + (rgba)189 << RED_SHIFT | (rgba)189 << GREEN_SHIFT | (rgba)189 << BLUE_SHIFT, + (rgba)190 << RED_SHIFT | (rgba)190 << GREEN_SHIFT | (rgba)190 << BLUE_SHIFT, + (rgba)191 << RED_SHIFT | (rgba)191 << GREEN_SHIFT | (rgba)191 << BLUE_SHIFT, + (rgba)192 << RED_SHIFT | (rgba)192 << GREEN_SHIFT | (rgba)192 << BLUE_SHIFT, + (rgba)193 << RED_SHIFT | (rgba)193 << GREEN_SHIFT | (rgba)193 << BLUE_SHIFT, + (rgba)194 << RED_SHIFT | (rgba)194 << GREEN_SHIFT | (rgba)194 << BLUE_SHIFT, + (rgba)195 << RED_SHIFT | (rgba)195 << GREEN_SHIFT | (rgba)195 << BLUE_SHIFT, + (rgba)196 << RED_SHIFT | (rgba)196 << GREEN_SHIFT | (rgba)196 << BLUE_SHIFT, + (rgba)197 << RED_SHIFT | (rgba)197 << GREEN_SHIFT | (rgba)197 << BLUE_SHIFT, + (rgba)198 << RED_SHIFT | (rgba)198 << GREEN_SHIFT | (rgba)198 << BLUE_SHIFT, + (rgba)199 << RED_SHIFT | (rgba)199 << GREEN_SHIFT | (rgba)199 << BLUE_SHIFT, + (rgba)200 << RED_SHIFT | (rgba)200 << GREEN_SHIFT | (rgba)200 << BLUE_SHIFT, + (rgba)201 << RED_SHIFT | (rgba)201 << GREEN_SHIFT | (rgba)201 << BLUE_SHIFT, + (rgba)202 << RED_SHIFT | (rgba)202 << GREEN_SHIFT | (rgba)202 << BLUE_SHIFT, + (rgba)203 << RED_SHIFT | (rgba)203 << GREEN_SHIFT | (rgba)203 << BLUE_SHIFT, + (rgba)204 << RED_SHIFT | (rgba)204 << GREEN_SHIFT | (rgba)204 << BLUE_SHIFT, + (rgba)205 << RED_SHIFT | (rgba)205 << GREEN_SHIFT | (rgba)205 << BLUE_SHIFT, + (rgba)206 << RED_SHIFT | (rgba)206 << GREEN_SHIFT | (rgba)206 << BLUE_SHIFT, + (rgba)207 << RED_SHIFT | (rgba)207 << GREEN_SHIFT | (rgba)207 << BLUE_SHIFT, + (rgba)208 << RED_SHIFT | (rgba)208 << GREEN_SHIFT | (rgba)208 << BLUE_SHIFT, + (rgba)209 << RED_SHIFT | (rgba)209 << GREEN_SHIFT | (rgba)209 << BLUE_SHIFT, + (rgba)210 << RED_SHIFT | (rgba)210 << GREEN_SHIFT | (rgba)210 << BLUE_SHIFT, + (rgba)211 << RED_SHIFT | (rgba)211 << GREEN_SHIFT | (rgba)211 << BLUE_SHIFT, + (rgba)212 << RED_SHIFT | (rgba)212 << GREEN_SHIFT | (rgba)212 << BLUE_SHIFT, + (rgba)213 << RED_SHIFT | (rgba)213 << GREEN_SHIFT | (rgba)213 << BLUE_SHIFT, + (rgba)214 << RED_SHIFT | (rgba)214 << GREEN_SHIFT | (rgba)214 << BLUE_SHIFT, + (rgba)215 << RED_SHIFT | (rgba)215 << GREEN_SHIFT | (rgba)215 << BLUE_SHIFT, + (rgba)216 << RED_SHIFT | (rgba)216 << GREEN_SHIFT | (rgba)216 << BLUE_SHIFT, + (rgba)217 << RED_SHIFT | (rgba)217 << GREEN_SHIFT | (rgba)217 << BLUE_SHIFT, + (rgba)218 << RED_SHIFT | (rgba)218 << GREEN_SHIFT | (rgba)218 << BLUE_SHIFT, + (rgba)219 << RED_SHIFT | (rgba)219 << GREEN_SHIFT | (rgba)219 << BLUE_SHIFT, + (rgba)220 << RED_SHIFT | (rgba)220 << GREEN_SHIFT | (rgba)220 << BLUE_SHIFT, + (rgba)221 << RED_SHIFT | (rgba)221 << GREEN_SHIFT | (rgba)221 << BLUE_SHIFT, + (rgba)222 << RED_SHIFT | (rgba)222 << GREEN_SHIFT | (rgba)222 << BLUE_SHIFT, + (rgba)223 << RED_SHIFT | (rgba)223 << GREEN_SHIFT | (rgba)223 << BLUE_SHIFT, + (rgba)224 << RED_SHIFT | (rgba)224 << GREEN_SHIFT | (rgba)224 << BLUE_SHIFT, + (rgba)225 << RED_SHIFT | (rgba)225 << GREEN_SHIFT | (rgba)225 << BLUE_SHIFT, + (rgba)226 << RED_SHIFT | (rgba)226 << GREEN_SHIFT | (rgba)226 << BLUE_SHIFT, + (rgba)227 << RED_SHIFT | (rgba)227 << GREEN_SHIFT | (rgba)227 << BLUE_SHIFT, + (rgba)228 << RED_SHIFT | (rgba)228 << GREEN_SHIFT | (rgba)228 << BLUE_SHIFT, + (rgba)229 << RED_SHIFT | (rgba)229 << GREEN_SHIFT | (rgba)229 << BLUE_SHIFT, + (rgba)230 << RED_SHIFT | (rgba)230 << GREEN_SHIFT | (rgba)230 << BLUE_SHIFT, + (rgba)231 << RED_SHIFT | (rgba)231 << GREEN_SHIFT | (rgba)231 << BLUE_SHIFT, + (rgba)232 << RED_SHIFT | (rgba)232 << GREEN_SHIFT | (rgba)232 << BLUE_SHIFT, + (rgba)233 << RED_SHIFT | (rgba)233 << GREEN_SHIFT | (rgba)233 << BLUE_SHIFT, + (rgba)234 << RED_SHIFT | (rgba)234 << GREEN_SHIFT | (rgba)234 << BLUE_SHIFT, + (rgba)235 << RED_SHIFT | (rgba)235 << GREEN_SHIFT | (rgba)235 << BLUE_SHIFT, + (rgba)236 << RED_SHIFT | (rgba)236 << GREEN_SHIFT | (rgba)236 << BLUE_SHIFT, + (rgba)237 << RED_SHIFT | (rgba)237 << GREEN_SHIFT | (rgba)237 << BLUE_SHIFT, + (rgba)238 << RED_SHIFT | (rgba)238 << GREEN_SHIFT | (rgba)238 << BLUE_SHIFT, + (rgba)239 << RED_SHIFT | (rgba)239 << GREEN_SHIFT | (rgba)239 << BLUE_SHIFT, + (rgba)240 << RED_SHIFT | (rgba)240 << GREEN_SHIFT | (rgba)240 << BLUE_SHIFT, + (rgba)241 << RED_SHIFT | (rgba)241 << GREEN_SHIFT | (rgba)241 << BLUE_SHIFT, + (rgba)242 << RED_SHIFT | (rgba)242 << GREEN_SHIFT | (rgba)242 << BLUE_SHIFT, + (rgba)243 << RED_SHIFT | (rgba)243 << GREEN_SHIFT | (rgba)243 << BLUE_SHIFT, + (rgba)244 << RED_SHIFT | (rgba)244 << GREEN_SHIFT | (rgba)244 << BLUE_SHIFT, + (rgba)245 << RED_SHIFT | (rgba)245 << GREEN_SHIFT | (rgba)245 << BLUE_SHIFT, + (rgba)246 << RED_SHIFT | (rgba)246 << GREEN_SHIFT | (rgba)246 << BLUE_SHIFT, + (rgba)247 << RED_SHIFT | (rgba)247 << GREEN_SHIFT | (rgba)247 << BLUE_SHIFT, + (rgba)248 << RED_SHIFT | (rgba)248 << GREEN_SHIFT | (rgba)248 << BLUE_SHIFT, + (rgba)249 << RED_SHIFT | (rgba)249 << GREEN_SHIFT | (rgba)249 << BLUE_SHIFT, + (rgba)250 << RED_SHIFT | (rgba)250 << GREEN_SHIFT | (rgba)250 << BLUE_SHIFT, + (rgba)251 << RED_SHIFT | (rgba)251 << GREEN_SHIFT | (rgba)251 << BLUE_SHIFT, + (rgba)252 << RED_SHIFT | (rgba)252 << GREEN_SHIFT | (rgba)252 << BLUE_SHIFT, + (rgba)253 << RED_SHIFT | (rgba)253 << GREEN_SHIFT | (rgba)253 << BLUE_SHIFT, + (rgba)254 << RED_SHIFT | (rgba)254 << GREEN_SHIFT | (rgba)254 << BLUE_SHIFT, + (rgba)255 << RED_SHIFT | (rgba)255 << GREEN_SHIFT | (rgba)255 << BLUE_SHIFT, +}; diff --git a/kernel/kls_xcf/xcf2pnm/utils.c b/kernel/kls_xcf/xcf2pnm/utils.c new file mode 100644 index 0000000..bde0782 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/utils.c @@ -0,0 +1,164 @@ +/* Generic support functions for Xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <errno.h> + +const char *progname = "$0" ; +int verboseFlag = 0 ; + + +static void __ATTRIBUTE__((noreturn)) +vFatalGeneric(int status,const char *format,va_list args) +{ + if( format ) { + if( *format == '!' ) { + vfprintf(stderr,format+1,args); + fprintf(stderr,": %s\n",strerror(errno)); + } else { + vfprintf(stderr,format,args); + fputc('\n',stderr); + } + } + exit(status); +} + +void +FatalGeneric(int status,const char* format,...) +{ + va_list v; va_start(v,format); + if( format ) fprintf(stderr,"%s: ",progname); + vFatalGeneric(status,format,v); +} + +void +FatalUnexpected(const char* format,...) +{ + va_list v; va_start(v,format); + fprintf(stderr,"%s: ",progname); + vFatalGeneric(127,format,v) ; +} + +void +FatalBadXCF(const char* format,...) +{ + va_list v; va_start(v,format); + fprintf(stderr,"%s: %s:\n ",progname,_("Corrupted or malformed XCF file")); + vFatalGeneric(125,format,v) ; +} + +void +xcfCheckspace(uint32_t addr,int spaceafter,const char *format,...) +{ + if( xcf_length < spaceafter || addr > xcf_length - spaceafter ) { + va_list v; va_start(v,format); + fprintf(stderr,"%s: %s\n ",progname,_("Corrupted or truncated XCF file")); + fprintf(stderr,"(0x%" PRIXPTR " bytes): ",(uintptr_t)xcf_length); + vFatalGeneric(125,format,v) ; + } +} + + +void +FatalUnsupportedXCF(const char* format,...) +{ + va_list v; va_start(v,format); + fprintf(stderr,"%s: %s\n ",progname, + _("The image contains features not understood by this program:")); + vFatalGeneric(123,format,v) ; +} + +void +gpl_blurb(void) +{ + fprintf(stderr,PACKAGE_STRING "\n"); + fprintf(stderr, + _("This program is free software; you can modify and distribute it\n" + "under the terms of the GNU General Public License, version 2.\n" + "There is no warranty for %s.\n\n"), + PACKAGE_NAME); + fprintf(stderr, + _("Type \"%s -h\" to get an option summary.\n"),progname); + exit(1) ; +} + +/* ******************************************************* */ + +void * +xcfmalloc(size_t size) +{ + void *ptr = malloc(size); + if( !ptr ) + FatalUnexpected(_("Out of memory")); + return ptr ; +} + +void +xcffree(void *block) +{ + if( xcf_file && + (uint8_t*)block >= xcf_file && + (uint8_t*)block < xcf_file + xcf_length ) + ; + else + free(block); +} + +/* ******************************************************* */ + +FILE * +openout(const char *name) +{ + FILE *newfile ; + if( strcmp(name,"-") == 0 ) + return stdout ; + newfile = fopen(name,"wb") ; + if( newfile == NULL ) + FatalUnexpected(_("!Cannot create file %s"),name); + return newfile ; +} + +void +closeout(FILE *f,const char *name) +{ + if( f == NULL ) + return ; + if( fflush(f) == 0 ) { + errno = 0 ; + if( !ferror(f) ) { + if( fclose(f) == 0 ) + return ; + } else if( errno == 0 ) { + /* Attempt to coax a valid errno out of the standard library, + * following an idea by Bruno Haible + * http://lists.gnu.org/archive/html/bug-gnulib/2003-09/msg00157.html + */ + if( fputc('\0', f) != EOF && + fflush(f) == 0 ) + errno = EIO ; /* Argh, everything succeds. Just call it an I/O error */ + } + } + FatalUnexpected(_("!Error writing file %s"),name); +} + + + + diff --git a/kernel/kls_xcf/xcf2pnm/xcf-general.c b/kernel/kls_xcf/xcf2pnm/xcf-general.c new file mode 100644 index 0000000..9231134 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/xcf-general.c @@ -0,0 +1,287 @@ +/* Generic functions for reading XCF files + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include <string.h> +#include <errno.h> +#ifdef HAVE_ICONV +# include <iconv.h> +#elif !defined(ICONV_CONST) +# define ICONV_CONST const +#endif + +uint8_t *xcf_file = 0 ; +size_t xcf_length ; +int use_utf8 = 0 ; + +uint32_t +xcfOffset(uint32_t addr,int spaceafter) +{ + uint32_t apparent ; + xcfCheckspace(addr,4,"(xcfOffset)"); + apparent = xcfL(addr); + xcfCheckspace(apparent,spaceafter, + "Too large offset (%" PRIX32 ") at position %" PRIX32, + apparent,addr); + return apparent ; +} + +int +xcfNextprop(uint32_t *master,uint32_t *body) +{ + uint32_t ptr, length, total, minlength ; + PropType type ; + ptr = *master ; + xcfCheckspace(ptr,8,"(property header)"); + type = xcfL(ptr); + length = xcfL(ptr+4); + *body = ptr+8 ; + + switch(type) { + case PROP_COLORMAP: + { + uint32_t ncolors ; + xcfCheckspace(ptr+8,4,"(colormap length)"); + ncolors = xcfL(ptr+8) ; + if( ncolors > 256 ) + FatalBadXCF("Colormap has %" PRIu32 " entries",ncolors); + /* Surprise! Some older verion of the Gimp computed the wrong length + * word, and the _reader_ always just reads three bytes per color + * and ignores the length tag! Duplicate this so we too can read + * the buggy XCF files. + */ + length = minlength = 4+3*ncolors; + break; + } + case PROP_COMPRESSION: minlength = 1; break; + case PROP_OPACITY: minlength = 4; break; + case PROP_APPLY_MASK: minlength = 4; break; + case PROP_OFFSETS: minlength = 8; break; + case PROP_MODE: minlength = 4; break; + default: minlength = 0; break; + } + if( length < minlength ) + FatalBadXCF("Short %s property at %" PRIX32 " (%" PRIu32 "<%" PRIu32 ")", + showPropType(type),ptr,length,minlength); + *master = ptr+8+length ; + total = 8 + length + (type != PROP_END ? 8 : 0) ; + if( total < length ) /* Check overwrap */ + FatalBadXCF("Overlong property at %" PRIX32, ptr); + xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr) ; + return type ; +} + +const char* +xcfString(uint32_t ptr,uint32_t *after) +{ + uint32_t length ; + unsigned i ; + ICONV_CONST char *utf8master ; + + xcfCheckspace(ptr,4,"(string length)"); + length = xcfL(ptr) ; + ptr += 4 ; + xcfCheckspace(ptr,length,"(string)"); + utf8master = (ICONV_CONST char*)(xcf_file+ptr) ; + if( after ) *after = ptr + length ; + if( length == 0 || utf8master[length-1] != 0 ) + FatalBadXCF("String at %" PRIX32 " not zero-terminated",ptr-4); + length-- ; + + if( use_utf8 ) return utf8master ; + + /* We assume that the local character set includes ASCII... + * Check if conversion is needed at all + */ + for( i=0 ; ; i++ ) { + if( i == length ) + return utf8master ; /* Only ASCII after all */ + if( utf8master[i] == 0 ) + FatalBadXCF("String at %" PRIX32 " has embedded zeroes",ptr-4); + if( (int8_t) utf8master[i] < 0 ) + break ; + } +#ifdef HAVE_ICONV + { + size_t targetsize = length+1 ; + int sloppy_translation = 0 ; + iconv_t cd = iconv_open("//TRANSLIT","UTF-8"); + if( cd == (iconv_t) -1 ) { + cd = iconv_open("","UTF-8"); + sloppy_translation = 1 ; + } + if( cd == (iconv_t) -1 ) + iconv_close(cd) ; /* Give up; perhaps iconv doesn't know UTF-8 */ + else + while(1) { + char *buffer = xcfmalloc(targetsize) ; + ICONV_CONST char *inbuf = utf8master ; + char *outbuf = buffer ; + size_t incount = length ; + size_t outcount = targetsize ; + while(1) { /* Loop for systems without //ICONV support */ + size_t result = iconv(cd,&inbuf,&incount,&outbuf,&outcount) ; + if( result == (size_t)-1 && errno == EILSEQ && + sloppy_translation && outcount > 0 ) { + *outbuf++ = '?' ; + outcount-- ; + while( (int8_t)*inbuf < 0 ) inbuf++, incount-- ; + continue ; + } + if( result != (size_t)-1 ) { + if( outcount == 0 ) + errno = E2BIG ; + else { + *outbuf = 0 ; + iconv_close(cd) ; + return buffer ; + } + } + break ; + } + if( errno == EILSEQ || errno == EINVAL ) + FatalBadXCF("Bad UTF-8 encoding '%s' at %" PRIXPTR, + inbuf,(uintptr_t)((inbuf-utf8master)+ptr)); + if( errno == E2BIG ) { + targetsize += 1+incount ; + xcffree(buffer) ; + continue ; + } + FatalUnexpected("!iconv on layer name at %"PRIX32,ptr); + } + } +#endif + { + static int warned = 0 ; + if( !warned ) { + fprintf(stderr,_("Warning: one or more layer names could not be\n" + " translated to the local character set.\n")); + warned = 1 ; + } + } + return utf8master ; +} + +/* ****************************************************************** */ + +void +computeDimensions(struct tileDimensions *d) +{ + d->c.r = d->c.l + d->width ; + d->c.b = d->c.t + d->height ; + d->tilesx = (d->width+TILE_WIDTH-1)/TILE_WIDTH ; + d->tilesy = (d->height+TILE_HEIGHT-1)/TILE_HEIGHT ; + d->ntiles = d->tilesx * d->tilesy ; +} + +struct xcfImage XCF ; + +void +getBasicXcfInfo(void) +{ + uint32_t ptr, data, layerfile ; + PropType type ; + int i ; + + xcfCheckspace(0,14+7*4,"(very short)"); + if( strcmp((char*)xcf_file,"gimp xcf file") == 0 ) + XCF.version = 0 ; + else if( xcf_file[13] == 0 && + sscanf((char*)xcf_file,"gimp xcf v%d",&XCF.version) == 1 ) + ; + else + FatalBadXCF(_("Not an XCF file at all (magic not recognized)")); + + if( XCF.version < 0 || XCF.version > 2 ) { + fprintf(stderr, + _("Warning: XCF version %d not supported (trying anyway...)\n"), + XCF.version); + } + + XCF.compression = COMPRESS_NONE ; + XCF.colormapptr = 0 ; + + ptr = 14 ; + XCF.width = xcfL(ptr); ptr += 4 ; + XCF.height = xcfL(ptr); ptr += 4 ; + XCF.type = xcfL(ptr); ptr += 4 ; + while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) { + switch(type) { + case PROP_COLORMAP: + XCF.colormapptr = data ; + break ; + case PROP_COMPRESSION: + XCF.compression = xcf_file[data] ; + break ; + default: + /* Ignore unknown properties */ + break ; + } + } + + layerfile = ptr ; + for( XCF.numLayers = 0 ; xcfOffset(ptr,8*4) ; XCF.numLayers++, ptr+=4 ) + ; + XCF.layers = xcfmalloc(XCF.numLayers * sizeof(struct xcfLayer)) ; + for( i = 0 ; i < XCF.numLayers ; i++ ) { + struct xcfLayer *L = XCF.layers + i ; + ptr = xcfL(layerfile+4*(XCF.numLayers-1-i)) ; + L->mode = GIMP_NORMAL_MODE ; + L->opacity = 255 ; + L->isVisible = 1 ; + L->hasMask = 0 ; + L->dim.width = xcfL(ptr); ptr+=4 ; + L->dim.height = xcfL(ptr); ptr+=4 ; + L->type = xcfL(ptr); ptr+=4 ; + L->name = xcfString(ptr,&ptr); + L->propptr = ptr ; + while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) { + switch(type) { + case PROP_OPACITY: + L->opacity = xcfL(data); + if( L->opacity > 255 ) + L->opacity = 255 ; + break ; + case PROP_VISIBLE: + L->isVisible = xcfL(data) != 0 ; + break ; + case PROP_APPLY_MASK: + L->hasMask = xcfL(data) != 0 ; + break ; + case PROP_OFFSETS: + L->dim.c.l = (int32_t)(xcfL(data )) ; + L->dim.c.t = (int32_t)(xcfL(data+4)) ; + break ; + case PROP_MODE: + L->mode = xcfL(data); + break ; + default: + /* Ignore unknown properties */ + break ; + } + } + xcfCheckspace(ptr,8,"(end of layer %s)",L->name); + L->pixels.tileptrs = 0 ; + L->pixels.hierarchy = xcfOffset(ptr ,4*4); + L->mask.tileptrs = 0 ; + L->mask.hierarchy = xcfOffset(ptr+4,4*4); + + computeDimensions(&L->dim); + } +} + diff --git a/kernel/kls_xcf/xcf2pnm/xcf2pnm.c b/kernel/kls_xcf/xcf2pnm/xcf2pnm.c new file mode 100644 index 0000000..06baf8d --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/xcf2pnm.c @@ -0,0 +1,269 @@ +/* Convert xcf files to ppm + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include "flatten.h" +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#if HAVE_GETOPT_H +#include <getopt.h> +#else +#include <unistd.h> +#endif +#ifndef HAVE_GETOPT_LONG +#define getopt_long(argc,argv,optstring,l1,l2) getopt(argc,argv,optstring) +#endif + +#include "xcf2pnm.oi" + +static int suppress_byline ; +static struct FlattenSpec flatspec ; +static FILE *outfile = NULL ; +static FILE *transfile = NULL ; + +static void +start_writing(FILE **f,int version) +{ + const char *format[] = {"(format zero)", + "PBM-ascii", + "PGM-ascii", + "PPM-ascii", + "PBM", + "PGM", + "PPM" }; + + if( verboseFlag ) + fprintf(stderr,f == &outfile ? _("Writing converted image as %s\n") + : _("Writing transparency map as %s\n"), + format[version]); + + *f = openout( f == &outfile ? flatspec.output_filename + : flatspec.transmap_filename ); + fprintf(*f,"P%d",version); + if( suppress_byline ) + ; + else if( f == &outfile ) + fprintf(*f,_(" # Converted by xcf2pnm %s"),PACKAGE_VERSION); + else + fprintf(*f,_(" # Transparency map by xcf2pnm %s"),PACKAGE_VERSION); + fprintf(*f,"\n%d %d\n%s", + flatspec.dim.width, + flatspec.dim.height, + version == 4 ? "" : "255\n"); +} + +int +put_pbm_row(FILE *file,unsigned num,rgba *pixels,rgba mask); + +int +put_pbm_row(FILE *file,unsigned num,rgba *pixels,rgba mask) { + unsigned out ; + unsigned i ; + int bitsleft = 8 ; + out = 0 ; + for( i=0; i<num; i++ ) { + out <<= 1 ; + if( (pixels[i] & mask) == 0 ) + out++ ; /* 1 is black */ + else if( (pixels[i] & mask) == mask ) + ; /* 0 is white */ + else + return 0 ; + if( --bitsleft == 0 ) { + putc( out, file ); + out = 0 ; + bitsleft = 8 ; + } + } + if( bitsleft < 8 ) + putc( out << bitsleft, file ); + return 1 ; +} + +static void +callback_common(unsigned num,rgba *pixels) +{ + if( flatspec.transmap_filename ) { + if( flatspec.partial_transparency_mode == ALLOW_PARTIAL_TRANSPARENCY ) { + unsigned i ; + if( transfile == NULL ) start_writing(&transfile,5); + for( i=0; i < num; i++ ) + putc( ALPHA(pixels[i]), transfile ); + } else { + if( transfile == NULL ) { + start_writing(&transfile,4); + } + /* Partial transparency should have been caught in the flattener, + * so just extract a single byte. + */ + put_pbm_row(transfile,num,pixels,(rgba)1 << ALPHA_SHIFT); + } + } else if( ALPHA(flatspec.default_pixel) < 128 ) { + unsigned i ; + for( i=0; i < num; i++ ) + if( !FULLALPHA(pixels[i]) ) + FatalGeneric(100,_("Transparency found, but -a option not given")); + } + xcffree(pixels) ; +} + +static void +ppm_callback(unsigned num,rgba *pixels) +{ + unsigned i ; + if( outfile == NULL ) start_writing(&outfile,6); + for( i=0; i < num; i++ ) { + putc( (pixels[i] >> RED_SHIFT) & 0xFF , outfile ); + putc( (pixels[i] >> GREEN_SHIFT) & 0xFF , outfile ); + putc( (pixels[i] >> BLUE_SHIFT) & 0xFF , outfile ); + } + callback_common(num,pixels); +} + +static void +pgm_callback(unsigned num,rgba *pixels) +{ + unsigned i ; + if( outfile == NULL ) start_writing(&outfile,5); + for( i=0; i < num; i++ ) { + int gray = degrayPixel(pixels[i]) ; + if( gray == -1 ) + FatalGeneric(103, + _("Grayscale output selected, but colored pixel(s) found")); + putc( gray, outfile ); + } + callback_common(num,pixels); +} + +static void +pbm_callback(unsigned num,rgba *pixels) +{ + if( outfile == NULL ) start_writing(&outfile,4); + if( !put_pbm_row(outfile,num,pixels, + ((rgba)255 << RED_SHIFT) + + ((rgba)255 << GREEN_SHIFT) + + ((rgba)255 << BLUE_SHIFT)) ) + FatalGeneric(103,_("Monochrome output selected, but not all pixels " + "are black or white")); + callback_common(num,pixels); +} + +static enum out_color_mode +guess_color_mode(const char *string) +{ + if( strlen(string) >= 3 ) { + string += strlen(string)-3 ; + if( strcmp(string,"ppm")==0 ) return COLOR_RGB ; + if( strcmp(string,"pgm")==0 ) return COLOR_GRAY ; + if( strcmp(string,"pbm")==0 ) return COLOR_MONO ; + } + return COLOR_BY_FILENAME ; +} + +static lineCallback +selectCallback(void) +{ + if( flatspec.transmap_filename && ALPHA(flatspec.default_pixel) >= 128 ) + FatalGeneric(101,_("The -a option was given, " + "but the image has no transparency")); + + switch( flatspec.out_color_mode ) { + default: + case COLOR_RGB: return &ppm_callback ; + case COLOR_GRAY: return &pgm_callback ; + case COLOR_MONO: return &pbm_callback ; + } +} + +int +main(int argc,char **argv) +{ + int option ; + const char *unzipper = NULL ; + const char *infile = NULL ; + + setlocale(LC_ALL,""); + progname = argv[0] ; + nls_init(); + + if( argc <= 1 ) gpl_blurb() ; + + init_flatspec(&flatspec) ; + flatspec.out_color_mode = COLOR_BY_FILENAME ; + while( (option=getopt_long(argc,argv,"-@#"OPTSTRING,longopts,NULL)) >= 0 ) + switch(option) { + +#define OPTION(char,long,desc,man) case char: +#include "options.i" + + case 1: + if( infile ) + add_layer_request(&flatspec,optarg); + else + infile = optarg ; + break ; + case '?': + exit(1); + case '@': + /* Non-documented option for build-time test */ + suppress_byline = 1 ; + break ; + case '#': + /* Non-documented option for xcfview */ + flatspec.default_pixel = CHECKERED_BACKGROUND ; + break ; + default: + FatalUnexpected("Getopt(_long) unexpectedly returned '%c'",option); + } + if( infile == NULL ) { + exit(1); + } + + if( flatspec.out_color_mode == COLOR_BY_FILENAME && + strlen(flatspec.output_filename) > 4 && + flatspec.output_filename[strlen(flatspec.output_filename)-4] == '.' ) + flatspec.out_color_mode = guess_color_mode(flatspec.output_filename); + + /* If the output filename was not enough cue, see if we're running + * through a symlink/hardlink that gives the required output format + */ + if( flatspec.out_color_mode == COLOR_BY_FILENAME && + strlen(progname) > 3 ) + flatspec.out_color_mode = guess_color_mode(progname); + + if( flatspec.out_color_mode == COLOR_BY_FILENAME ) + flatspec.out_color_mode = COLOR_BY_CONTENTS ; + + read_or_mmap_xcf(infile,unzipper); + getBasicXcfInfo() ; + initColormap(); + + complete_flatspec(&flatspec,NULL); + if( flatspec.process_in_memory ) { + rgba **allPixels = flattenAll(&flatspec); + analyse_colormode(&flatspec,allPixels,NULL); + shipoutWithCallback(&flatspec,allPixels,selectCallback()); + } else { + flattenIncrementally(&flatspec,selectCallback()); + } + closeout(outfile,flatspec.output_filename) ; + closeout(transfile,flatspec.transmap_filename) ; + return 0 ; +} diff --git a/kernel/kls_xcf/xcf2pnm/xcf2pnm.oi b/kernel/kls_xcf/xcf2pnm/xcf2pnm.oi new file mode 100644 index 0000000..382bd96 --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/xcf2pnm.oi @@ -0,0 +1,41 @@ +/* Autogenerated by mkopti.pl xcf2pnm */ +#define XCF2PNM +#define OPTI_TARGET "xcf2pnm" +#define XCF2FOO +#define OPTIONGROUP(a,b) + +#ifdef HAVE_GETOPT_LONG +static const struct option longopts[] = { + { "help", 0, 0, 'h'}, + { "version", 0, 0, 'V'}, + { "verbose", 0, 0, 'v'}, + { "bzip", 0, 0, 'j'}, + { "gzip", 0, 0, 'z'}, + { "unpack", 1, 0, 'Z'}, + { "output", 1, 0, 'o'}, + { "alpha", 1, 0, 'a'}, + { "background", 1, 0, 'b'}, + { "force-alpha", 0, 0, 'A'}, + { "color", 0, 0, 'c'}, + { "colour", 0, 0, 'c'}, + { "gray", 0, 0, 'g'}, + { "grey", 0, 0, 'g'}, + { "mono", 0, 0, 'm'}, + { "pnm", 0, 0, 'n'}, + { "truecolor", 0, 0, 'T'}, + { "for-gif", 0, 0, 'G'}, + { "dissolve", 0, 0, 'D'}, + { "full-image", 0, 0, 'f'}, + { "size", 1, 0, 'S'}, + { "offset", 1, 0, 'O'}, + { "autocrop", 0, 0, 'C'}, + { "mode", 1, 0, 300}, + { "percent", 1, 0, 301}, + { "opacity", 1, 0, 302}, + { "mask", 0, 0, 303}, + { "nomask", 0, 0, 304}, + { "utf8", 0, 0, 'u'}, +{0}}; +#endif + +#define OPTSTRING "hVvjzZ:o:a:b:AcgmnTGDfS:O:Cu" diff --git a/kernel/kls_xcf/xcf2pnm/xcftools.h b/kernel/kls_xcf/xcf2pnm/xcftools.h new file mode 100644 index 0000000..d3efbab --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/xcftools.h @@ -0,0 +1,182 @@ +/* Generic functions and macros for reading XCF files + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef XCFTOOLS_H +#define XCFTOOLS_H + +#include "enums.h" +#include <stddef.h> +#include <stdio.h> + +#define _(s) (s) +#define nls_init() (void)0 +#define N_(s) (s) + +#if HAVE_INTTYPES_H +# define __STDC_FORMAT_MACROS +# include <inttypes.h> +#else +/* These legacy fall-backs will probably work on every system + * that does not supply a inttypes.h ... */ +typedef unsigned char uint8_t ; +typedef unsigned long int uint32_t, uintptr_t ; +typedef signed char int8_t ; +typedef signed long int int32_t ; +# define PRIX32 "lX" +# define PRIu32 "lu" +# define PRIXPTR "lX" +#endif + +#if __GNUC__ +# define __ATTRIBUTE__ __attribute__ +#else +# define __ATTRIBUTE__(x) +#endif + +#if HAVE_NETINET_IN_H +# include <netinet/in.h> +#elif HAVE_ARPA_INET_H +# include <arpa/inet.h> +#elif WORDS_BIGENDIAN +# define ntohl(x) (x) +#else +static inline uint32_t ntohl(uint32_t a) { + return (a << 24) + ((a & 0xFF00) << 8) + ((a >> 8) & 0xFF00) + (a >> 24) ; +} +#endif + +#ifndef HAVE_STRCASECMP +#define strcasecmp strcmp +#endif + +/* Read a single word value from the XCF file */ + +/* Use + instead of | because that allows LEA instructions */ +#define xcfBE(a) ( ((uint32_t)xcf_file[(a) ] << 24) + \ + ((uint32_t)xcf_file[(a)+1] << 16) + \ + ((uint32_t)xcf_file[(a)+2] << 8 ) + \ + ((uint32_t)xcf_file[(a)+3] ) ) +#define xcfLE(a) ( ((uint32_t)xcf_file[(a) ] ) + \ + ((uint32_t)xcf_file[(a)+1] << 8 ) + \ + ((uint32_t)xcf_file[(a)+2] << 16) + \ + ((uint32_t)xcf_file[(a)+3] << 24) ) + +#if CAN_DO_UNALIGNED_WORDS +# define xcfL(a) ntohl(*(uint32_t *)(xcf_file + (a))) +#else +# define xcfL(a) ((a) & 3 ? xcfBE(a) : ntohl(*(uint32_t *)(xcf_file + (a)))) +#endif + +/* ****************************************************************** */ + +/* The following are exported from am OS-specific source file; + * io-unix.c on unixish systems. + */ +void read_or_mmap_xcf(const char* filename, const char *unzipper); +void free_or_close_xcf(void); + +/* ****************************************************************** */ +/* utils.c */ + +extern const char *progname ; +extern int verboseFlag ; + +void *xcfmalloc(size_t size); +void xcffree(void*); + +void FatalGeneric(int status,const char* format,...) + __ATTRIBUTE__((format(printf,2,3),noreturn)) ; +void FatalUnexpected(const char* format,...) + __ATTRIBUTE__((format(printf,1,2),noreturn)) ; +void FatalBadXCF(const char* format,...) + __ATTRIBUTE__((format(printf,1,2),noreturn)) ; +void FatalUnsupportedXCF(const char* format,...) + __ATTRIBUTE__((format(printf,1,2),noreturn)) ; + +void gpl_blurb(void) __ATTRIBUTE__((noreturn)); + +FILE* openout(const char*); +void closeout(FILE *,const char*); + +struct rect { + int t, b, l, r ; +}; + +#define isSubrect(A,B) \ + ((A).l >= (B).l && (A).r <= (B).r && (A).t >= (B).t && (A).b <= (B).b) +#define disjointRects(A,B) \ + ((A).l >= (B).r || (A).r <= (B).l || (A).t >= (B).b || (A).b <= (B).t) + +/* ****************************************************************** */ +/* xcf-general.c */ + +extern uint8_t *xcf_file ; +extern size_t xcf_length ; +extern int use_utf8 ; + +void xcfCheckspace(uint32_t addr,int spaceafter, const char *format,...) + __ATTRIBUTE__((format(printf,3,4))); +uint32_t xcfOffset(uint32_t addr,int spaceafter); + +int xcfNextprop(uint32_t *master,uint32_t *body); +const char* xcfString(uint32_t ptr,uint32_t *after); + +/* These are hardcoded in the Gimp sources: */ +#define TILE_WIDTH 64 +#define TILE_HEIGHT 64 + +struct tileDimensions { + struct rect c ; + unsigned width, height ; + unsigned tilesx, tilesy ; + unsigned ntiles ; +}; +/* computeDimensions assumes that width, height, c.l, and c.t are set */ +void computeDimensions(struct tileDimensions *); + +struct xcfTiles { + const struct _convertParams *params ; + uint32_t *tileptrs ; + uint32_t hierarchy ; +}; + +struct xcfLayer { + struct tileDimensions dim ; + const char *name ; + GimpLayerModeEffects mode ; + GimpImageType type ; + unsigned int opacity ; + int isVisible, hasMask ; + uint32_t propptr ; + struct xcfTiles pixels ; + struct xcfTiles mask ; +}; + +extern struct xcfImage { + int version ; + unsigned width, height ; + GimpImageBaseType type ; + XcfCompressionType compression ; + int numLayers ; + struct xcfLayer *layers ; + uint32_t colormapptr ; +} XCF ; + +void getBasicXcfInfo(void); + +#endif /* XCFTOOLS_H */ |