| /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                            X   X   CCCC  FFFFF                              % | 
 | %                             X X   C      F                                  % | 
 | %                              X    C      FFF                                % | 
 | %                             X X   C      F                                  % | 
 | %                            X   X   CCCC  F                                  % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                        Read GIMP XCF Image Format                           % | 
 | %                                                                             % | 
 | %                              Software Design                                % | 
 | %                              Leonard Rosenthol                              % | 
 | %                               November 2001                                 % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      % | 
 | %  dedicated to making software imaging solutions freely available.           % | 
 | %                                                                             % | 
 | %  You may not use this file except in compliance with the License.  You may  % | 
 | %  obtain a copy of the License at                                            % | 
 | %                                                                             % | 
 | %    http://www.imagemagick.org/script/license.php                            % | 
 | %                                                                             % | 
 | %  Unless required by applicable law or agreed to in writing, software        % | 
 | %  distributed under the License is distributed on an "AS IS" BASIS,          % | 
 | %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   % | 
 | %  See the License for the specific language governing permissions and        % | 
 | %  limitations under the License.                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | % | 
 | */ | 
 |  | 
 | /* | 
 |   Include declarations. | 
 | */ | 
 | #include "MagickCore/studio.h" | 
 | #include "MagickCore/blob.h" | 
 | #include "MagickCore/blob-private.h" | 
 | #include "MagickCore/cache.h" | 
 | #include "MagickCore/color.h" | 
 | #include "MagickCore/composite.h" | 
 | #include "MagickCore/exception.h" | 
 | #include "MagickCore/exception-private.h" | 
 | #include "MagickCore/image.h" | 
 | #include "MagickCore/image-private.h" | 
 | #include "MagickCore/list.h" | 
 | #include "MagickCore/magick.h" | 
 | #include "MagickCore/memory_.h" | 
 | #include "MagickCore/pixel.h" | 
 | #include "MagickCore/pixel-accessor.h" | 
 | #include "MagickCore/quantize.h" | 
 | #include "MagickCore/quantum-private.h" | 
 | #include "MagickCore/static.h" | 
 | #include "MagickCore/string_.h" | 
 | #include "MagickCore/module.h" | 
 |  | 
 | /* | 
 |   Typedef declarations. | 
 | */ | 
 | typedef enum | 
 | { | 
 |   GIMP_RGB, | 
 |   GIMP_GRAY, | 
 |   GIMP_INDEXED | 
 | } GimpImageBaseType; | 
 |  | 
 | typedef enum | 
 | { | 
 |   PROP_END                   =  0, | 
 |   PROP_COLORMAP              =  1, | 
 |   PROP_ACTIVE_LAYER          =  2, | 
 |   PROP_ACTIVE_CHANNEL        =  3, | 
 |   PROP_SELECTION             =  4, | 
 |   PROP_FLOATING_SELECTION    =  5, | 
 |   PROP_OPACITY               =  6, | 
 |   PROP_MODE                  =  7, | 
 |   PROP_VISIBLE               =  8, | 
 |   PROP_LINKED                =  9, | 
 |   PROP_PRESERVE_TRANSPARENCY = 10, | 
 |   PROP_APPLY_MASK            = 11, | 
 |   PROP_EDIT_MASK             = 12, | 
 |   PROP_SHOW_MASK             = 13, | 
 |   PROP_SHOW_MASKED           = 14, | 
 |   PROP_OFFSETS               = 15, | 
 |   PROP_COLOR                 = 16, | 
 |   PROP_COMPRESSION           = 17, | 
 |   PROP_GUIDES                = 18, | 
 |   PROP_RESOLUTION            = 19, | 
 |   PROP_TATTOO                = 20, | 
 |   PROP_PARASITES             = 21, | 
 |   PROP_UNIT                  = 22, | 
 |   PROP_PATHS                 = 23, | 
 |   PROP_USER_UNIT             = 24 | 
 | } PropType; | 
 |  | 
 | typedef enum | 
 | { | 
 |   COMPRESS_NONE              =  0, | 
 |   COMPRESS_RLE               =  1, | 
 |   COMPRESS_ZLIB              =  2,  /* unused */ | 
 |   COMPRESS_FRACTAL           =  3   /* unused */ | 
 | } XcfCompressionType; | 
 |  | 
 | typedef struct | 
 | { | 
 |   size_t | 
 |     width, | 
 |     height, | 
 |     image_type, | 
 |     bytes_per_pixel; | 
 |  | 
 |   int | 
 |     compression; | 
 |  | 
 |   size_t | 
 |     file_size; | 
 |  | 
 |   ExceptionInfo | 
 |     *exception; | 
 | } XCFDocInfo; | 
 |  | 
 | typedef struct | 
 | { | 
 |   char | 
 |     name[1024]; | 
 |  | 
 |   unsigned int | 
 |     active; | 
 |  | 
 |   size_t | 
 |     width, | 
 |     height, | 
 |     type, | 
 |     alpha, | 
 |     visible, | 
 |     linked, | 
 |     preserve_trans, | 
 |     apply_mask, | 
 |     show_mask, | 
 |     edit_mask, | 
 |     floating_offset; | 
 |  | 
 |   ssize_t | 
 |     offset_x, | 
 |     offset_y; | 
 |  | 
 |   size_t | 
 |     mode, | 
 |     tattoo; | 
 |  | 
 |   Image | 
 |     *image; | 
 | } XCFLayerInfo; | 
 |  | 
 | #define TILE_WIDTH   64 | 
 | #define TILE_HEIGHT  64 | 
 |  | 
 | typedef struct | 
 | { | 
 |   unsigned char | 
 |     red, | 
 |     green, | 
 |     blue, | 
 |     alpha; | 
 | } XCFPixelPacket; | 
 |  | 
 | /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %   I s X C F                                                                 % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | %  IsXCF() returns MagickTrue if the image format type, identified by the | 
 | %  magick string, is XCF (GIMP native format). | 
 | % | 
 | %  The format of the IsXCF method is: | 
 | % | 
 | %      MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) | 
 | % | 
 | %  A description of each parameter follows: | 
 | % | 
 | %    o magick: compare image format pattern against these bytes. | 
 | % | 
 | %    o length: Specifies the length of the magick string. | 
 | % | 
 | % | 
 | */ | 
 | static MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) | 
 | { | 
 |   if (length < 8) | 
 |     return(MagickFalse); | 
 |   if (LocaleNCompare((char *) magick,"gimp xcf",8) == 0) | 
 |     return(MagickTrue); | 
 |   return(MagickFalse); | 
 | } | 
 |  | 
 | typedef enum | 
 | { | 
 |   GIMP_NORMAL_MODE, | 
 |   GIMP_DISSOLVE_MODE, | 
 |   GIMP_BEHIND_MODE, | 
 |   GIMP_MULTIPLY_MODE, | 
 |   GIMP_SCREEN_MODE, | 
 |   GIMP_OVERLAY_MODE, | 
 |   GIMP_DIFFERENCE_MODE, | 
 |   GIMP_ADDITION_MODE, | 
 |   GIMP_SUBTRACT_MODE, | 
 |   GIMP_DARKEN_ONLY_MODE, | 
 |   GIMP_LIGHTEN_ONLY_MODE, | 
 |   GIMP_HUE_MODE, | 
 |   GIMP_SATURATION_MODE, | 
 |   GIMP_COLOR_MODE, | 
 |   GIMP_VALUE_MODE, | 
 |   GIMP_DIVIDE_MODE, | 
 |   GIMP_DODGE_MODE, | 
 |   GIMP_BURN_MODE, | 
 |   GIMP_HARDLIGHT_MODE | 
 | } GimpLayerModeEffects; | 
 |  | 
 | /* | 
 |   Simple utility routine to convert between PSD blending modes and | 
 |   ImageMagick compositing operators | 
 | */ | 
 | static CompositeOperator GIMPBlendModeToCompositeOperator( | 
 |   size_t blendMode) | 
 | { | 
 |   switch ( blendMode ) | 
 |   { | 
 |     case GIMP_NORMAL_MODE:    return( OverCompositeOp ); | 
 |     case GIMP_DISSOLVE_MODE:  return( DissolveCompositeOp ); | 
 |     case GIMP_MULTIPLY_MODE:  return( MultiplyCompositeOp ); | 
 |     case GIMP_SCREEN_MODE:    return( ScreenCompositeOp ); | 
 |     case GIMP_OVERLAY_MODE:    return( OverlayCompositeOp ); | 
 |     case GIMP_DIFFERENCE_MODE:  return( DifferenceCompositeOp ); | 
 |     case GIMP_ADDITION_MODE:  return( ModulusAddCompositeOp ); | 
 |     case GIMP_SUBTRACT_MODE:  return( ModulusSubtractCompositeOp ); | 
 |     case GIMP_DARKEN_ONLY_MODE:  return( DarkenCompositeOp ); | 
 |     case GIMP_LIGHTEN_ONLY_MODE:return( LightenCompositeOp ); | 
 |     case GIMP_HUE_MODE:      return( HueCompositeOp ); | 
 |     case GIMP_SATURATION_MODE:  return( SaturateCompositeOp ); | 
 |     case GIMP_COLOR_MODE:    return( ColorizeCompositeOp ); | 
 |     case GIMP_DODGE_MODE:    return( ColorDodgeCompositeOp ); | 
 |     case GIMP_BURN_MODE:    return( ColorBurnCompositeOp ); | 
 |     case GIMP_HARDLIGHT_MODE:  return( HardLightCompositeOp ); | 
 |     case GIMP_DIVIDE_MODE:    return( DivideDstCompositeOp ); | 
 |     /* these are the ones we don't support...yet */ | 
 |     case GIMP_BEHIND_MODE:    return( OverCompositeOp ); | 
 |     case GIMP_VALUE_MODE:    return( OverCompositeOp ); | 
 |     default: | 
 |       return(OverCompositeOp); | 
 |   } | 
 | } | 
 |  | 
 | /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | +   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                       % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | %  ReadBlobStringWithLongSize reads characters from a blob or file | 
 | %  starting with a ssize_t length byte and then characters to that length | 
 | % | 
 | %  The format of the ReadBlobStringWithLongSize method is: | 
 | % | 
 | %      char *ReadBlobStringWithLongSize(Image *image,char *string) | 
 | % | 
 | %  A description of each parameter follows: | 
 | % | 
 | %    o image: the image. | 
 | % | 
 | %    o string: the address of a character buffer. | 
 | % | 
 | */ | 
 |  | 
 | static inline size_t MagickMin(const size_t x,const size_t y) | 
 | { | 
 |   if (x < y) | 
 |     return(x); | 
 |   return(y); | 
 | } | 
 |  | 
 | static char *ReadBlobStringWithLongSize(Image *image,char *string,size_t max) | 
 | { | 
 |   int | 
 |     c; | 
 |  | 
 |   MagickOffsetType | 
 |     offset; | 
 |  | 
 |   register ssize_t | 
 |     i; | 
 |  | 
 |   size_t | 
 |     length; | 
 |  | 
 |   assert(image != (Image *) NULL); | 
 |   assert(image->signature == MagickSignature); | 
 |   assert(max != 0); | 
 |   if (image->debug != MagickFalse) | 
 |     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); | 
 |   length=ReadBlobMSBLong(image); | 
 |   for (i=0; i < (ssize_t) MagickMin(length,max-1); i++) | 
 |   { | 
 |     c=ReadBlobByte(image); | 
 |     if (c == EOF) | 
 |       return((char *) NULL); | 
 |     string[i]=(char) c; | 
 |   } | 
 |   string[i]='\0'; | 
 |   offset=SeekBlob(image,(MagickOffsetType) (length-i),SEEK_CUR); | 
 |   if (offset < 0) | 
 |     (void) ThrowMagickException(&image->exception,GetMagickModule(), | 
 |       CorruptImageError,"ImproperImageHeader","`%s'",image->filename); | 
 |   return(string); | 
 | } | 
 |  | 
 | static MagickBooleanType load_tile(Image *image,Image *tile_image, | 
 |   XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, | 
 |   ExceptionInfo *exception) | 
 | { | 
 |   ssize_t | 
 |     y; | 
 |  | 
 |   register ssize_t | 
 |     x; | 
 |  | 
 |   register Quantum | 
 |     *q; | 
 |  | 
 |   ssize_t | 
 |     count; | 
 |  | 
 |   unsigned char | 
 |     *graydata; | 
 |  | 
 |   XCFPixelPacket | 
 |     *xcfdata, | 
 |     *xcfodata; | 
 |  | 
 |   xcfdata=(XCFPixelPacket *) AcquireQuantumMemory(data_length,sizeof(*xcfdata)); | 
 |   if (xcfdata == (XCFPixelPacket *) NULL) | 
 |     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", | 
 |       image->filename); | 
 |   xcfodata=xcfdata; | 
 |   graydata=(unsigned char *) xcfdata;  /* used by gray and indexed */ | 
 |   count=ReadBlob(image,data_length,(unsigned char *) xcfdata); | 
 |   if (count != (ssize_t) data_length) | 
 |     ThrowBinaryException(CorruptImageError,"NotEnoughPixelData", | 
 |       image->filename); | 
 |   for (y=0; y < (ssize_t) tile_image->rows; y++) | 
 |   { | 
 |     q=QueueAuthenticPixels(tile_image,0,y,tile_image->columns,1,exception); | 
 |     if (q == (Quantum *) NULL) | 
 |       break; | 
 |     if (inDocInfo->image_type == GIMP_GRAY) | 
 |       { | 
 |         for (x=0; x < (ssize_t) tile_image->columns; x++) | 
 |         { | 
 |           SetPixelRed(tile_image,ScaleCharToQuantum(*graydata),q); | 
 |           SetPixelGreen(tile_image,GetPixelRed(image,q),q); | 
 |           SetPixelBlue(tile_image,GetPixelRed(image,q),q); | 
 |           SetPixelAlpha(tile_image,ScaleCharToQuantum((unsigned char) | 
 |             inLayerInfo->alpha),q); | 
 |           graydata++; | 
 |           q+=GetPixelChannels(tile_image); | 
 |         } | 
 |       } | 
 |     else | 
 |       if (inDocInfo->image_type == GIMP_RGB) | 
 |         { | 
 |           for (x=0; x < (ssize_t) tile_image->columns; x++) | 
 |           { | 
 |             SetPixelRed(tile_image,ScaleCharToQuantum(xcfdata->red),q); | 
 |             SetPixelGreen(tile_image,ScaleCharToQuantum(xcfdata->green),q); | 
 |             SetPixelBlue(tile_image,ScaleCharToQuantum(xcfdata->blue),q); | 
 |             SetPixelAlpha(tile_image,xcfdata->alpha == 0U ? OpaqueAlpha : | 
 |               ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q); | 
 |             xcfdata++; | 
 |             q+=GetPixelChannels(tile_image); | 
 |           } | 
 |         } | 
 |      if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) | 
 |        break; | 
 |   } | 
 |   xcfodata=(XCFPixelPacket *) RelinquishMagickMemory(xcfodata); | 
 |   return MagickTrue; | 
 | } | 
 |  | 
 | static MagickBooleanType load_tile_rle(Image *image,Image *tile_image, | 
 |   XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, | 
 |   ExceptionInfo *exception) | 
 | { | 
 |   MagickOffsetType | 
 |     size; | 
 |  | 
 |   register Quantum | 
 |     *q; | 
 |  | 
 |   size_t | 
 |     length; | 
 |  | 
 |   ssize_t | 
 |     bytes_per_pixel, | 
 |     count, | 
 |     i, | 
 |     j; | 
 |  | 
 |   unsigned char | 
 |     data, | 
 |     pixel, | 
 |     *xcfdata, | 
 |     *xcfodata, | 
 |     *xcfdatalimit; | 
 |  | 
 |   bytes_per_pixel=(ssize_t) inDocInfo->bytes_per_pixel; | 
 |   xcfdata=(unsigned char *) AcquireQuantumMemory(data_length,sizeof(*xcfdata)); | 
 |   if (xcfdata == (unsigned char *) NULL) | 
 |     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", | 
 |       image->filename); | 
 |   xcfodata=xcfdata; | 
 |   count=ReadBlob(image, (size_t) data_length, xcfdata); | 
 |   xcfdatalimit = xcfodata+count-1; | 
 |   exception=(&image->exception); | 
 |   for (i=0; i < (ssize_t) bytes_per_pixel; i++) | 
 |   { | 
 |     q=GetAuthenticPixels(tile_image,0,0,tile_image->columns,tile_image->rows, | 
 |       exception); | 
 |     size=(MagickOffsetType) tile_image->rows*tile_image->columns; | 
 |     while (size > 0) | 
 |     { | 
 |       if (xcfdata > xcfdatalimit) | 
 |         goto bogus_rle; | 
 |       pixel=(*xcfdata++); | 
 |       length=(size_t) pixel; | 
 |       if (length >= 128) | 
 |         { | 
 |           length=255-(length-1); | 
 |           if (length == 128) | 
 |             { | 
 |               if (xcfdata >= xcfdatalimit) | 
 |                 goto bogus_rle; | 
 |               length=(size_t) ((*xcfdata << 8) + xcfdata[1]); | 
 |               xcfdata+=2; | 
 |             } | 
 |             size-=length; | 
 |             if (size < 0) | 
 |               goto bogus_rle; | 
 |             if (&xcfdata[length-1] > xcfdatalimit) | 
 |               goto bogus_rle; | 
 |             while (length-- > 0) | 
 |             { | 
 |               data=(*xcfdata++); | 
 |               switch (i) | 
 |               { | 
 |                 case 0: | 
 |                 { | 
 |                   SetPixelRed(tile_image,ScaleCharToQuantum(data),q); | 
 |                   if (inDocInfo->image_type == GIMP_GRAY) | 
 |                     { | 
 |                       SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); | 
 |                       SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); | 
 |                       SetPixelAlpha(tile_image,ScaleCharToQuantum( | 
 |                         (unsigned char) inLayerInfo->alpha),q); | 
 |                     } | 
 |                   else | 
 |                     { | 
 |                       SetPixelGreen(tile_image,GetPixelRed(image,q),q); | 
 |                       SetPixelBlue(tile_image,GetPixelRed(image,q),q); | 
 |                       SetPixelAlpha(tile_image,ScaleCharToQuantum( | 
 |                         (unsigned char) inLayerInfo->alpha),q); | 
 |                     } | 
 |                   break; | 
 |                 } | 
 |                 case 1: | 
 |                 { | 
 |                   SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); | 
 |                   break; | 
 |                 } | 
 |                 case 2: | 
 |                 { | 
 |                   SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); | 
 |                   break; | 
 |                 } | 
 |                 case 3: | 
 |                 { | 
 |                   SetPixelAlpha(tile_image,data == 0 ? OpaqueAlpha : | 
 |                     ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q); | 
 |                   break; | 
 |                 } | 
 |               } | 
 |               q+=GetPixelChannels(tile_image); | 
 |             } | 
 |           } | 
 |         else | 
 |           { | 
 |             length+=1; | 
 |             if (length == 128) | 
 |               { | 
 |                 if (xcfdata >= xcfdatalimit) | 
 |                   goto bogus_rle; | 
 |                 length=(size_t) ((*xcfdata << 8) + xcfdata[1]); | 
 |                 xcfdata+=2; | 
 |               } | 
 |             size-=length; | 
 |             if (size < 0) | 
 |               goto bogus_rle; | 
 |             if (xcfdata > xcfdatalimit) | 
 |               goto bogus_rle; | 
 |             pixel=(*xcfdata++); | 
 |             for (j= 0; j < (ssize_t) length; j++) | 
 |             { | 
 |               data=pixel; | 
 |               switch (i) | 
 |               { | 
 |                 case 0: | 
 |                 { | 
 |                   SetPixelRed(tile_image,ScaleCharToQuantum(data),q); | 
 |                   if (inDocInfo->image_type == GIMP_GRAY) | 
 |                     { | 
 |                       SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); | 
 |                       SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); | 
 |                       SetPixelAlpha(tile_image,ScaleCharToQuantum( | 
 |                         (unsigned char) inLayerInfo->alpha),q); | 
 |                     } | 
 |                   else | 
 |                     { | 
 |                       SetPixelGreen(tile_image,GetPixelRed(image,q),q); | 
 |                       SetPixelBlue(tile_image,GetPixelRed(image,q),q); | 
 |                       SetPixelAlpha(tile_image,ScaleCharToQuantum( | 
 |                         (unsigned char) inLayerInfo->alpha),q); | 
 |                     } | 
 |                   break; | 
 |                 } | 
 |                 case 1: | 
 |                 { | 
 |                   SetPixelGreen(tile_image,ScaleCharToQuantum(data),q); | 
 |                   break; | 
 |                 } | 
 |                 case 2: | 
 |                 { | 
 |                   SetPixelBlue(tile_image,ScaleCharToQuantum(data),q); | 
 |                   break; | 
 |                 } | 
 |                 case 3: | 
 |                 { | 
 |                   SetPixelAlpha(tile_image,data == 0 ? OpaqueAlpha : | 
 |                     ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q); | 
 |                   break; | 
 |                 } | 
 |               } | 
 |               q+=GetPixelChannels(tile_image); | 
 |             } | 
 |           } | 
 |       } | 
 |       if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) | 
 |         break; | 
 |     } | 
 |   xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); | 
 |   return(MagickTrue); | 
 |  | 
 |   bogus_rle: | 
 |     if (xcfodata != (unsigned char *) NULL) | 
 |       xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); | 
 |   return(MagickFalse); | 
 | } | 
 |  | 
 | static MagickBooleanType load_level(Image *image,XCFDocInfo *inDocInfo, | 
 |   XCFLayerInfo *inLayerInfo) | 
 | { | 
 |   ExceptionInfo | 
 |     *exception; | 
 |  | 
 |   int | 
 |     destLeft = 0, | 
 |     destTop = 0; | 
 |  | 
 |   Image* | 
 |     tile_image; | 
 |  | 
 |   MagickBooleanType | 
 |     status; | 
 |  | 
 |   MagickOffsetType | 
 |     saved_pos, | 
 |     offset, | 
 |     offset2; | 
 |  | 
 |   register ssize_t | 
 |     i; | 
 |  | 
 |   size_t | 
 |     width, | 
 |     height, | 
 |     ntiles, | 
 |     ntile_rows, | 
 |     ntile_cols, | 
 |     tile_image_width, | 
 |     tile_image_height; | 
 |  | 
 |   /* start reading the data */ | 
 |   exception=inDocInfo->exception; | 
 |   width=ReadBlobMSBLong(image); | 
 |   height=ReadBlobMSBLong(image); | 
 |  | 
 |   /* read in the first tile offset. | 
 |    *  if it is '0', then this tile level is empty | 
 |    *  and we can simply return. | 
 |    */ | 
 |   offset=(MagickOffsetType) ReadBlobMSBLong(image); | 
 |   if (offset == 0) | 
 |     return(MagickTrue); | 
 |   /* Initialise the reference for the in-memory tile-compression | 
 |    */ | 
 |   ntile_rows=(height+TILE_HEIGHT-1)/TILE_HEIGHT; | 
 |   ntile_cols=(width+TILE_WIDTH-1)/TILE_WIDTH; | 
 |   ntiles=ntile_rows*ntile_cols; | 
 |   for (i = 0; i < (ssize_t) ntiles; i++) | 
 |   { | 
 |     status=MagickFalse; | 
 |     if (offset == 0) | 
 |       ThrowBinaryException(CorruptImageError,"NotEnoughTiles",image->filename); | 
 |     /* save the current position as it is where the | 
 |      *  next tile offset is stored. | 
 |      */ | 
 |     saved_pos=TellBlob(image); | 
 |     /* read in the offset of the next tile so we can calculate the amount | 
 |        of data needed for this tile*/ | 
 |     offset2=(MagickOffsetType)ReadBlobMSBLong(image); | 
 |     /* if the offset is 0 then we need to read in the maximum possible | 
 |        allowing for negative compression */ | 
 |     if (offset2 == 0) | 
 |       offset2=(MagickOffsetType) (offset + TILE_WIDTH * TILE_WIDTH * 4* 1.5); | 
 |     /* seek to the tile offset */ | 
 |     offset=SeekBlob(image, offset, SEEK_SET); | 
 |  | 
 |       /* allocate the image for the tile | 
 |         NOTE: the last tile in a row or column may not be a full tile! | 
 |       */ | 
 |       tile_image_width=(size_t) (destLeft == (int) ntile_cols-1 ? | 
 |         (int) width % TILE_WIDTH : TILE_WIDTH); | 
 |       if (tile_image_width == 0) tile_image_width=TILE_WIDTH; | 
 |       tile_image_height = (size_t) (destTop == (int) ntile_rows-1 ? | 
 |         (int) height % TILE_HEIGHT : TILE_HEIGHT); | 
 |       if (tile_image_height == 0) tile_image_height=TILE_HEIGHT; | 
 |       tile_image=CloneImage(inLayerInfo->image,tile_image_width, | 
 |         tile_image_height,MagickTrue,exception); | 
 |  | 
 |       /* read in the tile */ | 
 |       switch (inDocInfo->compression) | 
 |       { | 
 |         case COMPRESS_NONE: | 
 |           if (load_tile(image,tile_image,inDocInfo,inLayerInfo,(size_t) (offset2-offset),exception) == 0) | 
 |             status=MagickTrue; | 
 |           break; | 
 |         case COMPRESS_RLE: | 
 |           if (load_tile_rle (image,tile_image,inDocInfo,inLayerInfo, | 
 |               (int) (offset2-offset),exception) == 0) | 
 |             status=MagickTrue; | 
 |           break; | 
 |         case COMPRESS_ZLIB: | 
 |           ThrowBinaryException(CoderError,"ZipCompressNotSupported", | 
 |             image->filename) | 
 |         case COMPRESS_FRACTAL: | 
 |           ThrowBinaryException(CoderError,"FractalCompressNotSupported", | 
 |             image->filename) | 
 |       } | 
 |  | 
 |       /* composite the tile onto the layer's image, and then destroy it */ | 
 |       (void) CompositeImage(inLayerInfo->image,CopyCompositeOp,tile_image, | 
 |         destLeft * TILE_WIDTH,destTop*TILE_HEIGHT); | 
 |       tile_image=DestroyImage(tile_image); | 
 |  | 
 |       /* adjust tile position */ | 
 |       destLeft++; | 
 |       if (destLeft >= (int) ntile_cols) | 
 |         { | 
 |           destLeft = 0; | 
 |           destTop++; | 
 |         } | 
 |       if (status != MagickFalse) | 
 |         return(MagickFalse); | 
 |       /* restore the saved position so we'll be ready to | 
 |        *  read the next offset. | 
 |        */ | 
 |       offset=SeekBlob(image, saved_pos, SEEK_SET); | 
 |       /* read in the offset of the next tile */ | 
 |       offset=(MagickOffsetType) ReadBlobMSBLong(image); | 
 |     } | 
 |   if (offset != 0) | 
 |     ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename) | 
 |   return(MagickTrue); | 
 | } | 
 |  | 
 | static MagickBooleanType load_hierarchy(Image *image,XCFDocInfo *inDocInfo, | 
 |    XCFLayerInfo *inLayer) | 
 | { | 
 |   MagickOffsetType | 
 |     saved_pos, | 
 |     offset, | 
 |     junk; | 
 |  | 
 |   size_t | 
 |     width, | 
 |     height, | 
 |     bytes_per_pixel; | 
 |  | 
 |   width=ReadBlobMSBLong(image); | 
 |   (void) width; | 
 |   height=ReadBlobMSBLong(image); | 
 |   (void) height; | 
 |   bytes_per_pixel=inDocInfo->bytes_per_pixel=ReadBlobMSBLong(image); | 
 |   (void) bytes_per_pixel; | 
 |  | 
 |   /* load in the levels...we make sure that the number of levels | 
 |    *  calculated when the TileManager was created is the same | 
 |    *  as the number of levels found in the file. | 
 |    */ | 
 |   offset=(MagickOffsetType) ReadBlobMSBLong(image);  /* top level */ | 
 |  | 
 |   /* discard offsets for layers below first, if any. | 
 |    */ | 
 |   do | 
 |   { | 
 |     junk=(MagickOffsetType) ReadBlobMSBLong(image); | 
 |   } | 
 |   while (junk != 0); | 
 |  | 
 |   /* save the current position as it is where the | 
 |    *  next level offset is stored. | 
 |    */ | 
 |   saved_pos=TellBlob(image); | 
 |  | 
 |   /* seek to the level offset */ | 
 |   offset=SeekBlob(image, offset, SEEK_SET); | 
 |  | 
 |   /* read in the level */ | 
 |   if (load_level (image, inDocInfo, inLayer) == 0) | 
 |     return(MagickFalse); | 
 |   /* restore the saved position so we'll be ready to | 
 |    *  read the next offset. | 
 |    */ | 
 |   offset=SeekBlob(image, saved_pos, SEEK_SET); | 
 |   return(MagickTrue); | 
 | } | 
 |  | 
 | static MagickBooleanType ReadOneLayer(Image* image,XCFDocInfo* inDocInfo, | 
 |   XCFLayerInfo *outLayer, ExceptionInfo *exception ) | 
 | { | 
 |   MagickOffsetType | 
 |     offset; | 
 |  | 
 |   unsigned int | 
 |     foundPropEnd = 0; | 
 |  | 
 |   size_t | 
 |     hierarchy_offset, | 
 |     layer_mask_offset; | 
 |  | 
 |   /* clear the block! */ | 
 |   (void) ResetMagickMemory( outLayer, 0, sizeof( XCFLayerInfo ) ); | 
 |   /* read in the layer width, height, type and name */ | 
 |   outLayer->width = ReadBlobMSBLong(image); | 
 |   outLayer->height = ReadBlobMSBLong(image); | 
 |   outLayer->type = ReadBlobMSBLong(image); | 
 |   (void) ReadBlobStringWithLongSize(image, outLayer->name, | 
 |     sizeof(outLayer->name)); | 
 |   /* allocate the image for this layer */ | 
 |   outLayer->image=CloneImage(image,outLayer->width, outLayer->height,MagickTrue, | 
 |      &image->exception); | 
 |   if (outLayer->image == (Image *) NULL) | 
 |     return MagickFalse; | 
 |   /* read the layer properties! */ | 
 |   foundPropEnd = 0; | 
 |   while ( (foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse) ) { | 
 |   PropType    prop_type = (PropType) ReadBlobMSBLong(image); | 
 |   size_t  prop_size = ReadBlobMSBLong(image); | 
 |     switch (prop_type) | 
 |     { | 
 |     case PROP_END: | 
 |       foundPropEnd = 1; | 
 |       break; | 
 |     case PROP_ACTIVE_LAYER: | 
 |       outLayer->active = 1; | 
 |       break; | 
 |     case PROP_FLOATING_SELECTION: | 
 |       outLayer->floating_offset = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_OPACITY: | 
 |       outLayer->alpha = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_VISIBLE: | 
 |       outLayer->visible = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_LINKED: | 
 |       outLayer->linked = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_PRESERVE_TRANSPARENCY: | 
 |       outLayer->preserve_trans = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_APPLY_MASK: | 
 |       outLayer->apply_mask = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_EDIT_MASK: | 
 |       outLayer->edit_mask = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_SHOW_MASK: | 
 |       outLayer->show_mask = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_OFFSETS: | 
 |       outLayer->offset_x = (int) ReadBlobMSBLong(image); | 
 |       outLayer->offset_y = (int) ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_MODE: | 
 |       outLayer->mode = ReadBlobMSBLong(image); | 
 |       break; | 
 |     case PROP_TATTOO: | 
 |       outLayer->preserve_trans = ReadBlobMSBLong(image); | 
 |       break; | 
 |      case PROP_PARASITES: | 
 |      { | 
 |        if (DiscardBlobBytes(image,prop_size) == MagickFalse) | 
 |          ThrowFileException(&image->exception,CorruptImageError, | 
 |            "UnexpectedEndOfFile",image->filename); | 
 |  | 
 |         /* | 
 |        ssize_t base = info->cp; | 
 |        GimpParasite *p; | 
 |        while (info->cp - base < prop_size) | 
 |        { | 
 |        p = xcf_load_parasite(info); | 
 |        gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p); | 
 |        gimp_parasite_free(p); | 
 |        } | 
 |        if (info->cp - base != prop_size) | 
 |        g_message ("Error detected while loading a layer's parasites"); | 
 |        */ | 
 |      } | 
 |      break; | 
 |     default: | 
 |       /* g_message ("unexpected/unknown layer property: %d (skipping)", | 
 |          prop_type); */ | 
 |  | 
 |       { | 
 |       int buf[16]; | 
 |       ssize_t amount; | 
 |  | 
 |       /* read over it... */ | 
 |       while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) | 
 |         { | 
 |         amount = (ssize_t) MagickMin(16, prop_size); | 
 |         amount = ReadBlob(image, (size_t) amount, (unsigned char *) &buf); | 
 |         if (!amount) | 
 |           ThrowBinaryException(CorruptImageError,"CorruptImage", | 
 |             image->filename); | 
 |         prop_size -= (size_t) MagickMin(16, (size_t) amount); | 
 |         } | 
 |       } | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (foundPropEnd == MagickFalse) | 
 |     return(MagickFalse); | 
 |   /* clear the image based on the layer opacity */ | 
 |   outLayer->image->background_color.alpha= | 
 |     ScaleCharToQuantum((unsigned char) (255-outLayer->alpha));     | 
 |   (void) SetImageBackgroundColor(outLayer->image); | 
 |  | 
 |   /* set the compositing mode */ | 
 |   outLayer->image->compose = GIMPBlendModeToCompositeOperator( outLayer->mode ); | 
 |   if ( outLayer->visible == MagickFalse ) | 
 |     { | 
 |       /* BOGUS: should really be separate member var! */ | 
 |       outLayer->image->compose = NoCompositeOp; | 
 |     } | 
 |  | 
 |   /* read the hierarchy and layer mask offsets */ | 
 |   hierarchy_offset = ReadBlobMSBLong(image); | 
 |   layer_mask_offset = ReadBlobMSBLong(image); | 
 |  | 
 |   /* read in the hierarchy */ | 
 |   offset=SeekBlob(image, (MagickOffsetType) hierarchy_offset, SEEK_SET); | 
 |   if (offset < 0) | 
 |     (void) ThrowMagickException(&image->exception,GetMagickModule(), | 
 |       CorruptImageError,"InvalidImageHeader","`%s'",image->filename); | 
 |   if (load_hierarchy (image, inDocInfo, outLayer) == 0) | 
 |     return(MagickFalse); | 
 |  | 
 |   /* read in the layer mask */ | 
 |   if (layer_mask_offset != 0) | 
 |     { | 
 |       offset=SeekBlob(image, (MagickOffsetType) layer_mask_offset, SEEK_SET); | 
 |  | 
 | #if 0  /* BOGUS: support layer masks! */ | 
 |       layer_mask = xcf_load_layer_mask (info, gimage); | 
 |       if (layer_mask == 0) | 
 |   goto error; | 
 |  | 
 |       /* set the offsets of the layer_mask */ | 
 |       GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x; | 
 |       GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y; | 
 |  | 
 |       gimp_layer_add_mask (layer, layer_mask, MagickFalse); | 
 |  | 
 |       layer->mask->apply_mask = apply_mask; | 
 |       layer->mask->edit_mask  = edit_mask; | 
 |       layer->mask->show_mask  = show_mask; | 
 | #endif | 
 |   } | 
 |  | 
 |   /* attach the floating selection... */ | 
 | #if 0  /* BOGUS: we may need to read this, even if we don't support it! */ | 
 |   if (add_floating_sel) | 
 |     { | 
 |       GimpLayer *floating_sel; | 
 |  | 
 |       floating_sel = info->floating_sel; | 
 |       floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer)); | 
 |     } | 
 | #endif | 
 |  | 
 |   return MagickTrue; | 
 | } | 
 |  | 
 | /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %   R e a d X C F I m a g e                                                   % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | %  ReadXCFImage() reads a GIMP (GNU Image Manipulation Program) image | 
 | %  file and returns it.  It allocates the memory necessary for the new Image | 
 | %  structure and returns a pointer to the new image. | 
 | % | 
 | %  The format of the ReadXCFImage method is: | 
 | % | 
 | %      image=ReadXCFImage(image_info) | 
 | % | 
 | %  A description of each parameter follows: | 
 | % | 
 | %    o image_info: the image info. | 
 | % | 
 | %    o exception: return any errors or warnings in this structure. | 
 | % | 
 | % | 
 | */ | 
 | static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception) | 
 | { | 
 |   char | 
 |     magick[14]; | 
 |  | 
 |   Image | 
 |     *image; | 
 |  | 
 |   int | 
 |     foundPropEnd = 0; | 
 |  | 
 |   MagickBooleanType | 
 |     status; | 
 |  | 
 |   MagickOffsetType | 
 |     offset; | 
 |  | 
 |   register ssize_t | 
 |     i; | 
 |  | 
 |   size_t | 
 |     image_type, | 
 |     length; | 
 |  | 
 |   ssize_t | 
 |     count; | 
 |  | 
 |   XCFDocInfo | 
 |     doc_info; | 
 |  | 
 |   /* | 
 |     Open image file. | 
 |   */ | 
 |   assert(image_info != (const ImageInfo *) NULL); | 
 |   assert(image_info->signature == MagickSignature); | 
 |   if (image_info->debug != MagickFalse) | 
 |     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", | 
 |       image_info->filename); | 
 |   assert(exception != (ExceptionInfo *) NULL); | 
 |   assert(exception->signature == MagickSignature); | 
 |   image=AcquireImage(image_info,exception); | 
 |   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); | 
 |   if (status == MagickFalse) | 
 |     { | 
 |       image=DestroyImageList(image); | 
 |       return((Image *) NULL); | 
 |     } | 
 |   count=ReadBlob(image,14,(unsigned char *) magick); | 
 |   if ((count == 0) || | 
 |       (LocaleNCompare((char *) magick,"gimp xcf",8) != 0)) | 
 |     ThrowReaderException(CorruptImageError,"ImproperImageHeader"); | 
 |   (void) ResetMagickMemory(&doc_info,0,sizeof(XCFDocInfo)); | 
 |   doc_info.exception=exception; | 
 |   doc_info.width=ReadBlobMSBLong(image); | 
 |   doc_info.height=ReadBlobMSBLong(image); | 
 |   if ((doc_info.width > 262144) || (doc_info.height > 262144)) | 
 |     ThrowReaderException(CorruptImageError,"ImproperImageHeader"); | 
 |   doc_info.image_type=ReadBlobMSBLong(image); | 
 |   /* | 
 |     Initialize image attributes. | 
 |   */ | 
 |   image->columns=doc_info.width; | 
 |   image->rows=doc_info.height; | 
 |   image_type=doc_info.image_type; | 
 |   doc_info.file_size=GetBlobSize(image); | 
 |   image->compression=NoCompression; | 
 |   image->depth=8; | 
 |   if (image_type == GIMP_RGB) | 
 |     image->colorspace=RGBColorspace; | 
 |   else | 
 |     if (image_type == GIMP_GRAY) | 
 |       image->colorspace=GRAYColorspace; | 
 |     else | 
 |       if (image_type == GIMP_INDEXED) | 
 |         ThrowReaderException(CoderError,"ColormapTypeNotSupported"); | 
 |   (void) SetImageBackgroundColor(image); | 
 |   image->matte=MagickTrue; | 
 |   /* | 
 |     Read properties. | 
 |   */ | 
 |   while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse)) | 
 |   { | 
 |     PropType prop_type = (PropType) ReadBlobMSBLong(image); | 
 |     size_t prop_size = ReadBlobMSBLong(image); | 
 |  | 
 |     switch (prop_type) | 
 |     { | 
 |       case PROP_END: | 
 |         foundPropEnd=1; | 
 |         break; | 
 |       case PROP_COLORMAP: | 
 |       { | 
 |         /* Cannot rely on prop_size here--the value is set incorrectly | 
 |            by some Gimp versions. | 
 |         */ | 
 |         size_t num_colours = ReadBlobMSBLong(image); | 
 |         if (DiscardBlobBytes(image,3*num_colours) == MagickFalse) | 
 |           ThrowFileException(&image->exception,CorruptImageError, | 
 |             "UnexpectedEndOfFile",image->filename); | 
 |     /* | 
 |       if (info->file_version == 0) | 
 |       { | 
 |         gint i; | 
 |  | 
 |         g_message (_("XCF warning: version 0 of XCF file format\n" | 
 |            "did not save indexed colormaps correctly.\n" | 
 |            "Substituting grayscale map.")); | 
 |         info->cp += | 
 |           xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); | 
 |         gimage->cmap = g_new (guchar, gimage->num_cols*3); | 
 |         xcf_seek_pos (info, info->cp + gimage->num_cols); | 
 |         for (i = 0; i<gimage->num_cols; i++) | 
 |           { | 
 |             gimage->cmap[i*3+0] = i; | 
 |             gimage->cmap[i*3+1] = i; | 
 |             gimage->cmap[i*3+2] = i; | 
 |           } | 
 |       } | 
 |       else | 
 |       { | 
 |         info->cp += | 
 |           xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); | 
 |         gimage->cmap = g_new (guchar, gimage->num_cols*3); | 
 |         info->cp += | 
 |           xcf_read_int8 (info->fp, | 
 |                    (guint8*) gimage->cmap, gimage->num_cols*3); | 
 |       } | 
 |      */ | 
 |         break; | 
 |       } | 
 |       case PROP_COMPRESSION: | 
 |       { | 
 |         doc_info.compression = ReadBlobByte(image); | 
 |         if ((doc_info.compression != COMPRESS_NONE) && | 
 |             (doc_info.compression != COMPRESS_RLE) && | 
 |             (doc_info.compression != COMPRESS_ZLIB) && | 
 |             (doc_info.compression != COMPRESS_FRACTAL)) | 
 |           ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression"); | 
 |       } | 
 |       break; | 
 |  | 
 |       case PROP_GUIDES: | 
 |       { | 
 |          /* just skip it - we don't care about guides */ | 
 |         if (DiscardBlobBytes(image,prop_size) == MagickFalse) | 
 |           ThrowFileException(&image->exception,CorruptImageError, | 
 |             "UnexpectedEndOfFile",image->filename); | 
 |       } | 
 |       break; | 
 |  | 
 |     case PROP_RESOLUTION: | 
 |       { | 
 |         /* float xres = (float) */ (void) ReadBlobMSBLong(image); | 
 |         /* float yres = (float) */ (void) ReadBlobMSBLong(image); | 
 |  | 
 |         /* | 
 |         if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION || | 
 |             yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION) | 
 |         { | 
 |         g_message ("Warning, resolution out of range in XCF file"); | 
 |         xres = gimage->gimp->config->default_xresolution; | 
 |         yres = gimage->gimp->config->default_yresolution; | 
 |         } | 
 |         */ | 
 |  | 
 |  | 
 |         /* BOGUS: we don't write these yet because we aren't | 
 |               reading them properly yet :( | 
 |               image->x_resolution = xres; | 
 |               image->y_resolution = yres; | 
 |         */ | 
 |       } | 
 |       break; | 
 |  | 
 |     case PROP_TATTOO: | 
 |       { | 
 |         /* we need to read it, even if we ignore it */ | 
 |         /*size_t  tattoo_state = */ (void) ReadBlobMSBLong(image); | 
 |       } | 
 |       break; | 
 |  | 
 |     case PROP_PARASITES: | 
 |       { | 
 |         /* BOGUS: we may need these for IPTC stuff */ | 
 |         if (DiscardBlobBytes(image,prop_size) == MagickFalse) | 
 |           ThrowFileException(&image->exception,CorruptImageError, | 
 |             "UnexpectedEndOfFile",image->filename); | 
 |         /* | 
 |       gssize_t         base = info->cp; | 
 |       GimpParasite *p; | 
 |  | 
 |       while (info->cp - base < prop_size) | 
 |         { | 
 |           p = xcf_load_parasite (info); | 
 |           gimp_image_parasite_attach (gimage, p); | 
 |           gimp_parasite_free (p); | 
 |         } | 
 |       if (info->cp - base != prop_size) | 
 |         g_message ("Error detected while loading an image's parasites"); | 
 |       */ | 
 |           } | 
 |       break; | 
 |  | 
 |     case PROP_UNIT: | 
 |       { | 
 |         /* BOGUS: ignore for now... */ | 
 |       /*size_t unit =  */ (void) ReadBlobMSBLong(image); | 
 |       } | 
 |       break; | 
 |  | 
 |     case PROP_PATHS: | 
 |       { | 
 |       /* BOGUS: just skip it for now */ | 
 |         if (DiscardBlobBytes(image,prop_size) == MagickFalse) | 
 |           ThrowFileException(&image->exception,CorruptImageError, | 
 |             "UnexpectedEndOfFile",image->filename); | 
 |  | 
 |         /* | 
 |       PathList *paths = xcf_load_bzpaths (gimage, info); | 
 |       gimp_image_set_paths (gimage, paths); | 
 |       */ | 
 |       } | 
 |       break; | 
 |  | 
 |     case PROP_USER_UNIT: | 
 |       { | 
 |         char  unit_string[1000]; | 
 |         /*BOGUS: ignored for now */ | 
 |         /*float  factor = (float) */ (void) ReadBlobMSBLong(image); | 
 |         /* size_t digits =  */ (void) ReadBlobMSBLong(image); | 
 |         for (i=0; i<5; i++) | 
 |          (void) ReadBlobStringWithLongSize(image, unit_string, | 
 |            sizeof(unit_string)); | 
 |       } | 
 |      break; | 
 |  | 
 |       default: | 
 |       { | 
 |         int buf[16]; | 
 |         ssize_t amount; | 
 |  | 
 |       /* read over it... */ | 
 |       while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) | 
 |       { | 
 |         amount=(ssize_t) MagickMin(16, prop_size); | 
 |         amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf); | 
 |         if (!amount) | 
 |           ThrowReaderException(CorruptImageError,"CorruptImage"); | 
 |         prop_size -= (size_t) MagickMin(16,(size_t) amount); | 
 |       } | 
 |     } | 
 |     break; | 
 |   } | 
 |   } | 
 |   if (foundPropEnd == MagickFalse) | 
 |     ThrowReaderException(CorruptImageError,"ImproperImageHeader"); | 
 |  | 
 |   if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) | 
 |     { | 
 |       ; /* do nothing, were just pinging! */ | 
 |     } | 
 |   else | 
 |     { | 
 |       int | 
 |         current_layer = 0, | 
 |         foundAllLayers = MagickFalse, | 
 |         number_layers = 0; | 
 |  | 
 |       MagickOffsetType | 
 |         oldPos=TellBlob(image); | 
 |  | 
 |       XCFLayerInfo | 
 |         *layer_info; | 
 |  | 
 |       /*  | 
 |         the read pointer | 
 |       */ | 
 |       do | 
 |       { | 
 |         ssize_t offset = (int) ReadBlobMSBLong(image); | 
 |         if (offset == 0) | 
 |           foundAllLayers=MagickTrue; | 
 |         else | 
 |           number_layers++; | 
 |         if (EOFBlob(image) != MagickFalse) | 
 |           { | 
 |             ThrowFileException(exception,CorruptImageError, | 
 |               "UnexpectedEndOfFile",image->filename); | 
 |             break; | 
 |           } | 
 |     } while (foundAllLayers == MagickFalse); | 
 |     offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */ | 
 |     if (offset < 0) | 
 |       ThrowReaderException(CorruptImageError,"ImproperImageHeader"); | 
 |     /* allocate our array of layer info blocks */ | 
 |     length=(size_t) number_layers; | 
 |     layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length, | 
 |       sizeof(*layer_info)); | 
 |     if (layer_info == (XCFLayerInfo *) NULL) | 
 |       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); | 
 |     (void) ResetMagickMemory(layer_info,0,number_layers*sizeof(XCFLayerInfo)); | 
 |     for ( ; ; ) | 
 |     { | 
 |       MagickBooleanType | 
 |         layer_ok; | 
 |  | 
 |       MagickOffsetType | 
 |         offset, | 
 |         saved_pos; | 
 |  | 
 |       /* read in the offset of the next layer */ | 
 |       offset=(MagickOffsetType) ReadBlobMSBLong(image); | 
 |       /* if the offset is 0 then we are at the end | 
 |       *  of the layer list. | 
 |       */ | 
 |       if (offset == 0) | 
 |         break; | 
 |       /* save the current position as it is where the | 
 |       *  next layer offset is stored. | 
 |       */ | 
 |       saved_pos=TellBlob(image); | 
 |       /* seek to the layer offset */ | 
 |       offset=SeekBlob(image,offset,SEEK_SET); | 
 |       /* read in the layer */ | 
 |       layer_ok=ReadOneLayer(image,&doc_info,&layer_info[current_layer], | 
 |         exception); | 
 |       if (layer_ok == MagickFalse) | 
 |         { | 
 |           int j; | 
 |  | 
 |           for (j=0; j < current_layer; j++) | 
 |             layer_info[j].image=DestroyImage(layer_info[j].image); | 
 |         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); | 
 |       } | 
 |       /* restore the saved position so we'll be ready to | 
 |       *  read the next offset. | 
 |       */ | 
 |       offset=SeekBlob(image, saved_pos, SEEK_SET); | 
 |       current_layer++; | 
 |     } | 
 |     if (number_layers == 1) | 
 |       { | 
 |         /* | 
 |           Composite the layer data onto the main image, dispose the layer. | 
 |         */ | 
 |         (void) CompositeImage(image,OverCompositeOp,layer_info[0].image, | 
 |           layer_info[0].offset_x,layer_info[0].offset_y); | 
 |         layer_info[0].image =DestroyImage( layer_info[0].image); | 
 |       } | 
 |     else | 
 |       { | 
 | #if 0 | 
 |         { | 
 |         /* NOTE: XCF layers are REVERSED from composite order! */ | 
 |         signed int  j; | 
 |         for (j=number_layers-1; j>=0; j--) { | 
 |           /* BOGUS: need to consider layer blending modes!! */ | 
 |  | 
 |           if ( layer_info[j].visible ) { /* only visible ones, please! */ | 
 |             CompositeImage(image, OverCompositeOp, layer_info[j].image, | 
 |                      layer_info[j].offset_x, layer_info[j].offset_y ); | 
 |              layer_info[j].image =DestroyImage( layer_info[j].image ); | 
 |  | 
 |             /* Bob says that if we do this, we'll get REAL gray images! */ | 
 |             if ( image_type == GIMP_GRAY ) { | 
 |               QuantizeInfo  qi; | 
 |               GetQuantizeInfo(&qi); | 
 |               qi.colorspace = GRAYColorspace; | 
 |               QuantizeImage( &qi, layer_info[j].image ); | 
 |             } | 
 |           } | 
 |         } | 
 |       } | 
 | #else | 
 |       { | 
 |         /* NOTE: XCF layers are REVERSED from composite order! */ | 
 |         signed int  j; | 
 |  | 
 |         /* first we copy the last layer on top of the main image */ | 
 |         (void) CompositeImage(image,CopyCompositeOp, | 
 |           layer_info[number_layers-1].image, | 
 |           layer_info[number_layers-1].offset_x, | 
 |           layer_info[number_layers-1].offset_y); | 
 |           layer_info[number_layers-1].image=DestroyImage( | 
 |             layer_info[number_layers-1].image); | 
 |  | 
 |         /* now reverse the order of the layers as they are put | 
 |            into subimages | 
 |         */ | 
 |         j=number_layers-2; | 
 |         image->next=layer_info[j].image; | 
 |         layer_info[j].image->previous=image; | 
 |         layer_info[j].image->page.x=layer_info[j].offset_x; | 
 |         layer_info[j].image->page.y=layer_info[j].offset_y; | 
 |         layer_info[j].image->page.width=layer_info[j].width; | 
 |         layer_info[j].image->page.height=layer_info[j].height; | 
 |         for (j=number_layers-3; j>=0; j--) | 
 |         { | 
 |           if (j > 0) | 
 |             layer_info[j].image->next=layer_info[j-1].image; | 
 |           if (j < (number_layers-1)) | 
 |             layer_info[j].image->previous=layer_info[j+1].image; | 
 |           layer_info[j].image->page.x=layer_info[j].offset_x; | 
 |           layer_info[j].image->page.y=layer_info[j].offset_y; | 
 |           layer_info[j].image->page.width=layer_info[j].width; | 
 |           layer_info[j].image->page.height=layer_info[j].height; | 
 |         } | 
 |       } | 
 | #endif | 
 |     } | 
 |  | 
 |     layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info); | 
 |  | 
 | #if 0  /* BOGUS: do we need the channels?? */ | 
 |     while (MagickTrue) | 
 |     { | 
 |       /* read in the offset of the next channel */ | 
 |       info->cp += xcf_read_int32 (info->fp, &offset, 1); | 
 |  | 
 |       /* if the offset is 0 then we are at the end | 
 |       *  of the channel list. | 
 |       */ | 
 |       if (offset == 0) | 
 |         break; | 
 |  | 
 |       /* save the current position as it is where the | 
 |       *  next channel offset is stored. | 
 |       */ | 
 |       saved_pos = info->cp; | 
 |  | 
 |       /* seek to the channel offset */ | 
 |       xcf_seek_pos (info, offset); | 
 |  | 
 |       /* read in the layer */ | 
 |       channel = xcf_load_channel (info, gimage); | 
 |       if (channel == 0) | 
 |         goto error; | 
 |  | 
 |       num_successful_elements++; | 
 |  | 
 |       /* add the channel to the image if its not the selection */ | 
 |       if (channel != gimage->selection_mask) | 
 |         gimp_image_add_channel (gimage, channel, -1); | 
 |  | 
 |       /* restore the saved position so we'll be ready to | 
 |       *  read the next offset. | 
 |       */ | 
 |       xcf_seek_pos (info, saved_pos); | 
 |     } | 
 | #endif | 
 |   } | 
 |  | 
 |   (void) CloseBlob(image); | 
 |   if (image_type == GIMP_GRAY) | 
 |     image->type=GrayscaleType; | 
 |   return(GetFirstImageInList(image)); | 
 | } | 
 |  | 
 | /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %   R e g i s t e r X C F I m a g e                                           % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | %  RegisterXCFImage() adds attributes for the XCF image format to | 
 | %  the list of supported formats.  The attributes include the image format | 
 | %  tag, a method to read and/or write the format, whether the format | 
 | %  supports the saving of more than one frame to the same file or blob, | 
 | %  whether the format supports native in-memory I/O, and a brief | 
 | %  description of the format. | 
 | % | 
 | %  The format of the RegisterXCFImage method is: | 
 | % | 
 | %      size_t RegisterXCFImage(void) | 
 | % | 
 | */ | 
 | ModuleExport size_t RegisterXCFImage(void) | 
 | { | 
 |   MagickInfo | 
 |     *entry; | 
 |  | 
 |   entry=SetMagickInfo("XCF"); | 
 |   entry->decoder=(DecodeImageHandler *) ReadXCFImage; | 
 |   entry->magick=(IsImageFormatHandler *) IsXCF; | 
 |   entry->description=ConstantString("GIMP image"); | 
 |   entry->module=ConstantString("XCF"); | 
 |   entry->seekable_stream=MagickTrue; | 
 |   (void) RegisterMagickInfo(entry); | 
 |   return(MagickImageCoderSignature); | 
 | } | 
 |  | 
 | /* | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %   U n r e g i s t e r X C F I m a g e                                       % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %                                                                             % | 
 | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | 
 | % | 
 | %  UnregisterXCFImage() removes format registrations made by the | 
 | %  XCF module from the list of supported formats. | 
 | % | 
 | %  The format of the UnregisterXCFImage method is: | 
 | % | 
 | %      UnregisterXCFImage(void) | 
 | % | 
 | */ | 
 | ModuleExport void UnregisterXCFImage(void) | 
 | { | 
 |   (void) UnregisterMagickInfo("XCF"); | 
 | } |