Coverage Report

Created: 2026-09-14 07:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/coders/xcf.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2026 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
%
5
% This program is covered by multiple licenses, which are described in
6
% Copyright.txt. You should have received a copy of Copyright.txt with this
7
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
8
%
9
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
10
%                                                                             %
11
%                                                                             %
12
%                                                                             %
13
%                            X   X   CCCC  FFFFF                              %
14
%                             X X   C      F                                  %
15
%                              X    C      FFF                                %
16
%                             X X   C      F                                  %
17
%                            X   X   CCCC  F                                  %
18
%                                                                             %
19
%                                                                             %
20
%                       Read GIMP XCF Image Format.                           %
21
%                                                                             %
22
%                                                                             %
23
%                              Software Design                                %
24
%                              Leonard Rosenthol                              %
25
%                               November 2001                                 %
26
%                                                                             %
27
%                                                                             %
28
%                                                                             %
29
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
30
%
31
% https://testing.developer.gimp.org/core/standards/xcf
32
*/
33

34
/*
35
  Include declarations.
36
*/
37
#include "magick/studio.h"
38
#include "magick/blob.h"
39
#include "magick/pixel_cache.h"
40
#include "magick/composite.h"
41
#include "magick/log.h"
42
#include "magick/magick.h"
43
#include "magick/monitor.h"
44
#include "magick/quantize.h"
45
#include "magick/utility.h"
46
#include "magick/static.h"
47

48
/*
49
  Typedef declarations.
50
*/
51
typedef enum
52
{
53
  GIMP_RGB,
54
  GIMP_GRAY,
55
  GIMP_INDEXED
56
} GimpImageBaseType;
57
58
typedef enum
59
{
60
  PROP_END                   =  0,
61
  PROP_COLORMAP              =  1,
62
  PROP_ACTIVE_LAYER          =  2,
63
  PROP_ACTIVE_CHANNEL        =  3,
64
  PROP_SELECTION             =  4,
65
  PROP_FLOATING_SELECTION    =  5,
66
  PROP_OPACITY               =  6,
67
  PROP_MODE                  =  7,
68
  PROP_VISIBLE               =  8,
69
  PROP_LINKED                =  9,
70
  PROP_PRESERVE_TRANSPARENCY = 10,
71
  PROP_APPLY_MASK            = 11,
72
  PROP_EDIT_MASK             = 12,
73
  PROP_SHOW_MASK             = 13,
74
  PROP_SHOW_MASKED           = 14,
75
  PROP_OFFSETS               = 15,
76
  PROP_COLOR                 = 16,
77
  PROP_COMPRESSION           = 17,
78
  PROP_GUIDES                = 18,
79
  PROP_RESOLUTION            = 19,
80
  PROP_TATTOO                = 20,
81
  PROP_PARASITES             = 21,
82
  PROP_UNIT                  = 22,
83
  PROP_PATHS                 = 23,
84
  PROP_USER_UNIT             = 24
85
} PropType;
86
87
typedef enum
88
{
89
  COMPRESS_NONE              =  0,
90
  COMPRESS_RLE               =  1,
91
  COMPRESS_ZLIB              =  2,  /* unused */
92
  COMPRESS_FRACTAL           =  3   /* unused */
93
} XcfCompressionType;
94
95
typedef struct {
96
  magick_uint32_t
97
    width,
98
    height,
99
    image_type,
100
    bpp;  /* BYTES per pixel!! */
101
102
  int
103
    compression;
104
105
  /* not really part of the doc, but makes it easy to pass around! */
106
  ExceptionInfo
107
    *exception;
108
109
  /* File size */
110
  magick_off_t
111
    file_size;
112
} XCFDocInfo;
113
114
typedef struct {
115
  char
116
    name[1024];
117
118
  unsigned int
119
    active;
120
121
  magick_uint32_t
122
    width,
123
    height,
124
    type,
125
    opacity,
126
    visible,
127
    linked,
128
    preserve_trans,
129
    apply_mask,
130
    show_mask,
131
    edit_mask,
132
    floating_offset;
133
134
  magick_int32_t
135
    offset_x,
136
    offset_y;
137
138
  magick_uint32_t
139
    mode,
140
    tattoo;
141
142
  Image
143
    *image;
144
} XCFLayerInfo;
145
146
130k
#define TILE_WIDTH   64
147
130k
#define TILE_HEIGHT  64
148
149
typedef struct {
150
  unsigned char
151
    red,
152
    green,
153
    blue,
154
    opacity;  /* NOTE: reversed from IM! */
155
} XCFPixelPacket;
156

157
/*
158
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159
%                                                                             %
160
%                                                                             %
161
%                                                                             %
162
%   I s X C F                                                                 %
163
%                                                                             %
164
%                                                                             %
165
%                                                                             %
166
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167
%
168
%  Method IsXCF returns True if the image format type, identified by the
169
%  magick string, is XCF (GIMP native format).
170
%
171
%  The format of the IsXCF method is:
172
%
173
%      unsigned int IsXCF(const unsigned char *magick,const size_t length)
174
%
175
%  A description of each parameter follows:
176
%
177
%    o status:  Method IsXCF returns True if the image format type is XCF.
178
%
179
%    o magick: This string is generally the first few bytes of an image file
180
%      or blob.
181
%
182
%    o length: Specifies the length of the magick string.
183
%
184
%
185
*/
186
static unsigned int IsXCF(const unsigned char *magick,const size_t length)
187
0
{
188
0
  if (length < 8)
189
0
    return(False);
190
0
  if (LocaleNCompare((char *) magick,"gimp xcf",8) == 0)
191
0
    return(True);
192
0
  return(False);
193
0
}
194
195

196
typedef enum
197
{
198
  GIMP_NORMAL_MODE,
199
  GIMP_DISSOLVE_MODE,
200
  GIMP_BEHIND_MODE,
201
  GIMP_MULTIPLY_MODE,
202
  GIMP_SCREEN_MODE,
203
  GIMP_OVERLAY_MODE,
204
  GIMP_DIFFERENCE_MODE,
205
  GIMP_ADDITION_MODE,
206
  GIMP_SUBTRACT_MODE,
207
  GIMP_DARKEN_ONLY_MODE,
208
  GIMP_LIGHTEN_ONLY_MODE,
209
  GIMP_HUE_MODE,
210
  GIMP_SATURATION_MODE,
211
  GIMP_COLOR_MODE,
212
  GIMP_VALUE_MODE,
213
  GIMP_DIVIDE_MODE,
214
  GIMP_DODGE_MODE,
215
  GIMP_BURN_MODE,
216
  GIMP_HARDLIGHT_MODE
217
} GimpLayerModeEffects;
218
219
/*
220
  Simple utility routine to convert between PSD blending modes and
221
  GraphicsMagick compositing operators
222
*/
223
static CompositeOperator GIMPBlendModeToCompositeOperator( unsigned int blendMode )
224
5.31k
{
225
5.31k
  switch ( blendMode )
226
5.31k
    {
227
5.26k
    case GIMP_NORMAL_MODE:      return( OverCompositeOp );
228
1
    case GIMP_DISSOLVE_MODE:    return( DissolveCompositeOp );
229
0
    case GIMP_MULTIPLY_MODE:    return( MultiplyCompositeOp );
230
1
    case GIMP_SCREEN_MODE:      return( ScreenCompositeOp );
231
1
    case GIMP_OVERLAY_MODE:     return( OverlayCompositeOp );
232
0
    case GIMP_DIFFERENCE_MODE:  return( DifferenceCompositeOp );
233
0
    case GIMP_ADDITION_MODE:    return( AddCompositeOp );
234
0
    case GIMP_SUBTRACT_MODE:    return( SubtractCompositeOp );
235
0
    case GIMP_DARKEN_ONLY_MODE: return( DarkenCompositeOp );
236
0
    case GIMP_LIGHTEN_ONLY_MODE:return( LightenCompositeOp );
237
1
    case GIMP_HUE_MODE:         return( HueCompositeOp );
238
0
    case GIMP_SATURATION_MODE:  return( SaturateCompositeOp );
239
1
    case GIMP_COLOR_MODE:       return( ColorizeCompositeOp );
240
0
    case GIMP_DIVIDE_MODE:      return( DivideCompositeOp );
241
1
    case GIMP_HARDLIGHT_MODE:   return( HardLightCompositeOp );
242
0
    case GIMP_DODGE_MODE:       return( ColorDodgeCompositeOp );
243
1
    case GIMP_BURN_MODE:        return( ColorBurnCompositeOp );
244
    /* these are the ones we don't support...yet */
245
0
    case GIMP_BEHIND_MODE:      return( OverCompositeOp );
246
1
    case GIMP_VALUE_MODE:       return( OverCompositeOp );
247
44
    default:                    return( OverCompositeOp );
248
5.31k
    }
249
5.31k
}
250
251

252
/*
253
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
254
%                                                                             %
255
%                                                                             %
256
%                                                                             %
257
+   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                       %
258
%                                                                             %
259
%                                                                             %
260
%                                                                             %
261
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
262
%
263
%  Method ReadBlobStringWithLongSize reads characters from a blob or file
264
%  starting with a long length byte and then characters to that length
265
%
266
%  The format of the ReadBlobStringWithLongSize method is:
267
%
268
%      char *ReadBlobStringWithLongSize(Image *image,char *string)
269
%
270
%  A description of each parameter follows:
271
%
272
%    o status:  Method ReadBlobString returns the string on success, otherwise,
273
%      a null is returned.
274
%
275
%    o image: The image.
276
%
277
%    o string: The address of a character buffer.
278
%
279
%    o max: Length of 'string' array.
280
%
281
%
282
*/
283
static char *ReadBlobStringWithLongSize(Image *image,char *string,size_t max)
284
14.5k
{
285
14.5k
  int
286
14.5k
    c;
287
288
14.5k
  register unsigned long
289
14.5k
    i;
290
291
14.5k
  unsigned long
292
14.5k
    length;
293
294
14.5k
  assert(image != (Image *) NULL);
295
14.5k
  assert(image->signature == MagickSignature);
296
14.5k
  assert(max != 0);
297
14.5k
  length = ReadBlobMSBLong(image);
298
191k
  for (i=0; i < Min(length,max-1); i++)
299
179k
    {
300
179k
      c=ReadBlobByte(image);
301
179k
      if (c == EOF)
302
2.71k
        return((char *) NULL);
303
176k
      string[i]=c;
304
176k
    }
305
11.8k
  string[i]='\0';
306
11.8k
  (void) SeekBlob(image, length-i, SEEK_CUR);
307
11.8k
  return(string);
308
14.5k
}
309
310

