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

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