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