311
static MagickPassFail load_tile (Image* image, Image* tile_image, XCFDocInfo* inDocInfo,
312
                                 XCFLayerInfo*  inLayerInfo, size_t data_length)
313
30.7k
{
314
30.7k
  size_t
315
30.7k
    nmemb_read_successfully;
316
317
30.7k
  unsigned long
318
30.7k
    x,
319
30.7k
    y;
320
321
30.7k
  PixelPacket
322
30.7k
    *q;
323
324
30.7k
  XCFPixelPacket
325
30.7k
    *xcfdata,
326
30.7k
    *xcfodata;
327
328
30.7k
  unsigned char
329
30.7k
    *graydata;
330
331
  /*
332
    Validate that claimed data length is sufficient for tile.
333
  */
334
30.7k
  {
335
30.7k
    size_t
336
30.7k
      expected_data_length=0;
337
338
30.7k
    if (inDocInfo->image_type == GIMP_GRAY)
339
30.0k
      {
340
30.0k
        expected_data_length=MagickArraySize(tile_image->columns,tile_image->rows)*
341
30.0k
          sizeof(unsigned char);
342
30.0k
      }
343
692
    else if (inDocInfo->image_type == GIMP_RGB)
344
692
      {
345
692
        expected_data_length=MagickArraySize(tile_image->columns,tile_image->rows)*
346
692
          sizeof(XCFPixelPacket);
347
692
      }
348
30.7k
    if (expected_data_length && (expected_data_length > data_length))
349
42
      {
350
42
        ThrowException(&image->exception,CorruptImageError,CorruptImage,
351
42
                       "Claimed tile data length is insufficient for tile data");
352
42
        return MagickFail;
353
42
      }
354
30.7k
  }
355
356
30.7k
  xcfdata = xcfodata = MagickAllocateResourceLimitedMemory(XCFPixelPacket *,data_length);
357
30.7k
  graydata = (unsigned char *) xcfdata;  /* used by gray and indexed */
358
359
30.7k
  if (xcfdata == (XCFPixelPacket *) NULL)
360
0
    {
361
0
      ThrowException(&image->exception,ResourceLimitError,MemoryAllocationFailed,NULL);
362
0
      return MagickFail;
363
0
    }
364
365
30.7k
  nmemb_read_successfully = ReadBlob(image, data_length, xcfdata);
366
30.7k
  if (nmemb_read_successfully != data_length)
367
232
    {
368
232
      MagickFreeResourceLimitedMemory(XCFPixelPacket *,xcfodata);
369
232
      ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
370
0
    }
371
372
30.4k
  q=SetImagePixels(tile_image,0,0,tile_image->columns,tile_image->rows);
373
30.4k
  if (q == (PixelPacket *) NULL)
374
2
    {
375
2
      CopyException(&image->exception,&tile_image->exception);
376
2
      MagickFreeResourceLimitedMemory(XCFPixelPacket *,xcfodata);
377
2
      return MagickFail;
378
2
    }
379
380
266k
  for (x=0; x < tile_image->columns; x++)
381
235k
    {
382
235k
      if (inDocInfo->image_type == GIMP_GRAY)
383
231k
        {
384
5.22M
          for (y=tile_image->rows; y != 0; y--)
385
4.98M
            {
386
4.98M
              q->red =q->green=q->blue=ScaleCharToQuantum(*graydata);
387
4.98M
              q->opacity  = ScaleCharToQuantum(255U-inLayerInfo->opacity);
388
4.98M
              graydata++;
389
4.98M
              q++;
390
4.98M
            }
391
231k
        }
392
4.17k
      else if (inDocInfo->image_type == GIMP_RGB)
393
4.17k
        {
394
115k
          for (y=tile_image->rows; y != 0; y--)
395
111k
            {
396
111k
              q->red      = ScaleCharToQuantum(xcfdata->red);
397
111k
              q->green    = ScaleCharToQuantum(xcfdata->green);
398
111k
              q->blue     = ScaleCharToQuantum(xcfdata->blue);
399
111k
              q->opacity  = (Quantum) (xcfdata->opacity==0U ? TransparentOpacity :
400
111k
                                       ScaleCharToQuantum(255U-inLayerInfo->opacity));
401
111k
              xcfdata++;
402
111k
              q++;
403
111k
            }
404
4.17k
        }
405
235k
    }
406
407
30.4k
  MagickFreeResourceLimitedMemory(XCFPixelPacket *,xcfodata);
408
30.4k
  return MagickPass;
409
30.4k
}
410
411
static MagickPassFail load_tile_rle (Image* image,
412
                                     Image* tile_image,
413
                                     XCFDocInfo* inDocInfo,
414
                                     XCFLayerInfo*  inLayerInfo,
415
                                     size_t data_length)
416
33.2k
{
417
33.2k
  unsigned char
418
33.2k
    data,
419
33.2k
    val;
420
421
33.2k
  magick_int64_t
422
33.2k
    size;
423
424
33.2k
  size_t
425
33.2k
    nmemb_read_successfully;
426
427
33.2k
  int
428
    /* count, */
429
33.2k
    length,
430
33.2k
    bpp,    /* BYTES per pixel! */
431
33.2k
    i,
432
33.2k
    j;
433
434
33.2k
  unsigned char
435
33.2k
    *xcfdata,
436
33.2k
    *xcfodata,
437
33.2k
    *xcfdatalimit;
438
439
33.2k
  PixelPacket
440
33.2k
    *q;
441
442
33.2k
  bpp = (int) inDocInfo->bpp;
443
444
33.2k
  xcfdata = xcfodata = MagickAllocateResourceLimitedMemory(unsigned char *,data_length);
445
33.2k
  if (xcfdata == (unsigned char *) NULL)
446
0
    {
447
0
      ThrowException(&image->exception,ResourceLimitError,MemoryAllocationFailed,NULL);
448
0
      return MagickFail;
449
0
    }
450
451
33.2k
  nmemb_read_successfully = ReadBlob(image, data_length, xcfdata);
452
33.2k
  if (nmemb_read_successfully != data_length)
453
10
    {
454
10
      if (image->logging)
455
10
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
456
10
                              "Read %lu bytes, expected %lu bytes",
457
10
                              (unsigned long) nmemb_read_successfully,
458
10
                              (unsigned long) data_length);
459
10
      MagickFreeResourceLimitedMemory(unsigned char *,xcfodata);
460
10
      ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
461
0
    }
462
463
33.2k
  xcfdatalimit = &xcfodata[nmemb_read_successfully - 1];
464
465
34.3k
  for (i = 0; i < bpp; i++)
466
1.32k
    {
467
1.32k
      q=SetImagePixels(tile_image,0,0,tile_image->columns,tile_image->rows);
468
1.32k
      if (q == (PixelPacket *) NULL)
469
1
        {
470
1
          CopyException(&image->exception,&tile_image->exception);
471
1
          goto bogus_rle;
472
1
        }
473
1.32k
      size = MagickArraySize(tile_image->rows,tile_image->columns);
474
      /* count = 0; */
475
476
53.4k
      while (size > 0)
477
52.3k
        {
478
52.3k
          if (xcfdata > xcfdatalimit)
479
102
            {
480
102
              goto bogus_rle;
481
102
            }
482
483
52.2k
          val = *xcfdata++;
484
485
52.2k
          length = val;
486
487
52.2k
          if (length >= 128)
488
3.19k
            {
489
3.19k
              length = 255 - (length - 1);
490
491
3.19k
              if (length == 128)
492
452
                {
493
452
                  if (xcfdata >= xcfdatalimit)
494
1
                    {
495
1
                      goto bogus_rle;
496
1
                    }
497
498
451
                  length = ((*xcfdata << 8) + xcfdata[1]) & 0xFFFF;
499
451
                  xcfdata += 2;
500
451
                }
501
502
              /* count += length; */
503
3.19k
              size -= length;
504
505
3.19k
              if (size < 0)
506
20
                {
507
20
                  goto bogus_rle;
508
20
                }
509
510
3.17k
              if (&xcfdata[length-1] > xcfdatalimit)
511
16
                {
512
16
                  goto bogus_rle;
513
16
                }
514
515
78.3k
              while (length-- > 0)
516
75.1k
                {
517
75.1k
                  data = *xcfdata++;
518
75.1k
                  switch (i)
519
75.1k
                    {
520
52.8k
                    case 0:
521
52.8k
                      {
522
52.8k
                        q->red          = ScaleCharToQuantum(data);
523
52.8k
                        if ( inDocInfo->image_type == GIMP_GRAY )
524
34.5k
                          {
525
34.5k
                            q->green    = ScaleCharToQuantum(data);
526
34.5k
                            q->blue     = ScaleCharToQuantum(data);
527
34.5k
                            q->opacity  = ScaleCharToQuantum(255-inLayerInfo->opacity);
528
34.5k
                          }
529
18.2k
                        else
530
18.2k
                          {
531
18.2k
                            q->green    = q->red;
532
18.2k
                            q->blue     = q->red;
533
18.2k
                            q->opacity  = ScaleCharToQuantum(255-inLayerInfo->opacity);
534
18.2k
                          }
535
52.8k
                        break;
536
0
                      }
537
12.5k
                    case 1:
538
12.5k
                      {
539
12.5k
                        q->green = ScaleCharToQuantum(data);
540
12.5k
                        break;
541
0
                      }
542
2.72k
                    case 2:
543
2.72k
                      {
544
2.72k
                        q->blue  = ScaleCharToQuantum(data);
545
2.72k
                        break;
546
0
                      }
547
6.74k
                    case 3:
548
6.74k
                      {
549
6.74k
                        q->opacity = (Quantum) (data==0 ? TransparentOpacity :
550
6.74k
                                                ScaleCharToQuantum(255-inLayerInfo->opacity));
551
6.74k
                        break;
552
0
                      }
553
75.1k
                    }
554
75.1k
                  q++;
555
75.1k
                }
556
3.16k
            }
557
49.0k
          else
558
49.0k
            {
559
49.0k
              length += 1;
560
49.0k
              if (length == 128)
561
262
                {
562
262
                  if (xcfdata >= xcfdatalimit)
563
1
                    {
564
1
                      goto bogus_rle;
565
1
                    }
566
567
261
                  length = ((*xcfdata << 8) + xcfdata[1]) & 0xFFFF;
568
261
                  xcfdata += 2;
569
261
                }
570
571
              /* count += length; */
572
49.0k
              size -= length;
573
574
49.0k
              if (size < 0)
575
38
                {
576
38
                  goto bogus_rle;
577
38
                }
578
579
49.0k
              if (xcfdata > xcfdatalimit)
580
23
                {
581
23
                  goto bogus_rle;
582
23
                }
583
584
48.9k
              val = *xcfdata++;
585
586
473k
              for (j = 0; j < length; j++)
587
424k
                {
588
424k
                  data = val;
589
424k
                  switch (i)
590
424k
                    {
591
244k
                    case 0:
592
244k
                      {
593
244k
                        q->red = ScaleCharToQuantum(data);
594
244k
                        if ( inDocInfo->image_type == GIMP_GRAY )
595
112k
                          {
596
112k
                            q->green = ScaleCharToQuantum(data);
597
112k
                            q->blue = ScaleCharToQuantum(data);
598
112k
                            q->opacity = ScaleCharToQuantum(255-inLayerInfo->opacity);
599
112k
                          }
600
132k
                        else
601
132k
                          {
602
132k
                            q->green = q->red;
603
132k
                            q->blue = q->red;
604
132k
                            q->opacity = ScaleCharToQuantum(255-inLayerInfo->opacity);
605
132k
                          }
606
244k
                        break;
607
0
                      }
608
83.3k
                    case 1:
609
83.3k
                      {
610
83.3k
                        q->green = ScaleCharToQuantum(data);
611
83.3k
                        break;
612
0
                      }
613
63.9k
                    case 2:
614
63.9k
                      {
615
63.9k
                        q->blue = ScaleCharToQuantum(data);
616
63.9k
                        break;
617
0
                      }
618
27.0k
                    case 3:
619
27.0k
                      {
620
27.0k
                        q->opacity = (Quantum) (data==0 ? TransparentOpacity :
621
27.0k
                                                ScaleCharToQuantum(255-inLayerInfo->opacity));
622
27.0k
                        break;
623
0
                      }
624
424k
                    }
625
424k
                  q++;
626
424k
                }
627
48.9k
            }
628
52.2k
        }
629
1.12k
      if (SyncImagePixelsEx(tile_image,&tile_image->exception) == MagickFail)
630
0
        break;
631
1.12k
    }
