source: ImageMagick/trunk/coders/xcf.c @ 7274

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