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