632
33.0k
  MagickFreeResourceLimitedMemory(unsigned char *,xcfodata);
633
33.0k
  return MagickPass;
634
635
202
 bogus_rle:
636
202
  if (xcfodata)
637
202
    MagickFreeResourceLimitedMemory(unsigned char *,xcfodata);
638
639
202
  (void) LogMagickEvent(CoderEvent,GetMagickModule(), "Failed to RLE-decode tile");
640
202
  ThrowBinaryException(CorruptImageError,CorruptImage,image->filename);
641
0
  return MagickFail;
642
202
}
643
644
645
static MagickPassFail load_level (Image* image,
646
                                  XCFDocInfo* inDocInfo,
647
                                  XCFLayerInfo*
648
                                  inLayerInfo)
649
3.20k
{
650
3.20k
  magick_off_t
651
3.20k
    saved_pos,
652
3.20k
    offset,
653
3.20k
    offset2;
654
655
3.20k
  unsigned long
656
3.20k
    width,
657
3.20k
    height;
658
659
3.20k
  unsigned long
660
3.20k
    ntiles,
661
3.20k
    ntile_rows,
662
3.20k
    ntile_cols;
663
664
3.20k
  size_t
665
3.20k
    tile_data_size;
666
667
3.20k
  int
668
3.20k
    i;
669
670
3.20k
  Image*
671
3.20k
    tile_image;
672
673
3.20k
  int
674
3.20k
    destLeft = 0,
675
3.20k
    destTop = 0,
676
3.20k
    tile_image_width,
677
3.20k
    tile_image_height;
678
679
3.20k
  MagickPassFail
680
3.20k
    status = MagickPass;
681
682
3.20k
  ExceptionInfo
683
3.20k
    *exception = inDocInfo->exception;
684
685
  /* start reading the data */
686
3.20k
  width = ReadBlobMSBLong(image); /* width */
687
3.20k
  height = ReadBlobMSBLong(image); /* height */
688
689
  /* read in the first tile offset.
690
   *  if it is '0', then this tile level is empty
691
   *  and we can simply return.
692
   */
693
3.20k
  offset = ReadBlobMSBLong(image);
694
695
3.20k
  if (EOFBlob(image))
696
3.19k
    ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
697
698
3.19k
  if (offset == 0)
699
1.76k
    return MagickPass;
700
701
1.43k
  if (offset >= inDocInfo->file_size)
702
178
    {
703
178
      if (image->logging)
704
178
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
705
178
                              "Tile offset %ld (file size %lu)!",
706
178
                              (long) offset, (unsigned long) inDocInfo->file_size);
707
178
      ThrowBinaryException(CorruptImageError,CorruptImage,image->filename);
708
0
    }
709
710
1.25k
  if (image->logging)
711
1.25k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
712
1.25k
                          "load_level: dimensions %lux%lu, bpp %lu, offset %ld",
713
1.25k
                          width,height,(unsigned long) inDocInfo->bpp,
714
1.25k
                          (long) offset);
715
716
  /*
717
    Initialise the reference for the in-memory tile-compression
718
  */
719
1.25k
  ntile_rows=(height+TILE_HEIGHT-1)/TILE_HEIGHT;
720
1.25k
  ntile_cols=(width+TILE_WIDTH-1)/TILE_WIDTH;
721
1.25k
  ntiles=ntile_rows*ntile_cols;
722
723
1.25k
  if (image->logging)
724
1.25k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
725
1.25k
                          "Tile: dimensions %lux%lu, number of tiles %lu",
726
1.25k
                          ntile_cols,ntile_rows,ntiles);
727
728
64.7k
  for (i = 0; i < (long) ntiles; i++)
729
64.6k
    {
730
64.6k
      status = MagickPass;
731
732
64.6k
      if (offset == 0)
733
248
        {
734
248
          if (image->logging)
735
248
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
736
248
                                  "Tile: offset %ld!", (long) offset);
737
248
          ThrowBinaryException(CorruptImageError,NotEnoughTiles,image->filename);
738
0
        }
739
740
      /*
741
        save the current position as it is where the next tile offset
742
        is stored.
743
      */
744
64.3k
      saved_pos = TellBlob(image);
745
64.3k
      if (saved_pos < 0)
746
64.3k
        ThrowBinaryException(BlobError,UnableToObtainOffset,image->filename);
747
748
      /*
749
        read in the offset of the next tile so we can calculate the
750
        amount of data needed for this tile
751
      */
752
64.3k
      offset2 = ReadBlobMSBLong(image);
753
64.3k
      if (EOFBlob(image))
754
64.2k
        ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
755
64.2k
      if (offset2 >= inDocInfo->file_size)
756
232
        {
757
232
          if (image->logging)
758
232
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
759
232
                                  "Tile: offset %ld (file size %lu)!",
760
232
                                  (long) offset2, (unsigned long) inDocInfo->file_size);
761
232
          ThrowBinaryException(CorruptImageError,CorruptImage,image->filename);
762
0
        }
763
764
      /* verify that seek position is in file */
765
64.0k
      if ((magick_off_t) offset >= GetBlobSize(image))
766
0
        {
767
0
          if (image->logging)
768
0
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
769
0
                                  "Tile offset %" MAGICK_OFF_F "d is outside file bounds",
770
0
                                  (magick_off_t) offset);
771
0
          ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
772
0
        }
773
774
      /* seek to the tile offset */
775
64.0k
      if (SeekBlob(image, offset, SEEK_SET) != offset)
776
64.0k
        ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
777
778
      /* allocate the image for the tile
779
         NOTE: the last tile in a row or column may not be a full tile!
780
      */
781
64.0k
      tile_image_width=(size_t) (destLeft == (int) ntile_cols-1 ?
782
33.7k
        (int) width % TILE_WIDTH : TILE_WIDTH);
783
64.0k
      if (tile_image_width == 0) tile_image_width=TILE_WIDTH;
784
64.0k
      tile_image_height = (size_t) (destTop == (int) ntile_rows-1 ?
785
55.5k
        (int) height % TILE_HEIGHT : TILE_HEIGHT);
786
64.0k
      if (tile_image_height == 0) tile_image_height=TILE_HEIGHT;
787
64.0k
      tile_image=CloneImage(inLayerInfo->image, tile_image_width,
788
64.0k
                            tile_image_height,True,exception);
789
790
64.0k
      if (tile_image == (Image *) NULL)
791
0
        return MagickFail;
792
793
      /*
794
        Compute the tile data size.
795
      */
796
64.0k
      if (offset2 > offset)
797
2.50k
        {
798
          /*
799
            Tile data size is simply the difference in file offsets.
800
          */
801
2.50k
          tile_data_size=offset2-offset;
802
2.50k
          if (image->logging)
803
2.50k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
804
2.50k
                                  "Tile: start offset=%ld, end offset=%ld, data size=%lu",
805
2.50k
                                  (long) offset,(long) offset2,
806
2.50k
                                  (unsigned long) tile_data_size);
807
2.50k
        }
808
61.5k
      else
