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 }