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