809
61.5k
        {
810
61.5k
          size_t
811
61.5k
            packet_size=4;
812
813
61.5k
          if (inDocInfo->image_type == GIMP_GRAY)
814
35.6k
            packet_size=1;
815
816
61.5k
          if (COMPRESS_NONE == inDocInfo->compression)
817
30.4k
            {
818
              /*
819
                If compression is not used then enforce expected tile size.
820
              */
821
30.4k
              tile_data_size = MagickArraySize(MagickArraySize(tile_image->columns,tile_image->rows),packet_size);
822
30.4k
            }
823
31.0k
          else
824
31.0k
            {
825
              /*
826
                Estimate the tile size.  First we estimate the tile
827
                size, allowing for a possible expansion factor of 1.5.
828
                Then we truncate to the file length, whichever is
829
                smallest.
830
              */
831
31.0k
              offset2 = (magick_off_t) ((double) offset + (double) tile_image->columns*tile_image->rows * packet_size * 1.5);
832
31.0k
              tile_data_size = (size_t) offset2-offset;
833
31.0k
              if (image->logging)
834
31.0k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
835
31.0k
                                      "We estimated tile data size: %lu",
836
31.0k
                                      (unsigned long) tile_data_size);
837
31.0k
              offset2 = Min(offset2,GetBlobSize(image));
838
31.0k
              tile_data_size = (size_t) offset2-offset;
839
31.0k
              if (image->logging)
840
31.0k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
841
31.0k
                                      "Final tile data size: %lu",
842
31.0k
                                      (unsigned long) tile_data_size);
843
31.0k
              if (offset2 <= offset)
844
0
                {
845
0
                  DestroyImage(tile_image);
846
0
                  ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
847
0
                }
848
31.0k
            }
849
61.5k
        }
850
851
      /* read in the tile */
852
64.0k
      switch (inDocInfo->compression)
853
64.0k
        {
854
30.7k
        case COMPRESS_NONE:
855
30.7k
          status=load_tile(image,tile_image,inDocInfo,inLayerInfo,
856
30.7k
                           tile_data_size);
857
30.7k
          break;
858
33.2k
        case COMPRESS_RLE:
859
33.2k
          status=load_tile_rle(image,tile_image,inDocInfo,inLayerInfo,
860
33.2k
                               tile_data_size);
861
33.2k
          break;
862
1
        case COMPRESS_ZLIB:
863
1
          DestroyImage(tile_image);
864
1
          ThrowBinaryException(CoderError,ZipCompressionNotSupported,
865
1
                               image->filename);
866
3
        case COMPRESS_FRACTAL:
867
3
          DestroyImage(tile_image);
868
3
          ThrowBinaryException(CoderError,FractalCompressionNotSupported,
869
3
                               image->filename);
870
64.0k
        }
871
872
64.0k
      if (MagickPass == status)
873
63.5k
        {
874
          /*
875
            Composite the tile onto the layer's image, and then
876
            destroy it.  We temporarily disable the progress monitor
877
            so that the user does not see composition of individual
878
            tiles.
879
          */
880
#if 0
881
          const PixelPacket
882
            *p;
883
884
          PixelPacket
885
            *q;
886
887
          long
888
            canvas_x,
889
            canvas_y,
890
            y;
891
892
          unsigned long
893
            tile_width;
894
895
          canvas_x=destLeft*TILE_WIDTH;
896
          tile_width=tile_image->columns;
897
          for (y=0; y < (long) tile_image->columns; y++)
898
            {
899
              canvas_y=destTop*TILE_HEIGHT+y;
900
              p=AcquireImagePixels(tile_image,0,y,tile_image->columns,1,
901
                                   &inLayerInfo->image->exception);
902
              q=GetImagePixels(inLayerInfo->image,canvas_x,canvas_y,
903
                               tile_image->columns,1);
904
              if ((p != (const PixelPacket *) NULL) && (q != (PixelPacket *) NULL))
905
                (void) memcpy(q,p,tile_image->columns*sizeof(PixelPacket));
906
              else
907
                printf("null pointer canvas: %lux%lu tile: %lux%lu+%ld+%ld !\n",
908
                       inLayerInfo->image->columns,inLayerInfo->image->rows,
909
                       tile_width,1LU,canvas_x,canvas_y);
910
            }
911
#else
912
63.5k
          MonitorHandler
913
63.5k
            handler;
914
915
63.5k
          long
916
63.5k
            canvas_x,
917
63.5k
            canvas_y;
918
919
63.5k
          canvas_x=destLeft*TILE_WIDTH;
920
63.5k
          canvas_y=destTop*TILE_HEIGHT;
921
63.5k
          handler=SetMonitorHandler((MonitorHandler) NULL);
922
63.5k
          (void) CompositeImageRegion(CopyCompositeOp,NULL,tile_image->columns,
923
63.5k
                                      tile_image->rows,tile_image,0,0,
924
63.5k
                                      inLayerInfo->image,canvas_x,
925
63.5k
                                      canvas_y,&inLayerInfo->image->exception);
926
63.5k
          (void) SetMonitorHandler(handler);
927
63.5k
#endif
928
63.5k
        }
929
64.0k
      DestroyImage(tile_image);
930
64.0k
#if !defined(__COVERITY__) /* 384797 Unused value */
931
64.0k
      tile_image = (Image *) NULL;
932
64.0k
#endif /* if !defined(__COVERITY__) */
933
934
      /* adjust tile position */
935
64.0k
      destLeft++;
936
64.0k
      if (destLeft >= (int) ntile_cols)
937
33.7k
        {
938
33.7k
          destLeft = 0;
939
33.7k
          destTop++;
940
33.7k
        }
941
942
64.0k
      if (MagickPass != status)
943
488
        return status;
944
945
      /* restore the saved position so we'll be ready to
946
       *  read the next offset.
947
       */
948
63.5k
      (void) SeekBlob(image, saved_pos, SEEK_SET);
949
950
      /* read in the offset of the next tile */
951
63.5k
      offset = ReadBlobMSBLong(image);
952
63.5k
      if (EOFBlob(image))
953
63.5k
        ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
954
63.5k
      if (offset != 0)
955
63.1k
        if (!MagickMonitorFormatted(offset,inDocInfo->file_size,
956
63.1k
                                    &image->exception,LoadImageText,
957
63.1k
                                    image->filename,
958
63.1k
                                    image->columns,image->rows))
959
0
          break;
960
63.5k
    }
961
962
133
  if (offset != 0)
963
52
    {
964
52
      ThrowBinaryException(CorruptImageError,CorruptImage,image->filename);
965
0
    }
966
81
  else
967
81
    {
968
81
      (void) MagickMonitorFormatted(inDocInfo->file_size,
969
81
                                    inDocInfo->file_size+1,&image->exception,
970
81
                                    LoadImageText,image->filename,
971
81
                                    image->columns,image->rows);
972
81
    }
973
974
81
  return MagickPass;
975
133
}
976
977
static MagickPassFail load_hierarchy (Image *image, XCFDocInfo* inDocInfo, XCFLayerInfo*
978
                                      inLayer)
979
4.41k
{
980
4.41k
  unsigned long
981
4.41k
    width,
982
4.41k
    height;
983
984
4.41k
  magick_off_t
985
4.41k
    saved_pos,
986
4.41k
    offset;
987
988
4.41k
  unsigned long
989
4.41k
    junk;
990
991
4.41k
  width=ReadBlobMSBLong(image); /* width */
992
4.41k
  height=ReadBlobMSBLong(image); /* height */
993
4.41k
  inDocInfo->bpp = ReadBlobMSBLong(image); /* bpp */
994
995
  /* load in the levels...we make sure that the number of levels
996
   *  calculated when the TileManager was created is the same
997
   *  as the number of levels found in the file.
998
   */
999
4.41k
  offset = ReadBlobMSBLong(image);  /* top level */
1000
1001
4.41k
  if (EOFBlob(image))
1002
4.40k
    ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
1003
1004
4.40k
  if (image->logging)
1005
4.40k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1006
4.40k
                          "load_hierarchy: dimensions %lux%lu, bpp=%lu,"
1007
4.40k
                          " offset=%" MAGICK_OFF_F "d",
1008
4.40k
                          width,height,(unsigned long) inDocInfo->bpp,
1009
4.40k
                          (magick_off_t) offset);
1010
1011
  /* verify that seek position is in file */
1012
4.40k
  if ((magick_off_t) offset >= GetBlobSize(image))
1013
189
    {
1014
189
      if (image->logging)
1015
189
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1016
189
                              "Hierarchy offset %" MAGICK_OFF_F "d is outside file bounds",
1017
189
                              (magick_off_t) offset);
1018
189
      ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1019
0
    }
1020
1021
  /* discard offsets for layers below first, if any.
1022
   */
1023
4.21k
  do
1024
171k
    {
1025
171k
      junk = ReadBlobMSBLong(image);
1026
171k
    }
1027
171k
  while ((junk != 0) && (!EOFBlob(image)));
1028
1029
4.21k
  if (EOFBlob(image))
1030
3.20k
    ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
1031
1032
  /* save the current position as it is where the
1033
   *  next level offset is stored.
1034
   */
1035
3.20k
  saved_pos = TellBlob(image);
1036
3.20k
  if (saved_pos < 0)
1037
3.20k
    ThrowBinaryException(BlobError,UnableToObtainOffset,image->filename);
1038
1039
  /* verify that seek position is in file */
1040
3.20k
  if ((magick_off_t) offset >= GetBlobSize(image))
1041
0
    {
1042
0
      if (image->logging)
1043
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1044
0
                              "Level offset %" MAGICK_OFF_F "d is outside file bounds",
1045
0
                              (magick_off_t) offset);
1046
0
      ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1047
0
    }
1048
1049
  /* seek to the level offset */
1050
3.20k
  if (SeekBlob(image, offset, SEEK_SET) != offset)
1051
3.20k
    ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1052
1053
  /* read in the level */
1054
3.20k
  if (load_level (image, inDocInfo, inLayer) == MagickFail)
1055
1.35k
    return MagickFail;
1056
1057
  /* restore the saved position so we'll be ready to
1058
   *  read the next offset.
1059
   */
1060
1.84k
  if (SeekBlob(image, saved_pos, SEEK_SET) != saved_pos)
1061
1.84k
    ThrowBinaryException(BlobError,UnableToSeekToOffset,image->filename);
1062
1063
1.84k
  return MagickPass;
1064
1.84k
}
1065
1066
1067
static MagickPassFail ReadOneLayer( Image* image, XCFDocInfo* inDocInfo, XCFLayerInfo*
1068
                                    outLayer )
