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