1/* 2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3% % 4% % 5% % 6% X X CCCC FFFFF % 7% X X C F % 8% X C FFF % 9% X X C F % 10% X X CCCC F % 11% % 12% % 13% Read GIMP XCF Image Format % 14% % 15% Software Design % 16% Leonard Rosenthol % 17% November 2001 % 18% % 19% % 20% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37*/ 38 39/* 40 Include declarations. 41*/ 42#include "MagickCore/studio.h" 43#include "MagickCore/blob.h" 44#include "MagickCore/blob-private.h" 45#include "MagickCore/cache.h" 46#include "MagickCore/color.h" 47#include "MagickCore/composite.h" 48#include "MagickCore/exception.h" 49#include "MagickCore/exception-private.h" 50#include "MagickCore/image.h" 51#include "MagickCore/image-private.h" 52#include "MagickCore/list.h" 53#include "MagickCore/magick.h" 54#include "MagickCore/memory_.h" 55#include "MagickCore/pixel.h" 56#include "MagickCore/pixel-accessor.h" 57#include "MagickCore/property.h" 58#include "MagickCore/quantize.h" 59#include "MagickCore/quantum-private.h" 60#include "MagickCore/static.h" 61#include "MagickCore/string_.h" 62#include "MagickCore/module.h" 63 64/* 65 Typedef declarations. 66*/ 67typedef enum 68{ 69 GIMP_RGB, 70 GIMP_GRAY, 71 GIMP_INDEXED 72} GimpImageBaseType; 73 74typedef enum 75{ 76 PROP_END = 0, 77 PROP_COLORMAP = 1, 78 PROP_ACTIVE_LAYER = 2, 79 PROP_ACTIVE_CHANNEL = 3, 80 PROP_SELECTION = 4, 81 PROP_FLOATING_SELECTION = 5, 82 PROP_OPACITY = 6, 83 PROP_MODE = 7, 84 PROP_VISIBLE = 8, 85 PROP_LINKED = 9, 86 PROP_PRESERVE_TRANSPARENCY = 10, 87 PROP_APPLY_MASK = 11, 88 PROP_EDIT_MASK = 12, 89 PROP_SHOW_MASK = 13, 90 PROP_SHOW_MASKED = 14, 91 PROP_OFFSETS = 15, 92 PROP_COLOR = 16, 93 PROP_COMPRESSION = 17, 94 PROP_GUIDES = 18, 95 PROP_RESOLUTION = 19, 96 PROP_TATTOO = 20, 97 PROP_PARASITES = 21, 98 PROP_UNIT = 22, 99 PROP_PATHS = 23, 100 PROP_USER_UNIT = 24 101} PropType; 102 103typedef enum 104{ 105 COMPRESS_NONE = 0, 106 COMPRESS_RLE = 1, 107 COMPRESS_ZLIB = 2, /* unused */ 108 COMPRESS_FRACTAL = 3 /* unused */ 109} XcfCompressionType; 110 111typedef struct 112{ 113 size_t 114 width, 115 height, 116 image_type, 117 bytes_per_pixel; 118 119 int 120 compression; 121 122 size_t 123 file_size; 124 125 size_t 126 number_layers; 127} XCFDocInfo; 128 129typedef struct 130{ 131 char 132 name[1024]; 133 134 unsigned int 135 active; 136 137 size_t 138 width, 139 height, 140 type, 141 alpha, 142 visible, 143 linked, 144 preserve_trans, 145 apply_mask, 146 show_mask, 147 edit_mask, 148 floating_offset; 149 150 ssize_t 151 offset_x, 152 offset_y; 153 154 size_t 155 mode, 156 tattoo; 157 158 Image 159 *image; 160} XCFLayerInfo; 161 162#define TILE_WIDTH 64 163#define TILE_HEIGHT 64 164 165typedef struct 166{ 167 unsigned char 168 red, 169 green, 170 blue, 171 alpha; 172} XCFPixelInfo; 173 174/* 175%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 176% % 177% % 178% % 179% I s X C F % 180% % 181% % 182% % 183%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 184% 185% IsXCF() returns MagickTrue if the image format type, identified by the 186% magick string, is XCF (GIMP native format). 187% 188% The format of the IsXCF method is: 189% 190% MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) 191% 192% A description of each parameter follows: 193% 194% o magick: compare image format pattern against these bytes. 195% 196% o length: Specifies the length of the magick string. 197% 198% 199*/ 200static MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) 201{ 202 if (length < 8) 203 return(MagickFalse); 204 if (LocaleNCompare((char *) magick,"gimp xcf",8) == 0) 205 return(MagickTrue); 206 return(MagickFalse); 207} 208 209typedef enum 210{ 211 GIMP_NORMAL_MODE, 212 GIMP_DISSOLVE_MODE, 213 GIMP_BEHIND_MODE, 214 GIMP_MULTIPLY_MODE, 215 GIMP_SCREEN_MODE, 216 GIMP_OVERLAY_MODE, 217 GIMP_DIFFERENCE_MODE, 218 GIMP_ADDITION_MODE, 219 GIMP_SUBTRACT_MODE, 220 GIMP_DARKEN_ONLY_MODE, 221 GIMP_LIGHTEN_ONLY_MODE, 222 GIMP_HUE_MODE, 223 GIMP_SATURATION_MODE, 224 GIMP_COLOR_MODE, 225 GIMP_VALUE_MODE, 226 GIMP_DIVIDE_MODE, 227 GIMP_DODGE_MODE, 228 GIMP_BURN_MODE, 229 GIMP_HARDLIGHT_MODE 230} GimpLayerModeEffects; 231 232/* 233 Simple utility routine to convert between PSD blending modes and 234 ImageMagick compositing operators 235*/ 236static CompositeOperator GIMPBlendModeToCompositeOperator( 237 size_t blendMode) 238{ 239 switch ( blendMode ) 240 { 241 case GIMP_NORMAL_MODE: return(OverCompositeOp); 242 case GIMP_DISSOLVE_MODE: return(DissolveCompositeOp); 243 case GIMP_MULTIPLY_MODE: return(MultiplyCompositeOp); 244 case GIMP_SCREEN_MODE: return(ScreenCompositeOp); 245 case GIMP_OVERLAY_MODE: return(OverlayCompositeOp); 246 case GIMP_DIFFERENCE_MODE: return(DifferenceCompositeOp); 247 case GIMP_ADDITION_MODE: return(ModulusAddCompositeOp); 248 case GIMP_SUBTRACT_MODE: return(ModulusSubtractCompositeOp); 249 case GIMP_DARKEN_ONLY_MODE: return(DarkenCompositeOp); 250 case GIMP_LIGHTEN_ONLY_MODE: return(LightenCompositeOp); 251 case GIMP_HUE_MODE: return(HueCompositeOp); 252 case GIMP_SATURATION_MODE: return(SaturateCompositeOp); 253 case GIMP_COLOR_MODE: return(ColorizeCompositeOp); 254 case GIMP_DODGE_MODE: return(ColorDodgeCompositeOp); 255 case GIMP_BURN_MODE: return(ColorBurnCompositeOp); 256 case GIMP_HARDLIGHT_MODE: return(HardLightCompositeOp); 257 case GIMP_DIVIDE_MODE: return(DivideDstCompositeOp); 258 /* these are the ones we don't support...yet */ 259 case GIMP_BEHIND_MODE: return(OverCompositeOp); 260 case GIMP_VALUE_MODE: return(OverCompositeOp); 261 default: return(OverCompositeOp); 262 } 263} 264 265/* 266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 267% % 268% % 269% % 270+ R e a d B l o b S t r i n g W i t h L o n g S i z e % 271% % 272% % 273% % 274%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 275% 276% ReadBlobStringWithLongSize reads characters from a blob or file 277% starting with a ssize_t length byte and then characters to that length 278% 279% The format of the ReadBlobStringWithLongSize method is: 280% 281% char *ReadBlobStringWithLongSize(Image *image,char *string) 282% 283% A description of each parameter follows: 284% 285% o image: the image. 286% 287% o string: the address of a character buffer. 288% 289*/ 290 291static char *ReadBlobStringWithLongSize(Image *image,char *string,size_t max, 292 ExceptionInfo *exception) 293{ 294 int 295 c; 296 297 MagickOffsetType 298 offset; 299 300 register ssize_t 301 i; 302 303 size_t 304 length; 305 306 assert(image != (Image *) NULL); 307 assert(image->signature == MagickCoreSignature); 308 assert(max != 0); 309 if (image->debug != MagickFalse) 310 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 311 length=ReadBlobMSBLong(image); 312 for (i=0; i < (ssize_t) MagickMin(length,max-1); i++) 313 { 314 c=ReadBlobByte(image); 315 if (c == EOF) 316 return((char *) NULL); 317 string[i]=(char) c; 318 } 319 string[i]='\0'; 320 offset=SeekBlob(image,(MagickOffsetType) (length-i),SEEK_CUR); 321 if (offset < 0) 322 (void) ThrowMagickException(exception,GetMagickModule(), 323 CorruptImageError,"ImproperImageHeader","`%s'",image->filename); 324 return(string); 325} 326 327static MagickBooleanType load_tile(Image *image,Image *tile_image, 328 XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, 329 ExceptionInfo *exception) 330{ 331 ssize_t 332 y; 333 334 register ssize_t 335 x; 336 337 register Quantum 338 *q; 339 340 size_t 341 extent; 342 343 ssize_t 344 count; 345 346 unsigned char 347 *graydata; 348 349 XCFPixelInfo 350 *xcfdata, 351 *xcfodata; 352 353 extent=0; 354 if (inDocInfo->image_type == GIMP_GRAY) 355 extent=tile_image->columns*tile_image->rows*sizeof(*graydata); 356 else 357 if (inDocInfo->image_type == GIMP_RGB) 358 extent=tile_image->columns*tile_image->rows*sizeof(*xcfdata); 359 if (extent > data_length) 360 ThrowBinaryException(CorruptImageError,"NotEnoughPixelData", 361 image->filename); 362 xcfdata=(XCFPixelInfo *) AcquireQuantumMemory(MagickMax(data_length, 363 tile_image->columns*tile_image->rows),sizeof(*xcfdata)); 364 if (xcfdata == (XCFPixelInfo *) NULL) 365 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 366 image->filename); 367 xcfodata=xcfdata; 368 graydata=(unsigned char *) xcfdata; /* used by gray and indexed */ 369 count=ReadBlob(image,data_length,(unsigned char *) xcfdata); 370 if (count != (ssize_t) data_length) 371 ThrowBinaryException(CorruptImageError,"NotEnoughPixelData", 372 image->filename); 373 for (y=0; y < (ssize_t) tile_image->rows; y++) 374 { 375 q=GetAuthenticPixels(tile_image,0,y,tile_image->columns,1,exception); 376 if (q == (Quantum *) NULL) 377 break; 378 if (inDocInfo->image_type == GIMP_GRAY) 379 { 380 for (x=0; x < (ssize_t) tile_image->columns; x++) 381 { 382 SetPixelGray(tile_image,ScaleCharToQuantum(*graydata),q); 383 SetPixelAlpha(tile_image,ScaleCharToQuantum((unsigned char) 384 inLayerInfo->alpha),q); 385 graydata++; 386 q+=GetPixelChannels(tile_image); 387 } 388 } 389 else 390 if (inDocInfo->image_type == GIMP_RGB) 391 { 392 for (x=0; x < (ssize_t) tile_image->columns; x++) 393 { 394 SetPixelRed(tile_image,ScaleCharToQuantum(xcfdata->red),q); 395 SetPixelGreen(tile_image,ScaleCharToQuantum(xcfdata->green),q); 396 SetPixelBlue(tile_image,ScaleCharToQuantum(xcfdata->blue),q); 397 SetPixelAlpha(tile_image,xcfdata->alpha == 255U ? TransparentAlpha : 398 ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q); 399 xcfdata++; 400 q+=GetPixelChannels(tile_image); 401 } 402 } 403 if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) 404 break; 405 } 406 xcfodata=(XCFPixelInfo *) RelinquishMagickMemory(xcfodata); 407 return MagickTrue; 408} 409 410static MagickBooleanType load_tile_rle(Image *image,Image *tile_image, 411 XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, 412 ExceptionInfo *exception) 413{ 414 MagickOffsetType 415 size; 416 417 Quantum 418 alpha; 419 420 register Quantum 421 *q; 422 423 size_t 424 length; 425 426 ssize_t 427 bytes_per_pixel, 428 count, 429 i, 430 j; 431 432 unsigned char 433 data, 434 pixel, 435 *xcfdata, 436 *xcfodata, 437 *xcfdatalimit; 438 439 bytes_per_pixel=(ssize_t) inDocInfo->bytes_per_pixel; 440 xcfdata=(unsigned char *) AcquireQuantumMemory(data_length,sizeof(*xcfdata)); 441 if (xcfdata == (unsigned char *) NULL) 442 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", 443 image->filename); 444 xcfodata=xcfdata; 445 count=ReadBlob(image, (size_t) data_length, xcfdata); 446 xcfdatalimit = xcfodata+count-1; 447 alpha=ScaleCharToQuantum((unsigned char) inLayerInfo->alpha); 448 for (i=0; i < (ssize_t) bytes_per_pixel; i++) 449 { 450 q=GetAuthenticPixels(tile_image,0,0,tile_image->columns,tile_image->rows, 451 exception); 452 if (q == (Quantum *) NULL) 453 continue; 454 size=(MagickOffsetType) tile_image->rows*tile_image->columns; 455 while (size > 0) 456 { 457 if (xcfdata > xcfdatalimit) 458 goto bogus_rle; 459 pixel=(*xcfdata++); 460 length=(size_t) pixel; 461 if (length >= 128) 462 { 463 length=255-(length-1); 464 if (length == 128) 465 { 466 if (xcfdata >= xcfdatalimit) 467 goto bogus_rle; 468 length=(size_t) ((*xcfdata << 8) + xcfdata[1]); 469 xcfdata+=2; 470 } 471 size-=length; 472 if (size < 0) 473 goto bogus_rle; 474 if (&xcfdata[length-1] > xcfdatalimit) 475 goto bogus_rle; 476 while (length-- > 0) 477 { 478 data=(*xcfdata++); 479 switch (i) 480 { 481 case 0: 482 { 483 if (inDocInfo->image_type == GIMP_GRAY) 484 SetPixelGray(tile_image,ScaleCharToQuantum(data),q); 485 else 486 { 487 SetPixelRed(tile_image,ScaleCharToQuantum(data),q); 488 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); 489 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); 490 } 491 SetPixelAlpha(tile_image,alpha,q); 492 break; 493 } 494 case 1: 495 { 496 if (inDocInfo->image_type == GIMP_GRAY) 497 SetPixelAlpha(tile_image,ScaleCharToQuantum(data),q); 498 else 499 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); 500 break; 501 } 502 case 2: 503 { 504 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); 505 break; 506 } 507 case 3: 508 { 509 SetPixelAlpha(tile_image,ScaleCharToQuantum(data),q); 510 break; 511 } 512 } 513 q+=GetPixelChannels(tile_image); 514 } 515 } 516 else 517 { 518 length+=1; 519 if (length == 128) 520 { 521 if (xcfdata >= xcfdatalimit) 522 goto bogus_rle; 523 length=(size_t) ((*xcfdata << 8) + xcfdata[1]); 524 xcfdata+=2; 525 } 526 size-=length; 527 if (size < 0) 528 goto bogus_rle; 529 if (xcfdata > xcfdatalimit) 530 goto bogus_rle; 531 pixel=(*xcfdata++); 532 for (j=0; j < (ssize_t) length; j++) 533 { 534 data=pixel; 535 switch (i) 536 { 537 case 0: 538 { 539 if (inDocInfo->image_type == GIMP_GRAY) 540 SetPixelGray(tile_image,ScaleCharToQuantum(data),q); 541 else 542 { 543 SetPixelRed(tile_image,ScaleCharToQuantum(data),q); 544 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); 545 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); 546 } 547 SetPixelAlpha(tile_image,alpha,q); 548 break; 549 } 550 case 1: 551 { 552 if (inDocInfo->image_type == GIMP_GRAY) 553 SetPixelAlpha(tile_image,ScaleCharToQuantum(data),q); 554 else 555 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); 556 break; 557 } 558 case 2: 559 { 560 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); 561 break; 562 } 563 case 3: 564 { 565 SetPixelAlpha(tile_image,ScaleCharToQuantum(data),q); 566 break; 567 } 568 } 569 q+=GetPixelChannels(tile_image); 570 } 571 } 572 } 573 if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) 574 break; 575 } 576 xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); 577 return(MagickTrue); 578 579 bogus_rle: 580 if (xcfodata != (unsigned char *) NULL) 581 xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); 582 return(MagickFalse); 583} 584 585static MagickBooleanType load_level(Image *image,XCFDocInfo *inDocInfo, 586 XCFLayerInfo *inLayerInfo,ExceptionInfo *exception) 587{ 588 int 589 destLeft = 0, 590 destTop = 0; 591 592 Image* 593 tile_image; 594 595 MagickBooleanType 596 status; 597 598 MagickOffsetType 599 saved_pos, 600 offset, 601 offset2; 602 603 register ssize_t 604 i; 605 606 size_t 607 width, 608 height, 609 ntiles, 610 ntile_rows, 611 ntile_cols, 612 tile_image_width, 613 tile_image_height; 614 615 /* start reading the data */ 616 width=ReadBlobMSBLong(image); 617 height=ReadBlobMSBLong(image); 618 619 /* read in the first tile offset. 620 * if it is '0', then this tile level is empty 621 * and we can simply return. 622 */ 623 offset=(MagickOffsetType) ReadBlobMSBLong(image); 624 if (offset == 0) 625 return(MagickTrue); 626 /* Initialise the reference for the in-memory tile-compression 627 */ 628 ntile_rows=(height+TILE_HEIGHT-1)/TILE_HEIGHT; 629 ntile_cols=(width+TILE_WIDTH-1)/TILE_WIDTH; 630 ntiles=ntile_rows*ntile_cols; 631 for (i = 0; i < (ssize_t) ntiles; i++) 632 { 633 status=MagickFalse; 634 if (offset == 0) 635 ThrowBinaryException(CorruptImageError,"NotEnoughTiles",image->filename); 636 /* save the current position as it is where the 637 * next tile offset is stored. 638 */ 639 saved_pos=TellBlob(image); 640 /* read in the offset of the next tile so we can calculate the amount 641 of data needed for this tile*/ 642 offset2=(MagickOffsetType)ReadBlobMSBLong(image); 643 /* if the offset is 0 then we need to read in the maximum possible 644 allowing for negative compression */ 645 if (offset2 == 0) 646 offset2=(MagickOffsetType) (offset + TILE_WIDTH * TILE_WIDTH * 4* 1.5); 647 /* seek to the tile offset */ 648 offset=SeekBlob(image, offset, SEEK_SET); 649 650 /* 651 Allocate the image for the tile. NOTE: the last tile in a row or 652 column may not be a full tile! 653 */ 654 tile_image_width=(size_t) (destLeft == (int) ntile_cols-1 ? 655 (int) width % TILE_WIDTH : TILE_WIDTH); 656 if (tile_image_width == 0) 657 tile_image_width=TILE_WIDTH; 658 tile_image_height = (size_t) (destTop == (int) ntile_rows-1 ? 659 (int) height % TILE_HEIGHT : TILE_HEIGHT); 660 if (tile_image_height == 0) 661 tile_image_height=TILE_HEIGHT; 662 tile_image=CloneImage(inLayerInfo->image,tile_image_width, 663 tile_image_height,MagickTrue,exception); 664 665 /* read in the tile */ 666 switch (inDocInfo->compression) 667 { 668 case COMPRESS_NONE: 669 if (load_tile(image,tile_image,inDocInfo,inLayerInfo,(size_t) (offset2-offset),exception) == 0) 670 status=MagickTrue; 671 break; 672 case COMPRESS_RLE: 673 if (load_tile_rle (image,tile_image,inDocInfo,inLayerInfo, 674 (int) (offset2-offset),exception) == 0) 675 status=MagickTrue; 676 break; 677 case COMPRESS_ZLIB: 678 ThrowBinaryException(CoderError,"ZipCompressNotSupported", 679 image->filename) 680 case COMPRESS_FRACTAL: 681 ThrowBinaryException(CoderError,"FractalCompressNotSupported", 682 image->filename) 683 } 684 685 /* composite the tile onto the layer's image, and then destroy it */ 686 (void) CompositeImage(inLayerInfo->image,tile_image,CopyCompositeOp, 687 MagickTrue,destLeft * TILE_WIDTH,destTop*TILE_HEIGHT,exception); 688 tile_image=DestroyImage(tile_image); 689 690 /* adjust tile position */ 691 destLeft++; 692 if (destLeft >= (int) ntile_cols) 693 { 694 destLeft = 0; 695 destTop++; 696 } 697 if (status != MagickFalse) 698 return(MagickFalse); 699 /* restore the saved position so we'll be ready to 700 * read the next offset. 701 */ 702 offset=SeekBlob(image, saved_pos, SEEK_SET); 703 /* read in the offset of the next tile */ 704 offset=(MagickOffsetType) ReadBlobMSBLong(image); 705 } 706 if (offset != 0) 707 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename) 708 return(MagickTrue); 709} 710 711static MagickBooleanType load_hierarchy(Image *image,XCFDocInfo *inDocInfo, 712 XCFLayerInfo *inLayer, ExceptionInfo *exception) 713{ 714 MagickOffsetType 715 saved_pos, 716 offset, 717 junk; 718 719 size_t 720 width, 721 height, 722 bytes_per_pixel; 723 724 width=ReadBlobMSBLong(image); 725 (void) width; 726 height=ReadBlobMSBLong(image); 727 (void) height; 728 bytes_per_pixel=inDocInfo->bytes_per_pixel=ReadBlobMSBLong(image); 729 (void) bytes_per_pixel; 730 731 /* load in the levels...we make sure that the number of levels 732 * calculated when the TileManager was created is the same 733 * as the number of levels found in the file. 734 */ 735 offset=(MagickOffsetType) ReadBlobMSBLong(image); /* top level */ 736 737 /* discard offsets for layers below first, if any. 738 */ 739 do 740 { 741 junk=(MagickOffsetType) ReadBlobMSBLong(image); 742 } 743 while (junk != 0); 744 745 /* save the current position as it is where the 746 * next level offset is stored. 747 */ 748 saved_pos=TellBlob(image); 749 750 /* seek to the level offset */ 751 offset=SeekBlob(image, offset, SEEK_SET); 752 753 /* read in the level */ 754 if (load_level (image, inDocInfo, inLayer, exception) == 0) 755 return(MagickFalse); 756 /* restore the saved position so we'll be ready to 757 * read the next offset. 758 */ 759 offset=SeekBlob(image, saved_pos, SEEK_SET); 760 return(MagickTrue); 761} 762 763static void InitXCFImage(XCFLayerInfo *outLayer,ExceptionInfo *exception) 764{ 765 outLayer->image->page.x=outLayer->offset_x; 766 outLayer->image->page.y=outLayer->offset_y; 767 outLayer->image->page.width=outLayer->width; 768 outLayer->image->page.height=outLayer->height; 769 (void) SetImageProperty(outLayer->image,"label",(char *)outLayer->name, 770 exception); 771} 772 773static MagickBooleanType ReadOneLayer(const ImageInfo *image_info,Image* image, 774 XCFDocInfo* inDocInfo,XCFLayerInfo *outLayer,const ssize_t layer, 775 ExceptionInfo *exception) 776{ 777 MagickOffsetType 778 offset; 779 780 unsigned int 781 foundPropEnd = 0; 782 783 size_t 784 hierarchy_offset, 785 layer_mask_offset; 786 787 /* clear the block! */ 788 (void) ResetMagickMemory( outLayer, 0, sizeof( XCFLayerInfo ) ); 789 /* read in the layer width, height, type and name */ 790 outLayer->width = ReadBlobMSBLong(image); 791 outLayer->height = ReadBlobMSBLong(image); 792 outLayer->type = ReadBlobMSBLong(image); 793 (void) ReadBlobStringWithLongSize(image, outLayer->name, 794 sizeof(outLayer->name),exception); 795 /* read the layer properties! */ 796 foundPropEnd = 0; 797 while ( (foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse) ) { 798 PropType prop_type = (PropType) ReadBlobMSBLong(image); 799 size_t prop_size = ReadBlobMSBLong(image); 800 switch (prop_type) 801 { 802 case PROP_END: 803 foundPropEnd = 1; 804 break; 805 case PROP_ACTIVE_LAYER: 806 outLayer->active = 1; 807 break; 808 case PROP_FLOATING_SELECTION: 809 outLayer->floating_offset = ReadBlobMSBLong(image); 810 break; 811 case PROP_OPACITY: 812 outLayer->alpha = ReadBlobMSBLong(image); 813 break; 814 case PROP_VISIBLE: 815 outLayer->visible = ReadBlobMSBLong(image); 816 break; 817 case PROP_LINKED: 818 outLayer->linked = ReadBlobMSBLong(image); 819 break; 820 case PROP_PRESERVE_TRANSPARENCY: 821 outLayer->preserve_trans = ReadBlobMSBLong(image); 822 break; 823 case PROP_APPLY_MASK: 824 outLayer->apply_mask = ReadBlobMSBLong(image); 825 break; 826 case PROP_EDIT_MASK: 827 outLayer->edit_mask = ReadBlobMSBLong(image); 828 break; 829 case PROP_SHOW_MASK: 830 outLayer->show_mask = ReadBlobMSBLong(image); 831 break; 832 case PROP_OFFSETS: 833 outLayer->offset_x = ReadBlobMSBSignedLong(image); 834 outLayer->offset_y = ReadBlobMSBSignedLong(image); 835 break; 836 case PROP_MODE: 837 outLayer->mode = ReadBlobMSBLong(image); 838 break; 839 case PROP_TATTOO: 840 outLayer->preserve_trans = ReadBlobMSBLong(image); 841 break; 842 case PROP_PARASITES: 843 { 844 if (DiscardBlobBytes(image,prop_size) == MagickFalse) 845 ThrowFileException(exception,CorruptImageError, 846 "UnexpectedEndOfFile",image->filename); 847 848 /* 849 ssize_t base = info->cp; 850 GimpParasite *p; 851 while (info->cp - base < prop_size) 852 { 853 p = xcf_load_parasite(info); 854 gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p); 855 gimp_parasite_free(p); 856 } 857 if (info->cp - base != prop_size) 858 g_message ("Error detected while loading a layer's parasites"); 859 */ 860 } 861 break; 862 default: 863 /* g_message ("unexpected/unknown layer property: %d (skipping)", 864 prop_type); */ 865 866 { 867 int buf[16]; 868 ssize_t amount; 869 870 /* read over it... */ 871 while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) 872 { 873 amount = (ssize_t) MagickMin(16, prop_size); 874 amount = ReadBlob(image, (size_t) amount, (unsigned char *) &buf); 875 if (!amount) 876 ThrowBinaryException(CorruptImageError,"CorruptImage", 877 image->filename); 878 prop_size -= (size_t) MagickMin(16, (size_t) amount); 879 } 880 } 881 break; 882 } 883 } 884 885 if (foundPropEnd == MagickFalse) 886 return(MagickFalse); 887 /* allocate the image for this layer */ 888 if (image_info->number_scenes != 0) 889 { 890 ssize_t 891 scene; 892 893 scene=inDocInfo->number_layers-layer-1; 894 if (scene > (ssize_t) (image_info->scene+image_info->number_scenes-1)) 895 { 896 outLayer->image=CloneImage(image,0,0,MagickTrue,exception); 897 if (outLayer->image == (Image *) NULL) 898 return(MagickFalse); 899 InitXCFImage(outLayer,exception); 900 return(MagickTrue); 901 } 902 } 903 outLayer->image=CloneImage(image,outLayer->width, outLayer->height,MagickTrue, 904 exception); 905 if (outLayer->image == (Image *) NULL) 906 return(MagickFalse); 907 /* clear the image based on the layer opacity */ 908 outLayer->image->background_color.alpha= 909 ScaleCharToQuantum((unsigned char) outLayer->alpha); 910 (void) SetImageBackgroundColor(outLayer->image,exception); 911 912 InitXCFImage(outLayer,exception); 913 914 /* set the compositing mode */ 915 outLayer->image->compose = GIMPBlendModeToCompositeOperator( outLayer->mode ); 916 if ( outLayer->visible == MagickFalse ) 917 { 918 /* BOGUS: should really be separate member var! */ 919 outLayer->image->compose = NoCompositeOp; 920 } 921 922 /* read the hierarchy and layer mask offsets */ 923 hierarchy_offset = ReadBlobMSBLong(image); 924 layer_mask_offset = ReadBlobMSBLong(image); 925 926 /* read in the hierarchy */ 927 offset=SeekBlob(image, (MagickOffsetType) hierarchy_offset, SEEK_SET); 928 if (offset < 0) 929 (void) ThrowMagickException(exception,GetMagickModule(), 930 CorruptImageError,"InvalidImageHeader","`%s'",image->filename); 931 if (load_hierarchy (image, inDocInfo, outLayer, exception) == 0) 932 return(MagickFalse); 933 934 /* read in the layer mask */ 935 if (layer_mask_offset != 0) 936 { 937 offset=SeekBlob(image, (MagickOffsetType) layer_mask_offset, SEEK_SET); 938 939#if 0 /* BOGUS: support layer masks! */ 940 layer_mask = xcf_load_layer_mask (info, gimage); 941 if (layer_mask == 0) 942 goto error; 943 944 /* set the offsets of the layer_mask */ 945 GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x; 946 GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y; 947 948 gimp_layer_add_mask (layer, layer_mask, MagickFalse); 949 950 layer->mask->apply_mask = apply_mask; 951 layer->mask->edit_mask = edit_mask; 952 layer->mask->show_mask = show_mask; 953#endif 954 } 955 956 /* attach the floating selection... */ 957#if 0 /* BOGUS: we may need to read this, even if we don't support it! */ 958 if (add_floating_sel) 959 { 960 GimpLayer *floating_sel; 961 962 floating_sel = info->floating_sel; 963 floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer)); 964 } 965#endif 966 967 return MagickTrue; 968} 969 970/* 971%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 972% % 973% % 974% % 975% R e a d X C F I m a g e % 976% % 977% % 978% % 979%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 980% 981% ReadXCFImage() reads a GIMP (GNU Image Manipulation Program) image 982% file and returns it. It allocates the memory necessary for the new Image 983% structure and returns a pointer to the new image. 984% 985% The format of the ReadXCFImage method is: 986% 987% image=ReadXCFImage(image_info) 988% 989% A description of each parameter follows: 990% 991% o image_info: the image info. 992% 993% o exception: return any errors or warnings in this structure. 994% 995*/ 996static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception) 997{ 998 char 999 magick[14]; 1000 1001 Image 1002 *image; 1003 1004 int 1005 foundPropEnd = 0; 1006 1007 MagickBooleanType 1008 status; 1009 1010 MagickOffsetType 1011 offset; 1012 1013 register ssize_t 1014 i; 1015 1016 size_t 1017 image_type, 1018 length; 1019 1020 ssize_t 1021 count; 1022 1023 XCFDocInfo 1024 doc_info; 1025 1026 /* 1027 Open image file. 1028 */ 1029 assert(image_info != (const ImageInfo *) NULL); 1030 assert(image_info->signature == MagickCoreSignature); 1031 if (image_info->debug != MagickFalse) 1032 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 1033 image_info->filename); 1034 assert(exception != (ExceptionInfo *) NULL); 1035 assert(exception->signature == MagickCoreSignature); 1036 image=AcquireImage(image_info,exception); 1037 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 1038 if (status == MagickFalse) 1039 { 1040 image=DestroyImageList(image); 1041 return((Image *) NULL); 1042 } 1043 count=ReadBlob(image,14,(unsigned char *) magick); 1044 if ((count != 14) || 1045 (LocaleNCompare((char *) magick,"gimp xcf",8) != 0)) 1046 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 1047 (void) ResetMagickMemory(&doc_info,0,sizeof(XCFDocInfo)); 1048 doc_info.width=ReadBlobMSBLong(image); 1049 doc_info.height=ReadBlobMSBLong(image); 1050 if ((doc_info.width > 262144) || (doc_info.height > 262144)) 1051 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 1052 doc_info.image_type=ReadBlobMSBLong(image); 1053 /* 1054 Initialize image attributes. 1055 */ 1056 image->columns=doc_info.width; 1057 image->rows=doc_info.height; 1058 image_type=doc_info.image_type; 1059 doc_info.file_size=GetBlobSize(image); 1060 image->compression=NoCompression; 1061 image->depth=8; 1062 status=SetImageExtent(image,image->columns,image->rows,exception); 1063 if (status == MagickFalse) 1064 return(DestroyImageList(image)); 1065 if (image_type == GIMP_RGB) 1066 SetImageColorspace(image,sRGBColorspace,exception); 1067 else 1068 if (image_type == GIMP_GRAY) 1069 SetImageColorspace(image,GRAYColorspace,exception); 1070 else 1071 if (image_type == GIMP_INDEXED) 1072 ThrowReaderException(CoderError,"ColormapTypeNotSupported"); 1073 (void) SetImageBackgroundColor(image,exception); 1074 (void) SetImageAlpha(image,OpaqueAlpha,exception); 1075 /* 1076 Read properties. 1077 */ 1078 while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse)) 1079 { 1080 PropType prop_type = (PropType) ReadBlobMSBLong(image); 1081 size_t prop_size = ReadBlobMSBLong(image); 1082 1083 switch (prop_type) 1084 { 1085 case PROP_END: 1086 foundPropEnd=1; 1087 break; 1088 case PROP_COLORMAP: 1089 { 1090 /* Cannot rely on prop_size here--the value is set incorrectly 1091 by some Gimp versions. 1092 */ 1093 size_t num_colours = ReadBlobMSBLong(image); 1094 if (DiscardBlobBytes(image,3*num_colours) == MagickFalse) 1095 ThrowFileException(exception,CorruptImageError, 1096 "UnexpectedEndOfFile",image->filename); 1097 /* 1098 if (info->file_version == 0) 1099 { 1100 gint i; 1101 1102 g_message (_("XCF warning: version 0 of XCF file format\n" 1103 "did not save indexed colormaps correctly.\n" 1104 "Substituting grayscale map.")); 1105 info->cp += 1106 xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); 1107 gimage->cmap = g_new (guchar, gimage->num_cols*3); 1108 xcf_seek_pos (info, info->cp + gimage->num_cols); 1109 for (i = 0; i<gimage->num_cols; i++) 1110 { 1111 gimage->cmap[i*3+0] = i; 1112 gimage->cmap[i*3+1] = i; 1113 gimage->cmap[i*3+2] = i; 1114 } 1115 } 1116 else 1117 { 1118 info->cp += 1119 xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); 1120 gimage->cmap = g_new (guchar, gimage->num_cols*3); 1121 info->cp += 1122 xcf_read_int8 (info->fp, 1123 (guint8*) gimage->cmap, gimage->num_cols*3); 1124 } 1125 */ 1126 break; 1127 } 1128 case PROP_COMPRESSION: 1129 { 1130 doc_info.compression = ReadBlobByte(image); 1131 if ((doc_info.compression != COMPRESS_NONE) && 1132 (doc_info.compression != COMPRESS_RLE) && 1133 (doc_info.compression != COMPRESS_ZLIB) && 1134 (doc_info.compression != COMPRESS_FRACTAL)) 1135 ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression"); 1136 } 1137 break; 1138 1139 case PROP_GUIDES: 1140 { 1141 /* just skip it - we don't care about guides */ 1142 if (DiscardBlobBytes(image,prop_size) == MagickFalse) 1143 ThrowFileException(exception,CorruptImageError, 1144 "UnexpectedEndOfFile",image->filename); 1145 } 1146 break; 1147 1148 case PROP_RESOLUTION: 1149 { 1150 /* float xres = (float) */ (void) ReadBlobMSBLong(image); 1151 /* float yres = (float) */ (void) ReadBlobMSBLong(image); 1152 1153 /* 1154 if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION || 1155 yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION) 1156 { 1157 g_message ("Warning, resolution out of range in XCF file"); 1158 xres = gimage->gimp->config->default_xresolution; 1159 yres = gimage->gimp->config->default_yresolution; 1160 } 1161 */ 1162 1163 1164 /* BOGUS: we don't write these yet because we aren't 1165 reading them properly yet :( 1166 image->resolution.x = xres; 1167 image->resolution.y = yres; 1168 */ 1169 } 1170 break; 1171 1172 case PROP_TATTOO: 1173 { 1174 /* we need to read it, even if we ignore it */ 1175 /*size_t tattoo_state = */ (void) ReadBlobMSBLong(image); 1176 } 1177 break; 1178 1179 case PROP_PARASITES: 1180 { 1181 /* BOGUS: we may need these for IPTC stuff */ 1182 if (DiscardBlobBytes(image,prop_size) == MagickFalse) 1183 ThrowFileException(exception,CorruptImageError, 1184 "UnexpectedEndOfFile",image->filename); 1185 /* 1186 gssize_t base = info->cp; 1187 GimpParasite *p; 1188 1189 while (info->cp - base < prop_size) 1190 { 1191 p = xcf_load_parasite (info); 1192 gimp_image_parasite_attach (gimage, p); 1193 gimp_parasite_free (p); 1194 } 1195 if (info->cp - base != prop_size) 1196 g_message ("Error detected while loading an image's parasites"); 1197 */ 1198 } 1199 break; 1200 1201 case PROP_UNIT: 1202 { 1203 /* BOGUS: ignore for now... */ 1204 /*size_t unit = */ (void) ReadBlobMSBLong(image); 1205 } 1206 break; 1207 1208 case PROP_PATHS: 1209 { 1210 /* BOGUS: just skip it for now */ 1211 if (DiscardBlobBytes(image,prop_size) == MagickFalse) 1212 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", 1213 image->filename); 1214 1215 /* 1216 PathList *paths = xcf_load_bzpaths (gimage, info); 1217 gimp_image_set_paths (gimage, paths); 1218 */ 1219 } 1220 break; 1221 1222 case PROP_USER_UNIT: 1223 { 1224 char unit_string[1000]; 1225 /*BOGUS: ignored for now */ 1226 /*float factor = (float) */ (void) ReadBlobMSBLong(image); 1227 /* size_t digits = */ (void) ReadBlobMSBLong(image); 1228 for (i=0; i<5; i++) 1229 (void) ReadBlobStringWithLongSize(image, unit_string, 1230 sizeof(unit_string),exception); 1231 } 1232 break; 1233 1234 default: 1235 { 1236 int buf[16]; 1237 ssize_t amount; 1238 1239 /* read over it... */ 1240 while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) 1241 { 1242 amount=(ssize_t) MagickMin(16, prop_size); 1243 amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf); 1244 if (!amount) 1245 ThrowReaderException(CorruptImageError,"CorruptImage"); 1246 prop_size -= (size_t) MagickMin(16,(size_t) amount); 1247 } 1248 } 1249 break; 1250 } 1251 } 1252 if (foundPropEnd == MagickFalse) 1253 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 1254 1255 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) 1256 { 1257 ; /* do nothing, were just pinging! */ 1258 } 1259 else 1260 { 1261 int 1262 current_layer = 0, 1263 foundAllLayers = MagickFalse, 1264 number_layers = 0; 1265 1266 MagickOffsetType 1267 oldPos=TellBlob(image); 1268 1269 XCFLayerInfo 1270 *layer_info; 1271 1272 /* 1273 the read pointer 1274 */ 1275 do 1276 { 1277 ssize_t offset = ReadBlobMSBSignedLong(image); 1278 if (offset == 0) 1279 foundAllLayers=MagickTrue; 1280 else 1281 number_layers++; 1282 if (EOFBlob(image) != MagickFalse) 1283 { 1284 ThrowFileException(exception,CorruptImageError, 1285 "UnexpectedEndOfFile",image->filename); 1286 break; 1287 } 1288 } while (foundAllLayers == MagickFalse); 1289 doc_info.number_layers=number_layers; 1290 offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */ 1291 if (offset < 0) 1292 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 1293 /* allocate our array of layer info blocks */ 1294 length=(size_t) number_layers; 1295 layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length, 1296 sizeof(*layer_info)); 1297 if (layer_info == (XCFLayerInfo *) NULL) 1298 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 1299 (void) ResetMagickMemory(layer_info,0,number_layers*sizeof(XCFLayerInfo)); 1300 for ( ; ; ) 1301 { 1302 MagickBooleanType 1303 layer_ok; 1304 1305 MagickOffsetType 1306 offset, 1307 saved_pos; 1308 1309 /* read in the offset of the next layer */ 1310 offset=(MagickOffsetType) ReadBlobMSBLong(image); 1311 /* if the offset is 0 then we are at the end 1312 * of the layer list. 1313 */ 1314 if (offset == 0) 1315 break; 1316 /* save the current position as it is where the 1317 * next layer offset is stored. 1318 */ 1319 saved_pos=TellBlob(image); 1320 /* seek to the layer offset */ 1321 offset=SeekBlob(image,offset,SEEK_SET); 1322 /* read in the layer */ 1323 layer_ok=ReadOneLayer(image_info,image,&doc_info, 1324 &layer_info[current_layer],current_layer,exception); 1325 if (layer_ok == MagickFalse) 1326 { 1327 int j; 1328 1329 for (j=0; j < current_layer; j++) 1330 layer_info[j].image=DestroyImage(layer_info[j].image); 1331 layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info); 1332 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 1333 } 1334 /* restore the saved position so we'll be ready to 1335 * read the next offset. 1336 */ 1337 offset=SeekBlob(image, saved_pos, SEEK_SET); 1338 current_layer++; 1339 } 1340#if 0 1341 { 1342 /* NOTE: XCF layers are REVERSED from composite order! */ 1343 signed int j; 1344 for (j=number_layers-1; j>=0; j--) { 1345 /* BOGUS: need to consider layer blending modes!! */ 1346 1347 if ( layer_info[j].visible ) { /* only visible ones, please! */ 1348 CompositeImage(image, OverCompositeOp, layer_info[j].image, 1349 layer_info[j].offset_x, layer_info[j].offset_y ); 1350 layer_info[j].image =DestroyImage( layer_info[j].image ); 1351 1352 /* If we do this, we'll get REAL gray images! */ 1353 if ( image_type == GIMP_GRAY ) { 1354 QuantizeInfo qi; 1355 GetQuantizeInfo(&qi); 1356 qi.colorspace = GRAYColorspace; 1357 QuantizeImage( &qi, layer_info[j].image ); 1358 } 1359 } 1360 } 1361 } 1362#else 1363 { 1364 /* NOTE: XCF layers are REVERSED from composite order! */ 1365 ssize_t j; 1366 1367 /* now reverse the order of the layers as they are put 1368 into subimages 1369 */ 1370 for (j=(ssize_t) number_layers-1; j >= 0; j--) 1371 AppendImageToList(&image,layer_info[j].image); 1372 } 1373#endif 1374 1375 layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info); 1376 1377#if 0 /* BOGUS: do we need the channels?? */ 1378 while (MagickTrue) 1379 { 1380 /* read in the offset of the next channel */ 1381 info->cp += xcf_read_int32 (info->fp, &offset, 1); 1382 1383 /* if the offset is 0 then we are at the end 1384 * of the channel list. 1385 */ 1386 if (offset == 0) 1387 break; 1388 1389 /* save the current position as it is where the 1390 * next channel offset is stored. 1391 */ 1392 saved_pos = info->cp; 1393 1394 /* seek to the channel offset */ 1395 xcf_seek_pos (info, offset); 1396 1397 /* read in the layer */ 1398 channel = xcf_load_channel (info, gimage); 1399 if (channel == 0) 1400 goto error; 1401 1402 num_successful_elements++; 1403 1404 /* add the channel to the image if its not the selection */ 1405 if (channel != gimage->selection_mask) 1406 gimp_image_add_channel (gimage, channel, -1); 1407 1408 /* restore the saved position so we'll be ready to 1409 * read the next offset. 1410 */ 1411 xcf_seek_pos (info, saved_pos); 1412 } 1413#endif 1414 } 1415 1416 (void) CloseBlob(image); 1417 DestroyImage(RemoveFirstImageFromList(&image)); 1418 if (image_type == GIMP_GRAY) 1419 image->type=GrayscaleType; 1420 return(GetFirstImageInList(image)); 1421} 1422 1423/* 1424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1425% % 1426% % 1427% % 1428% R e g i s t e r X C F I m a g e % 1429% % 1430% % 1431% % 1432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1433% 1434% RegisterXCFImage() adds attributes for the XCF image format to 1435% the list of supported formats. The attributes include the image format 1436% tag, a method to read and/or write the format, whether the format 1437% supports the saving of more than one frame to the same file or blob, 1438% whether the format supports native in-memory I/O, and a brief 1439% description of the format. 1440% 1441% The format of the RegisterXCFImage method is: 1442% 1443% size_t RegisterXCFImage(void) 1444% 1445*/ 1446ModuleExport size_t RegisterXCFImage(void) 1447{ 1448 MagickInfo 1449 *entry; 1450 1451 entry=AcquireMagickInfo("XCF","XCF","GIMP image"); 1452 entry->decoder=(DecodeImageHandler *) ReadXCFImage; 1453 entry->magick=(IsImageFormatHandler *) IsXCF; 1454 entry->flags|=CoderSeekableStreamFlag; 1455 (void) RegisterMagickInfo(entry); 1456 return(MagickImageCoderSignature); 1457} 1458 1459/* 1460%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1461% % 1462% % 1463% % 1464% U n r e g i s t e r X C F I m a g e % 1465% % 1466% % 1467% % 1468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1469% 1470% UnregisterXCFImage() removes format registrations made by the 1471% XCF module from the list of supported formats. 1472% 1473% The format of the UnregisterXCFImage method is: 1474% 1475% UnregisterXCFImage(void) 1476% 1477*/ 1478ModuleExport void UnregisterXCFImage(void) 1479{ 1480 (void) UnregisterMagickInfo("XCF"); 1481} 1482