1069
9.86k
{
1070
9.86k
  unsigned int
1071
9.86k
    i;
1072
1073
9.86k
  unsigned int
1074
9.86k
    foundPropEnd = 0;
1075
1076
9.86k
  unsigned long
1077
9.86k
    hierarchy_offset,
1078
9.86k
    layer_mask_offset;
1079
1080
9.86k
  magick_off_t start_offset;
1081
1082
9.86k
  start_offset = TellBlob(image);
1083
1084
  /* clear the block! */
1085
9.86k
  (void) memset( outLayer, 0, sizeof( XCFLayerInfo ) );
1086
1087
  /* read in the layer width, height, type and name */
1088
9.86k
  outLayer->width = ReadBlobMSBLong(image);
1089
9.86k
  outLayer->height = ReadBlobMSBLong(image);
1090
9.86k
  outLayer->type = ReadBlobMSBLong(image);
1091
9.86k
  (void) ReadBlobStringWithLongSize(image, outLayer->name,
1092
9.86k
                                    sizeof(outLayer->name));
1093
1094
9.86k
  if (EOFBlob(image))
1095
7.22k
    ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
1096
1097
7.22k
  if (image->logging)
1098
7.22k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1099
7.22k
                          "Loading layer \"%s\", dimensions %lux%lu, type %lu",
1100
7.22k
                          outLayer->name,
1101
7.22k
                          (unsigned long) outLayer->width,
1102
7.22k
                          (unsigned long) outLayer->height,
1103
7.22k
                          (unsigned long) outLayer->type);
1104
1105
7.22k
  if ((outLayer->width == 0) || (outLayer->height == 0))
1106
6.87k
    ThrowBinaryException(CorruptImageError,ImproperImageHeader,image->filename);
1107
1108
  /* allocate the image for this layer */
1109
6.87k
  outLayer->image=CloneImage(image,outLayer->width, outLayer->height,True,
1110
6.87k
                             &image->exception);
1111
6.87k
  if (outLayer->image == (Image *) NULL)
1112
0
    return MagickFail;
1113
1114
  /* read the layer properties! */
1115
6.87k
  foundPropEnd = 0;
1116
23.3k
  while ( !foundPropEnd && !EOFBlob(image) )
1117
16.5k
    {
1118
16.5k
      PropType    prop_type = (PropType) ReadBlobMSBLong(image);
1119
16.5k
      size_t      prop_size = ReadBlobMSBLong(image);
1120
1121
16.5k
      switch (prop_type)
1122
16.5k
        {
1123
6.46k
        case PROP_END:
1124
6.46k
          foundPropEnd = 1;
1125
6.46k
          break;
1126
499
        case PROP_ACTIVE_LAYER:
1127
499
          outLayer->active = 1;
1128
499
          break;
1129
402
        case PROP_FLOATING_SELECTION:
1130
402
          outLayer->floating_offset = ReadBlobMSBLong(image);
1131
402
          break;
1132
836
        case PROP_OPACITY:
1133
836
          outLayer->opacity = ReadBlobMSBLong(image);
1134
836
          if (outLayer->opacity > 255)
1135
574
            outLayer->opacity = 255;
1136
836
          break;
1137
270
        case PROP_VISIBLE:
1138
270
          outLayer->visible = ReadBlobMSBLong(image);
1139
270
          break;
1140
224
        case PROP_LINKED:
1141
224
          outLayer->linked = ReadBlobMSBLong(image);
1142
224
          break;
1143
264
        case PROP_PRESERVE_TRANSPARENCY:
1144
264
          outLayer->preserve_trans = ReadBlobMSBLong(image);
1145
264
          break;
1146
226
        case PROP_APPLY_MASK:
1147
226
          outLayer->apply_mask = ReadBlobMSBLong(image);
1148
226
          break;
1149
320
        case PROP_EDIT_MASK:
1150
320
          outLayer->edit_mask = ReadBlobMSBLong(image);
1151
320
          break;
1152
218
        case PROP_SHOW_MASK:
1153
218
          outLayer->show_mask = ReadBlobMSBLong(image);
1154
218
          break;
1155
523
        case PROP_OFFSETS:
1156
523
          outLayer->offset_x = (magick_int32_t) ReadBlobMSBLong(image);
1157
523
          outLayer->offset_y = (magick_int32_t) ReadBlobMSBLong(image);
1158
523
          break;
1159
279
        case PROP_MODE:
1160
279
          outLayer->mode = ReadBlobMSBLong(image);
1161
279
          break;
1162
247
        case PROP_TATTOO:
1163
247
          outLayer->preserve_trans = ReadBlobMSBLong(image);
1164
247
          break;
1165
578
        case PROP_PARASITES:
1166
578
          {
1167
1.65M
            for (i=0; i < prop_size; i++ )
1168
1.64M
              if (ReadBlobByte(image) == EOF)
1169
79
                break;
1170
1171
            /*
1172
              long base = info->cp;
1173
              GimpParasite *p;
1174
              while (info->cp - base < prop_size)
1175
              {
1176
              p = xcf_load_parasite(info);
1177
              gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p);
1178
              gimp_parasite_free(p);
1179
              }
1180
              if (info->cp - base != prop_size)
1181
              g_message ("Error detected while loading a layer's parasites");
1182
            */
1183
578
          }
1184
578
          break;
1185
5.14k
        default:
1186
          /* g_message ("unexpected/unknown layer property: %d (skipping)",
1187
             prop_type); */
1188
1189
5.14k
          {
1190
5.14k
            int buf[16];
1191
5.14k
            size_t amount;
1192
1193
            /* read over it... */
1194
22.2k
            while (prop_size > 0 && !EOFBlob(image))
1195
17.1k
              {
1196
17.1k
                amount = Min (16, prop_size);
1197
231k
                for (i=0; i < amount; i++)
1198
214k
                  if (ReadBlob(image, amount, &buf) != amount)
1199
124
                    break;
1200
17.1k
                prop_size -= Min (16, amount);
1201
17.1k
              }
1202
5.14k
          }
1203
5.14k
          break;
1204
16.5k
        }
1205
16.5k
    }
1206
6.87k
  if (EOFBlob(image))
1207
5.85k
    ThrowBinaryException(CorruptImageError,UnexpectedEndOfFile,image->filename);
1208
1209
5.85k
  if (!foundPropEnd)
1210
0
    return MagickFail;
1211
1212
  /* clear the image based on the layer opacity */
1213
5.85k
  if (SetImage(outLayer->image,(Quantum)(255-outLayer->opacity)) != MagickPass)
1214
541
    return MagickFail;
1215
1216
  /* set the compositing mode */
1217
5.31k
  outLayer->image->compose = GIMPBlendModeToCompositeOperator( outLayer->mode );
1218
5.31k
  if ( outLayer->visible == False )
1219
5.27k
    {
1220
      /* BOGUS: should really be separate member var! */
1221
5.27k
      outLayer->image->compose = NoCompositeOp;
1222
5.27k
    }
1223
1224
  /* read the hierarchy and layer mask offsets */
1225
5.31k
  hierarchy_offset = ReadBlobMSBLong(image);
1226
5.31k
  layer_mask_offset = ReadBlobMSBLong(image);
1227
1228
  /* verify that seek position is in file */
1229
5.31k
  if ((magick_off_t) hierarchy_offset >= GetBlobSize(image))
1230
760
    {
1231
760
      if (image->logging)
1232
760
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1233
760
                              "Hierarchy offset %" MAGICK_OFF_F "d is outside file bounds",
1234
760
                              (magick_off_t) hierarchy_offset);
1235
760
      ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1236
0
    }
1237
  /*
1238
    Verify that seek position is not too small.
1239
    Seek position provides ample opportunity for abuse.
1240
  */
1241
4.55k
  if ((magick_off_t) hierarchy_offset <= start_offset)
1242
136
    {
1243
136
      if (image->logging)
1244
136
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1245
136
                              "Hierarchy offset %" MAGICK_OFF_F "d is unreasonable",
1246
136
                              (magick_off_t) hierarchy_offset);
1247
136
      ThrowBinaryException(CorruptImageError,ImproperImageHeader,image->filename);
1248
0
    }
1249
1250
  /* read in the hierarchy */
1251
4.41k
  if (SeekBlob(image, hierarchy_offset, SEEK_SET) != (magick_off_t) hierarchy_offset)
1252
4.41k
    ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1253
4.41k
  if (image->logging)
1254
4.41k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1255
4.41k
                          "Hierarchy offset %" MAGICK_OFF_F "d",
1256
4.41k
                          (magick_off_t) hierarchy_offset);
1257
4.41k
  if (load_hierarchy (image, inDocInfo, outLayer) == MagickFail)
1258
2.57k
    return MagickFail;
1259
1260
  /* read in the layer mask */
1261
1.84k
  if (layer_mask_offset != 0)
1262
1.02k
    {
1263
      /* verify that seek position is in file */
1264
1.02k
      if ((magick_off_t) layer_mask_offset >= GetBlobSize(image))
1265
362
        {
1266
362
          if (image->logging)
1267
362
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1268
362
                                  "Layer mask offset %" MAGICK_OFF_F "d is outside file bounds",
1269
362
                                  (magick_off_t) layer_mask_offset);
1270
362
          ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1271
0
        }
1272
      /*
1273
        Verify that seek position is not too small.
1274
        Seek position provides ample opportunity for abuse.
1275
      */
1276
662
      if ((magick_off_t) layer_mask_offset <= start_offset)
1277
51
        {
1278
51
          if (image->logging)
1279
51
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1280
51
                                  "Layer mask offset %" MAGICK_OFF_F "d is unreasonable",
1281
51
                                  (magick_off_t) hierarchy_offset);
1282
51
          ThrowBinaryException(CorruptImageError,ImproperImageHeader,image->filename);
1283
0
        }
1284
611
      if (SeekBlob(image, layer_mask_offset, SEEK_SET) != (magick_off_t) layer_mask_offset)
1285
611
        ThrowBinaryException(CorruptImageError,InsufficientImageDataInFile,image->filename);
1286
1287
#if 0  /* BOGUS: support layer masks! */
1288
      layer_mask = xcf_load_layer_mask (info, gimage);
1289
      if (!layer_mask)
1290
        goto error;
1291
1292
      /* set the offsets of the layer_mask */
1293
      GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
1294
      GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
1295
1296
      gimp_layer_add_mask (layer, layer_mask, False);
1297
1298
      layer->mask->apply_mask = apply_mask;
