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