File indexing completed on 2025-03-09 04:09:53

0001 /*  Pixel and tile functions for xcftools
0002  *
0003  * This file was written by Henning Makholm <henning@makholm.net>
0004  * It is hereby in the public domain.
0005  *
0006  * In jurisdictions that do not recognise grants of copyright to the
0007  * public domain: I, the author and (presumably, in those jurisdictions)
0008  * copyright holder, hereby permit anyone to distribute and use this code,
0009  * in source code or binary form, with or without modifications. This
0010  * permission is world-wide and irrevocable.
0011  *
0012  * Of course, I will not be liable for any errors or shortcomings in the
0013  * code, since I give it away without asking any compenstations.
0014  *
0015  * If you use or distribute this code, I would appreciate receiving
0016  * credit for writing it, in whichever way you find proper and customary.
0017  */
0018 
0019 #define DEBUG
0020 #include "xcftools.h"
0021 #include "pixels.h"
0022 #include <assert.h>
0023 #include <string.h>
0024 
0025 rgba colormap[256] ;
0026 unsigned colormapLength=0 ;
0027 
0028 int
0029 degrayPixel(rgba pixel)
0030 {
0031   if( ((pixel >> RED_SHIFT) & 255) == ((pixel >> GREEN_SHIFT) & 255) &&
0032       ((pixel >> RED_SHIFT) & 255) == ((pixel >> BLUE_SHIFT) & 255) )
0033     return (pixel >> RED_SHIFT) & 255 ;
0034   return -1 ;
0035 }
0036 
0037 /* ****************************************************************** */
0038 
0039 typedef const struct _convertParams {
0040   int bpp ;
0041   int shift[4] ;
0042   uint32_t base_pixel ;
0043   const rgba *lookup ;
0044 } convertParams ;
0045 #define RGB_SHIFT RED_SHIFT, GREEN_SHIFT, BLUE_SHIFT
0046 #ifdef OPAQUE
0047 #undef OPAQUE
0048 #endif
0049 #define OPAQUE (255 << ALPHA_SHIFT)
0050 static convertParams convertRGB       = { 3, {RGB_SHIFT}, OPAQUE, 0 };
0051 static convertParams convertRGBA      = { 4, {RGB_SHIFT, ALPHA_SHIFT}, 0,0 };
0052 static convertParams convertGRAY      = { 1, {-1}, OPAQUE, graytable };
0053 static convertParams convertGRAYA     = { 2, {-1,ALPHA_SHIFT}, 0, graytable };
0054 static convertParams convertINDEXED   = { 1, {-1}, OPAQUE, colormap };
0055 static convertParams convertINDEXEDA  = { 2, {-1,ALPHA_SHIFT}, 0, colormap };
0056 
0057 static convertParams convertColormap  = { 3, {RGB_SHIFT}, 0, 0 };
0058 static convertParams convertChannel   = { 1, {ALPHA_SHIFT}, 0, 0 };
0059 
0060 /* ****************************************************************** */
0061 
0062 static inline int
0063 tileDirectoryOneLevel(struct tileDimensions *dim,uint32_t ptr, int* ptrOut)
0064 {
0065   if( ptr == 0 ) {
0066       *ptrOut = 0;
0067     return XCF_OK; /* allowed by xcf, apparently */
0068   }
0069   if( xcfL(ptr  ) != dim->c.r - dim->c.l ||
0070           xcfL(ptr+4) != dim->c.b - dim->c.t ) {
0071     FatalBadXCF("Drawable size mismatch at %" PRIX32, ptr);
0072     *ptrOut = XCF_PTR_EMPTY;
0073     return XCF_ERROR;
0074   }
0075   *ptrOut = (ptr += 8) ;
0076   return XCF_OK;
0077 }
0078 
0079 static int
0080 initTileDirectory(struct tileDimensions *dim,struct xcfTiles *tiles,
0081                   const char *type)
0082 {
0083   uint32_t ptr ;
0084   uint32_t data ;
0085 
0086   ptr = tiles->hierarchy ;
0087   tiles->hierarchy = 0 ;
0088   int ptrOut;
0089   if (tileDirectoryOneLevel(dim,ptr, &ptrOut) != XCF_OK) {
0090       return XCF_ERROR;
0091   }
0092   if (ptrOut == XCF_PTR_EMPTY) {
0093       return XCF_OK;
0094   }
0095   ptr = ptrOut;
0096   if( tiles->params == &convertChannel ) {
0097     /* A layer mask is a channel.
0098      * Skip a name and a property list.
0099      */
0100      xcfString(ptr,&ptr);
0101     PropType type;
0102     int response;
0103     while( (response = xcfNextprop(&ptr,&data, &type)) != XCF_ERROR && type != PROP_END ) {
0104 
0105     }
0106     if (response != XCF_OK) {
0107         return XCF_ERROR;
0108     }
0109     uint32_t ptrout;
0110     if(xcfOffset(ptr,4*4, &ptrout) != XCF_OK) return XCF_ERROR;
0111     ptr = ptrout;
0112     if (tileDirectoryOneLevel(dim,ptr, &ptrOut) != XCF_OK) {
0113         return XCF_ERROR;
0114     }
0115     if (ptrOut == XCF_PTR_EMPTY) {
0116         return XCF_OK;
0117     }
0118     ptr = ptrOut;
0119   }
0120   /* The XCF format has a dummy "hierarchy" level which was
0121    * once meant to mean something, but never happened. It contains
0122    * the bpp value and a list of "level" pointers; but only the
0123    * first level actually contains data.
0124    */
0125   data = xcfL(ptr) ;
0126   if( xcfL(ptr) != tiles->params->bpp ) {
0127       FatalBadXCF("%" PRIuPTR " bytes per pixel for %s drawable",
0128                   xcfL(ptr),
0129                   type);
0130       return XCF_ERROR;
0131   }
0132   uint32_t ptrout;
0133   if(xcfOffset(ptr+4,3*4, &ptrout) != XCF_OK) return XCF_ERROR;
0134   ptr = ptrout;
0135   if (tileDirectoryOneLevel(dim,ptr, &ptrOut) != XCF_OK) {
0136       return XCF_ERROR;
0137   }
0138   if (ptrOut == XCF_PTR_EMPTY) {
0139       return XCF_OK;
0140   }
0141   ptr = ptrOut;
0142 
0143   if (xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr) != XCF_OK) {
0144       return XCF_ERROR;
0145   }
0146 /*  if( xcfL(ptr + dim->ntiles*4) != 0 )
0147     FatalBadXCF("Wrong sized tile directory at %" PRIX32,ptr);*/
0148 
0149 #define REUSE_RAW_DATA tiles->tileptrs = (uint32_t*)(xcf_file + ptr)
0150 #if defined(WORDS_BIGENDIAN) && defined(CAN_DO_UNALIGNED_WORDS)
0151   REUSE_RAW_DATA;
0152 #else
0153 # if defined(WORDS_BIGENDIAN)
0154   if( (ptr&3) == 0 ) REUSE_RAW_DATA; else
0155 # endif
0156     {
0157       unsigned i ;
0158       tiles->tileptrs = xcfmalloc(dim->ntiles * sizeof(uint32_t)) ;
0159       for( i = 0 ; i < dim->ntiles ; i++ )
0160         tiles->tileptrs[i] = xcfL(ptr+i*4);
0161     }
0162 #endif
0163   return XCF_OK;
0164 }
0165 
0166 int
0167 initLayer(struct xcfLayer *layer) {
0168   if( layer->dim.ntiles == 0 ||
0169       (layer->pixels.hierarchy == 0 && layer->mask.hierarchy == 0) )
0170     return XCF_OK;
0171   switch(layer->type) {
0172 #define DEF(X) case GIMP_##X##_IMAGE: layer->pixels.params = &convert##X; break
0173     DEF(RGB);
0174     DEF(RGBA);
0175     DEF(GRAY);
0176     DEF(GRAYA);
0177     DEF(INDEXED);
0178     DEF(INDEXEDA);
0179   default:
0180     {
0181         FatalUnsupportedXCF(_("Layer type %s"),_(showGimpImageType(layer->type)));
0182         return XCF_ERROR;
0183     }
0184 
0185   }
0186   if (initTileDirectory(&layer->dim,&layer->pixels,
0187                                     _(showGimpImageType(layer->type)))  != XCF_OK) {
0188       return XCF_ERROR;
0189   }
0190   layer->mask.params = &convertChannel ;
0191   if (initTileDirectory(&layer->dim,&layer->mask,"layer mask") != XCF_OK) {
0192           return XCF_ERROR;
0193    }
0194   return XCF_OK;
0195 }
0196 static int copyStraightPixels(rgba *dest,unsigned npixels,
0197                                uint32_t ptr,convertParams *params);
0198 int
0199 initColormap(void) {
0200   uint32_t ncolors ;
0201   if( XCF.colormapptr == 0 ) {
0202     colormapLength = 0 ;
0203     return XCF_OK;
0204   }
0205   ncolors = xcfL(XCF.colormapptr) ;
0206   if( ncolors > 256 ) {
0207     FatalUnsupportedXCF(_("Color map has more than 256 entries"));
0208     return XCF_ERROR;
0209   }
0210   if(copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap) != XCF_OK) {
0211       return XCF_ERROR;
0212   }
0213   colormapLength = ncolors ;
0214 #ifdef xDEBUG
0215   {
0216     unsigned j ;
0217     fprintf(stderr,"Colormap decoding OK\n");
0218     for( j = 0 ; j < ncolors ; j++ ) {
0219       if( j % 8 == 0 ) fprintf(stderr,"\n");
0220       fprintf(stderr," %08x",colormap[j]);
0221     }
0222     fprintf(stderr,"\n");
0223   }
0224 #endif
0225     return XCF_OK;
0226 }
0227 
0228 /* ****************************************************************** */
0229 
0230 struct Tile *
0231 newTile(struct rect r)
0232 {
0233   unsigned npixels = (unsigned)(r.b-r.t) * (unsigned)(r.r-r.l) ;
0234   struct Tile *data
0235     = xcfmalloc(sizeof(struct Tile) -
0236                 sizeof(rgba)*(TILE_HEIGHT*TILE_WIDTH - npixels)) ;
0237   data->count = npixels ;
0238   data->refcount = 1 ;
0239   data->summary = 0 ;
0240   return data ;
0241 }
0242 
0243 struct Tile *
0244 forkTile(struct Tile* tile)
0245 {
0246   if( ++tile->refcount <= 0 ) {
0247     FatalUnsupportedXCF(_("Unbelievably many layers?\n"
0248                           "More likely to be a bug in %s"),progname);
0249     return XCF_PTR_EMPTY;
0250   }
0251   return tile ;
0252 }
0253 
0254 void
0255 freeTile(struct Tile* tile)
0256 {
0257   if( --tile->refcount == 0 )
0258     xcffree(tile) ;
0259 }
0260 
0261 summary_t
0262 tileSummary(struct Tile *tile)
0263 {
0264   unsigned i ;
0265   summary_t summary ;
0266   if( (tile->summary & TILESUMMARY_UPTODATE) != 0 )
0267     return tile->summary ;
0268   summary = TILESUMMARY_ALLNULL + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
0269   for( i=0; summary && i<tile->count; i++ ) {
0270     if( FULLALPHA(tile->pixels[i]) )
0271       summary &= ~TILESUMMARY_ALLNULL ;
0272     else if( NULLALPHA(tile->pixels[i]) )
0273       summary &= ~TILESUMMARY_ALLFULL ;
0274     else
0275       summary = 0 ;
0276   }
0277   summary += TILESUMMARY_UPTODATE ;
0278   tile->summary = summary ;
0279   return summary ;
0280 }
0281 
0282 void
0283 fillTile(struct Tile *tile,rgba data)
0284 {
0285   unsigned i ;
0286   for( i = 0 ; i < tile->count ; i++ )
0287     tile->pixels[i] = data ;
0288   if( FULLALPHA(data) )
0289     tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP;
0290   else if (NULLALPHA(data) )
0291     tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLNULL+TILESUMMARY_CRISP;
0292   else
0293     tile->summary = TILESUMMARY_UPTODATE ;
0294 }
0295 
0296 /* ****************************************************************** */
0297 
0298 static int
0299 copyStraightPixels(rgba *dest,unsigned npixels,
0300                    uint32_t ptr,convertParams *params)
0301 {
0302   unsigned bpp = params->bpp;
0303   const rgba *lookup = params->lookup;
0304   rgba base_pixel = params->base_pixel ;
0305   uint8_t *bp = xcf_file + ptr ;
0306   int response;
0307   if ((response = xcfCheckspace(ptr,bpp*npixels,
0308                                 "pixel array (%u x %d bpp) at %"PRIX32,npixels,bpp,ptr)) != XCF_OK) {
0309       return XCF_ERROR;
0310   }
0311   while( npixels-- ) {
0312     rgba pixel = base_pixel ;
0313     unsigned i ;
0314     for( i = 0 ; i < bpp ; i++ ) {
0315       if( params->shift[i] < 0 ) {
0316         pixel += lookup[*bp++] ;
0317       } else {
0318         pixel += *bp++ << params->shift[i] ;
0319       }
0320     }
0321     *dest++ = pixel ;
0322   }
0323   return XCF_OK;
0324 }
0325 
0326 static inline int
0327 copyRLEpixels(rgba *dest,unsigned npixels,uint32_t ptr,convertParams *params)
0328 {
0329   unsigned i,j ;
0330   rgba base_pixel = params->base_pixel ;
0331 
0332 #ifdef xDEBUG
0333   fprintf(stderr,"RLE stream at %x, want %u x %u pixels, base %x\n",
0334           ptr,params->bpp,npixels,base_pixel);
0335 #endif
0336 
0337   /* This algorithm depends on the indexed byte always being the first one */
0338   if( params->shift[0] < -1 )
0339     base_pixel = 0 ;
0340   for( j = npixels ; j-- ; )
0341     dest[j] = base_pixel ;
0342 
0343   for( i = 0 ; i < params->bpp ; i++ ) {
0344     int shift = params->shift[i] ;
0345     if( shift < 0 )
0346       shift = 0 ;
0347     for( j = 0 ; j < npixels ; ) {
0348       int countspec ;
0349       unsigned count ;
0350       if (xcfCheckspace(ptr,2,"RLE data stream") != XCF_OK) {
0351           return XCF_ERROR;
0352       }
0353       countspec = (int8_t) xcf_file[ptr++] ;
0354       count = countspec >= 0 ? countspec+1 : -countspec ;
0355       if( count == 128 ) {
0356           if (xcfCheckspace(ptr,3,"RLE long count") != XCF_OK) {
0357               return XCF_ERROR;
0358           }
0359         count = xcf_file[ptr++] << 8 ;
0360         count += xcf_file[ptr++] ;
0361       }
0362       if( j + count > npixels ) {
0363         FatalBadXCF("Overlong RLE run at %"PRIX32" (plane %u, %u left)",
0364                     ptr,i,npixels-j);
0365         return XCF_ERROR;
0366       }
0367       if( countspec >= 0 ) {
0368         rgba data = (uint32_t) xcf_file[ptr++] << shift ;
0369         while( count-- )
0370           dest[j++] += data ;
0371       } else {
0372         while( count-- )
0373           dest[j++] += (uint32_t) xcf_file[ptr++] << shift ;
0374       }
0375     }
0376     if( i == 0 && params->shift[0] < 0 ) {
0377       const rgba *lookup = params->lookup ;
0378       base_pixel = params->base_pixel ;
0379       for( j = npixels ; j-- ; ) {
0380         dest[j] = lookup[dest[j]-base_pixel] + base_pixel ;
0381       }
0382     }
0383   }
0384 #ifdef xDEBUG
0385   fprintf(stderr,"RLE decoding OK at %"PRIX32"\n",ptr);
0386   /*
0387   for( j = 0 ; j < npixels ; j++ ) {
0388     if( j % 8 == 0 ) fprintf(stderr,"\n");
0389     fprintf(stderr," %8x",dest[j]);
0390   }
0391   fprintf(stderr,"\n");
0392   */
0393 #endif
0394     return XCF_OK;
0395 }
0396 
0397 static inline int
0398 copyTilePixels(struct Tile *dest, uint32_t ptr,convertParams *params)
0399 {
0400   if( FULLALPHA(params->base_pixel) )
0401     dest->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP;
0402   else
0403     dest->summary = 0 ;
0404   switch( XCF.compression ) {
0405   case COMPRESS_NONE:
0406       if (copyStraightPixels(dest->pixels,dest->count,ptr,params) != XCF_OK) {
0407           return XCF_ERROR;
0408       }
0409     break ;
0410   case COMPRESS_RLE:
0411       if (copyRLEpixels(dest->pixels,dest->count,ptr,params) != XCF_OK) {
0412           return XCF_ERROR;
0413       }
0414     break ;
0415   default:
0416     {
0417         FatalUnsupportedXCF(_("%s compression"),
0418                         _(showXcfCompressionType(XCF.compression)));
0419         return XCF_ERROR;
0420     }
0421   }
0422   return XCF_OK;
0423 }
0424 
0425 
0426 struct Tile *
0427 getMaskOrLayerTile(struct tileDimensions *dim, struct xcfTiles *tiles,
0428                    struct rect want)
0429 {
0430   struct Tile *tile = newTile(want);
0431 
0432   if (want.l >= want.r || want.t >= want.b ) {
0433       freeTile(tile);
0434       return XCF_PTR_EMPTY;
0435   }
0436 
0437   if( tiles->tileptrs == 0 ) {
0438     fillTile(tile,0);
0439     return tile ;
0440   }
0441 
0442 #ifdef xDEBUG
0443   fprintf(stderr,"getMaskOrLayer: (%d-%d),(%d-%d)\n",left,right,top,bottom);
0444 #endif
0445 
0446   if( isSubrect(want,dim->c) &&
0447       (want.l - dim->c.l) % TILE_WIDTH == 0 &&
0448       (want.t - dim->c.t) % TILE_HEIGHT == 0 ) {
0449     int tx = TILE_NUM(want.l - dim->c.l);
0450     int ty = TILE_NUM(want.t - dim->c.t);
0451     if( want.r == TILEXn(*dim,tx+1) && want.b == TILEYn(*dim,ty+1) ) {
0452       /* The common case? An entire single tile from the layer */
0453         if (copyTilePixels(tile,tiles->tileptrs[tx + ty*dim->tilesx],tiles->params) != XCF_OK) {
0454             freeTile(tile);
0455             return XCF_PTR_EMPTY;
0456         }
0457       return tile ;
0458     }
0459   }
0460 
0461   /* OK, we must construct the wanted tile as a jigsaw */
0462   {
0463     unsigned width = want.r-want.l ;
0464     rgba *pixvert = tile->pixels ;
0465     rgba *pixhoriz ;
0466     int y, ty, l0, l1 ;
0467     int x, tx, c0, c1 ;
0468     unsigned lstart, lnum ;
0469     unsigned cstart, cnum ;
0470 
0471     if( !isSubrect(want,dim->c) ) {
0472       if( want.l < dim->c.l ) pixvert += (dim->c.l - want.l),
0473                                 want.l = dim->c.l ;
0474       if( want.r > dim->c.r ) want.r = dim->c.r ;
0475       if( want.t < dim->c.t ) pixvert += (dim->c.t - want.t) * width,
0476                                 want.t = dim->c.t ;
0477       if( want.b > dim->c.b ) want.b = dim->c.b ;
0478       fillTile(tile,0);
0479     } else {
0480       tile->summary = -1 ; /* I.e. whatever the jigsaw pieces say */
0481     }
0482 
0483 #ifdef xDEBUG
0484     fprintf(stderr,"jig0 (%d-%d),(%d-%d)\n",left,right,top,bottom);
0485 #endif
0486 
0487     for( y=want.t, ty=TILE_NUM(want.t-dim->c.t), l0=TILEYn(*dim,ty);
0488          y<want.b;
0489          pixvert += lnum*width, ty++, y=l0=l1 ) {
0490       l1 = TILEYn(*dim,ty+1) ;
0491       lstart = y - l0 ;
0492       lnum = (l1 > want.b ? want.b : l1) - y ;
0493 
0494       pixhoriz = pixvert ;
0495       for( x=want.l, tx=TILE_NUM(want.l-dim->c.l), c0=TILEXn(*dim,tx);
0496            x<want.r;
0497            pixhoriz += cnum, tx++, x=c0=c1 ) {
0498         c1 = TILEXn(*dim,tx+1);
0499         cstart = x - c0 ;
0500         cnum = (c1 > want.r ? want.r : c1) - x ;
0501 
0502         {
0503           static struct Tile tmptile ;
0504           unsigned dwidth = c1-c0 ;
0505           unsigned i, j ;
0506           tmptile.count = (c1-c0)*(l1-l0) ;
0507 #ifdef xDEBUG
0508           fprintf(stderr,"jig ty=%u(%u-%u-%u)(%u+%u) tx=%u(%u-%u-%u)(%u+%u)\n",
0509                   ty,l0,y,l1,lstart,lnum,
0510                   tx,c0,x,c1,cstart,cnum);
0511 #endif
0512           if (copyTilePixels(&tmptile,
0513                              tiles->tileptrs[tx+ty*dim->tilesx],tiles->params) != XCF_OK) {
0514               freeTile(tile);
0515               return XCF_PTR_EMPTY;
0516           }
0517 
0518           for(i=0; i<lnum; i++)
0519             for(j=0; j<cnum; j++)
0520               pixhoriz[i*width+j]
0521                 = tmptile.pixels[(i+lstart)*dwidth+(j+cstart)];
0522           tile->summary &= tmptile.summary ;
0523         }
0524       }
0525     }
0526   }
0527   return tile ;
0528 }
0529 
0530 void
0531 applyMask(struct Tile *tile, struct Tile *mask)
0532 {
0533   unsigned i ;
0534   assertTileCompatibility(tile,mask);
0535   assert( tile->count == mask->count );
0536   INIT_SCALETABLE_IF(1);
0537   invalidateSummary(tile,0);
0538   for( i=0; i < tile->count ;i++ )
0539     tile->pixels[i] = NEWALPHA(tile->pixels[i],
0540                                scaletable[mask->pixels[i]>>ALPHA_SHIFT]
0541                                          [ALPHA(tile->pixels[i])]);
0542   freeTile(mask);
0543 }
0544 
0545 struct Tile *
0546 getLayerTile(struct xcfLayer *layer,const struct rect *where)
0547 {
0548   struct Tile *data ;
0549 
0550 #ifdef xDEBUG
0551   fprintf(stderr,"getLayerTile(%s): (%d-%d),(%d-%d)\n",
0552           layer->name,where->l,where->r,where->t,where->b);
0553 #endif
0554 
0555   if( disjointRects(*where,layer->dim.c) ||
0556       layer->opacity == 0 ) {
0557     data = newTile(*where);
0558     fillTile(data,0);
0559     return data ;
0560   }
0561 
0562   data = getMaskOrLayerTile(&layer->dim,&layer->pixels,*where);
0563   if (data == XCF_PTR_EMPTY) {
0564       return XCF_PTR_EMPTY;
0565   }
0566   if( (data->summary & TILESUMMARY_ALLNULL) != 0 )
0567     return data ;
0568   if( layer->hasMask ) {
0569     struct Tile *mask = getMaskOrLayerTile(&layer->dim,&layer->mask,*where);
0570     if (mask == XCF_PTR_EMPTY) { /* error */
0571         freeTile(data);
0572         return XCF_PTR_EMPTY;
0573     }
0574     applyMask(data,mask);
0575   }
0576   if( layer->opacity < 255 ) {
0577     const uint8_t *ourtable ;
0578     int i ;
0579     invalidateSummary(data,~(TILESUMMARY_CRISP | TILESUMMARY_ALLFULL));
0580     INIT_SCALETABLE_IF(1);
0581     ourtable = scaletable[layer->opacity] ;
0582     for( i=0; i < data->count; i++ )
0583       data->pixels[i]
0584         = NEWALPHA(data->pixels[i],ourtable[ALPHA(data->pixels[i])]) ;
0585   }
0586   return data ;
0587 }