1299
      layer->mask->edit_mask  = edit_mask;
1300
      layer->mask->show_mask  = show_mask;
1301
#endif
1302
611
    }
1303
1304
  /* attach the floating selection... */
1305
#if 0  /* BOGUS: we may need to read this, even if we don't support it! */
1306
  if (add_floating_sel)
1307
    {
1308
      GimpLayer *floating_sel;
1309
1310
      floating_sel = info->floating_sel;
1311
      floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer));
1312
    }
1313
#endif
1314
1315
1.43k
  return MagickPass;
1316
1.84k
}
1317
1318
/*
1319
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1320
%                                                                             %
1321
%                                                                             %
1322
%                                                                             %
1323
%   R e a d X C F I m a g e                                                   %
1324
%                                                                             %
1325
%                                                                             %
1326
%                                                                             %
1327
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1328
%
1329
%  Method ReadXCFImage reads a GIMP (GNU Image Manipulation Program) image
1330
%  file and returns it.  It allocates the memory necessary for the new Image
1331
%  structure and returns a pointer to the new image.
1332
%
1333
%  The format of the ReadXCFImage method is:
1334
%
1335
%      image=ReadXCFImage(image_info)
1336
%
1337
%  A description of each parameter follows:
1338
%
1339
%    o image:  Method ReadXCFImage returns a pointer to the image after
1340
%      reading.  A null image is returned if there is a memory shortage or
1341
%      if the image cannot be read.
1342
%
1343
%    o image_info: Specifies a pointer to a ImageInfo structure.
1344
%
1345
%    o exception: return any errors or warnings in this structure.
1346
%
1347
%
1348
*/
1349
#define DestroyLayerInfo(number_layers,layer_info)             \
1350
8.43k
  do {                                                         \
1351
8.43k
    size_t                                                     \
1352
8.43k
      j;                                                       \
1353
8.43k
                                                               \
1354
8.43k
    if (layer_info != (XCFLayerInfo *) NULL)                    \
1355
8.43k
      {                                                         \
1356
16.8k
        for (j=0; j < number_layers; j++)                       \
1357
8.43k
          {                                                     \
1358
8.43k
            if (layer_info[j].image != (Image *) NULL)          \
1359
8.43k
              {                                                 \
1360
5.44k
                DestroyImage(layer_info[j].image);              \
1361
5.44k
                layer_info[j].image = (Image *) NULL;           \
1362
5.44k
              }                                                 \
1363
8.43k
          }                                                     \
1364
8.43k
      }                                                         \
1365
8.43k
    MagickFreeResourceLimitedMemory(XCFLayerInfo *,layer_info); \
1366
8.43k
  } while (0);
1367
static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception)
1368
163k
{
1369
163k
  char
1370
163k
    magick[14];
1371
1372
163k
  Image
1373
163k
    *image;
1374
1375
163k
  unsigned int
1376
163k
    status;
1377
1378
163k
  unsigned long
1379
163k
    i,
1380
163k
    image_type;
1381
1382
163k
  int
1383
163k
    foundPropEnd = 0;
1384
1385
163k
  size_t
1386
163k
    count;
1387
1388
163k
  XCFDocInfo
1389
163k
    doc_info;
1390
1391
  /*
1392
    Open image file.
1393
  */
1394
163k
  assert(image_info != (const ImageInfo *) NULL);
1395
163k
  assert(image_info->signature == MagickSignature);
1396
163k
  assert(exception != (ExceptionInfo *) NULL);
1397
163k
  assert(exception->signature == MagickSignature);
1398
163k
  image=AllocateImage(image_info);
1399
163k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1400
163k
  if (status == MagickFail)
1401
163k
    ThrowReaderException(FileOpenError,UnableToOpenFile,image);
1402
163k
  count=ReadBlob(image,14,(char *) magick);
1403
163k
  if ((count != 14) ||
1404
159k
      (LocaleNCompare((char *) magick,"gimp xcf",8) != 0))
1405
159k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
1406
  /* clear the docinfo stuff */
1407
159k
  (void) memset( &doc_info, 0, sizeof(XCFDocInfo));
1408
159k
  doc_info.exception = exception;
1409
1410
  /* read the three simple values */
1411
159k
  image->columns = doc_info.width = ReadBlobMSBLong(image);
1412
159k
  image->rows = doc_info.height = ReadBlobMSBLong(image);
1413
159k
  image_type = doc_info.image_type = ReadBlobMSBLong(image);
1414
1415
  /*
1416
    Get file size to use for validation later.
1417
  */
1418
159k
  doc_info.file_size=GetBlobSize(image);
1419
1420
159k
  if (image->logging)
1421
159k
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1422
159k
                          "XCF dimensions %lux%lu, type %s",
1423
159k
                          image->columns,image->rows,
1424
159k
                          (image_type == GIMP_RGB ? "RGB" :
1425
159k
                           (image_type == GIMP_GRAY ? "GRAY" :
1426
35.3k
                            (image_type == GIMP_INDEXED ? "INDEXED" :
1427
26.3k
                             "unknown"))));
1428
1429
159k
  if ((image->columns == 0) || (image->rows == 0))
1430
148k
    ThrowReaderException(CorruptImageError,NegativeOrZeroImageSize,image);
1431
1432
  /* setup some things about the image...*/
1433
148k
  image->compression=NoCompression;
1434
148k
  image->depth = 8;
1435
148k
  if ( image_type == GIMP_RGB )
1436
118k
    {
1437
118k
      image->colorspace=RGBColorspace;
1438
118k
    }
1439
30.8k
  else if ( image_type == GIMP_GRAY )
1440
9.03k
    {
1441
9.03k
      image->colorspace=GRAYColorspace;
1442
9.03k
    }
1443
21.8k
  else if ( image_type == GIMP_INDEXED )
1444
2.30k
    {
1445
2.30k
      ThrowReaderException(CoderError,ColormapTypeNotSupported,image);
1446
0
    }
1447
19.5k
  else
1448
19.5k
    {
1449
19.5k
      ThrowReaderException(CorruptImageError,ImageTypeNotSupported,image);
1450
0
    }
1451
1452
127k
  if (CheckImagePixelLimits(image, exception) != MagickPass)
1453
106k
    ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
1454
1455
  /*
1456
    SetImage can be very expensive but we do it here because it is
1457
    expected that the canvas is initialized to opaque-black and
1458
    operations may be done using uninitialized pixels if we don't
1459
    initialize here.
1460
  */
1461
106k
  SetRedSample(&image->background_color,0);
1462
106k
  SetGreenSample(&image->background_color,0);
1463
106k
  SetBlueSample(&image->background_color,0);
1464
106k
  SetOpacitySample(&image->background_color,OpaqueOpacity);
1465
106k
  if (SetImage(image,OpaqueOpacity) != MagickPass)
1466
55.3k
    ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); /* ??? */
1467
1468
51.4k
  image->matte=True;  /* XCF always has a matte! */
1469
1470
  /* read properties */
1471
104k
  while ( !foundPropEnd && !EOFBlob(image) )
1472
79.0k
    {
1473
79.0k
      PropType    prop_type = (PropType) ReadBlobMSBLong(image);
1474
79.0k
      size_t      prop_size = ReadBlobMSBLong(image);
1475
1476
79.0k
      switch ( prop_type )
1477
79.0k
        {
1478
21.2k
        case PROP_END:
1479
21.2k
          foundPropEnd = 1;
1480
21.2k
          break;
1481
1482
4.51k
        case PROP_COLORMAP:
1483
          /* BOGUS: just skip it for now */
1484
1.04M
          for (i=0;  i <prop_size; i++ )
1485
1.03M
            if (ReadBlobByte(image) == EOF)
1486
3.74k
              ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
1487
          /*
1488
            if (info->file_version == 0)
1489
            {
1490
            gint i;
1491
1492
            g_message (_("XCF warning: version 0 of XCF file format\n"
1493
            "did not save indexed colormaps correctly.\n"
1494
            "Substituting grayscale map."));
1495
            info->cp +=
1496
            xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
1497
            gimage->cmap = g_new (guchar, gimage->num_cols*3);
1498
            xcf_seek_pos (info, info->cp + gimage->num_cols);
1499
            for (i = 0; i<gimage->num_cols; i++)
1500
            {
1501
            gimage->cmap[i*3+0] = i;
1502
            gimage->cmap[i*3+1] = i;
1503
            gimage->cmap[i*3+2] = i;
1504
            }
1505
            }
1506
            else
1507
            {
1508
            info->cp +=
1509
            xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
1510
            gimage->cmap = g_new (guchar, gimage->num_cols*3);
1511
            info->cp +=
1512
            xcf_read_int8 (info->fp,
1513
            (guint8*) gimage->cmap, gimage->num_cols*3);
1514
            }
1515
          */
1516
3.74k
          break;
1517
1518
8.05k
        case PROP_COMPRESSION:
1519
8.05k
          {
1520
8.05k
            int c;
1521
8.05k
            c = ReadBlobByte(image);
1522
8.05k
            if (c == EOF)
1523
1.53k
              ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,
1524
8.05k
                                   image);
1525
6.52k
            doc_info.compression = c;
1526
6.52k
            if ((doc_info.compression != COMPRESS_NONE) &&
1527
5.31k
                (doc_info.compression != COMPRESS_RLE) &&
1528
4.30k
                (doc_info.compression != COMPRESS_ZLIB) &&
1529
3.99k
                (doc_info.compression != COMPRESS_FRACTAL))
1530
3.78k
              ThrowReaderException(CorruptImageError,CompressionNotValid,
1531
6.52k
                                   image);
1532
2.73k
          }
1533
0
          break;
1534
1535
6.09k
        case PROP_GUIDES:
1536
6.09k
          {
1537
            /* just skip it - we don't care about guides */
1538
348k
            for (i=0; i < prop_size; i++ )
1539
347k
              if (ReadBlobByte(image) == EOF)
1540
5.45k
                ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,
1541
6.09k
                                     image);
1542
636
          }
1543
0
          break;
1544
1545
280
        case PROP_RESOLUTION:
