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