diff options
author | Henning Makholm <henning@makholm.net> | 2006-01-27 18:00:00 (EST) |
---|---|---|
committer | Julien Jorge <julien.jorge@stuff-o-matic.com> | 2013-01-10 16:00:40 (EST) |
commit | 7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717 (patch) | |
tree | 98d2772f50aaddb02ac94492d2b8b151aa3e9465 /pixels.c | |
download | xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.zip xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.gz xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.bz2 |
Import of release 0.7
Diffstat (limited to 'pixels.c')
-rw-r--r-- | pixels.c | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/pixels.c b/pixels.c new file mode 100644 index 0000000..20e6c81 --- /dev/null +++ b/pixels.c @@ -0,0 +1,483 @@ +/* Pixel and tile functions for xcftools + * + * Copyright (C) 2006 Henning Makholm + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define DEBUG +#include "xcftools.h" +#include "pixels.h" +#include <assert.h> +#include <string.h> + +rgba colormap[256] ; +unsigned colormapLength=0 ; + +int +degrayPixel(rgba pixel) +{ + if( ((pixel >> RED_SHIFT) & 255) == ((pixel >> GREEN_SHIFT) & 255) && + ((pixel >> RED_SHIFT) & 255) == ((pixel >> BLUE_SHIFT) & 255) ) + return (pixel >> RED_SHIFT) & 255 ; + return -1 ; +} + +/* ****************************************************************** */ + +typedef const struct _convertParams { + int bpp ; + int shift[4] ; + uint32_t base_pixel ; + const rgba *lookup ; +} convertParams ; +#define RGB_SHIFT RED_SHIFT, GREEN_SHIFT, BLUE_SHIFT +#define OPAQUE (255 << ALPHA_SHIFT) +static convertParams convertRGB = { 3, {RGB_SHIFT}, OPAQUE, 0 }; +static convertParams convertRGBA = { 4, {RGB_SHIFT, ALPHA_SHIFT}, 0,0 }; +static convertParams convertGRAY = { 1, {-1}, OPAQUE, graytable }; +static convertParams convertGRAYA = { 2, {-1,ALPHA_SHIFT}, 0, graytable }; +static convertParams convertINDEXED = { 1, {-1}, OPAQUE, colormap }; +static convertParams convertINDEXEDA = { 2, {-1,ALPHA_SHIFT}, 0, colormap }; + +static convertParams convertColormap = { 3, {RGB_SHIFT}, 0, 0 }; +static convertParams convertChannel = { 1, {ALPHA_SHIFT}, 0, 0 }; + +/* ****************************************************************** */ + +static inline int +tileDirectoryOneLevel(struct tileDimensions *dim,uint32_t ptr) +{ + if( ptr == 0 ) + return 0 ; + if( xcfL(ptr ) != dim->c.r - dim->c.l || + xcfL(ptr+4) != dim->c.b - dim->c.t ) + FatalBadXCF("Drawable size mismatch at %" PRIX32, ptr); + return ptr += 8 ; +} + +static void +initTileDirectory(struct tileDimensions *dim,struct xcfTiles *tiles, + const char *type) +{ + uint32_t ptr ; + uint32_t data ; + + ptr = tiles->hierarchy ; + tiles->hierarchy = 0 ; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + if( tiles->params == &convertChannel ) { + /* A layer mask is a channel. + * Skip a name and a property list. + */ + xcfString(ptr,&ptr); + while( xcfNextprop(&ptr,&data) != PROP_END ) + ; + ptr = xcfOffset(ptr,4*4); + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + } + /* The XCF format has a dummy "hierarchy" level which was + * once meant to mean something, but never happened. It contains + * the bpp value and a list of "level" pointers; but only the + * first level actually contains data. + */ + data = xcfL(ptr) ; + if( xcfL(ptr) != tiles->params->bpp ) + FatalBadXCF("%"PRIu32" bytes per pixel for %s drawable",xcfL(ptr),type); + ptr = xcfOffset(ptr+4,3*4) ; + if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ; + + xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr); + if( xcfL(ptr + dim->ntiles*4) != 0 ) + FatalBadXCF("Wrong sized tile directory at %" PRIX32,ptr); +#define REUSE_RAW_DATA tiles->tileptrs = (uint32_t*)(xcf_file + ptr) +#if defined(WORDS_BIGENDIAN) && defined(CAN_DO_UNALIGNED_WORDS) + REUSE_RAW_DATA; +#else +# if defined(WORDS_BIGENDIAN) + if( (ptr&3) == 0 ) REUSE_RAW_DATA; else +# endif + { + unsigned i ; + tiles->tileptrs = xcfmalloc(dim->ntiles * sizeof(uint32_t)) ; + for( i = 0 ; i < dim->ntiles ; i++ ) + tiles->tileptrs[i] = xcfL(ptr+i*4); + } +#endif +} + +void +initLayer(struct xcfLayer *layer) { + if( layer->dim.ntiles == 0 || + (layer->pixels.hierarchy == 0 && layer->mask.hierarchy == 0) ) + return ; + switch(layer->type) { +#define DEF(X) case GIMP_##X##_IMAGE: layer->pixels.params = &convert##X; break + DEF(RGB); + DEF(RGBA); + DEF(GRAY); + DEF(GRAYA); + DEF(INDEXED); + DEF(INDEXEDA); + default: + FatalUnsupportedXCF(_("Layer type %s"),showGimpImageType(layer->type)); + } + initTileDirectory(&layer->dim,&layer->pixels,showGimpImageType(layer->type)); + layer->mask.params = &convertChannel ; + initTileDirectory(&layer->dim,&layer->mask,"layer mask"); +} +static void copyStraightPixels(rgba *dest,unsigned npixels, + uint32_t ptr,convertParams *params); +void +initColormap(void) { + uint32_t ncolors ; + if( XCF.colormapptr == 0 ) { + colormapLength = 0 ; + return ; + } + ncolors = xcfL(XCF.colormapptr) ; + if( ncolors > 256 ) + FatalUnsupportedXCF(_("Color map has more than 256 entries")); + copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap); + colormapLength = ncolors ; +#ifdef xDEBUG + { + unsigned j ; + fprintf(stderr,"Colormap decoding OK\n"); + for( j = 0 ; j < ncolors ; j++ ) { + if( j % 8 == 0 ) fprintf(stderr,"\n"); + fprintf(stderr," %08x",colormap[j]); + } + fprintf(stderr,"\n"); + } +#endif +} + +/* ****************************************************************** */ + +struct Tile * +newTile(struct rect r) +{ + unsigned npixels = (unsigned)(r.b-r.t) * (unsigned)(r.r-r.l) ; + struct Tile *data + = xcfmalloc(sizeof(struct Tile) - + sizeof(rgba)*(TILE_HEIGHT*TILE_WIDTH - npixels)) ; + data->count = npixels ; + data->refcount = 1 ; + data->summary = 0 ; + return data ; +} + +struct Tile * +forkTile(struct Tile* tile) +{ + if( ++tile->refcount <= 0 ) + FatalUnsupportedXCF(_("Unbelievably many layers?" + "More likely to be a bug in %s"),progname); + return tile ; +} + +void +freeTile(struct Tile* tile) +{ + if( --tile->refcount == 0 ) + xcffree(tile) ; +} + +summary_t +tileSummary(struct Tile *tile) +{ + unsigned i ; + summary_t summary ; + if( (tile->summary & TILESUMMARY_UPTODATE) != 0 ) + return tile->summary ; + summary = TILESUMMARY_ALLNULL + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ; + for( i=0; summary && i<tile->count; i++ ) { + if( FULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLNULL ; + else if( NULLALPHA(tile->pixels[i]) ) + summary &= ~TILESUMMARY_ALLFULL ; + else + summary = 0 ; + } + summary += TILESUMMARY_UPTODATE ; + tile->summary = summary ; + return summary ; +} + +void +fillTile(struct Tile *tile,rgba data) +{ + unsigned i ; + for( i = 0 ; i < tile->count ; i++ ) + tile->pixels[i] = data ; + if( FULLALPHA(data) ) + tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP; + else if (NULLALPHA(data) ) + tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLNULL+TILESUMMARY_CRISP; + else + tile->summary = TILESUMMARY_UPTODATE ; +} + +/* ****************************************************************** */ + +static void +copyStraightPixels(rgba *dest,unsigned npixels, + uint32_t ptr,convertParams *params) +{ + unsigned bpp = params->bpp; + const rgba *lookup = params->lookup; + rgba base_pixel = params->base_pixel ; + uint8_t *bp = xcf_file + ptr ; + xcfCheckspace(ptr,bpp*npixels, + "pixel array (%u x %d bpp) at %"PRIX32,npixels,bpp,ptr); + while( npixels-- ) { + rgba pixel = base_pixel ; + unsigned i ; + for( i = 0 ; i < bpp ; i++ ) { + if( params->shift[i] < 0 ) { + pixel += lookup[*bp++] ; + } else { + pixel += *bp++ << params->shift[i] ; + } + } + *dest++ = pixel ; + } +} + +static inline void +copyRLEpixels(rgba *dest,unsigned npixels,uint32_t ptr,convertParams *params) +{ + unsigned i,j ; + rgba base_pixel = params->base_pixel ; + +#ifdef xDEBUG + fprintf(stderr,"RLE stream at %x, want %u x %u pixels, base %x\n", + ptr,params->bpp,npixels,base_pixel); +#endif + + + /* This algorithm depends on the indexed byte always being the first one */ + if( params->shift[0] < -1 ) + base_pixel = 0 ; + for( j = npixels ; j-- ; ) + dest[j] = base_pixel ; + + for( i = 0 ; i < params->bpp ; i++ ) { + int shift = params->shift[i] ; + if( shift < 0 ) + shift = 0 ; + for( j = 0 ; j < npixels ; ) { + int countspec ; + unsigned count ; + xcfCheckspace(ptr,2,"RLE data stream"); + countspec = (int8_t) xcf_file[ptr++] ; + count = countspec >= 0 ? countspec+1 : -countspec ; + if( count == 128 ) { + xcfCheckspace(ptr,3,"RLE long count"); + count = xcf_file[ptr++] << 8 ; + count += xcf_file[ptr++] ; + } + if( j + count > npixels ) + FatalBadXCF("Overlong RLE run at %"PRIX32" (plane %u, %u left)", + ptr,i,npixels-j); + if( countspec >= 0 ) { + rgba data = (uint32_t) xcf_file[ptr++] << shift ; + while( count-- ) + dest[j++] += data ; + } else { + while( count-- ) + dest[j++] += (uint32_t) xcf_file[ptr++] << shift ; + } + } + if( i == 0 && params->shift[0] < 0 ) { + const rgba *lookup = params->lookup ; + base_pixel = params->base_pixel ; + for( j = npixels ; j-- ; ) { + dest[j] = lookup[dest[j]-base_pixel] + base_pixel ; + } + } + } +#ifdef xDEBUG + fprintf(stderr,"RLE decoding OK at %"PRIX32"\n",ptr); + /* + for( j = 0 ; j < npixels ; j++ ) { + if( j % 8 == 0 ) fprintf(stderr,"\n"); + fprintf(stderr," %8x",dest[j]); + } + fprintf(stderr,"\n"); + */ +#endif +} + +static inline void +copyTilePixels(struct Tile *dest, uint32_t ptr,convertParams *params) +{ + if( FULLALPHA(params->base_pixel) ) + dest->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP; + else + dest->summary = 0 ; + switch( XCF.compression ) { + case COMPRESS_NONE: + copyStraightPixels(dest->pixels,dest->count,ptr,params); + break ; + case COMPRESS_RLE: + copyRLEpixels(dest->pixels,dest->count,ptr,params); + break ; + default: + FatalUnsupportedXCF(_("%s compression"), + showXcfCompressionType(XCF.compression)); + } +} + +static struct Tile * +getMaskOrLayerTile(struct tileDimensions *dim, struct xcfTiles *tiles, + struct rect want) +{ + struct Tile *tile = newTile(want); + + assert( want.l < want.r && want.t < want.b ); + if( tiles->tileptrs == 0 ) { + fillTile(tile,0); + return tile ; + } + +#ifdef xDEBUG + fprintf(stderr,"getMaskOrLayer: (%d-%d),(%d-%d)\n",left,right,top,bottom); +#endif + + if( isSubrect(want,dim->c) && + (want.l - dim->c.l) % TILE_WIDTH == 0 && + (want.t - dim->c.t) % TILE_HEIGHT == 0 ) { + unsigned tx = (want.l - dim->c.l) / TILE_WIDTH ; + unsigned ty = (want.t - dim->c.t) / TILE_WIDTH ; + if( want.r == TILEXn(*dim,tx+1) && want.b == TILEYn(*dim,ty+1) ) { + /* The common case? An entire single tile from the layer */ + copyTilePixels(tile,tiles->tileptrs[tx + ty*dim->tilesx],tiles->params); + return tile ; + } + } + + /* OK, we must construct the wanted tile as a jigsaw */ + { + unsigned width = want.r-want.l ; + rgba *pixvert = tile->pixels ; + rgba *pixhoriz ; + unsigned y, ty, l0, l1, lstart, lnum ; + unsigned x, tx, c0, c1, cstart, cnum ; + + if( !isSubrect(want,dim->c) ) { + if( want.l < dim->c.l ) pixvert += (dim->c.l - want.l), + want.l = dim->c.l ; + if( want.r > dim->c.r ) want.r = dim->c.r ; + if( want.t < dim->c.t ) pixvert += (dim->c.t - want.t) * width, + want.t = dim->c.t ; + if( want.b > dim->c.b ) want.b = dim->c.b ; + fillTile(tile,0); + } else { + tile->summary = -1 ; /* I.e. whatever the jigsaw pieces say */ + } + +#ifdef xDEBUG + fprintf(stderr,"jig0 (%d-%d),(%d-%d)\n",left,right,top,bottom); +#endif + + for( y=want.t, ty=(want.t-dim->c.t)/TILE_HEIGHT, l0=TILEYn(*dim,ty); + y<want.b; + pixvert += lnum*width, ty++, y=l0=l1 ) { + l1 = TILEYn(*dim,ty+1) ; + lstart = y - l0 ; + lnum = (l1 > want.b ? want.b : l1) - y ; + + pixhoriz = pixvert ; + for( x=want.l, tx=(want.l-dim->c.l)/TILE_WIDTH, c0=TILEXn(*dim,tx); + x<want.r; + pixhoriz += cnum, tx++, x=c0=c1 ) { + c1 = TILEXn(*dim,tx+1); + cstart = x - c0 ; + cnum = (c1 > want.r ? want.r : c1) - x ; + + { + static struct Tile tmptile ; + unsigned dwidth = c1-c0 ; + unsigned i, j ; + tmptile.count = (c1-c0)*(l1-l0) ; +#ifdef xDEBUG + fprintf(stderr,"jig ty=%u(%u-%u-%u)(%u+%u) tx=%u(%u-%u-%u)(%u+%u)\n", + ty,l0,y,l1,lstart,lnum, + tx,c0,x,c1,cstart,cnum); +#endif + copyTilePixels(&tmptile, + tiles->tileptrs[tx+ty*dim->tilesx],tiles->params); + for(i=0; i<lnum; i++) + for(j=0; j<cnum; j++) + pixhoriz[i*width+j] + = tmptile.pixels[(i+lstart)*dwidth+(j+cstart)]; + tile->summary &= tmptile.summary ; + } + } + } + } + return tile ; +} + +void +applyMask(struct Tile *tile, struct Tile *mask) +{ + unsigned i ; + assertTileCompatibility(tile,mask); + assert( tile->count == mask->count ); + 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 = scaletable[layer->opacity] ; + int i ; + invalidateSummary(data,~(TILESUMMARY_CRISP | TILESUMMARY_ALLFULL)); + for( i=0; i < data->count; i++ ) + data->pixels[i] + = NEWALPHA(data->pixels[i],ourtable[ALPHA(data->pixels[i])]) ; + } + return data ; +} + |