1546
280
          {
1547
280
            /* float xres = (float) */ (void) ReadBlobMSBLong(image);
1548
280
            /* float yres = (float) */ (void) ReadBlobMSBLong(image);
1549
1550
            /*
1551
              if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
1552
              yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
1553
              {
1554
              g_message ("Warning, resolution out of range in XCF file");
1555
              xres = gimage->gimp->config->default_xresolution;
1556
              yres = gimage->gimp->config->default_yresolution;
1557
              }
1558
            */
1559
1560
1561
            /* BOGUS: we don't write these yet because we aren't
1562
               reading them properly yet :( */
1563
            /* image->x_resolution = xres; */
1564
            /* image->y_resolution = yres; */
1565
280
          }
1566
280
          break;
1567
1568
316
        case PROP_TATTOO:
1569
316
          {
1570
            /* we need to read it, even if we ignore it */
1571
316
            /*unsigned long  tattoo_state = */ (void) ReadBlobMSBLong(image);
1572
316
          }
1573
316
          break;
1574
1575
7.12k
        case PROP_PARASITES:
1576
7.12k
          {
1577
            /* BOGUS: we may need these for IPTC stuff */
1578
67.8k
            for (i=0; i < prop_size; i++ )
1579
62.7k
              if (ReadBlobByte(image) == EOF)
1580
5.13k
                ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
1581
1582
            /*
1583
              glong         base = info->cp;
1584
              GimpParasite *p;
1585
1586
              while (info->cp - base < prop_size)
1587
              {
1588
              p = xcf_load_parasite (info);
1589
              gimp_image_parasite_attach (gimage, p);
1590
              gimp_parasite_free (p);
1591
              }
1592
              if (info->cp - base != prop_size)
1593
              g_message ("Error detected while loading an image's parasites");
1594
            */
1595
5.13k
          }
1596
0
          break;
1597
1598
685
        case PROP_UNIT:
1599
685
          {
1600
            /* BOGUS: ignore for now... */
1601
685
            /*unsigned long unit =  */ (void) ReadBlobMSBLong(image);
1602
685
          }
1603
685
          break;
1604
1605
2.22k
        case PROP_PATHS:
1606
2.22k
          {
1607
            /* BOGUS: just skip it for now */
1608
614k
            for (i=0; i < prop_size; i++ )
1609
613k
              if (ReadBlobByte(image) == EOF)
1610
1.27k
                ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image);
1611
1612
            /*
1613
              PathList *paths = xcf_load_bzpaths (gimage, info);
1614
              gimp_image_set_paths (gimage, paths);
1615
            */
1616
951
          }
1617
0
          break;
1618
1619
940
        case PROP_USER_UNIT:
1620
940
          {
1621
940
            char  unit_string[1000];
1622
            /*BOGUS: ignored for now */
1623
940
            /*float  factor = (float) */ (void) ReadBlobMSBLong(image);
1624
940
            /* unsigned long digits =  */ (void) ReadBlobMSBLong(image);
1625
5.64k
            for (i=0; i < 5; i++)
1626
4.70k
              (void) ReadBlobStringWithLongSize(image, unit_string,
1627
4.70k
                                                sizeof(unit_string));
1628
940
          }
1629
940
          break;
1630
1631
27.5k
        default:
1632
          /* g_message ("unexpected/unknown image property: %d (skipping)",
1633
             prop_type); */
1634
1635
27.5k
          {
1636
27.5k
            int buf[16];
1637
27.5k
            size_t amount;
1638
1639
            /* read over it... */
1640
38.2k
            while (prop_size > 0) /* size_t prop_size, amount */
1641
21.6k
              {
1642
21.6k
                amount = Min (sizeof(buf), prop_size);
1643
97.0k
                for (i=0; i < amount; i++)
1644
86.4k
                  {
1645
86.4k
                    amount = ReadBlob(image, amount, &buf);
1646
86.4k
                    if (amount == 0U)
1647
11.0k
                      ThrowReaderException(CorruptImageError,
1648
86.4k
                                           UnexpectedEndOfFile,image);
1649
75.4k
                  }
1650
10.6k
                prop_size -= Min (16U, amount);
1651
10.6k
              }
1652
27.5k
          }
1653
16.5k
          break;
1654
79.0k
        }
1655
79.0k
    }
1656
1657
25.5k
  if (!foundPropEnd)
1658
21.2k
    ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
1659
1660
21.2k
  if (image_info->ping && (image_info->subrange != 0))
1661
0
    {
1662
0
      ; /* do nothing, we were just pinging! */
1663
0
    }
1664
21.2k
  else
1665
21.2k
    {
1666
21.2k
      XCFLayerInfo
1667
21.2k
        *layer_info;
1668
1669
21.2k
      unsigned long
1670
21.2k
        number_layers = 0,
1671
21.2k
        num_layers = 0;
1672
1673
21.2k
      long
1674
21.2k
        current_layer = 0,
1675
21.2k
        first_layer = 0,
1676
21.2k
        last_layer = 0,
1677
21.2k
        T = 0;
1678
1679
21.2k
      MagickBool
1680
21.2k
        foundAllLayers = MagickFalse;
1681
1682
21.2k
      magick_off_t
1683
21.2k
        oldPos;
1684
1685
21.2k
      unsigned int
1686
21.2k
        previous_offset;
1687
1688
21.2k
      if (CheckImagePixelLimits(image, exception) != MagickPass)
1689
21.2k
        ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
1690
1691
      /* BIG HACK
1692
         because XCF doesn't include the layer count, and we
1693
         want to know it in advance in order to allocate memory,
1694
         we have to scan the layer offset list, and then reposition
1695
         the read pointer
1696
      */
1697
21.2k
      oldPos = TellBlob(image);
1698
21.2k
      if (oldPos < 0)
1699
21.2k
        ThrowReaderException(BlobError,UnableToObtainOffset,image);
1700
1701
21.2k
      previous_offset = 0;
1702
21.2k
      do
1703
37.4k
        {
1704
37.4k
          magick_uint32_t
1705
37.4k
            offset = ReadBlobMSBLong(image);
1706
1707
37.4k
          if (image->logging)
1708
37.4k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1709
37.4k
                                  "Layer Offset[%lu] = %u",
1710
37.4k
                                  number_layers, offset);
1711
1712
37.4k
          if (offset >= doc_info.file_size)
1713
3.49k
            {
1714
3.49k
              if (image->logging)
1715
3.49k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1716
3.49k
                                      "Layer Offset %u"
1717
3.49k
                                      " is outside of file bounds",
1718
3.49k
                                      offset);
1719
3.49k
              ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
1720
0
            }
1721
          /*
1722
            Are layer offsets assured to be ascending?
1723
          */
1724
34.0k
          if ((offset != 0) && (offset <= previous_offset))
1725
1.74k
            {
1726
1.74k
              if (image->logging)
1727
1.74k
                (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1728
1.74k
                                      "Layer Offset %u"
1729
1.74k
                                      " is not ascending",
1730
1.74k
                                      offset);
1731
1.74k
              ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
1732
0
            }
1733
1734
32.2k
          if ( offset == 0 )
1735
16.0k
            foundAllLayers = MagickTrue;
1736
16.2k
          else
1737
16.2k
            number_layers++;
1738
1739
          /* Check for too many layers */
1740
32.2k
          if (number_layers == (unsigned long) LONG_MAX)
1741
32.2k
            ThrowReaderException(CorruptImageError,CorruptImage,image);
1742
1743
32.2k
          previous_offset=offset;
1744
32.2k
        } while ( !foundAllLayers );
1745
1746
16.0k
      if (SeekBlob(image, oldPos, SEEK_SET) != oldPos) /* restore the position! */
1747
16.0k
        ThrowReaderException(BlobError,UnableToSeekToOffset,image);
1748
1749
16.0k
      first_layer = image_info->subimage;
1750
16.0k
      num_layers = number_layers;
1751
1752
16.0k
      if (number_layers == 0)
1753
9.86k
        ThrowReaderException(CorruptImageError,ImproperImageHeader,image);
1754
1755
      /* subrange==0 means read all the images */
1756
9.86k
      if( image_info->subrange > 0UL && image_info->subrange < number_layers )
1757
4.26k
        num_layers = image_info->subrange;
1758
9.86k
      last_layer = first_layer + num_layers-1;
1759
1760
9.86k
      if (image->logging)
1761
9.86k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1762
9.86k
                              "XCF number_layers=%lu first_layer=%ld last_layer=%ld",
1763
9.86k
                              number_layers, first_layer, last_layer);
1764
1765
      /* XCF has layers backwards, so this gets a bit complicated */
1766
9.86k
      T = last_layer;
1767
9.86k
      last_layer = number_layers - first_layer - 1;
1768
9.86k
      first_layer = number_layers - T - 1;
1769
9.86k
      number_layers = num_layers;
1770
1771
9.86k
      if (image->logging)
1772
9.86k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1773
9.86k
                              "XCF reading layers %ld to %ld inclusive", first_layer,
1774
9.86k
                              last_layer);
1775
1776
      /* allocate our array of layer info blocks */
1777
9.86k
      layer_info=MagickAllocateResourceLimitedArray(XCFLayerInfo *,
1778
9.86k
                                     number_layers,
1779
9.86k
                                     sizeof(XCFLayerInfo));
1780
9.86k
      if (layer_info == (XCFLayerInfo *) NULL)
1781
9.86k
        ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image);
1782
9.86k
      (void) memset(layer_info,0,number_layers*sizeof(XCFLayerInfo));
1783
1784
9.86k
      for ( ; ; )
