File indexing completed on 2025-03-09 04:09:52
0001 /* Flattening selections function for xcftools 0002 * 0003 * This file was written by Henning Makholm <henning@makholm.net> 0004 * It is hereby in the public domain. 0005 * 0006 * In jurisdictions that do not recognise grants of copyright to the 0007 * public domain: I, the author and (presumably, in those jurisdictions) 0008 * copyright holder, hereby permit anyone to distribute and use this code, 0009 * in source code or binary form, with or without modifications. This 0010 * permission is world-wide and irrevocable. 0011 * 0012 * Of course, I will not be liable for any errors or shortcomings in the 0013 * code, since I give it away without asking any compenstations. 0014 * 0015 * If you use or distribute this code, I would appreciate receiving 0016 * credit for writing it, in whichever way you find proper and customary. 0017 */ 0018 0019 #include "xcftools.h" 0020 #include "flatten.h" 0021 #include <string.h> 0022 #include <stdlib.h> 0023 0024 void 0025 init_flatspec(struct FlattenSpec *spec) 0026 { 0027 spec->window_mode = USE_CANVAS ; 0028 spec->default_pixel = PERHAPS_ALPHA_CHANNEL ; 0029 spec->numLayers = 0 ; 0030 spec->layers = NULL ; 0031 spec->transmap_filename = NULL ; 0032 spec->output_filename = "-" ; 0033 spec->out_color_mode = COLOR_BY_CONTENTS ; 0034 spec->partial_transparency_mode = ALLOW_PARTIAL_TRANSPARENCY ; 0035 spec->process_in_memory = 0 ; 0036 spec->gimpish_indexed = 1 ; 0037 } 0038 0039 int 0040 add_layer_request(struct FlattenSpec *spec, const char *layer) 0041 { 0042 spec->layers = realloc(spec->layers, 0043 sizeof(struct xcfLayer) * (1+spec->numLayers)); 0044 if( spec->layers == NULL ) { 0045 FatalUnexpected(_("Out of memory")); 0046 return XCF_ERROR; 0047 } 0048 spec->layers[spec->numLayers].name = layer ; 0049 spec->layers[spec->numLayers].mode = (GimpLayerModeEffects)-1 ; 0050 spec->layers[spec->numLayers].opacity = 9999 ; 0051 spec->layers[spec->numLayers].hasMask = -1 ; 0052 spec->numLayers++ ; 0053 return XCF_OK; 0054 } 0055 0056 struct xcfLayer * 0057 lastlayerspec(struct FlattenSpec *spec,const char *option) 0058 { 0059 if( spec->numLayers == 0 ) { 0060 FatalGeneric(20,_("The %s option must follow a layer name on the " 0061 "command line"),option); 0062 return XCF_PTR_EMPTY; 0063 } 0064 return spec->layers + (spec->numLayers-1) ; 0065 } 0066 0067 static int 0068 typeHasTransparency(GimpImageType type) 0069 { 0070 switch( type ) { 0071 case GIMP_RGB_IMAGE: 0072 case GIMP_GRAY_IMAGE: 0073 case GIMP_INDEXED_IMAGE: 0074 return 0 ; 0075 case GIMP_RGBA_IMAGE: 0076 case GIMP_GRAYA_IMAGE: 0077 case GIMP_INDEXEDA_IMAGE: 0078 return 1 ; 0079 } 0080 return 1 ; 0081 } 0082 0083 static enum out_color_mode 0084 color_by_layers(struct FlattenSpec *spec) 0085 { 0086 int colormap_is_colored = 0 ; 0087 enum out_color_mode grayish ; 0088 int i ; 0089 0090 if( spec->default_pixel == CHECKERED_BACKGROUND ) 0091 grayish = COLOR_GRAY ; 0092 else { 0093 int degrayed = degrayPixel(spec->default_pixel); 0094 if( degrayed < 0 ) { 0095 return COLOR_RGB ; 0096 } else if( spec->gimpish_indexed && 0097 (degrayed == 0 || degrayed == 255) ) { 0098 grayish = COLOR_MONO ; 0099 } else { 0100 grayish = COLOR_GRAY ; 0101 } 0102 } 0103 for( i=0; i<colormapLength; i++ ) { 0104 if( colormap[i] == NEWALPHA(0,0) || colormap[i] == NEWALPHA(-1,0) ) 0105 continue ; 0106 if( degrayPixel(colormap[i]) == -1 ) { 0107 colormap_is_colored = 1 ; 0108 break ; 0109 } else { 0110 grayish = COLOR_GRAY ; 0111 } 0112 } 0113 for( i=0; i<spec->numLayers; i++ ) 0114 switch( spec->layers[i].type ) { 0115 case GIMP_RGB_IMAGE: 0116 case GIMP_RGBA_IMAGE: 0117 return COLOR_RGB ; 0118 case GIMP_GRAY_IMAGE: 0119 case GIMP_GRAYA_IMAGE: 0120 grayish = COLOR_GRAY ; 0121 break ; 0122 case GIMP_INDEXED_IMAGE: 0123 case GIMP_INDEXEDA_IMAGE: 0124 if( colormap_is_colored ) return COLOR_RGB ; 0125 break ; 0126 } 0127 return grayish ; 0128 } 0129 0130 int 0131 complete_flatspec(struct FlattenSpec *spec, guesser guess_callback) 0132 { 0133 unsigned i ; 0134 int anyPartial ; 0135 0136 /* Find the layers to convert. 0137 */ 0138 if( spec->numLayers == 0 ) { 0139 spec->layers = XCF.layers ; 0140 spec->numLayers = XCF.numLayers ; 0141 } else { 0142 for( i=0; i<spec->numLayers; i++ ) { 0143 GimpLayerModeEffects mode ; 0144 int opacity, hasMask ; 0145 unsigned j ; 0146 0147 for( j=0; ; j++ ) { 0148 if( j == XCF.numLayers ) { 0149 FatalGeneric(22,_("The image has no layer called '%s'"), 0150 spec->layers[i].name); 0151 return XCF_ERROR; 0152 } 0153 if( strcmp(spec->layers[i].name,XCF.layers[j].name) == 0 ) 0154 break ; 0155 } 0156 mode = spec->layers[i].mode == (GimpLayerModeEffects)-1 ? 0157 XCF.layers[j].mode : spec->layers[i].mode ; 0158 opacity = spec->layers[i].opacity == 9999 ? 0159 XCF.layers[j].opacity : spec->layers[i].opacity ; 0160 hasMask = spec->layers[i].hasMask == -1 ? 0161 XCF.layers[j].hasMask : spec->layers[i].hasMask ; 0162 if( hasMask && !XCF.layers[j].hasMask && 0163 XCF.layers[j].mask.hierarchy == 0 ) { 0164 FatalGeneric(22,_("Layer '%s' has no layer mask to enable"), 0165 spec->layers[i].name); 0166 return XCF_ERROR; 0167 } 0168 spec->layers[i] = XCF.layers[j] ; 0169 spec->layers[i].mode = mode ; 0170 spec->layers[i].opacity = opacity ; 0171 spec->layers[i].hasMask = hasMask ; 0172 spec->layers[i].isVisible = 1 ; 0173 } 0174 } 0175 0176 /* Force the mode of the lowest visible layer to be Normal or Dissolve. 0177 * That may not be logical, but the Gimp does it 0178 */ 0179 for( i=0; i < spec->numLayers; i++ ) { 0180 if( spec->layers[i].isVisible ) { 0181 if( spec->layers[i].mode != GIMP_DISSOLVE_MODE ) 0182 spec->layers[i].mode = GIMP_NORMAL_MODE ; 0183 break ; 0184 } 0185 } 0186 0187 /* Mimic the Gimp's behavior on indexed layers */ 0188 if( XCF.type == GIMP_INDEXED && spec->gimpish_indexed ) { 0189 for( i=0; i<spec->numLayers; i++ ) 0190 if( spec->layers[i].mode != GIMP_DISSOLVE_MODE ) 0191 spec->layers[i].mode = GIMP_NORMAL_NOPARTIAL_MODE ; 0192 } else 0193 spec->gimpish_indexed = 0 ; 0194 0195 /* compute dimensions of the window */ 0196 if( spec->window_mode == AUTOCROP ) { 0197 int first = 1 ; 0198 for( i=0; i<spec->numLayers; i++ ) 0199 if( spec->layers[i].isVisible ) { 0200 if (computeDimensions(&spec->layers[i].dim) != XCF_OK) { 0201 return XCF_ERROR; 0202 } 0203 if( first ) { 0204 spec->dim = spec->layers[i].dim ; 0205 first = 0 ; 0206 } else { 0207 if( spec->dim.c.l > spec->layers[i].dim.c.l ) 0208 spec->dim.c.l = spec->layers[i].dim.c.l ; 0209 if( spec->dim.c.r < spec->layers[i].dim.c.r ) 0210 spec->dim.c.r = spec->layers[i].dim.c.r ; 0211 if( spec->dim.c.t > spec->layers[i].dim.c.t ) 0212 spec->dim.c.t = spec->layers[i].dim.c.t ; 0213 if( spec->dim.c.b < spec->layers[i].dim.c.b ) 0214 spec->dim.c.b = spec->layers[i].dim.c.b ; 0215 } 0216 } 0217 if( first ) { 0218 spec->window_mode = USE_CANVAS ; 0219 } else { 0220 spec->dim.width = spec->dim.c.r - spec->dim.c.l ; 0221 spec->dim.height = spec->dim.c.b - spec->dim.c.t ; 0222 } 0223 } 0224 if( spec->window_mode != AUTOCROP ) { 0225 if( (spec->window_mode & MANUAL_OFFSET) == 0 ) 0226 spec->dim.c.t = spec->dim.c.l = 0 ; 0227 if( (spec->window_mode & MANUAL_CROP) == 0 ) { 0228 spec->dim.height = XCF.height ; 0229 spec->dim.width = XCF.width ; 0230 } 0231 } 0232 if (computeDimensions(&spec->dim) != XCF_OK) { 0233 return XCF_ERROR; 0234 } 0235 0236 /* Turn off layers that we don't hit at all */ 0237 for( i=0; i<spec->numLayers; i++ ) 0238 if( spec->layers[i].isVisible && 0239 disjointRects(spec->dim.c,spec->layers[i].dim.c) ) 0240 spec->layers[i].isVisible = 0 ; 0241 0242 /* See if there is a completely covering layer somewhere in the stack */ 0243 /* Also check if partial transparency is possible */ 0244 anyPartial = 0 ; 0245 for( i=spec->numLayers; i-- ; ) { 0246 if( !spec->layers[i].isVisible ) 0247 continue ; 0248 if( typeHasTransparency(spec->layers[i].type) ) { 0249 if( spec->layers[i].mode == GIMP_NORMAL_MODE ) 0250 anyPartial = 1; 0251 } else if( isSubrect(spec->dim.c,spec->layers[i].dim.c) && 0252 !spec->layers[i].hasMask && 0253 (spec->layers[i].mode == GIMP_NORMAL_MODE || 0254 spec->layers[i].mode == GIMP_NORMAL_NOPARTIAL_MODE || 0255 spec->layers[i].mode == GIMP_DISSOLVE_MODE) ) { 0256 /* This layer fills out the entire image. 0257 * Turn off only lower layers, and note that we cannot have 0258 * transparency at all. 0259 */ 0260 while(i) spec->layers[--i].isVisible = 0 ; 0261 if( spec->default_pixel != FORCE_ALPHA_CHANNEL ) 0262 spec->default_pixel = NEWALPHA(colormap[0],255); 0263 anyPartial = 0 ; 0264 break ; 0265 } 0266 } 0267 if( spec->partial_transparency_mode == ALLOW_PARTIAL_TRANSPARENCY && 0268 (!anyPartial || ALPHA(spec->default_pixel) >= 128) ) 0269 spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ; 0270 0271 /* Initialize layers and print overview if we're verbose */ 0272 for( i=spec->numLayers; i--; ) 0273 if( spec->layers[i].isVisible ) { 0274 if (initLayer(&spec->layers[i]) != XCF_OK) { 0275 return XCF_ERROR; 0276 } 0277 if( verboseFlag ) { 0278 fprintf(stderr,"%dx%d%+d%+d %s %s", 0279 spec->layers[i].dim.width, spec->layers[i].dim.height, 0280 spec->layers[i].dim.c.l - spec->dim.c.l, 0281 spec->layers[i].dim.c.t - spec->dim.c.t, 0282 _(showGimpImageType(spec->layers[i].type)), 0283 _(showGimpLayerModeEffects(spec->layers[i].mode))); 0284 if( spec->layers[i].opacity < 255 ) 0285 fprintf(stderr,"/%02d%%",spec->layers[i].opacity * 100 / 255); 0286 if( XCF.layers[i].hasMask ) 0287 fprintf(stderr,_("/mask")); 0288 fprintf(stderr," %s\n",spec->layers[i].name); 0289 } 0290 } 0291 0292 /* Resolve color mode unless we wait until we have the entire image */ 0293 if( spec->out_color_mode == COLOR_BY_CONTENTS && 0294 !spec->process_in_memory ) { 0295 if( guess_callback ) 0296 spec->out_color_mode = guess_callback(spec,NULL); 0297 if( spec->out_color_mode == COLOR_BY_CONTENTS ) 0298 spec->out_color_mode = color_by_layers(spec) ; 0299 } 0300 return XCF_OK; 0301 } 0302 0303 int 0304 analyse_colormode(struct FlattenSpec *spec,rgba **allPixels, 0305 guesser guess_callback) 0306 { 0307 unsigned x,y ; 0308 int status ; 0309 /* 8 - looking for any transparency 0310 * 4 - looking for partially transparent pixels 0311 * 2 - looking for pixels other than black and white 0312 * 1 - looking for colored pixels 0313 */ 0314 int known_absent = 0 ; 0315 int assume_present = 0 ; 0316 0317 if( spec->out_color_mode == COLOR_BY_CONTENTS && guess_callback ) 0318 spec->out_color_mode = guess_callback(spec,allPixels) ; 0319 0320 if( spec->out_color_mode == COLOR_RGB ) assume_present |= 3 ; 0321 if( spec->out_color_mode == COLOR_INDEXED ) assume_present |= 3 ; 0322 if( spec->out_color_mode == COLOR_GRAY ) assume_present |= 2 ; 0323 switch( color_by_layers(spec) ) { 0324 case COLOR_GRAY: known_absent |= 1 ; break ; 0325 case COLOR_MONO: known_absent |= 3 ; break ; 0326 default: break ; 0327 } 0328 if( spec->partial_transparency_mode == DISSOLVE_PARTIAL_TRANSPARENCY || 0329 spec->partial_transparency_mode == PARTIAL_TRANSPARENCY_IMPOSSIBLE ) 0330 known_absent |= 4 ; 0331 if( ALPHA(spec->default_pixel) >= 128 ) known_absent |= 12 ; 0332 else if( spec->default_pixel == FORCE_ALPHA_CHANNEL ) assume_present |= 8 ; 0333 0334 status = 15 - (known_absent | assume_present) ; 0335 0336 for( y=0; status && y<spec->dim.height; y++ ) { 0337 rgba *row = allPixels[y] ; 0338 if( (status & 3) != 0 ) { 0339 /* We're still interested in color */ 0340 for( x=0; status && x<spec->dim.width; x++ ) { 0341 if( NULLALPHA(row[x]) ) 0342 status &= ~8 ; 0343 else { 0344 rgba full = row[x] | (255 << ALPHA_SHIFT) ; 0345 if( !FULLALPHA(row[x]) ) status &= ~12 ; 0346 if( full == NEWALPHA(0,255) || full == NEWALPHA(-1,255) ) 0347 /* Black or white */ ; 0348 else if( degrayPixel(row[x]) != -1 ) 0349 status &= ~2 ; /* gray */ 0350 else 0351 status &= ~3 ; /* color */ 0352 } 0353 } 0354 } else { 0355 /* Not interested in color */ 0356 for( x=0; status && x<spec->dim.width; x++ ) { 0357 if( NULLALPHA(row[x]) ) 0358 status &= ~8 ; 0359 else if( !FULLALPHA(row[x]) ) 0360 status &= ~12 ; 0361 } 0362 } 0363 } 0364 0365 status |= known_absent ; 0366 0367 switch( spec->out_color_mode ) { 0368 case COLOR_INDEXED: /* The caller takes responsibility */ 0369 case COLOR_RGB: /* Everything is fine. */ 0370 break ; 0371 case COLOR_GRAY: 0372 if( (status & 1) == 0 ) { 0373 FatalGeneric(103, 0374 _("Grayscale output selected, but colored pixel(s) found")); 0375 return XCF_ERROR; 0376 } 0377 break ; 0378 case COLOR_MONO: 0379 if( (status & 2) == 0 ) { 0380 FatalGeneric(103,_("Monochrome output selected, but not all pixels " 0381 "are black or white")); 0382 return XCF_ERROR; 0383 } 0384 break ; 0385 case COLOR_BY_FILENAME: /* Should not happen ... */ 0386 case COLOR_BY_CONTENTS: 0387 if( (status & 1) == 0 ) 0388 spec->out_color_mode = COLOR_RGB ; 0389 else if( (status & 2) == 0 ) 0390 spec->out_color_mode = COLOR_GRAY ; 0391 else 0392 spec->out_color_mode = COLOR_MONO ; 0393 break ; 0394 } 0395 0396 if( (status & 12) == 12 ) /* No transparency found */ 0397 spec->default_pixel = NEWALPHA(colormap[0],255); 0398 else if( (status & 12) == 4 ) 0399 spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ; 0400 return XCF_OK; 0401 }