summaryrefslogtreecommitdiffstats
path: root/pixels.c
diff options
context:
space:
mode:
authorHenning 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)
commit7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717 (patch)
tree98d2772f50aaddb02ac94492d2b8b151aa3e9465 /pixels.c
downloadxcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.zip
xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.gz
xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.bz2
Import of release 0.7
Diffstat (limited to 'pixels.c')
-rw-r--r--pixels.c483
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 ;
+}
+