1785
15.7k
        {
1786
15.7k
          magick_off_t
1787
15.7k
            offset,
1788
15.7k
            saved_pos;
1789
1790
15.7k
          MagickPassFail
1791
15.7k
            layer_ok;
1792
1793
          /* read in the offset of the next layer */
1794
15.7k
          offset = ReadBlobMSBLong(image);
1795
1796
          /* if the offset is 0 then we are at the end
1797
           *  of the layer list.
1798
           */
1799
15.7k
          if (offset == 0)
1800
1.43k
            break;
1801
1802
          /* save the current position as it is where the
1803
           *  next layer offset is stored.
1804
           */
1805
14.2k
          saved_pos = TellBlob(image);
1806
14.2k
          if (saved_pos < 0)
1807
0
            {
1808
0
              MagickFreeResourceLimitedMemory(XCFLayerInfo *,layer_info);
1809
0
              ThrowReaderException(BlobError,UnableToObtainOffset,image);
1810
0
            }
1811
1812
14.2k
          if ( first_layer <= current_layer && current_layer <= last_layer )
1813
9.86k
            {
1814
              /* verify that seek position is in file */
1815
9.86k
              if ((magick_off_t) offset >= GetBlobSize(image))
1816
0
                {
1817
0
                  if (image->logging)
1818
0
                    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1819
0
                                          "Layer offset %" MAGICK_OFF_F "d is outside file bounds",
1820
0
                                          (magick_off_t) offset);
1821
0
                  DestroyLayerInfo(number_layers,layer_info);
1822
0
                  ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
1823
0
                }
1824
1825
              /* seek to the layer offset */
1826
9.86k
              if (SeekBlob(image, offset, SEEK_SET) != offset)
1827
0
                {
1828
                  /* FIXME: CID 64064: leaks layer_info */
1829
0
                  DestroyLayerInfo(number_layers,layer_info);
1830
0
                  ThrowReaderException(CorruptImageError,InsufficientImageDataInFile,image);
1831
0
                }
1832
1833
              /* read in the layer */
1834
9.86k
              layer_ok = ReadOneLayer( image, &doc_info, &layer_info[current_layer-first_layer] );
1835
9.86k
              if (!layer_ok)
1836
8.43k
                {
1837
#if 0
1838
                  int
1839
                    j;
1840
1841
                  for (j=0; j <= (current_layer-first_layer); j++)
1842
                    {
1843
                      if (layer_info[j].image)
1844
                        {
1845
                          DestroyImage(layer_info[j].image);
1846
                          layer_info[j].image = (Image *) NULL;
1847
                        }
1848
                    }
1849
                  MagickFreeResourceLimitedMemory(XCFLayerInfo *,layer_info);
1850
#endif
1851
8.43k
                  DestroyLayerInfo(number_layers,layer_info);
1852
8.43k
                  CopyException(exception,&image->exception);
1853
8.43k
                  CloseBlob(image);
1854
8.43k
                  DestroyImageList(image);
1855
8.43k
                  return (Image *) NULL;
1856
8.43k
                }
1857
              /* restore the saved position so we'll be ready to
1858
               *  read the next offset.
1859
               */
1860
1.43k
              if (SeekBlob(image, saved_pos, SEEK_SET) != saved_pos)
1861
0
                {
1862
                  /* FIXME: CID 64064: leaks layer_info */
1863
0
                  DestroyLayerInfo(number_layers,layer_info);
1864
0
                  ThrowReaderException(BlobError,UnableToSeekToOffset,image);
1865
0
                }
1866
1.43k
            }
1867
1868
5.84k
          current_layer++;
1869
5.84k
        }
1870
1871
1.43k
      if ( number_layers == 1 )
1872
1.43k
        {
1873
          /* composite the layer data onto the main image & then dispose the layer */
1874
1.43k
          if (image->logging)
1875
1.43k
            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1876
1.43k
                                  "Composite Layer[0]: %lux%lu%+d%+d",
1877
1.43k
                                    layer_info[0].image->columns,
1878
1.43k
                                    layer_info[0].image->rows,
1879
1.43k
                                    layer_info[0].offset_x,
1880
1.43k
                                    layer_info[0].offset_y);
1881
1.43k
          (void) CompositeImage(image, OverCompositeOp, layer_info[0].image,
1882
1.43k
                                layer_info[0].offset_x, layer_info[0].offset_y );
1883
1.43k
          DestroyImage( layer_info[0].image );
1884
1.43k
          layer_info[0].image = (Image *) NULL;
1885
1.43k
        }
1886
0
      else
1887
0
        {
1888
#if 0
1889
          {
1890
            /* NOTE: XCF layers are REVERSED from composite order! */
1891
            long
1892
              j;
1893
1894
            for (j=(long) (number_layers-1); j>=0; j--)
1895
              {
1896
                /* BOGUS: need to consider layer blending modes!! */
1897
                if ( layer_info[j].visible )  /* only visible ones, please! */
1898
                  {
1899
                    if (image->logging)
1900
                      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1901
                                            "Composite Layer[%lu]: %lux%lu%+d%+d",
1902
                                            j,
1903
                                            layer_info[j].image->columns,
1904
                                            layer_info[j].image->rows,
1905
                                            layer_info[j].offset_x,
1906
                                            layer_info[j].offset_y);
1907
                    CompositeImage(image, OverCompositeOp, layer_info[j].image,
1908
                                   layer_info[j].offset_x, layer_info[j].offset_y );
1909
                    DestroyImage( layer_info[j].image );
1910
                    layer_info[j].image = (Image *) NULL;
1911
                  }
1912
              }
1913
          }
1914
#else
1915
0
          {
1916
            /* NOTE: XCF layers are REVERSED from composite order! */
1917
0
            long
1918
0
              j;
1919
1920
            /* first we copy the last layer on top of the main image */
1921
0
            if (image->logging)
1922
0
              (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1923
0
                                    "Composite Layer[%lu]: %lux%lu%+d%+d",
1924
0
                                    number_layers-1,
1925
0
                                    layer_info[number_layers-1].image->columns,
1926
0
                                    layer_info[number_layers-1].image->rows,
1927
0
                                    layer_info[number_layers-1].offset_x,
1928
0
                                    layer_info[number_layers-1].offset_y);
1929
0
            (void) CompositeImage(image, CopyCompositeOp, layer_info[number_layers-1].image,
1930
0
                                  layer_info[number_layers-1].offset_x,
1931
0
                                  layer_info[number_layers-1].offset_y );
1932
0
            DestroyImage( layer_info[number_layers-1].image );
1933
0
            layer_info[number_layers-1].image = (Image *) NULL;
1934
1935
            /* now reverse the order of the layers as they are put
1936
               into subimages
1937
            */
1938
0
            image->next=layer_info[number_layers-2].image;
1939
0
            layer_info[number_layers-2].image->previous=image;
1940
0
            for (j=(long) ((number_layers-2)); j >= 0; j--)
1941
0
              {
1942
0
                if (j > 0)
1943
0
                  layer_info[j].image->next=layer_info[j-1].image;
1944
0
                if (j < ((long) (number_layers-1)))
1945
0
                  layer_info[j].image->previous=layer_info[j+1].image;
1946
0
                layer_info[j].image->page.x = layer_info[j].offset_x;
1947
0
                layer_info[j].image->page.y = layer_info[j].offset_y;
1948
0
                layer_info[j].image->page.width = layer_info[j].width;
1949
0
                layer_info[j].image->page.height = layer_info[j].height;
1950
0
              }
1951
0
          }
1952
0
#endif
1953
0
        }
1954
1955
1.43k
      MagickFreeResourceLimitedMemory(XCFLayerInfo *,layer_info);
1956
1957
#if 0  /* BOGUS: do we need the channels?? */
1958
      while (True)
1959
        {
1960
          /* read in the offset of the next channel */
1961
          info->cp += xcf_read_int32 (info->fp, &offset, 1);
1962
1963
          /* if the offset is 0 then we are at the end
1964
           *  of the channel list.
1965
           */
1966
          if (offset == 0)
1967
            break;
1968
1969
          /* save the current position as it is where the
1970
           *  next channel offset is stored.
1971
           */
1972
          saved_pos = info->cp;
1973
1974
          /* seek to the channel offset */
1975
          xcf_seek_pos (info, offset);
1976
1977
          /* read in the layer */
1978
          channel = xcf_load_channel (info, gimage);
1979
          if (!channel)
1980
            goto error;
1981
1982
          num_successful_elements++;
1983
1984
          /* add the channel to the image if its not the selection */
1985
          if (channel != gimage->selection_mask)
1986
            gimp_image_add_channel (gimage, channel, -1);
1987
1988
          /* restore the saved position so we'll be ready to
1989
           *  read the next offset.
1990
           */
1991
          xcf_seek_pos (info, saved_pos);
1992
        }
1993
#endif
1994
1.43k
    }
1995
1996
1.43k
  CloseBlob(image);
1997
1.43k
  if ( image_type == GIMP_GRAY )
1998
176
    image->is_grayscale=MagickTrue;
1999
1.43k
  StopTimer(&image->timer);
2000
1.43k
  return(image);
2001
21.2k
}
2002

2003
/*
2004
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2005
%                                                                             %
2006
%                                                                             %
2007
%                                                                             %
2008
%   R e g i s t e r X C F I m a g e                                           %
2009
%                                                                             %
2010
%                                                                             %
2011
%                                                                             %
2012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2013
%
2014
%  Method RegisterXCFImage adds attributes for the XCF image format to
2015
%  the list of supported formats.  The attributes include the image format
2016
%  tag, a method to read and/or write the format, whether the format
2017
%  supports the saving of more than one frame to the same file or blob,
2018
%  whether the format supports native in-memory I/O, and a brief
2019
%  description of the format.
2020
%
2021
%  The format of the RegisterXCFImage method is:
2022
%
2023
%      RegisterXCFImage(void)
2024
%
2025
*/
2026
ModuleExport void RegisterXCFImage(void)
2027
4
{
2028
4
  MagickInfo
2029
4
    *entry;
2030
2031
4
  entry=SetMagickInfo("XCF");
2032
4
  entry->decoder=(DecoderHandler) ReadXCFImage;
2033
4
  entry->magick=(MagickHandler) IsXCF;
2034
4
  entry->description="GIMP image";
2035
4
  entry->module="XCF";
2036
4
  entry->seekable_stream=True;
2037
4
  (void) RegisterMagickInfo(entry);
2038
4
}
2039

2040
/*
2041
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2042
%                                                                             %
2043
%                                                                             %
2044
%                                                                             %
2045
%   U n r e g i s t e r X C F I m a g e                                       %
2046
%                                                                             %
2047
%                                                                             %
2048
%                                                                             %
2049
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2050
%
2051
%  Method UnregisterXCFImage removes format registrations made by the
2052
%  XCF module from the list of supported formats.
2053
%
2054
%  The format of the UnregisterXCFImage method is:
2055
%
2056
%      UnregisterXCFImage(void)
2057
%
2058
*/
2059
ModuleExport void UnregisterXCFImage(void)
2060
0
{
2061
0
  (void) UnregisterMagickInfo("XCF");
2062
0
}