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