diff options
author | Henning Makholm <henning@makholm.net> | 2006-01-27 18:00:00 (EST) |
---|---|---|
committer | Julien Jorge <julien.jorge@stuff-o-matic.com> | 2013-01-10 16:00:40 (EST) |
commit | 7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717 (patch) | |
tree | 98d2772f50aaddb02ac94492d2b8b151aa3e9465 /flatspec.c | |
download | xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.zip xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.gz xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.bz2 |
Import of release 0.7
Diffstat (limited to 'flatspec.c')
-rw-r--r-- | flatspec.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/flatspec.c b/flatspec.c new file mode 100644 index 0000000..e318458 --- /dev/null +++ b/flatspec.c @@ -0,0 +1,339 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "xcftools.h" +#include "flatten.h" +#include <string.h> +#include <stdlib.h> + +void +init_flatspec(struct FlattenSpec *spec) +{ + spec->dim.c.l = spec->dim.c.r = spec->dim.c.t = spec->dim.c.b = 0 ; + spec->dim.height = -1 ; + spec->dim.width = -1 ; + 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( 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 ; + + if( spec->dim.height == -1 ) { + spec->dim.height = XCF.height ; + spec->dim.width = XCF.width ; + } + computeDimensions(&spec->dim); + + /* 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 ; + + /* 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 || FULLALPHA(spec->default_pixel)) ) + 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( FULLALPHA(spec->default_pixel) ) 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 ; +} |