diff options
Diffstat (limited to 'kernel/kls_xcf/xcf2pnm/flatten.c')
-rw-r--r-- | kernel/kls_xcf/xcf2pnm/flatten.c | 689 |
1 files changed, 689 insertions, 0 deletions
diff --git a/kernel/kls_xcf/xcf2pnm/flatten.c b/kernel/kls_xcf/xcf2pnm/flatten.c new file mode 100644 index 0000000..c564ddd --- /dev/null +++ b/kernel/kls_xcf/xcf2pnm/flatten.c @@ -0,0 +1,689 @@ +/* Flattning functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "xcftools.h" +#include "flatten.h" +#include "pixels.h" +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +static rgba __ATTRIBUTE__((noinline,const)) +composite_one(rgba bot,rgba top) +{ + unsigned tfrac, alpha ; + + tfrac = ALPHA(top) ; + alpha = 255 ; + if( !FULLALPHA(bot) ) { + alpha = 255 ^ scaletable[255-ALPHA(bot)][255-ALPHA(top)] ; + /* This peculiar combination of ^ and - makes the GCC code + * generator for i386 particularly happy. + */ + tfrac = (256*ALPHA(top) - 1) / alpha ; + /* Tfrac is the fraction of the coposited pixel's covered area + * that comes from the top pixel. + * For mathematical accuracy we ought to scale by 255 and + * subtract alpha/2, but this is faster, and never misses the + * true value by more than one 1/255. This effect is completely + * overshadowed by the linear interpolation in the first place. + * (I.e. gamma is ignored when combining intensities). + * [In any case, complete fairness is not possible: if the + * bottom pixel had alpha=170 and the top has alpha=102, + * each should contribute equally to the color of the + * resulting alpha=204 pixel, which is not possible in general] + * Subtracting one helps the topfrac never be 256, which would + * be bad. + * On the other hand it means that we would get tfrac=-1 if the + * top pixel is completely transparent, and we get a division + * by zero if _both_ pixels are fully transparent. These cases + * must be handled by all callers. + * More snooping in the Gimp sources reveal that it uses + * floating-point for its equivalent of tfrac when the + * bottom layer has an alpha channel. (alphify() macro + * in paint-funcs.c). What gives? + */ + } + return (alpha << ALPHA_SHIFT) + + ((uint32_t)scaletable[ tfrac ][255&(top>>RED_SHIFT )] << RED_SHIFT ) + + ((uint32_t)scaletable[ tfrac ][255&(top>>GREEN_SHIFT)] << GREEN_SHIFT ) + + ((uint32_t)scaletable[ tfrac ][255&(top>>BLUE_SHIFT )] << BLUE_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>RED_SHIFT )] << RED_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>GREEN_SHIFT)] << GREEN_SHIFT ) + + ((uint32_t)scaletable[255^tfrac][255&(bot>>BLUE_SHIFT )] << BLUE_SHIFT ) + ; +} + +/* merge_normal() takes ownership of bot. + * merge_normal() will share ownership of top. + * Return: may be shared. + */ +static struct Tile * __ATTRIBUTE__((noinline)) +merge_normal(struct Tile *bot, struct Tile *top) +{ + unsigned i ; + assertTileCompatibility(bot,top); + + /* See if there is an easy winner */ + if( (bot->summary & TILESUMMARY_ALLNULL) || + (top->summary & TILESUMMARY_ALLFULL) ) { + freeTile(bot); + return top ; + } + if( top->summary & TILESUMMARY_ALLNULL ) { + freeTile(top); + return bot ; + } + + /* Try hard to make top win */ + for( i=0; ; i++ ) { + if( i == top->count ) { + freeTile(bot); + return top ; + } + if( !(NULLALPHA(bot->pixels[i]) || FULLALPHA(top->pixels[i])) ) + break ; + } + + INIT_SCALETABLE_IF( !(top->summary & TILESUMMARY_CRISP) ); + + /* Otherwise bot wins, but is forever changed ... */ + if( (top->summary & TILESUMMARY_ALLNULL) == 0 ) { + unsigned i ; + invalidateSummary(bot,0); + for( i=0 ; i < top->count ; i++ ) { + if( !NULLALPHA(top->pixels[i]) ) { + if( FULLALPHA(top->pixels[i]) || NULLALPHA(bot->pixels[i]) ) + bot->pixels[i] = top->pixels[i] ; + else + bot->pixels[i] = composite_one(bot->pixels[i],top->pixels[i]); + } + } + } + freeTile(top); + return bot ; +} + +#define exotic_combinator static inline unsigned __ATTRIBUTE__((const)) + + + +exotic_combinator +ucombine_ADDITION(uint8_t bot,uint8_t top) +{ + return bot+top > 255 ? 255 : bot+top ; +} + +exotic_combinator +ucombine_SUBTRACT(uint8_t bot,uint8_t top) +{ + return top>bot ? 0 : bot-top ; +} + +exotic_combinator +ucombine_LIGHTEN_ONLY(uint8_t bot,uint8_t top) +{ + return top > bot ? top : bot ; +} + +exotic_combinator +ucombine_DARKEN_ONLY(uint8_t bot,uint8_t top) +{ + return top < bot ? top : bot ; +} + +exotic_combinator +ucombine_DIFFERENCE(uint8_t bot,uint8_t top) +{ + return top > bot ? top-bot : bot-top ; +} + +exotic_combinator +ucombine_MULTIPLY(uint8_t bot,uint8_t top) +{ + return scaletable[bot][top] ; +} + +exotic_combinator +ucombine_DIVIDE(uint8_t bot,uint8_t top) +{ + int result = (int)bot*256 / (1+top) ; + return result >= 256 ? 255 : result ; +} + +exotic_combinator +ucombine_SCREEN(uint8_t bot,uint8_t top) +{ + /* An inverted version of "multiply" */ + return 255 ^ scaletable[255-bot][255-top] ; +} + +exotic_combinator +ucombine_OVERLAY(uint8_t bot,uint8_t top) +{ + return scaletable[bot][bot] + + 2*scaletable[top][scaletable[bot][255-bot]] ; + /* This strange formula is equivalent to + * (1-top)*(bot^2) + top*(1-(1-top)^2) + * that is, the top value is used to interpolate between + * the self-multiply and the self-screen of the bottom. + */ + /* Note: This is exactly what the "Soft light" effect also + * does, though with different code in the Gimp. + */ +} + +exotic_combinator +ucombine_DODGE(uint8_t bot,uint8_t top) +{ + return ucombine_DIVIDE(bot,255-top); +} + +exotic_combinator +ucombine_BURN(uint8_t bot,uint8_t top) +{ + return 255 - ucombine_DIVIDE(255-bot,top); +} + +exotic_combinator +ucombine_HARDLIGHT(uint8_t bot,uint8_t top) +{ + if( top >= 128 ) + return 255 ^ scaletable[255-bot][2*(255-top)] ; + else + return scaletable[bot][2*top]; + /* The code that implements "hardlight" in Gimp 2.2.10 has some + * rounding errors, but this is undoubtedly what is meant. + */ +} + +exotic_combinator +ucombine_GRAIN_EXTRACT(uint8_t bot,uint8_t top) +{ + int temp = (int)bot - (int)top + 128 ; + return temp < 0 ? 0 : temp >= 256 ? 255 : temp ; +} + +exotic_combinator +ucombine_GRAIN_MERGE(uint8_t bot,uint8_t top) +{ + int temp = (int)bot + (int)top - 128 ; + return temp < 0 ? 0 : temp >= 256 ? 255 : temp ; +} + +struct HSV { + enum { HUE_RED_GREEN_BLUE,HUE_RED_BLUE_GREEN,HUE_BLUE_RED_GREEN, + HUE_BLUE_GREEN_RED,HUE_GREEN_BLUE_RED,HUE_GREEN_RED_BLUE } hue; + unsigned ch1, ch2, ch3 ; +}; + +static void +RGBtoHSV(rgba rgb,struct HSV *hsv) +{ + unsigned RED = (uint8_t)(rgb >> RED_SHIFT); + unsigned GREEN = (uint8_t)(rgb >> GREEN_SHIFT); + unsigned BLUE = (uint8_t)(rgb >> BLUE_SHIFT) ; + #define HEXTANT(b,m,t) hsv->ch1 = b, hsv->ch2 = m, hsv->ch3 = t, \ + hsv->hue = HUE_ ## b ## _ ## m ## _ ## t + if( GREEN <= RED ) + if( BLUE <= RED ) + if( GREEN <= BLUE ) + HEXTANT(GREEN,BLUE,RED); + else + HEXTANT(BLUE,GREEN,RED); + else + HEXTANT(GREEN,RED,BLUE); + else if( BLUE <= RED ) + HEXTANT(BLUE,RED,GREEN); + else if( BLUE <= GREEN ) + HEXTANT(RED,BLUE,GREEN); + else + HEXTANT(RED,GREEN,BLUE); + #undef HEXTANT +} + +/* merge_exotic() destructively updates bot. + * merge_exotic() reads but does not free top. + */ +static void __ATTRIBUTE__((noinline)) +merge_exotic(struct Tile *bot, const struct Tile *top, + GimpLayerModeEffects mode) +{ + unsigned i ; + assertTileCompatibility(bot,top); + if( (bot->summary & TILESUMMARY_ALLNULL) != 0 ) return ; + if( (top->summary & TILESUMMARY_ALLNULL) != 0 ) return ; + assert( bot->refcount == 1 ); + /* The transparency status of bot never changes */ + + INIT_SCALETABLE_IF(1); + + for( i=0; i < top->count ; i++ ) { + uint32_t RED, GREEN, BLUE ; + if( NULLALPHA(bot->pixels[i]) || NULLALPHA(top->pixels[i]) ) + continue ; +#define UNIFORM(mode) case GIMP_ ## mode ## _MODE: \ + RED = ucombine_ ## mode (bot->pixels[i]>>RED_SHIFT , \ + top->pixels[i]>>RED_SHIFT ); \ + GREEN = ucombine_ ## mode (bot->pixels[i]>>GREEN_SHIFT, \ + top->pixels[i]>>GREEN_SHIFT); \ + BLUE = ucombine_ ## mode (bot->pixels[i]>>BLUE_SHIFT , \ + top->pixels[i]>>BLUE_SHIFT ); \ + break ; + switch( mode ) { + case GIMP_NORMAL_MODE: + case GIMP_DISSOLVE_MODE: + FatalUnexpected("Normal and Dissolve mode can't happen here!"); + UNIFORM(ADDITION); + UNIFORM(SUBTRACT); + UNIFORM(LIGHTEN_ONLY); + UNIFORM(DARKEN_ONLY); + UNIFORM(DIFFERENCE); + UNIFORM(MULTIPLY); + UNIFORM(DIVIDE); + UNIFORM(SCREEN); + case GIMP_SOFTLIGHT_MODE: /* A synonym for "overlay"! */ + UNIFORM(OVERLAY); + UNIFORM(DODGE); + UNIFORM(BURN); + UNIFORM(HARDLIGHT); + UNIFORM(GRAIN_EXTRACT); + UNIFORM(GRAIN_MERGE); + case GIMP_HUE_MODE: + case GIMP_SATURATION_MODE: + case GIMP_VALUE_MODE: + case GIMP_COLOR_MODE: + { + static struct HSV hsvTop, hsvBot ; + RGBtoHSV(top->pixels[i],&hsvTop); + if( mode == GIMP_HUE_MODE && hsvTop.ch1 == hsvTop.ch3 ) + continue ; + RGBtoHSV(bot->pixels[i],&hsvBot); + if( mode == GIMP_VALUE_MODE ) { + if( hsvBot.ch3 ) { + hsvBot.ch1 = (hsvBot.ch1*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3; + hsvBot.ch2 = (hsvBot.ch2*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3; + hsvBot.ch3 = hsvTop.ch3 ; + } else { + hsvBot.ch1 = hsvBot.ch2 = hsvBot.ch3 = hsvTop.ch3 ; + } + } else { + unsigned mfNum, mfDenom ; + if( mode == GIMP_HUE_MODE || mode == GIMP_COLOR_MODE ) { + mfNum = hsvTop.ch2-hsvTop.ch1 ; + mfDenom = hsvTop.ch3-hsvTop.ch1 ; + hsvBot.hue = hsvTop.hue ; + } else { + mfNum = hsvBot.ch2-hsvBot.ch1 ; + mfDenom = hsvBot.ch3-hsvBot.ch1 ; + } + if( mode == GIMP_SATURATION_MODE ) { + if( hsvTop.ch3 == 0 ) + hsvBot.ch1 = hsvBot.ch3 ; /* Black has no saturation */ + else + hsvBot.ch1 = (hsvTop.ch1*hsvBot.ch3 + hsvTop.ch3/2) / hsvTop.ch3; + } else if( mode == GIMP_COLOR_MODE ) { + /* GIMP_COLOR_MODE works in HSL space instead of HSV. We must + * transfer H and S, keeping the L = ch1+ch3 of the bottom pixel, + * but the S we transfer works differently from the S in HSV. + */ + unsigned L = hsvTop.ch1 + hsvTop.ch3 ; + unsigned sNum = hsvTop.ch3 - hsvTop.ch1 ; + unsigned sDenom = L < 256 ? L : 510-L ; + if( sDenom == 0 ) sDenom = 1 ; /* sNum will be 0 */ + L = hsvBot.ch1 + hsvBot.ch3 ; + if( L < 256 ) { + /* Ideally we want to compute L/2 * (1-sNum/sDenom) + * But shuffle this a bit so we can use integer arithmetic. + * The "-1" in the rounding prevents us from ending up with + * ch1 > ch3. + */ + hsvBot.ch1 = (L*(sDenom-sNum)+sDenom-1)/(2*sDenom); + hsvBot.ch3 = L - hsvBot.ch1 ; + } else { + /* Here our goal is 255 - (510-L)/2 * (1-sNum/sDenom) */ + hsvBot.ch3 = 255 - ((510-L)*(sDenom-sNum)+sDenom-1)/(2*sDenom); + hsvBot.ch1 = L - hsvBot.ch3 ; + } + assert(hsvBot.ch3 <= 255); + assert(hsvBot.ch3 >= hsvBot.ch1); + } + if( mfDenom == 0 ) + hsvBot.ch2 = hsvBot.ch1 ; + else + hsvBot.ch2 = hsvBot.ch1 + + (mfNum*(hsvBot.ch3-hsvBot.ch1) + mfDenom/2) / mfDenom ; + } + switch( hsvBot.hue ) { + #define HEXTANT(b,m,t) case HUE_ ## b ## _ ## m ## _ ## t : \ + b = hsvBot.ch1; m = hsvBot.ch2; t = hsvBot.ch3; break; + HEXTANT(RED,GREEN,BLUE); + HEXTANT(RED,BLUE,GREEN); + HEXTANT(BLUE,RED,GREEN); + HEXTANT(BLUE,GREEN,RED); + HEXTANT(GREEN,BLUE,RED); + HEXTANT(GREEN,RED,BLUE); + #undef HEXTANT + } + break ; + } + default: + FatalUnsupportedXCF(_("'%s' layer mode"), + _(showGimpLayerModeEffects(mode))); + } + if( FULLALPHA(bot->pixels[i] & top->pixels[i]) ) + bot->pixels[i] = (bot->pixels[i] & (255 << ALPHA_SHIFT)) + + (RED << RED_SHIFT) + + (GREEN << GREEN_SHIFT) + + (BLUE << BLUE_SHIFT) ; + else { + rgba bp = bot->pixels[i] ; + /* In a sane world, the alpha of the top pixel would simply be + * used to interpolate linearly between the bottom pixel's base + * color and the effect-computed color. + * But no! What the Gimp actually does is empirically + * described by the following (which borrows code from + * composite_one() that makes no theoretical sense here): + */ + unsigned tfrac = ALPHA(top->pixels[i]) ; + if( !FULLALPHA(bp) ) { + unsigned pseudotop = (tfrac < ALPHA(bp) ? tfrac : ALPHA(bp)); + unsigned alpha = 255 ^ scaletable[255-ALPHA(bp)][255-pseudotop] ; + tfrac = (256*pseudotop - 1) / alpha ; + } + bot->pixels[i] = (bp & (255 << ALPHA_SHIFT)) + + ((rgba)scaletable[ tfrac ][ RED ] << RED_SHIFT ) + + ((rgba)scaletable[ tfrac ][ GREEN ] << GREEN_SHIFT) + + ((rgba)scaletable[ tfrac ][ BLUE ] << BLUE_SHIFT ) + + ((rgba)scaletable[255^tfrac][255&(bp>>RED_SHIFT )] << RED_SHIFT ) + + ((rgba)scaletable[255^tfrac][255&(bp>>GREEN_SHIFT)] << GREEN_SHIFT) + + ((rgba)scaletable[255^tfrac][255&(bp>>BLUE_SHIFT )] << BLUE_SHIFT ) ; + } + } + return ; +} + +static void +dissolveTile(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + assert( tile->refcount == 1 ); + if( (tile->summary & TILESUMMARY_CRISP) ) + return ; + summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i = 0 ; i < tile->count ; i++ ) { + if( FULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLNULL ; + else if ( NULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLFULL ; + else if( ALPHA(tile->pixels[i]) > rand() % 0xFF ) { + tile->pixels[i] |= 255 << ALPHA_SHIFT ; + summary &= ~TILESUMMARY_ALLNULL ; + } else { + tile->pixels[i] = 0 ; + summary &= ~TILESUMMARY_ALLFULL ; + } + } + tile->summary = summary ; +} + +static void +roundAlpha(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + assert( tile->refcount == 1 ); + if( (tile->summary & TILESUMMARY_CRISP) ) + return ; + summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i = 0 ; i < tile->count ; i++ ) { + if( ALPHA(tile->pixels[i]) >= 128 ) { + tile->pixels[i] |= 255 << ALPHA_SHIFT ; + summary &= ~TILESUMMARY_ALLNULL ; + } else { + tile->pixels[i] = 0 ; + summary &= ~TILESUMMARY_ALLFULL ; + } + } + tile->summary = summary ; +} + +/* flattenTopdown() shares ownership of top. + * The return value may be a shared tile. + */ +static struct Tile * +flattenTopdown(struct FlattenSpec *spec, struct Tile *top, + unsigned nlayers, const struct rect *where) +{ + struct Tile *tile; + + while( nlayers-- ) { + if( tileSummary(top) & TILESUMMARY_ALLFULL ) + return top ; + if( !spec->layers[nlayers].isVisible ) + continue ; + + tile = getLayerTile(&spec->layers[nlayers],where); + + if( tile->summary & TILESUMMARY_ALLNULL ) + continue ; /* Simulate a tail call */ + + switch( spec->layers[nlayers].mode ) { + case GIMP_NORMAL_NOPARTIAL_MODE: + roundAlpha(tile) ; + /* fall through */ + if(0) { + case GIMP_DISSOLVE_MODE: + dissolveTile(tile); + /* fall through */ + } + case GIMP_NORMAL_MODE: + top = merge_normal(tile,top); + break ; + default: + { + struct Tile *below, *above ; + unsigned i ; + if( !(top->summary & TILESUMMARY_ALLNULL) ) { + rgba tile_or = 0 ; + invalidateSummary(tile,0); + for( i=0; i<top->count; i++ ) + if( FULLALPHA(top->pixels[i]) ) + tile->pixels[i] = 0 ; + else + tile_or |= tile->pixels[i] ; + /* If the tile only has pixels that will be covered by 'top' anyway, + * forget it anyway. + */ + if( ALPHA(tile_or) == 0 ) { + freeTile(tile); + break ; /* from the switch, which will continue the while */ + } + } + /* Create a dummy top for the layers below this */ + if( top->summary & TILESUMMARY_CRISP ) { + above = forkTile(top); + } else { + summary_t summary = TILESUMMARY_ALLNULL ; + above = newTile(*where); + for( i=0; i<top->count; i++ ) + if( FULLALPHA(top->pixels[i]) ) { + above->pixels[i] = -1 ; + summary = 0 ; + } else + above->pixels[i] = 0 ; + above->summary = TILESUMMARY_UPTODATE + TILESUMMARY_CRISP + summary; + } + below = flattenTopdown(spec, above, nlayers, where); + if( below->refcount > 1 ) { + assert( below == top ); + /* This can only happen if 'below' is a copy of 'top' + * THROUGH 'above', which in turn means that none of all + * this is visible after all. So just free it and return 'top'. + */ + freeTile(below); + return top ; + } + merge_exotic(below,tile,spec->layers[nlayers].mode); + freeTile(tile); + top = merge_normal(below,top); + return top ; + } + } + } + return top ; +} + +static void +addBackground(struct FlattenSpec *spec, struct Tile *tile, unsigned ncols) +{ + unsigned i ; + + if( tileSummary(tile) & TILESUMMARY_ALLFULL ) + return ; + + switch( spec->partial_transparency_mode ) { + case FORBID_PARTIAL_TRANSPARENCY: + if( !(tileSummary(tile) & TILESUMMARY_CRISP) ) + FatalGeneric(102,_("Flattened image has partially transparent pixels")); + break ; + case DISSOLVE_PARTIAL_TRANSPARENCY: + dissolveTile(tile); + break ; + case ALLOW_PARTIAL_TRANSPARENCY: + case PARTIAL_TRANSPARENCY_IMPOSSIBLE: + break ; + } + + if( spec->default_pixel == CHECKERED_BACKGROUND ) { + INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP ) ); + for( i=0; i<tile->count; i++ ) + if( !FULLALPHA(tile->pixels[i]) ) { + rgba fillwith = ((i/ncols)^(i%ncols))&8 ? 0x66 : 0x99 ; + fillwith = graytable[fillwith] + (255 << ALPHA_SHIFT) ; + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = fillwith ; + else + tile->pixels[i] = composite_one(fillwith,tile->pixels[i]); + } + tile->summary = TILESUMMARY_UPTODATE + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + return ; + } + if( !FULLALPHA(spec->default_pixel) ) return ; + if( tileSummary(tile) & TILESUMMARY_ALLNULL ) { + fillTile(tile,spec->default_pixel); + } else { + INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP) ); + for( i=0; i<tile->count; i++ ) + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = spec->default_pixel ; + else if( FULLALPHA(tile->pixels[i]) ) + ; + else + tile->pixels[i] = composite_one(spec->default_pixel,tile->pixels[i]); + + tile->summary = TILESUMMARY_UPTODATE + + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + } +} + +void +flattenIncrementally(struct FlattenSpec *spec,lineCallback callback) +{ + rgba *rows[TILE_HEIGHT] ; + unsigned i, y, nrows, ncols ; + struct rect where ; + struct Tile *tile ; + static struct Tile toptile ; + + toptile.count = TILE_HEIGHT * TILE_WIDTH ; + fillTile(&toptile,0); + + for( where.t = spec->dim.c.t; where.t < spec->dim.c.b; where.t=where.b ) { + where.b = (where.t+TILE_HEIGHT) - where.t % TILE_HEIGHT ; + if( where.b > spec->dim.c.b ) where.b = spec->dim.c.b ; + nrows = where.b - where.t ; + for( y = 0; y < nrows ; y++ ) + rows[y] = xcfmalloc(4*(spec->dim.c.r-spec->dim.c.l)); + + for( where.l = spec->dim.c.l; where.l < spec->dim.c.r; where.l=where.r ) { + where.r = (where.l+TILE_WIDTH) - where.l % TILE_WIDTH ; + if( where.r > spec->dim.c.r ) where.r = spec->dim.c.r ; + ncols = where.r - where.l ; + + toptile.count = ncols * nrows ; + toptile.refcount = 2 ; /* For bug checking */ + assert( toptile.summary == TILESUMMARY_UPTODATE + + TILESUMMARY_ALLNULL + TILESUMMARY_CRISP ); + tile = flattenTopdown(spec,&toptile,spec->numLayers,&where) ; + toptile.refcount-- ; /* addBackground may change destructively */ + addBackground(spec,tile,ncols); + + for( i = 0 ; i < tile->count ; i++ ) + if( NULLALPHA(tile->pixels[i]) ) + tile->pixels[i] = 0 ; + for( y = 0 ; y < nrows ; y++ ) + memcpy(rows[y] + (where.l - spec->dim.c.l), + tile->pixels + y * ncols, ncols*4); + + if( tile == &toptile ) { + fillTile(&toptile,0); + } else { + freeTile(tile); + } + } + for( y = 0 ; y < nrows ; y++ ) + callback(spec->dim.width,rows[y]); + } +} + +static rgba **collectPointer ; + +static void +collector(unsigned num,rgba *row) +{ + *collectPointer++ = row ; +} + +rgba ** +flattenAll(struct FlattenSpec *spec) +{ + rgba **rows = xcfmalloc(spec->dim.height * sizeof(rgba*)); + if( verboseFlag ) + fprintf(stderr,_("Flattening image ...")); + collectPointer = rows ; + flattenIncrementally(spec,collector); + if( verboseFlag ) + fprintf(stderr,"\n"); + return rows ; +} + +void +shipoutWithCallback(struct FlattenSpec *spec, rgba **pixels, + lineCallback callback) +{ + unsigned i ; + for( i = 0; i < spec->dim.height; i++ ) { + callback(spec->dim.width,pixels[i]); + } + xcffree(pixels); +} |