Coverage Report

Created: 2026-10-28 07:26

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