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