Coverage Report

Created: 2025-07-23 08:18

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