diff --git a/plugins/impex/xcf/3rdparty/xcftools/flatspec.c b/plugins/impex/xcf/3rdparty/xcftools/flatspec.c index ec12a67faf..a0980f0da1 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/flatspec.c +++ b/plugins/impex/xcf/3rdparty/xcftools/flatspec.c @@ -1,380 +1,383 @@ /* Flattening selections function for xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include "flatten.h" #include #include 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 ; int i ; if( spec->default_pixel == CHECKERED_BACKGROUND ) grayish = COLOR_GRAY ; else { int degrayed = degrayPixel(spec->default_pixel); if( degrayed < 0 ) { return COLOR_RGB ; } else if( spec->gimpish_indexed && (degrayed == 0 || degrayed == 255) ) { grayish = COLOR_MONO ; } else { grayish = COLOR_GRAY ; } } for( i=0; inumLayers; 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 +int 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; inumLayers; 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; inumLayers; 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; inumLayers; 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; inumLayers; 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].hasMask && (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 only 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 (initLayer(&spec->layers[i]) != XCF_OK) { + return XCF_ERROR; + } 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) ; } + return XCF_OK; } 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 && ydim.height; y++ ) { rgba *row = allPixels[y] ; if( (status & 3) != 0 ) { /* We're still interested in color */ for( x=0; status && xdim.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 && xdim.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/plugins/impex/xcf/3rdparty/xcftools/flatten.c b/plugins/impex/xcf/3rdparty/xcftools/flatten.c index c021bbe9e8..f67794ad90 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/flatten.c +++ b/plugins/impex/xcf/3rdparty/xcftools/flatten.c @@ -1,690 +1,707 @@ /* Flattning functions for xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include "flatten.h" #include "pixels.h" #include #include #include 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 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)) +static int __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 ; + if( (bot->summary & TILESUMMARY_ALLNULL) != 0 ) return XCF_OK; + if( (top->summary & TILESUMMARY_ALLNULL) != 0 ) return XCF_OK; 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!"); + { + FatalUnexpected("Normal and Dissolve mode can't happen here!"); + return XCF_ERROR; + } 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 default: FatalUnexpected("Hue hextant is %d", hsvBot.hue); } break ; } default: - FatalUnsupportedXCF(_("'%s' layer mode"), + { + FatalUnsupportedXCF(_("'%s' layer mode"), _(showGimpLayerModeEffects(mode))); + return XCF_ERROR; + } } 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 ; + return XCF_OK; } 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) ; /* Falls through */ case GIMP_DISSOLVE_MODE: dissolveTile(tile); /* Falls 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; icount; 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; icount; 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 == XCF_PTR_EMPTY) { + return XCF_PTR_EMPTY; + } if( below->refcount > 1 ) { - assert( below == top ); + if (below != top) { + return XCF_PTR_EMPTY; + } /* 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); + if (merge_exotic(below,tile,spec->layers[nlayers].mode) != XCF_OK) { + return XCF_PTR_EMPTY; + } 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; icount; 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; icount; 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 +int 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 = TILE_TOP(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 = TILE_LEFT(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) ; + if (tile == XCF_PTR_EMPTY) { + return XCF_ERROR; + } 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]); } + return XCF_OK; } static rgba **collectPointer ; static void collector(unsigned num,rgba *row) { num += 0; *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/plugins/impex/xcf/3rdparty/xcftools/flatten.h b/plugins/impex/xcf/3rdparty/xcftools/flatten.h index 2047cbca6d..9e16e3c30a 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/flatten.h +++ b/plugins/impex/xcf/3rdparty/xcftools/flatten.h @@ -1,77 +1,77 @@ /* Flattning functions for xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #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); +int 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); +int flattenIncrementally(struct FlattenSpec *,lineCallback); rgba **flattenAll(struct FlattenSpec*); void shipoutWithCallback(struct FlattenSpec *,rgba **pixels,lineCallback); #endif /* FLATTEN_H */ diff --git a/plugins/impex/xcf/3rdparty/xcftools/pixels.c b/plugins/impex/xcf/3rdparty/xcftools/pixels.c index d7037937c9..4efd6d76a8 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/pixels.c +++ b/plugins/impex/xcf/3rdparty/xcftools/pixels.c @@ -1,491 +1,552 @@ /* Pixel and tile functions for xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #define DEBUG #include "xcftools.h" #include "pixels.h" #include #include 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 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 ) + xcfL(ptr+4) != dim->c.b - dim->c.t ) { FatalBadXCF("Drawable size mismatch at %" PRIX32, ptr); + return 0; + } return ptr += 8 ; } -static void +static int initTileDirectory(struct tileDimensions *dim,struct xcfTiles *tiles, const char *type) { uint32_t ptr ; uint32_t data ; + int response; + ptr = tiles->hierarchy ; tiles->hierarchy = 0 ; - if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return XCF_OK; 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 ; + xcfString(ptr,&ptr); + PropType type; + while( (response = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) { + + } + if (response != XCF_OK) { + return XCF_ERROR; + } + uint32_t ptrout; + if(xcfOffset(ptr,4*4, &ptrout) != XCF_OK) return XCF_ERROR; + ptr = ptrout; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return XCF_OK; } /* 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 ) + 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 ; + return XCF_ERROR; + } + uint32_t ptrout; + if(xcfOffset(ptr+4,3*4, &ptrout) != XCF_OK) return XCF_ERROR; + ptr = ptrout; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return XCF_OK; - xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr); + if ((response = xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr)) != XCF_OK) { + return XCF_ERROR; + } /* 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 + return XCF_OK; } -void +int initLayer(struct xcfLayer *layer) { if( layer->dim.ntiles == 0 || (layer->pixels.hierarchy == 0 && layer->mask.hierarchy == 0) ) - return ; + return XCF_OK; 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))); + { + FatalUnsupportedXCF(_("Layer type %s"),_(showGimpImageType(layer->type))); + return XCF_ERROR; + } + + } + if (initTileDirectory(&layer->dim,&layer->pixels, + _(showGimpImageType(layer->type))) != XCF_OK) { + return XCF_ERROR; } - initTileDirectory(&layer->dim,&layer->pixels, - _(showGimpImageType(layer->type))); layer->mask.params = &convertChannel ; - initTileDirectory(&layer->dim,&layer->mask,"layer mask"); + if (initTileDirectory(&layer->dim,&layer->mask,"layer mask") != XCF_OK) { + return XCF_ERROR; + } + return XCF_OK; } -static void copyStraightPixels(rgba *dest,unsigned npixels, +static int copyStraightPixels(rgba *dest,unsigned npixels, uint32_t ptr,convertParams *params); -void +int initColormap(void) { uint32_t ncolors ; if( XCF.colormapptr == 0 ) { colormapLength = 0 ; - return ; + return XCF_OK; } ncolors = xcfL(XCF.colormapptr) ; - if( ncolors > 256 ) + if( ncolors > 256 ) { FatalUnsupportedXCF(_("Color map has more than 256 entries")); - copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap); + return XCF_ERROR; + } + if(copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap) != XCF_OK) { + return XCF_ERROR; + } 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 + return XCF_OK; } /* ****************************************************************** */ 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 && icount; 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 +static int 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); + int response; + if ((response = xcfCheckspace(ptr,bpp*npixels, + "pixel array (%u x %d bpp) at %"PRIX32,npixels,bpp,ptr)) != XCF_OK) { + return XCF_ERROR; + } 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 ; } + return XCF_OK; } -static void +static int 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 + int response; /* 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"); + if ((response = xcfCheckspace(ptr,2,"RLE data stream")) != XCF_OK) { + return XCF_ERROR; + } countspec = (int8_t) xcf_file[ptr++] ; count = countspec >= 0 ? countspec+1 : -countspec ; if( count == 128 ) { - xcfCheckspace(ptr,3,"RLE long count"); + if ((response = xcfCheckspace(ptr,3,"RLE long count")) != XCF_OK) { + return XCF_ERROR; + } count = xcf_file[ptr++] << 8 ; count += xcf_file[ptr++] ; } - if( j + count > npixels ) + if( j + count > npixels ) { FatalBadXCF("Overlong RLE run at %"PRIX32" (plane %u, %u left)", ptr,i,npixels-j); + return XCF_ERROR; + } 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 + return XCF_OK; } -static void +static int 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); + if (copyStraightPixels(dest->pixels,dest->count,ptr,params) != XCF_OK) { + return XCF_ERROR; + } break ; case COMPRESS_RLE: - copyRLEpixels(dest->pixels,dest->count,ptr,params); + if (copyRLEpixels(dest->pixels,dest->count,ptr,params) != XCF_OK) { + return XCF_ERROR; + } break ; default: - FatalUnsupportedXCF(_("%s compression"), + { + FatalUnsupportedXCF(_("%s compression"), _(showXcfCompressionType(XCF.compression))); + return XCF_ERROR; + } } + return XCF_OK; } 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 (want.l >= want.r || want.t >= want.b ) { + return XCF_PTR_EMPTY; + } + + 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 ) { int tx = TILE_NUM(want.l - dim->c.l); int ty = TILE_NUM(want.t - dim->c.t); 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); + if (copyTilePixels(tile,tiles->tileptrs[tx + ty*dim->tilesx],tiles->params) != XCF_OK) { + return XCF_PTR_EMPTY; + } return tile ; } } /* OK, we must construct the wanted tile as a jigsaw */ { unsigned width = want.r-want.l ; rgba *pixvert = tile->pixels ; rgba *pixhoriz ; int y, ty, l0, l1 ; int x, tx, c0, c1 ; unsigned lstart, lnum ; unsigned 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=TILE_NUM(want.t-dim->c.t), l0=TILEYn(*dim,ty); y want.b ? want.b : l1) - y ; pixhoriz = pixvert ; for( x=want.l, tx=TILE_NUM(want.l-dim->c.l), c0=TILEXn(*dim,tx); x 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); + if (copyTilePixels(&tmptile, + tiles->tileptrs[tx+ty*dim->tilesx],tiles->params) != XCF_OK) { + return XCF_PTR_EMPTY; + } + for(i=0; isummary &= 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/plugins/impex/xcf/3rdparty/xcftools/pixels.h b/plugins/impex/xcf/3rdparty/xcftools/pixels.h index 5342351367..6351784b3c 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/pixels.h +++ b/plugins/impex/xcf/3rdparty/xcftools/pixels.h @@ -1,130 +1,130 @@ /* Pixel and tile functions for xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #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 initLayer(struct xcfLayer *); +int 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 defined(__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 /* everything 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 *); struct Tile * getMaskOrLayerTile(struct tileDimensions *dim, struct xcfTiles *tiles, struct rect want); #endif /* FLATTEN_H */ diff --git a/plugins/impex/xcf/3rdparty/xcftools/utils.c b/plugins/impex/xcf/3rdparty/xcftools/utils.c index 37b5aa8236..df5e50c7a1 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/utils.c +++ b/plugins/impex/xcf/3rdparty/xcftools/utils.c @@ -1,159 +1,161 @@ /* Generic support functions for Xcftools * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include #include #include #include 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); + /* don't exit here - Krita can't handle errors otherwise */ + /* 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 +int 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) ; + return XCF_ERROR; } + return XCF_OK; } 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, _("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 succedes. Just call it an I/O error */ } } FatalUnexpected(_("!Error writing file %s"),name); } diff --git a/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c b/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c index e5ae5a6049..6db76c76b7 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c +++ b/plugins/impex/xcf/3rdparty/xcftools/xcf-general.c @@ -1,305 +1,381 @@ /* Generic functions for reading XCF files * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #include "xcftools.h" #include #include #ifdef HAVE_ICONV # include #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) +int +xcfOffset(uint32_t addr,int spaceafter, uint32_t* apparent) { - uint32_t apparent ; - xcfCheckspace(addr,4,"(xcfOffset)"); - apparent = xcfL(addr); - xcfCheckspace(apparent,spaceafter, + if (!apparent) { + return XCF_ERROR; + } + if(xcfCheckspace(addr,4,"(xcfOffset)") != XCF_OK) { + return XCF_ERROR; + } + *apparent = xcfL(addr); + if (xcfCheckspace(*apparent,spaceafter, "Too large offset (%" PRIX32 ") at position %" PRIX32, - apparent,addr); - return apparent ; + *apparent,addr) != XCF_OK) { + return XCF_ERROR; + } + return XCF_OK; } int -xcfNextprop(uint32_t *master,uint32_t *body) +xcfNextprop(uint32_t *master,uint32_t *body, PropType *typeOut) { + int response; + + if (typeOut == 0) { + return XCF_ERROR; + } + uint32_t ptr, length, total, minlength ; PropType type ; ptr = *master ; - xcfCheckspace(ptr,8,"(property header)"); + if ((response = xcfCheckspace(ptr,8,"(property header)")) != XCF_OK) { + return XCF_ERROR; + } type = xcfL(ptr); length = xcfL(ptr+4); *body = ptr+8 ; switch(type) { case PROP_COLORMAP: { uint32_t ncolors ; - xcfCheckspace(ptr+8,4,"(colormap length)"); + if ((response = xcfCheckspace(ptr+8,4,"(colormap length)")) != XCF_OK) { + return XCF_ERROR; + } ncolors = xcfL(ptr+8) ; - if( ncolors > 256 ) + if( ncolors > 256 ) { FatalBadXCF("Colormap has %" PRIu32 " entries",ncolors); + return XCF_ERROR; + } + /* Surprise! Some older version 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 ) + if( length < minlength ) { FatalBadXCF("Short %s property at %" PRIX32 " (%" PRIu32 "<%" PRIu32 ")", showPropType(type),ptr,length,minlength); + return XCF_ERROR; + } *master = ptr+8+length ; total = 8 + length + (type != PROP_END ? 8 : 0) ; - if( total < length ) /* Check overwrap */ + if( total < length ) { /* Check overwrap */ FatalBadXCF("Overlong property at %" PRIX32, ptr); - xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr) ; - return type ; + return XCF_ERROR; + } + if((response = xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr)) != 0) { + return XCF_ERROR; + } + *typeOut = type; + return XCF_OK; } const char* xcfString(uint32_t ptr,uint32_t *after) { uint32_t length ; unsigned i ; ICONV_CONST char *utf8master ; - xcfCheckspace(ptr,4,"(string length)"); + int response; + if ((response = xcfCheckspace(ptr,4,"(string length)")) != XCF_OK) { + return XCF_PTR_EMPTY; + } length = xcfL(ptr) ; ptr += 4 ; - xcfCheckspace(ptr,length,"(string)"); + if ((response = xcfCheckspace(ptr,length,"(string)")) != XCF_OK) { + return XCF_PTR_EMPTY; + } utf8master = (ICONV_CONST char*)(xcf_file+ptr) ; if( after ) *after = ptr + length ; - if( length == 0 || utf8master[length-1] != 0 ) + if( length == 0 || utf8master[length-1] != 0 ) { FatalBadXCF("String at %" PRIX32 " not zero-terminated",ptr-4); + return XCF_PTR_EMPTY; + } 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 ) + if( utf8master[i] == 0 ) { FatalBadXCF("String at %" PRIX32 " has embedded zeroes",ptr-4); + return XCF_PTR_EMPTY; + } 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 ) + if( errno == EILSEQ || errno == EINVAL ) { FatalBadXCF("Bad UTF-8 encoding '%s' at %" PRIXPTR, inbuf,(uintptr_t)((inbuf-utf8master)+ptr)); + return XCF_PTR_EMPTY; + } if( errno == E2BIG ) { targetsize += 1+incount ; xcffree(buffer) ; continue ; } FatalUnexpected("!iconv on layer name at %"PRIX32,ptr); + return XCF_PTR_EMPTY: } } #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 +int getBasicXcfInfo(void) { + uint32_t ptr, data, layerfile ; PropType type ; int i, j ; + + int errorStatus; + uint32_t ptrout; + + if ((errorStatus = xcfCheckspace(0,14+7*4,"(very short)")) != 0) { + return XCF_ERROR; + } - 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 + else { FatalBadXCF(_("Not an XCF file at all (magic not recognized)")); + return XCF_ERROR; + } if (XCF.version < 0 || XCF.version > 3) { - return; + return XCF_ERROR; } 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 ) { + while( (errorStatus = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) { + if (errorStatus != XCF_OK) { + return XCF_ERROR; + } + 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.numLayers = 0; + while (1) { + errorStatus = xcfOffset(ptr,8*4, &ptrout); + if (errorStatus != XCF_OK) { + return XCF_ERROR; + } + if (!ptrout) { + break; + } + 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); + if (L->name == XCF_PTR_EMPTY) { + return XCF_ERROR; + } L->propptr = ptr ; L->isGroup = 0; L->pathLength = 0; L->path = NULL; - while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) { + while( (errorStatus = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) { + if (errorStatus != XCF_OK) { + return XCF_ERROR; + } 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 ; case PROP_GROUP_ITEM: L->isGroup = 1 ; break; case PROP_ITEM_PATH: L->pathLength = (ptr - data - 2) / 4 ; if ( L->pathLength != 0 ) { L->path = xcfmalloc( L->pathLength * sizeof(unsigned) ) ; for ( j = 0; j!=L->pathLength; j++ ) *(L->path + j) = (unsigned)xcfL(data + 4 * j); } break; default: /* Ignore unknown properties */ break ; } } - xcfCheckspace(ptr,8,"(end of layer %s)",L->name); + if ((errorStatus = xcfCheckspace(ptr,8,"(end of layer %s)",L->name)) != XCF_OK) { + return XCF_ERROR; + } L->pixels.tileptrs = 0 ; - L->pixels.hierarchy = xcfOffset(ptr ,4*4); + + if (xcfOffset(ptr , 4*4, &(L->pixels.hierarchy)) != XCF_OK) { + return XCF_ERROR; + } L->mask.tileptrs = 0 ; - L->mask.hierarchy = xcfOffset(ptr+4,4*4); + if (xcfOffset(ptr+4, 4*4, &(L->mask.hierarchy)) != XCF_OK) { + return XCF_ERROR; + } computeDimensions(&L->dim); + } + return XCF_OK; } diff --git a/plugins/impex/xcf/3rdparty/xcftools/xcftools.h b/plugins/impex/xcf/3rdparty/xcftools/xcftools.h index 7b5eb5f00b..2b2b06eb10 100644 --- a/plugins/impex/xcf/3rdparty/xcftools/xcftools.h +++ b/plugins/impex/xcf/3rdparty/xcftools/xcftools.h @@ -1,201 +1,208 @@ /* Generic functions and macros for reading XCF files * * This file was written by Henning Makholm * It is hereby in the public domain. * * In jurisdictions that do not recognise grants of copyright to the * public domain: I, the author and (presumably, in those jurisdictions) * copyright holder, hereby permit anyone to distribute and use this code, * in source code or binary form, with or without modifications. This * permission is world-wide and irrevocable. * * Of course, I will not be liable for any errors or shortcomings in the * code, since I give it away without asking any compenstations. * * If you use or distribute this code, I would appreciate receiving * credit for writing it, in whichever way you find proper and customary. */ #ifndef XCFTOOLS_H #define XCFTOOLS_H #include "config.h" #include "enums.h" #include #include #if defined(HAVE_GETTEXT) && defined(ENABLE_NLS) #include #define _(s) gettext(s) void nls_init(void); #else #define _(s) (s) #define nls_init() (void)0 #endif #define N_(s) (s) #if HAVE_INTTYPES_H # define __STDC_FORMAT_MACROS # include #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; 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 #elif HAVE_ARPA_INET_H # include #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 defined(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 */ + +#define XCF_ERROR 1 +#define XCF_OK 0 +#define XCF_PTR_ERROR (-1) +#define XCF_PTR_EMPTY 0 + + 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)) ; + __ATTRIBUTE__((format(printf,2,3))) ; void FatalUnexpected(const char* format,...) - __ATTRIBUTE__((format(printf,1,2),noreturn)) ; + __ATTRIBUTE__((format(printf,1,2))) ; void FatalBadXCF(const char* format,...) - __ATTRIBUTE__((format(printf,1,2),noreturn)) ; + __ATTRIBUTE__((format(printf,1,2))) ; void FatalUnsupportedXCF(const char* format,...) - __ATTRIBUTE__((format(printf,1,2),noreturn)) ; + __ATTRIBUTE__((format(printf,1,2))) ; 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,...) +int xcfCheckspace(uint32_t addr,int spaceafter, const char *format,...) __ATTRIBUTE__((format(printf,3,4))); -uint32_t xcfOffset(uint32_t addr,int spaceafter); +int xcfOffset(uint32_t addr,int spaceafter, uint32_t* apparent); -int xcfNextprop(uint32_t *master,uint32_t *body); +int xcfNextprop(uint32_t *master,uint32_t *body, PropType* type); const char* xcfString(uint32_t ptr,uint32_t *after); /* These are hardcoded in the Gimp sources: */ #define TILE_SHIFT 6 #define TILE_WIDTH (1<> TILE_SHIFT) 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 ; int isGroup ; unsigned pathLength ; unsigned *path ; }; extern struct xcfImage { int version ; unsigned width, height ; GimpImageBaseType type ; XcfCompressionType compression ; int numLayers ; struct xcfLayer *layers ; uint32_t colormapptr ; } XCF ; -void getBasicXcfInfo(void); +int getBasicXcfInfo(void); #endif /* XCFTOOLS_H */ diff --git a/plugins/impex/xcf/kis_xcf_import.cpp b/plugins/impex/xcf/kis_xcf_import.cpp index 138c82e4e9..218f96d488 100644 --- a/plugins/impex/xcf/kis_xcf_import.cpp +++ b/plugins/impex/xcf/kis_xcf_import.cpp @@ -1,317 +1,330 @@ /* * Copyright (c) 2009 Cyrille Berger * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kis_xcf_import.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_iterator_ng.h" #include "kis_types.h" #include extern "C" { #include "xcftools.h" #include "pixels.h" #define GET_RED(x) (x >> RED_SHIFT) #define GET_GREEN(x) (x >> GREEN_SHIFT) #define GET_BLUE(x) (x >> BLUE_SHIFT) #define GET_ALPHA(x) (x >> ALPHA_SHIFT) } QString layerModeG2K(GimpLayerModeEffects mode) { switch (mode) { case GIMP_NORMAL_MODE: return COMPOSITE_OVER; case GIMP_DISSOLVE_MODE: return COMPOSITE_DISSOLVE; case GIMP_MULTIPLY_MODE: return COMPOSITE_MULT; case GIMP_SCREEN_MODE: return COMPOSITE_SCREEN; case GIMP_OVERLAY_MODE: case GIMP_SOFTLIGHT_MODE: return COMPOSITE_OVERLAY; case GIMP_DIFFERENCE_MODE: return COMPOSITE_DIFF; case GIMP_ADDITION_MODE: return COMPOSITE_ADD; case GIMP_SUBTRACT_MODE: return COMPOSITE_SUBTRACT; case GIMP_DARKEN_ONLY_MODE: return COMPOSITE_DARKEN; case GIMP_LIGHTEN_ONLY_MODE: return COMPOSITE_LIGHTEN; case GIMP_HUE_MODE: return COMPOSITE_HUE_HSL; case GIMP_SATURATION_MODE: return COMPOSITE_SATURATION_HSV; case GIMP_COLOR_MODE: return COMPOSITE_COLOR_HSL; case GIMP_VALUE_MODE: return COMPOSITE_VALUE; case GIMP_DIVIDE_MODE: return COMPOSITE_DIVIDE; case GIMP_DODGE_MODE: return COMPOSITE_DODGE; case GIMP_BURN_MODE: return COMPOSITE_BURN; case GIMP_ERASE_MODE: return COMPOSITE_ERASE; case GIMP_REPLACE_MODE: return COMPOSITE_COPY; case GIMP_HARDLIGHT_MODE: return COMPOSITE_HARD_LIGHT; case GIMP_COLOR_ERASE_MODE: case GIMP_NORMAL_NOPARTIAL_MODE: case GIMP_ANTI_ERASE_MODE: case GIMP_GRAIN_EXTRACT_MODE: return COMPOSITE_GRAIN_EXTRACT; case GIMP_GRAIN_MERGE_MODE: return COMPOSITE_GRAIN_MERGE; case GIMP_BEHIND_MODE: break; } dbgFile << "Unknown mode: " << mode; return COMPOSITE_OVER; } struct Layer { KisLayerSP layer; int depth; KisMaskSP mask; }; KisGroupLayerSP findGroup(const QVector &layers, const Layer& layer, int i) { for (; i < layers.size(); ++i) { KisGroupLayerSP group = dynamic_cast(const_cast(layers[i].layer.data())); if (group && (layers[i].depth == layer.depth -1)) { return group; } } return 0; } void addLayers(const QVector &layers, KisImageSP image, int depth) { for(int i = 0; i < layers.size(); i++) { const Layer &layer = layers[i]; if (layer.depth == depth) { KisGroupLayerSP group = (depth == 0 ? image->rootLayer() : findGroup(layers, layer, i)); image->addNode(layer.layer, group); if (layer.mask) { image->addNode(layer.mask, layer.layer); } } } } K_PLUGIN_FACTORY_WITH_JSON(XCFImportFactory, "krita_xcf_import.json", registerPlugin();) KisXCFImport::KisXCFImport(QObject *parent, const QVariantList &) : KisImportExportFilter(parent) { } KisXCFImport::~KisXCFImport() { } KisImportExportFilter::ConversionStatus KisXCFImport::convert(KisDocument *document, QIODevice *io, KisPropertiesConfigurationSP /*configuration*/) { + int errorStatus; + dbgFile << "Start decoding file"; QByteArray data = io->readAll(); xcf_file = (uint8_t*)data.data(); xcf_length = data.size(); io->close(); // Decode the data - getBasicXcfInfo() ; + if (getBasicXcfInfo() != XCF_OK) { + if (XCF.version > 3) { + return KisImportExportFilter::ConversionStatus::UnsupportedVersion; + } + return KisImportExportFilter::ConversionStatus::ParsingError; + } + + if (XCF.version < 0 || XCF.version > 3) { document->setErrorMessage(i18n("This XCF file is too new; Krita cannot support XCF files written by GIMP 2.9 or newer.")); return KisImportExportFilter::UnsupportedVersion; } - initColormap(); + if(initColormap() != XCF_OK) { + return KisImportExportFilter::ConversionStatus::ParsingError; + } dbgFile << XCF.version << "width = " << XCF.width << "height = " << XCF.height << "layers = " << XCF.numLayers; // Create the image KisImageSP image = new KisImage(document->createUndoStore(), XCF.width, XCF.height, KoColorSpaceRegistry::instance()->rgb8(), "built image"); QVector layers; uint maxDepth = 0; // Read layers for (int i = 0; i < XCF.numLayers; ++i) { Layer layer; xcfLayer& xcflayer = XCF.layers[i]; dbgFile << i << " name = " << xcflayer.name << " opacity = " << xcflayer.opacity << "group:" << xcflayer.isGroup << xcflayer.pathLength; dbgFile << ppVar(xcflayer.dim.width) << ppVar(xcflayer.dim.height) << ppVar(xcflayer.dim.tilesx) << ppVar(xcflayer.dim.tilesy) << ppVar(xcflayer.dim.ntiles) << ppVar(xcflayer.dim.c.t) << ppVar(xcflayer.dim.c.l) << ppVar(xcflayer.dim.c.r) << ppVar(xcflayer.dim.c.b); maxDepth = qMax(maxDepth, xcflayer.pathLength); bool isRgbA = false; // Select the color space const KoColorSpace* colorSpace = 0; switch (xcflayer.type) { case GIMP_INDEXED_IMAGE: case GIMP_INDEXEDA_IMAGE: case GIMP_RGB_IMAGE: case GIMP_RGBA_IMAGE: colorSpace = KoColorSpaceRegistry::instance()->rgb8(); isRgbA = true; break; case GIMP_GRAY_IMAGE: case GIMP_GRAYA_IMAGE: colorSpace = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), ""); isRgbA = false; break; } // Create the layer KisLayerSP kisLayer; if (xcflayer.isGroup) { kisLayer = new KisGroupLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity); } else { kisLayer = new KisPaintLayer(image, QString::fromUtf8(xcflayer.name), xcflayer.opacity, colorSpace); } // Set some properties kisLayer->setCompositeOpId(layerModeG2K(xcflayer.mode)); kisLayer->setVisible(xcflayer.isVisible); kisLayer->disableAlphaChannel(xcflayer.mode != GIMP_NORMAL_MODE); layer.layer = kisLayer; layer.depth = xcflayer.pathLength; // Copy the data in the image - initLayer(&xcflayer); + if ((errorStatus = initLayer(&xcflayer)) != XCF_OK) { + return KisImportExportFilter::ConversionStatus::ParsingError; + } int left = xcflayer.dim.c.l; int top = xcflayer.dim.c.t; if (!xcflayer.isGroup) { // Copy the data; for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) { for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) { rect want; want.l = x + left; want.t = y + top; want.b = want.t + TILE_HEIGHT; want.r = want.l + TILE_WIDTH; Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.pixels, want); KisHLineIteratorSP it = kisLayer->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH); rgba* data = tile->pixels; for (int v = 0; v < TILE_HEIGHT; ++v) { if (isRgbA) { // RGB image do { KoBgrTraits::setRed(it->rawData(), GET_RED(*data)); KoBgrTraits::setGreen(it->rawData(), GET_GREEN(*data)); KoBgrTraits::setBlue(it->rawData(), GET_BLUE(*data)); KoBgrTraits::setOpacity(it->rawData(), quint8(GET_ALPHA(*data)), 1); ++data; } while (it->nextPixel()); } else { // Grayscale image do { it->rawData()[0] = GET_RED(*data); it->rawData()[1] = GET_ALPHA(*data); ++data; } while (it->nextPixel()); } it->nextRow(); } } } // Move the layer to its position kisLayer->paintDevice()->setX(left); kisLayer->paintDevice()->setY(top); } // Create the mask if (xcflayer.hasMask) { KisTransparencyMaskSP mask = new KisTransparencyMask(); layer.mask = mask; mask->initSelection(kisLayer); for (unsigned int x = 0; x < xcflayer.dim.width; x += TILE_WIDTH) { for (unsigned int y = 0; y < xcflayer.dim.height; y += TILE_HEIGHT) { rect want; want.l = x + left; want.t = y + top; want.b = want.t + TILE_HEIGHT; want.r = want.l + TILE_WIDTH; Tile* tile = getMaskOrLayerTile(&xcflayer.dim, &xcflayer.mask, want); KisHLineIteratorSP it = mask->paintDevice()->createHLineIteratorNG(x, y, TILE_WIDTH); rgba* data = tile->pixels; for (int v = 0; v < TILE_HEIGHT; ++v) { do { it->rawData()[0] = GET_ALPHA(*data); ++data; } while (it->nextPixel()); it->nextRow(); } } } mask->paintDevice()->setX(left); mask->paintDevice()->setY(top); } dbgFile << xcflayer.pixels.tileptrs; layers.append(layer); } for (uint i = 0; i <= maxDepth; ++i) { addLayers(layers, image, i); } document->setCurrentImage(image); return KisImportExportFilter::OK; } #include "kis_xcf_import.moc"