blob: 69db38df71229b2c0eabf300ef9fb879987912b3 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% X X CCCC FFFFF %
7% X X C F %
8% X C FFF %
9% X X C F %
10% X X CCCC F %
11% %
12% %
13% Read GIMP XCF Image Format %
14% %
15% Software Design %
16% Leonard Rosenthol %
17% November 2001 %
18% %
19% %
cristy45ef08f2012-12-07 13:13:34 +000020% Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/blob.h"
44#include "MagickCore/blob-private.h"
45#include "MagickCore/cache.h"
46#include "MagickCore/color.h"
47#include "MagickCore/composite.h"
48#include "MagickCore/exception.h"
49#include "MagickCore/exception-private.h"
50#include "MagickCore/image.h"
51#include "MagickCore/image-private.h"
52#include "MagickCore/list.h"
53#include "MagickCore/magick.h"
54#include "MagickCore/memory_.h"
55#include "MagickCore/pixel.h"
56#include "MagickCore/pixel-accessor.h"
57#include "MagickCore/quantize.h"
58#include "MagickCore/quantum-private.h"
59#include "MagickCore/static.h"
60#include "MagickCore/string_.h"
61#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000062
63/*
64 Typedef declarations.
65*/
66typedef enum
67{
68 GIMP_RGB,
69 GIMP_GRAY,
70 GIMP_INDEXED
71} GimpImageBaseType;
72
73typedef enum
74{
75 PROP_END = 0,
76 PROP_COLORMAP = 1,
77 PROP_ACTIVE_LAYER = 2,
78 PROP_ACTIVE_CHANNEL = 3,
79 PROP_SELECTION = 4,
80 PROP_FLOATING_SELECTION = 5,
81 PROP_OPACITY = 6,
82 PROP_MODE = 7,
83 PROP_VISIBLE = 8,
84 PROP_LINKED = 9,
85 PROP_PRESERVE_TRANSPARENCY = 10,
86 PROP_APPLY_MASK = 11,
87 PROP_EDIT_MASK = 12,
88 PROP_SHOW_MASK = 13,
89 PROP_SHOW_MASKED = 14,
90 PROP_OFFSETS = 15,
91 PROP_COLOR = 16,
92 PROP_COMPRESSION = 17,
93 PROP_GUIDES = 18,
94 PROP_RESOLUTION = 19,
95 PROP_TATTOO = 20,
96 PROP_PARASITES = 21,
97 PROP_UNIT = 22,
98 PROP_PATHS = 23,
99 PROP_USER_UNIT = 24
100} PropType;
101
102typedef enum
103{
104 COMPRESS_NONE = 0,
105 COMPRESS_RLE = 1,
106 COMPRESS_ZLIB = 2, /* unused */
107 COMPRESS_FRACTAL = 3 /* unused */
108} XcfCompressionType;
109
110typedef struct
111{
cristybb503372010-05-27 20:51:26 +0000112 size_t
cristy3ed852e2009-09-05 21:47:34 +0000113 width,
114 height,
115 image_type,
116 bytes_per_pixel;
117
118 int
119 compression;
120
121 size_t
122 file_size;
123
cristy2cbb3b32012-04-01 19:11:10 +0000124 size_t
125 number_layers;
cristy3ed852e2009-09-05 21:47:34 +0000126} XCFDocInfo;
127
128typedef struct
129{
130 char
131 name[1024];
132
133 unsigned int
134 active;
135
cristybb503372010-05-27 20:51:26 +0000136 size_t
cristy3ed852e2009-09-05 21:47:34 +0000137 width,
138 height,
139 type,
cristy4c08aed2011-07-01 19:47:50 +0000140 alpha,
cristy3ed852e2009-09-05 21:47:34 +0000141 visible,
142 linked,
143 preserve_trans,
144 apply_mask,
145 show_mask,
146 edit_mask,
147 floating_offset;
148
149 ssize_t
150 offset_x,
151 offset_y;
152
cristybb503372010-05-27 20:51:26 +0000153 size_t
cristy3ed852e2009-09-05 21:47:34 +0000154 mode,
155 tattoo;
156
157 Image
158 *image;
159} XCFLayerInfo;
160
161#define TILE_WIDTH 64
162#define TILE_HEIGHT 64
163
164typedef struct
165{
166 unsigned char
167 red,
168 green,
169 blue,
cristy4c08aed2011-07-01 19:47:50 +0000170 alpha;
cristy101ab702011-10-13 13:06:32 +0000171} XCFPixelInfo;
cristy3ed852e2009-09-05 21:47:34 +0000172
173/*
174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
175% %
176% %
177% %
178% I s X C F %
179% %
180% %
181% %
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183%
184% IsXCF() returns MagickTrue if the image format type, identified by the
185% magick string, is XCF (GIMP native format).
186%
187% The format of the IsXCF method is:
188%
189% MagickBooleanType IsXCF(const unsigned char *magick,const size_t length)
190%
191% A description of each parameter follows:
192%
193% o magick: compare image format pattern against these bytes.
194%
195% o length: Specifies the length of the magick string.
196%
197%
198*/
199static MagickBooleanType IsXCF(const unsigned char *magick,const size_t length)
200{
201 if (length < 8)
202 return(MagickFalse);
203 if (LocaleNCompare((char *) magick,"gimp xcf",8) == 0)
204 return(MagickTrue);
205 return(MagickFalse);
206}
207
208typedef enum
209{
210 GIMP_NORMAL_MODE,
211 GIMP_DISSOLVE_MODE,
212 GIMP_BEHIND_MODE,
213 GIMP_MULTIPLY_MODE,
214 GIMP_SCREEN_MODE,
215 GIMP_OVERLAY_MODE,
216 GIMP_DIFFERENCE_MODE,
217 GIMP_ADDITION_MODE,
218 GIMP_SUBTRACT_MODE,
219 GIMP_DARKEN_ONLY_MODE,
220 GIMP_LIGHTEN_ONLY_MODE,
221 GIMP_HUE_MODE,
222 GIMP_SATURATION_MODE,
223 GIMP_COLOR_MODE,
224 GIMP_VALUE_MODE,
225 GIMP_DIVIDE_MODE,
226 GIMP_DODGE_MODE,
227 GIMP_BURN_MODE,
228 GIMP_HARDLIGHT_MODE
229} GimpLayerModeEffects;
230
231/*
232 Simple utility routine to convert between PSD blending modes and
233 ImageMagick compositing operators
234*/
235static CompositeOperator GIMPBlendModeToCompositeOperator(
cristybb503372010-05-27 20:51:26 +0000236 size_t blendMode)
cristy3ed852e2009-09-05 21:47:34 +0000237{
238 switch ( blendMode )
239 {
240 case GIMP_NORMAL_MODE: return( OverCompositeOp );
241 case GIMP_DISSOLVE_MODE: return( DissolveCompositeOp );
242 case GIMP_MULTIPLY_MODE: return( MultiplyCompositeOp );
243 case GIMP_SCREEN_MODE: return( ScreenCompositeOp );
244 case GIMP_OVERLAY_MODE: return( OverlayCompositeOp );
245 case GIMP_DIFFERENCE_MODE: return( DifferenceCompositeOp );
cristy4c08aed2011-07-01 19:47:50 +0000246 case GIMP_ADDITION_MODE: return( ModulusAddCompositeOp );
247 case GIMP_SUBTRACT_MODE: return( ModulusSubtractCompositeOp );
cristy3ed852e2009-09-05 21:47:34 +0000248 case GIMP_DARKEN_ONLY_MODE: return( DarkenCompositeOp );
249 case GIMP_LIGHTEN_ONLY_MODE:return( LightenCompositeOp );
250 case GIMP_HUE_MODE: return( HueCompositeOp );
251 case GIMP_SATURATION_MODE: return( SaturateCompositeOp );
252 case GIMP_COLOR_MODE: return( ColorizeCompositeOp );
253 case GIMP_DODGE_MODE: return( ColorDodgeCompositeOp );
254 case GIMP_BURN_MODE: return( ColorBurnCompositeOp );
255 case GIMP_HARDLIGHT_MODE: return( HardLightCompositeOp );
cristy4c08aed2011-07-01 19:47:50 +0000256 case GIMP_DIVIDE_MODE: return( DivideDstCompositeOp );
cristy3ed852e2009-09-05 21:47:34 +0000257 /* these are the ones we don't support...yet */
258 case GIMP_BEHIND_MODE: return( OverCompositeOp );
259 case GIMP_VALUE_MODE: return( OverCompositeOp );
260 default:
261 return(OverCompositeOp);
262 }
263}
264
265/*
266%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267% %
268% %
269% %
270+ R e a d B l o b S t r i n g W i t h L o n g S i z e %
271% %
272% %
273% %
274%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275%
276% ReadBlobStringWithLongSize reads characters from a blob or file
cristybb503372010-05-27 20:51:26 +0000277% starting with a ssize_t length byte and then characters to that length
cristy3ed852e2009-09-05 21:47:34 +0000278%
279% The format of the ReadBlobStringWithLongSize method is:
280%
281% char *ReadBlobStringWithLongSize(Image *image,char *string)
282%
283% A description of each parameter follows:
284%
285% o image: the image.
286%
287% o string: the address of a character buffer.
288%
289*/
290
291static inline size_t MagickMin(const size_t x,const size_t y)
292{
293 if (x < y)
294 return(x);
295 return(y);
296}
297
cristyc82a27b2011-10-21 01:07:16 +0000298static char *ReadBlobStringWithLongSize(Image *image,char *string,size_t max,
299 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000300{
301 int
302 c;
303
304 MagickOffsetType
305 offset;
306
cristybb503372010-05-27 20:51:26 +0000307 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000308 i;
309
cristybb503372010-05-27 20:51:26 +0000310 size_t
cristy3ed852e2009-09-05 21:47:34 +0000311 length;
312
313 assert(image != (Image *) NULL);
314 assert(image->signature == MagickSignature);
315 assert(max != 0);
316 if (image->debug != MagickFalse)
317 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
318 length=ReadBlobMSBLong(image);
cristybb503372010-05-27 20:51:26 +0000319 for (i=0; i < (ssize_t) MagickMin(length,max-1); i++)
cristy3ed852e2009-09-05 21:47:34 +0000320 {
321 c=ReadBlobByte(image);
322 if (c == EOF)
323 return((char *) NULL);
324 string[i]=(char) c;
325 }
326 string[i]='\0';
327 offset=SeekBlob(image,(MagickOffsetType) (length-i),SEEK_CUR);
328 if (offset < 0)
cristyc82a27b2011-10-21 01:07:16 +0000329 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +0000330 CorruptImageError,"ImproperImageHeader","`%s'",image->filename);
331 return(string);
332}
333
334static MagickBooleanType load_tile(Image *image,Image *tile_image,
cristy018f07f2011-09-04 21:15:19 +0000335 XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length,
336 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000337{
cristybb503372010-05-27 20:51:26 +0000338 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000339 y;
340
cristybb503372010-05-27 20:51:26 +0000341 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000342 x;
343
cristy4c08aed2011-07-01 19:47:50 +0000344 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000345 *q;
346
347 ssize_t
348 count;
349
350 unsigned char
351 *graydata;
352
cristy101ab702011-10-13 13:06:32 +0000353 XCFPixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000354 *xcfdata,
355 *xcfodata;
356
cristy101ab702011-10-13 13:06:32 +0000357 xcfdata=(XCFPixelInfo *) AcquireQuantumMemory(data_length,sizeof(*xcfdata));
358 if (xcfdata == (XCFPixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000359 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
360 image->filename);
361 xcfodata=xcfdata;
362 graydata=(unsigned char *) xcfdata; /* used by gray and indexed */
363 count=ReadBlob(image,data_length,(unsigned char *) xcfdata);
364 if (count != (ssize_t) data_length)
365 ThrowBinaryException(CorruptImageError,"NotEnoughPixelData",
366 image->filename);
cristybb503372010-05-27 20:51:26 +0000367 for (y=0; y < (ssize_t) tile_image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000368 {
cristy8415f062012-04-03 00:12:53 +0000369 q=GetAuthenticPixels(tile_image,0,y,tile_image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000370 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000371 break;
372 if (inDocInfo->image_type == GIMP_GRAY)
373 {
cristybb503372010-05-27 20:51:26 +0000374 for (x=0; x < (ssize_t) tile_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000375 {
cristyaf6eb932012-01-01 01:22:59 +0000376 SetPixelGray(tile_image,ScaleCharToQuantum(*graydata),q);
cristy4c08aed2011-07-01 19:47:50 +0000377 SetPixelAlpha(tile_image,ScaleCharToQuantum((unsigned char)
378 inLayerInfo->alpha),q);
cristy3ed852e2009-09-05 21:47:34 +0000379 graydata++;
cristyed231572011-07-14 02:18:59 +0000380 q+=GetPixelChannels(tile_image);
cristy3ed852e2009-09-05 21:47:34 +0000381 }
382 }
383 else
384 if (inDocInfo->image_type == GIMP_RGB)
385 {
cristybb503372010-05-27 20:51:26 +0000386 for (x=0; x < (ssize_t) tile_image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000387 {
cristy4c08aed2011-07-01 19:47:50 +0000388 SetPixelRed(tile_image,ScaleCharToQuantum(xcfdata->red),q);
389 SetPixelGreen(tile_image,ScaleCharToQuantum(xcfdata->green),q);
390 SetPixelBlue(tile_image,ScaleCharToQuantum(xcfdata->blue),q);
cristyff08add2012-04-02 00:33:26 +0000391 SetPixelAlpha(tile_image,xcfdata->alpha == 255U ? TransparentAlpha :
cristy4c08aed2011-07-01 19:47:50 +0000392 ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q);
cristy3ed852e2009-09-05 21:47:34 +0000393 xcfdata++;
cristyed231572011-07-14 02:18:59 +0000394 q+=GetPixelChannels(tile_image);
cristy3ed852e2009-09-05 21:47:34 +0000395 }
396 }
397 if (SyncAuthenticPixels(tile_image,exception) == MagickFalse)
398 break;
399 }
cristy101ab702011-10-13 13:06:32 +0000400 xcfodata=(XCFPixelInfo *) RelinquishMagickMemory(xcfodata);
cristy3ed852e2009-09-05 21:47:34 +0000401 return MagickTrue;
402}
403
404static MagickBooleanType load_tile_rle(Image *image,Image *tile_image,
cristy018f07f2011-09-04 21:15:19 +0000405 XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length,
406 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000407{
cristy3ed852e2009-09-05 21:47:34 +0000408 MagickOffsetType
409 size;
410
cristy2cbb3b32012-04-01 19:11:10 +0000411 Quantum
412 alpha;
413
cristy4c08aed2011-07-01 19:47:50 +0000414 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000415 *q;
416
cristy3ed852e2009-09-05 21:47:34 +0000417 size_t
418 length;
419
cristyc6da28e2011-04-28 01:41:35 +0000420 ssize_t
421 bytes_per_pixel,
422 count,
423 i,
424 j;
425
cristy3ed852e2009-09-05 21:47:34 +0000426 unsigned char
427 data,
428 pixel,
429 *xcfdata,
430 *xcfodata,
431 *xcfdatalimit;
432
433 bytes_per_pixel=(ssize_t) inDocInfo->bytes_per_pixel;
434 xcfdata=(unsigned char *) AcquireQuantumMemory(data_length,sizeof(*xcfdata));
435 if (xcfdata == (unsigned char *) NULL)
436 ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
437 image->filename);
438 xcfodata=xcfdata;
439 count=ReadBlob(image, (size_t) data_length, xcfdata);
440 xcfdatalimit = xcfodata+count-1;
cristy2cbb3b32012-04-01 19:11:10 +0000441 alpha=ScaleCharToQuantum((unsigned char) inLayerInfo->alpha);
cristybb503372010-05-27 20:51:26 +0000442 for (i=0; i < (ssize_t) bytes_per_pixel; i++)
cristy3ed852e2009-09-05 21:47:34 +0000443 {
cristy8415f062012-04-03 00:12:53 +0000444 q=GetAuthenticPixels(tile_image,0,0,tile_image->columns,tile_image->rows,
cristy2e8bde02010-06-22 11:03:25 +0000445 exception);
cristy2cbb3b32012-04-01 19:11:10 +0000446 if (q == (Quantum *) NULL)
447 continue;
cristy3ed852e2009-09-05 21:47:34 +0000448 size=(MagickOffsetType) tile_image->rows*tile_image->columns;
449 while (size > 0)
450 {
451 if (xcfdata > xcfdatalimit)
452 goto bogus_rle;
453 pixel=(*xcfdata++);
454 length=(size_t) pixel;
455 if (length >= 128)
456 {
457 length=255-(length-1);
458 if (length == 128)
459 {
460 if (xcfdata >= xcfdatalimit)
461 goto bogus_rle;
462 length=(size_t) ((*xcfdata << 8) + xcfdata[1]);
463 xcfdata+=2;
464 }
465 size-=length;
466 if (size < 0)
467 goto bogus_rle;
468 if (&xcfdata[length-1] > xcfdatalimit)
469 goto bogus_rle;
470 while (length-- > 0)
471 {
472 data=(*xcfdata++);
473 switch (i)
474 {
475 case 0:
476 {
cristyaf6eb932012-01-01 01:22:59 +0000477 if (inDocInfo->image_type != GIMP_GRAY)
cristy3ed852e2009-09-05 21:47:34 +0000478 {
cristyaf6eb932012-01-01 01:22:59 +0000479 SetPixelRed(tile_image,ScaleCharToQuantum(data),q);
cristy4c08aed2011-07-01 19:47:50 +0000480 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q);
481 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q);
cristy2cbb3b32012-04-01 19:11:10 +0000482 SetPixelAlpha(tile_image,data == 255U ? alpha :
483 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000484 }
485 else
486 {
cristyaf6eb932012-01-01 01:22:59 +0000487 SetPixelGray(tile_image,ScaleCharToQuantum(data),q);
cristy2cbb3b32012-04-01 19:11:10 +0000488 SetPixelAlpha(tile_image,data == 255U ? alpha :
489 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000490 }
491 break;
492 }
493 case 1:
494 {
cristy4c08aed2011-07-01 19:47:50 +0000495 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000496 break;
497 }
498 case 2:
499 {
cristy4c08aed2011-07-01 19:47:50 +0000500 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000501 break;
502 }
503 case 3:
504 {
cristy2cbb3b32012-04-01 19:11:10 +0000505 SetPixelAlpha(tile_image,data == 255U ? alpha :
506 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000507 break;
508 }
509 }
cristyed231572011-07-14 02:18:59 +0000510 q+=GetPixelChannels(tile_image);
cristy3ed852e2009-09-05 21:47:34 +0000511 }
512 }
513 else
514 {
515 length+=1;
516 if (length == 128)
517 {
518 if (xcfdata >= xcfdatalimit)
519 goto bogus_rle;
520 length=(size_t) ((*xcfdata << 8) + xcfdata[1]);
521 xcfdata+=2;
522 }
523 size-=length;
524 if (size < 0)
525 goto bogus_rle;
526 if (xcfdata > xcfdatalimit)
527 goto bogus_rle;
528 pixel=(*xcfdata++);
cristy2cbb3b32012-04-01 19:11:10 +0000529 for (j=0; j < (ssize_t) length; j++)
cristy3ed852e2009-09-05 21:47:34 +0000530 {
531 data=pixel;
532 switch (i)
533 {
534 case 0:
535 {
cristyaf6eb932012-01-01 01:22:59 +0000536 if (inDocInfo->image_type != GIMP_GRAY)
cristy3ed852e2009-09-05 21:47:34 +0000537 {
cristyaf6eb932012-01-01 01:22:59 +0000538 SetPixelRed(tile_image,ScaleCharToQuantum(data),q);
cristy4c08aed2011-07-01 19:47:50 +0000539 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q);
540 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q);
cristy2cbb3b32012-04-01 19:11:10 +0000541 SetPixelAlpha(tile_image,data == 255U ? alpha :
542 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000543 }
544 else
545 {
cristyaf6eb932012-01-01 01:22:59 +0000546 SetPixelGray(tile_image,ScaleCharToQuantum(data),q);
cristy2cbb3b32012-04-01 19:11:10 +0000547 SetPixelAlpha(tile_image,data == 255U ? alpha :
548 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000549 }
550 break;
551 }
552 case 1:
553 {
cristy4c08aed2011-07-01 19:47:50 +0000554 SetPixelGreen(tile_image,ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000555 break;
556 }
557 case 2:
558 {
cristy4c08aed2011-07-01 19:47:50 +0000559 SetPixelBlue(tile_image,ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000560 break;
561 }
562 case 3:
563 {
cristy2cbb3b32012-04-01 19:11:10 +0000564 SetPixelAlpha(tile_image,data == 255U ? alpha :
565 ScaleCharToQuantum(data),q);
cristy3ed852e2009-09-05 21:47:34 +0000566 break;
567 }
568 }
cristyed231572011-07-14 02:18:59 +0000569 q+=GetPixelChannels(tile_image);
cristy3ed852e2009-09-05 21:47:34 +0000570 }
571 }
572 }
573 if (SyncAuthenticPixels(tile_image,exception) == MagickFalse)
574 break;
575 }
576 xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata);
577 return(MagickTrue);
578
579 bogus_rle:
580 if (xcfodata != (unsigned char *) NULL)
581 xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata);
582 return(MagickFalse);
583}
584
585static MagickBooleanType load_level(Image *image,XCFDocInfo *inDocInfo,
cristy2cbb3b32012-04-01 19:11:10 +0000586 XCFLayerInfo *inLayerInfo,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000587{
cristy3ed852e2009-09-05 21:47:34 +0000588 int
589 destLeft = 0,
590 destTop = 0;
591
592 Image*
593 tile_image;
594
595 MagickBooleanType
596 status;
597
598 MagickOffsetType
599 saved_pos,
600 offset,
601 offset2;
602
cristybb503372010-05-27 20:51:26 +0000603 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000604 i;
605
cristybb503372010-05-27 20:51:26 +0000606 size_t
cristy3ed852e2009-09-05 21:47:34 +0000607 width,
608 height,
609 ntiles,
610 ntile_rows,
611 ntile_cols,
612 tile_image_width,
613 tile_image_height;
614
615 /* start reading the data */
cristy3ed852e2009-09-05 21:47:34 +0000616 width=ReadBlobMSBLong(image);
617 height=ReadBlobMSBLong(image);
618
619 /* read in the first tile offset.
620 * if it is '0', then this tile level is empty
621 * and we can simply return.
622 */
623 offset=(MagickOffsetType) ReadBlobMSBLong(image);
624 if (offset == 0)
625 return(MagickTrue);
626 /* Initialise the reference for the in-memory tile-compression
627 */
628 ntile_rows=(height+TILE_HEIGHT-1)/TILE_HEIGHT;
629 ntile_cols=(width+TILE_WIDTH-1)/TILE_WIDTH;
630 ntiles=ntile_rows*ntile_cols;
cristybb503372010-05-27 20:51:26 +0000631 for (i = 0; i < (ssize_t) ntiles; i++)
cristy3ed852e2009-09-05 21:47:34 +0000632 {
633 status=MagickFalse;
634 if (offset == 0)
635 ThrowBinaryException(CorruptImageError,"NotEnoughTiles",image->filename);
636 /* save the current position as it is where the
637 * next tile offset is stored.
638 */
639 saved_pos=TellBlob(image);
640 /* read in the offset of the next tile so we can calculate the amount
641 of data needed for this tile*/
642 offset2=(MagickOffsetType)ReadBlobMSBLong(image);
643 /* if the offset is 0 then we need to read in the maximum possible
644 allowing for negative compression */
645 if (offset2 == 0)
646 offset2=(MagickOffsetType) (offset + TILE_WIDTH * TILE_WIDTH * 4* 1.5);
647 /* seek to the tile offset */
648 offset=SeekBlob(image, offset, SEEK_SET);
649
cristy46e350e2012-02-11 18:56:14 +0000650 /*
651 Allocate the image for the tile. NOTE: the last tile in a row or
652 column may not be a full tile!
cristy3ed852e2009-09-05 21:47:34 +0000653 */
cristybb503372010-05-27 20:51:26 +0000654 tile_image_width=(size_t) (destLeft == (int) ntile_cols-1 ?
cristy3ed852e2009-09-05 21:47:34 +0000655 (int) width % TILE_WIDTH : TILE_WIDTH);
cristy46e350e2012-02-11 18:56:14 +0000656 if (tile_image_width == 0)
657 tile_image_width=TILE_WIDTH;
cristybb503372010-05-27 20:51:26 +0000658 tile_image_height = (size_t) (destTop == (int) ntile_rows-1 ?
cristy3ed852e2009-09-05 21:47:34 +0000659 (int) height % TILE_HEIGHT : TILE_HEIGHT);
cristy46e350e2012-02-11 18:56:14 +0000660 if (tile_image_height == 0)
661 tile_image_height=TILE_HEIGHT;
cristy3ed852e2009-09-05 21:47:34 +0000662 tile_image=CloneImage(inLayerInfo->image,tile_image_width,
663 tile_image_height,MagickTrue,exception);
664
665 /* read in the tile */
666 switch (inDocInfo->compression)
667 {
668 case COMPRESS_NONE:
cristy018f07f2011-09-04 21:15:19 +0000669 if (load_tile(image,tile_image,inDocInfo,inLayerInfo,(size_t) (offset2-offset),exception) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000670 status=MagickTrue;
671 break;
672 case COMPRESS_RLE:
673 if (load_tile_rle (image,tile_image,inDocInfo,inLayerInfo,
cristy018f07f2011-09-04 21:15:19 +0000674 (int) (offset2-offset),exception) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000675 status=MagickTrue;
676 break;
677 case COMPRESS_ZLIB:
678 ThrowBinaryException(CoderError,"ZipCompressNotSupported",
679 image->filename)
680 case COMPRESS_FRACTAL:
681 ThrowBinaryException(CoderError,"FractalCompressNotSupported",
682 image->filename)
683 }
684
685 /* composite the tile onto the layer's image, and then destroy it */
cristyfeb3e962012-03-29 17:25:55 +0000686 (void) CompositeImage(inLayerInfo->image,tile_image,CopyCompositeOp,
cristy547ca552012-03-31 16:15:43 +0000687 MagickTrue,destLeft * TILE_WIDTH,destTop*TILE_HEIGHT,exception);
cristy3ed852e2009-09-05 21:47:34 +0000688 tile_image=DestroyImage(tile_image);
689
690 /* adjust tile position */
691 destLeft++;
692 if (destLeft >= (int) ntile_cols)
693 {
694 destLeft = 0;
695 destTop++;
696 }
697 if (status != MagickFalse)
698 return(MagickFalse);
699 /* restore the saved position so we'll be ready to
700 * read the next offset.
701 */
702 offset=SeekBlob(image, saved_pos, SEEK_SET);
703 /* read in the offset of the next tile */
704 offset=(MagickOffsetType) ReadBlobMSBLong(image);
705 }
706 if (offset != 0)
707 ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename)
708 return(MagickTrue);
709}
710
711static MagickBooleanType load_hierarchy(Image *image,XCFDocInfo *inDocInfo,
cristy2cbb3b32012-04-01 19:11:10 +0000712 XCFLayerInfo *inLayer, ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000713{
714 MagickOffsetType
715 saved_pos,
716 offset,
717 junk;
718
cristybb503372010-05-27 20:51:26 +0000719 size_t
cristy3ed852e2009-09-05 21:47:34 +0000720 width,
721 height,
722 bytes_per_pixel;
723
724 width=ReadBlobMSBLong(image);
cristyda16f162011-02-19 23:52:17 +0000725 (void) width;
cristy3ed852e2009-09-05 21:47:34 +0000726 height=ReadBlobMSBLong(image);
cristyda16f162011-02-19 23:52:17 +0000727 (void) height;
cristy3ed852e2009-09-05 21:47:34 +0000728 bytes_per_pixel=inDocInfo->bytes_per_pixel=ReadBlobMSBLong(image);
cristyda16f162011-02-19 23:52:17 +0000729 (void) bytes_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000730
731 /* load in the levels...we make sure that the number of levels
732 * calculated when the TileManager was created is the same
733 * as the number of levels found in the file.
734 */
735 offset=(MagickOffsetType) ReadBlobMSBLong(image); /* top level */
736
737 /* discard offsets for layers below first, if any.
738 */
739 do
740 {
741 junk=(MagickOffsetType) ReadBlobMSBLong(image);
742 }
743 while (junk != 0);
744
745 /* save the current position as it is where the
746 * next level offset is stored.
747 */
748 saved_pos=TellBlob(image);
749
750 /* seek to the level offset */
751 offset=SeekBlob(image, offset, SEEK_SET);
752
753 /* read in the level */
cristy2cbb3b32012-04-01 19:11:10 +0000754 if (load_level (image, inDocInfo, inLayer, exception) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000755 return(MagickFalse);
756 /* restore the saved position so we'll be ready to
757 * read the next offset.
758 */
759 offset=SeekBlob(image, saved_pos, SEEK_SET);
760 return(MagickTrue);
761}
762
cristy2cbb3b32012-04-01 19:11:10 +0000763static MagickBooleanType ReadOneLayer(const ImageInfo *image_info,Image* image,
764 XCFDocInfo* inDocInfo,XCFLayerInfo *outLayer,const ssize_t layer,
765 ExceptionInfo *exception )
cristy3ed852e2009-09-05 21:47:34 +0000766{
cristy3ed852e2009-09-05 21:47:34 +0000767 MagickOffsetType
768 offset;
769
770 unsigned int
771 foundPropEnd = 0;
772
cristybb503372010-05-27 20:51:26 +0000773 size_t
cristy3ed852e2009-09-05 21:47:34 +0000774 hierarchy_offset,
775 layer_mask_offset;
776
777 /* clear the block! */
778 (void) ResetMagickMemory( outLayer, 0, sizeof( XCFLayerInfo ) );
779 /* read in the layer width, height, type and name */
780 outLayer->width = ReadBlobMSBLong(image);
781 outLayer->height = ReadBlobMSBLong(image);
782 outLayer->type = ReadBlobMSBLong(image);
783 (void) ReadBlobStringWithLongSize(image, outLayer->name,
cristyc82a27b2011-10-21 01:07:16 +0000784 sizeof(outLayer->name),exception);
cristy3ed852e2009-09-05 21:47:34 +0000785 /* read the layer properties! */
786 foundPropEnd = 0;
787 while ( (foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse) ) {
788 PropType prop_type = (PropType) ReadBlobMSBLong(image);
cristybb503372010-05-27 20:51:26 +0000789 size_t prop_size = ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000790 switch (prop_type)
791 {
792 case PROP_END:
793 foundPropEnd = 1;
794 break;
795 case PROP_ACTIVE_LAYER:
796 outLayer->active = 1;
797 break;
798 case PROP_FLOATING_SELECTION:
799 outLayer->floating_offset = ReadBlobMSBLong(image);
800 break;
801 case PROP_OPACITY:
cristy4c08aed2011-07-01 19:47:50 +0000802 outLayer->alpha = ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000803 break;
804 case PROP_VISIBLE:
805 outLayer->visible = ReadBlobMSBLong(image);
806 break;
807 case PROP_LINKED:
808 outLayer->linked = ReadBlobMSBLong(image);
809 break;
810 case PROP_PRESERVE_TRANSPARENCY:
811 outLayer->preserve_trans = ReadBlobMSBLong(image);
812 break;
813 case PROP_APPLY_MASK:
814 outLayer->apply_mask = ReadBlobMSBLong(image);
815 break;
816 case PROP_EDIT_MASK:
817 outLayer->edit_mask = ReadBlobMSBLong(image);
818 break;
819 case PROP_SHOW_MASK:
820 outLayer->show_mask = ReadBlobMSBLong(image);
821 break;
822 case PROP_OFFSETS:
cristy6cff05d2010-09-02 11:22:46 +0000823 outLayer->offset_x = (int) ReadBlobMSBLong(image);
824 outLayer->offset_y = (int) ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +0000825 break;
826 case PROP_MODE:
827 outLayer->mode = ReadBlobMSBLong(image);
828 break;
829 case PROP_TATTOO:
830 outLayer->preserve_trans = ReadBlobMSBLong(image);
831 break;
832 case PROP_PARASITES:
833 {
cristyd4297022010-09-16 22:59:09 +0000834 if (DiscardBlobBytes(image,prop_size) == MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +0000835 ThrowFileException(exception,CorruptImageError,
cristyd4297022010-09-16 22:59:09 +0000836 "UnexpectedEndOfFile",image->filename);
cristy3ed852e2009-09-05 21:47:34 +0000837
838 /*
cristybb503372010-05-27 20:51:26 +0000839 ssize_t base = info->cp;
cristy3ed852e2009-09-05 21:47:34 +0000840 GimpParasite *p;
841 while (info->cp - base < prop_size)
842 {
843 p = xcf_load_parasite(info);
844 gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p);
845 gimp_parasite_free(p);
846 }
847 if (info->cp - base != prop_size)
848 g_message ("Error detected while loading a layer's parasites");
849 */
850 }
851 break;
852 default:
853 /* g_message ("unexpected/unknown layer property: %d (skipping)",
854 prop_type); */
855
856 {
857 int buf[16];
858 ssize_t amount;
859
860 /* read over it... */
861 while ((prop_size > 0) && (EOFBlob(image) == MagickFalse))
862 {
863 amount = (ssize_t) MagickMin(16, prop_size);
864 amount = ReadBlob(image, (size_t) amount, (unsigned char *) &buf);
865 if (!amount)
866 ThrowBinaryException(CorruptImageError,"CorruptImage",
867 image->filename);
cristybb503372010-05-27 20:51:26 +0000868 prop_size -= (size_t) MagickMin(16, (size_t) amount);
cristy3ed852e2009-09-05 21:47:34 +0000869 }
870 }
871 break;
872 }
873 }
874
875 if (foundPropEnd == MagickFalse)
876 return(MagickFalse);
cristy2cbb3b32012-04-01 19:11:10 +0000877 /* allocate the image for this layer */
878 if (image_info->number_scenes != 0)
879 {
880 ssize_t
881 scene;
882
883 scene=inDocInfo->number_layers-layer-1;
cristyd9ecd042012-06-17 18:26:12 +0000884 if (scene > (ssize_t) (image_info->scene+image_info->number_scenes-1))
cristy2cbb3b32012-04-01 19:11:10 +0000885 {
886 outLayer->image=CloneImage(image,0,0,MagickTrue,exception);
cristy8415f062012-04-03 00:12:53 +0000887 if (outLayer->image == (Image *) NULL)
888 return(MagickFalse);
889 outLayer->image->page.x=outLayer->offset_x;
890 outLayer->image->page.y=outLayer->offset_y;
891 outLayer->image->page.width=outLayer->width;
892 outLayer->image->page.height=outLayer->height;
cristy2cbb3b32012-04-01 19:11:10 +0000893 return(MagickTrue);
894 }
895 }
896 outLayer->image=CloneImage(image,outLayer->width, outLayer->height,MagickTrue,
897 exception);
898 if (outLayer->image == (Image *) NULL)
cristy8415f062012-04-03 00:12:53 +0000899 return(MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000900 /* clear the image based on the layer opacity */
cristy4c08aed2011-07-01 19:47:50 +0000901 outLayer->image->background_color.alpha=
cristy547ca552012-03-31 16:15:43 +0000902 ScaleCharToQuantum((unsigned char) outLayer->alpha);
cristyea1a8aa2011-10-20 13:24:06 +0000903 (void) SetImageBackgroundColor(outLayer->image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000904
cristy8415f062012-04-03 00:12:53 +0000905 outLayer->image->page.x=outLayer->offset_x;
906 outLayer->image->page.y=outLayer->offset_y;
907 outLayer->image->page.width=outLayer->width;
908 outLayer->image->page.height=outLayer->height;
cristy3ed852e2009-09-05 21:47:34 +0000909 /* set the compositing mode */
910 outLayer->image->compose = GIMPBlendModeToCompositeOperator( outLayer->mode );
911 if ( outLayer->visible == MagickFalse )
912 {
913 /* BOGUS: should really be separate member var! */
914 outLayer->image->compose = NoCompositeOp;
915 }
916
917 /* read the hierarchy and layer mask offsets */
918 hierarchy_offset = ReadBlobMSBLong(image);
919 layer_mask_offset = ReadBlobMSBLong(image);
920
921 /* read in the hierarchy */
922 offset=SeekBlob(image, (MagickOffsetType) hierarchy_offset, SEEK_SET);
923 if (offset < 0)
cristyc82a27b2011-10-21 01:07:16 +0000924 (void) ThrowMagickException(exception,GetMagickModule(),
cristy3ed852e2009-09-05 21:47:34 +0000925 CorruptImageError,"InvalidImageHeader","`%s'",image->filename);
cristy2cbb3b32012-04-01 19:11:10 +0000926 if (load_hierarchy (image, inDocInfo, outLayer, exception) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000927 return(MagickFalse);
928
929 /* read in the layer mask */
930 if (layer_mask_offset != 0)
931 {
932 offset=SeekBlob(image, (MagickOffsetType) layer_mask_offset, SEEK_SET);
933
934#if 0 /* BOGUS: support layer masks! */
935 layer_mask = xcf_load_layer_mask (info, gimage);
936 if (layer_mask == 0)
937 goto error;
938
939 /* set the offsets of the layer_mask */
940 GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x;
941 GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y;
942
943 gimp_layer_add_mask (layer, layer_mask, MagickFalse);
944
945 layer->mask->apply_mask = apply_mask;
946 layer->mask->edit_mask = edit_mask;
947 layer->mask->show_mask = show_mask;
948#endif
949 }
950
951 /* attach the floating selection... */
952#if 0 /* BOGUS: we may need to read this, even if we don't support it! */
953 if (add_floating_sel)
954 {
955 GimpLayer *floating_sel;
956
957 floating_sel = info->floating_sel;
958 floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer));
959 }
960#endif
961
962 return MagickTrue;
963}
cristy8f0fbc02012-03-31 16:31:42 +0000964
cristy3ed852e2009-09-05 21:47:34 +0000965/*
966%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
967% %
968% %
969% %
970% R e a d X C F I m a g e %
971% %
972% %
973% %
974%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
975%
976% ReadXCFImage() reads a GIMP (GNU Image Manipulation Program) image
977% file and returns it. It allocates the memory necessary for the new Image
978% structure and returns a pointer to the new image.
979%
980% The format of the ReadXCFImage method is:
981%
982% image=ReadXCFImage(image_info)
983%
984% A description of each parameter follows:
985%
986% o image_info: the image info.
987%
988% o exception: return any errors or warnings in this structure.
989%
cristy3ed852e2009-09-05 21:47:34 +0000990*/
991static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception)
992{
993 char
994 magick[14];
995
996 Image
997 *image;
998
999 int
1000 foundPropEnd = 0;
1001
1002 MagickBooleanType
1003 status;
1004
1005 MagickOffsetType
1006 offset;
1007
cristybb503372010-05-27 20:51:26 +00001008 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +00001009 i;
1010
1011 size_t
cristyc6da28e2011-04-28 01:41:35 +00001012 image_type,
cristy3ed852e2009-09-05 21:47:34 +00001013 length;
1014
1015 ssize_t
1016 count;
1017
cristy3ed852e2009-09-05 21:47:34 +00001018 XCFDocInfo
1019 doc_info;
1020
1021 /*
1022 Open image file.
1023 */
1024 assert(image_info != (const ImageInfo *) NULL);
1025 assert(image_info->signature == MagickSignature);
1026 if (image_info->debug != MagickFalse)
1027 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1028 image_info->filename);
1029 assert(exception != (ExceptionInfo *) NULL);
1030 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +00001031 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +00001032 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1033 if (status == MagickFalse)
1034 {
1035 image=DestroyImageList(image);
1036 return((Image *) NULL);
1037 }
1038 count=ReadBlob(image,14,(unsigned char *) magick);
1039 if ((count == 0) ||
1040 (LocaleNCompare((char *) magick,"gimp xcf",8) != 0))
1041 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1042 (void) ResetMagickMemory(&doc_info,0,sizeof(XCFDocInfo));
cristy3ed852e2009-09-05 21:47:34 +00001043 doc_info.width=ReadBlobMSBLong(image);
1044 doc_info.height=ReadBlobMSBLong(image);
1045 if ((doc_info.width > 262144) || (doc_info.height > 262144))
1046 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1047 doc_info.image_type=ReadBlobMSBLong(image);
1048 /*
1049 Initialize image attributes.
1050 */
1051 image->columns=doc_info.width;
1052 image->rows=doc_info.height;
1053 image_type=doc_info.image_type;
1054 doc_info.file_size=GetBlobSize(image);
1055 image->compression=NoCompression;
1056 image->depth=8;
1057 if (image_type == GIMP_RGB)
cristye2c4f182012-05-12 14:11:53 +00001058 SetImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001059 else
1060 if (image_type == GIMP_GRAY)
cristye2c4f182012-05-12 14:11:53 +00001061 SetImageColorspace(image,GRAYColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +00001062 else
1063 if (image_type == GIMP_INDEXED)
1064 ThrowReaderException(CoderError,"ColormapTypeNotSupported");
cristy547ca552012-03-31 16:15:43 +00001065 (void) SetImageBackgroundColor(image,exception);
cristy8a46d822012-08-28 23:32:39 +00001066 image->alpha_trait=BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +00001067 /*
1068 Read properties.
1069 */
1070 while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse))
1071 {
1072 PropType prop_type = (PropType) ReadBlobMSBLong(image);
cristybb503372010-05-27 20:51:26 +00001073 size_t prop_size = ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +00001074
1075 switch (prop_type)
1076 {
1077 case PROP_END:
1078 foundPropEnd=1;
1079 break;
1080 case PROP_COLORMAP:
1081 {
1082 /* Cannot rely on prop_size here--the value is set incorrectly
1083 by some Gimp versions.
1084 */
cristybb503372010-05-27 20:51:26 +00001085 size_t num_colours = ReadBlobMSBLong(image);
cristyd4297022010-09-16 22:59:09 +00001086 if (DiscardBlobBytes(image,3*num_colours) == MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +00001087 ThrowFileException(exception,CorruptImageError,
cristyd4297022010-09-16 22:59:09 +00001088 "UnexpectedEndOfFile",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001089 /*
1090 if (info->file_version == 0)
1091 {
1092 gint i;
1093
1094 g_message (_("XCF warning: version 0 of XCF file format\n"
1095 "did not save indexed colormaps correctly.\n"
1096 "Substituting grayscale map."));
1097 info->cp +=
1098 xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
1099 gimage->cmap = g_new (guchar, gimage->num_cols*3);
1100 xcf_seek_pos (info, info->cp + gimage->num_cols);
1101 for (i = 0; i<gimage->num_cols; i++)
1102 {
1103 gimage->cmap[i*3+0] = i;
1104 gimage->cmap[i*3+1] = i;
1105 gimage->cmap[i*3+2] = i;
1106 }
1107 }
1108 else
1109 {
1110 info->cp +=
1111 xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1);
1112 gimage->cmap = g_new (guchar, gimage->num_cols*3);
1113 info->cp +=
1114 xcf_read_int8 (info->fp,
1115 (guint8*) gimage->cmap, gimage->num_cols*3);
1116 }
1117 */
1118 break;
1119 }
1120 case PROP_COMPRESSION:
1121 {
1122 doc_info.compression = ReadBlobByte(image);
1123 if ((doc_info.compression != COMPRESS_NONE) &&
1124 (doc_info.compression != COMPRESS_RLE) &&
1125 (doc_info.compression != COMPRESS_ZLIB) &&
1126 (doc_info.compression != COMPRESS_FRACTAL))
1127 ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression");
1128 }
1129 break;
1130
1131 case PROP_GUIDES:
1132 {
1133 /* just skip it - we don't care about guides */
cristyd4297022010-09-16 22:59:09 +00001134 if (DiscardBlobBytes(image,prop_size) == MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +00001135 ThrowFileException(exception,CorruptImageError,
cristyd4297022010-09-16 22:59:09 +00001136 "UnexpectedEndOfFile",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001137 }
1138 break;
1139
1140 case PROP_RESOLUTION:
1141 {
1142 /* float xres = (float) */ (void) ReadBlobMSBLong(image);
1143 /* float yres = (float) */ (void) ReadBlobMSBLong(image);
1144
1145 /*
1146 if (xres < GIMP_MIN_RESOLUTION || xres > GIMP_MAX_RESOLUTION ||
1147 yres < GIMP_MIN_RESOLUTION || yres > GIMP_MAX_RESOLUTION)
1148 {
1149 g_message ("Warning, resolution out of range in XCF file");
1150 xres = gimage->gimp->config->default_xresolution;
1151 yres = gimage->gimp->config->default_yresolution;
1152 }
1153 */
1154
1155
1156 /* BOGUS: we don't write these yet because we aren't
1157 reading them properly yet :(
cristy2a11bef2011-10-28 18:33:11 +00001158 image->resolution.x = xres;
1159 image->resolution.y = yres;
cristy3ed852e2009-09-05 21:47:34 +00001160 */
1161 }
1162 break;
1163
1164 case PROP_TATTOO:
1165 {
1166 /* we need to read it, even if we ignore it */
cristybb503372010-05-27 20:51:26 +00001167 /*size_t tattoo_state = */ (void) ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +00001168 }
1169 break;
1170
1171 case PROP_PARASITES:
1172 {
1173 /* BOGUS: we may need these for IPTC stuff */
cristyd4297022010-09-16 22:59:09 +00001174 if (DiscardBlobBytes(image,prop_size) == MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +00001175 ThrowFileException(exception,CorruptImageError,
cristyd4297022010-09-16 22:59:09 +00001176 "UnexpectedEndOfFile",image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001177 /*
cristybb503372010-05-27 20:51:26 +00001178 gssize_t base = info->cp;
cristy3ed852e2009-09-05 21:47:34 +00001179 GimpParasite *p;
1180
1181 while (info->cp - base < prop_size)
1182 {
1183 p = xcf_load_parasite (info);
1184 gimp_image_parasite_attach (gimage, p);
1185 gimp_parasite_free (p);
1186 }
1187 if (info->cp - base != prop_size)
1188 g_message ("Error detected while loading an image's parasites");
1189 */
1190 }
1191 break;
1192
1193 case PROP_UNIT:
1194 {
1195 /* BOGUS: ignore for now... */
cristybb503372010-05-27 20:51:26 +00001196 /*size_t unit = */ (void) ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +00001197 }
1198 break;
1199
1200 case PROP_PATHS:
1201 {
1202 /* BOGUS: just skip it for now */
cristyd4297022010-09-16 22:59:09 +00001203 if (DiscardBlobBytes(image,prop_size) == MagickFalse)
cristyc82a27b2011-10-21 01:07:16 +00001204 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
1205 image->filename);
cristy3ed852e2009-09-05 21:47:34 +00001206
1207 /*
1208 PathList *paths = xcf_load_bzpaths (gimage, info);
1209 gimp_image_set_paths (gimage, paths);
1210 */
1211 }
1212 break;
1213
1214 case PROP_USER_UNIT:
1215 {
1216 char unit_string[1000];
1217 /*BOGUS: ignored for now */
1218 /*float factor = (float) */ (void) ReadBlobMSBLong(image);
cristybb503372010-05-27 20:51:26 +00001219 /* size_t digits = */ (void) ReadBlobMSBLong(image);
cristy547ca552012-03-31 16:15:43 +00001220 for (i=0; i<5; i++)
1221 (void) ReadBlobStringWithLongSize(image, unit_string,
cristyc82a27b2011-10-21 01:07:16 +00001222 sizeof(unit_string),exception);
cristy3ed852e2009-09-05 21:47:34 +00001223 }
1224 break;
1225
1226 default:
1227 {
1228 int buf[16];
cristybb503372010-05-27 20:51:26 +00001229 ssize_t amount;
cristy3ed852e2009-09-05 21:47:34 +00001230
1231 /* read over it... */
1232 while ((prop_size > 0) && (EOFBlob(image) == MagickFalse))
1233 {
cristybb503372010-05-27 20:51:26 +00001234 amount=(ssize_t) MagickMin(16, prop_size);
1235 amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf);
cristy3ed852e2009-09-05 21:47:34 +00001236 if (!amount)
1237 ThrowReaderException(CorruptImageError,"CorruptImage");
cristybb503372010-05-27 20:51:26 +00001238 prop_size -= (size_t) MagickMin(16,(size_t) amount);
cristy3ed852e2009-09-05 21:47:34 +00001239 }
1240 }
1241 break;
1242 }
1243 }
1244 if (foundPropEnd == MagickFalse)
1245 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1246
1247 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
1248 {
1249 ; /* do nothing, were just pinging! */
1250 }
1251 else
1252 {
1253 int
1254 current_layer = 0,
cristy547ca552012-03-31 16:15:43 +00001255 foundAllLayers = MagickFalse,
1256 number_layers = 0;
cristy3ed852e2009-09-05 21:47:34 +00001257
1258 MagickOffsetType
cristy547ca552012-03-31 16:15:43 +00001259 oldPos=TellBlob(image);
cristy3ed852e2009-09-05 21:47:34 +00001260
1261 XCFLayerInfo
1262 *layer_info;
1263
1264 /*
1265 the read pointer
1266 */
1267 do
1268 {
cristy6cff05d2010-09-02 11:22:46 +00001269 ssize_t offset = (int) ReadBlobMSBLong(image);
cristy3ed852e2009-09-05 21:47:34 +00001270 if (offset == 0)
1271 foundAllLayers=MagickTrue;
1272 else
1273 number_layers++;
1274 if (EOFBlob(image) != MagickFalse)
1275 {
1276 ThrowFileException(exception,CorruptImageError,
1277 "UnexpectedEndOfFile",image->filename);
1278 break;
1279 }
1280 } while (foundAllLayers == MagickFalse);
cristy2cbb3b32012-04-01 19:11:10 +00001281 doc_info.number_layers=number_layers;
cristy3ed852e2009-09-05 21:47:34 +00001282 offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */
1283 if (offset < 0)
1284 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
1285 /* allocate our array of layer info blocks */
1286 length=(size_t) number_layers;
1287 layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length,
1288 sizeof(*layer_info));
1289 if (layer_info == (XCFLayerInfo *) NULL)
1290 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1291 (void) ResetMagickMemory(layer_info,0,number_layers*sizeof(XCFLayerInfo));
1292 for ( ; ; )
1293 {
1294 MagickBooleanType
1295 layer_ok;
1296
1297 MagickOffsetType
1298 offset,
1299 saved_pos;
1300
1301 /* read in the offset of the next layer */
1302 offset=(MagickOffsetType) ReadBlobMSBLong(image);
1303 /* if the offset is 0 then we are at the end
1304 * of the layer list.
1305 */
1306 if (offset == 0)
1307 break;
1308 /* save the current position as it is where the
1309 * next layer offset is stored.
1310 */
1311 saved_pos=TellBlob(image);
cristy547ca552012-03-31 16:15:43 +00001312 /* seek to the layer offset */
1313 offset=SeekBlob(image,offset,SEEK_SET);
1314 /* read in the layer */
cristy2cbb3b32012-04-01 19:11:10 +00001315 layer_ok=ReadOneLayer(image_info,image,&doc_info,
1316 &layer_info[current_layer],current_layer,exception);
cristy547ca552012-03-31 16:15:43 +00001317 if (layer_ok == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001318 {
cristy547ca552012-03-31 16:15:43 +00001319 int j;
1320
1321 for (j=0; j < current_layer; j++)
1322 layer_info[j].image=DestroyImage(layer_info[j].image);
cristy2cbb3b32012-04-01 19:11:10 +00001323 layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info);
1324 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1325 }
cristy547ca552012-03-31 16:15:43 +00001326 /* restore the saved position so we'll be ready to
1327 * read the next offset.
1328 */
1329 offset=SeekBlob(image, saved_pos, SEEK_SET);
1330 current_layer++;
1331 }
cristy3ed852e2009-09-05 21:47:34 +00001332 if (number_layers == 1)
1333 {
1334 /*
1335 Composite the layer data onto the main image, dispose the layer.
1336 */
cristyfeb3e962012-03-29 17:25:55 +00001337 (void) CompositeImage(image,layer_info[0].image,OverCompositeOp,
cristy39172402012-03-30 13:04:39 +00001338 MagickTrue,layer_info[0].offset_x,layer_info[0].offset_y,exception);
cristy547ca552012-03-31 16:15:43 +00001339 layer_info[0].image =DestroyImage( layer_info[0].image);
cristy3ed852e2009-09-05 21:47:34 +00001340 }
1341 else
1342 {
1343#if 0
1344 {
1345 /* NOTE: XCF layers are REVERSED from composite order! */
1346 signed int j;
1347 for (j=number_layers-1; j>=0; j--) {
1348 /* BOGUS: need to consider layer blending modes!! */
1349
1350 if ( layer_info[j].visible ) { /* only visible ones, please! */
cristy547ca552012-03-31 16:15:43 +00001351 CompositeImage(image, OverCompositeOp, layer_info[j].image,
1352 layer_info[j].offset_x, layer_info[j].offset_y );
cristy3ed852e2009-09-05 21:47:34 +00001353 layer_info[j].image =DestroyImage( layer_info[j].image );
1354
cristy8f0fbc02012-03-31 16:31:42 +00001355 /* If we do this, we'll get REAL gray images! */
cristy3ed852e2009-09-05 21:47:34 +00001356 if ( image_type == GIMP_GRAY ) {
1357 QuantizeInfo qi;
1358 GetQuantizeInfo(&qi);
1359 qi.colorspace = GRAYColorspace;
1360 QuantizeImage( &qi, layer_info[j].image );
1361 }
1362 }
1363 }
1364 }
1365#else
1366 {
1367 /* NOTE: XCF layers are REVERSED from composite order! */
cristy8415f062012-04-03 00:12:53 +00001368 ssize_t j;
cristy3ed852e2009-09-05 21:47:34 +00001369
1370 /* first we copy the last layer on top of the main image */
cristyfeb3e962012-03-29 17:25:55 +00001371 (void) CompositeImage(image,layer_info[number_layers-1].image,
cristy39172402012-03-30 13:04:39 +00001372 CopyCompositeOp,MagickTrue,layer_info[number_layers-1].offset_x,
cristye941a752011-10-15 01:52:48 +00001373 layer_info[number_layers-1].offset_y,exception);
cristy547ca552012-03-31 16:15:43 +00001374 layer_info[number_layers-1].image=DestroyImage(
1375 layer_info[number_layers-1].image);
cristy3ed852e2009-09-05 21:47:34 +00001376
1377 /* now reverse the order of the layers as they are put
1378 into subimages
1379 */
cristy8415f062012-04-03 00:12:53 +00001380 for (j=(ssize_t) number_layers-2; j >= 0; j--)
1381 AppendImageToList(&image,layer_info[j].image);
cristy3ed852e2009-09-05 21:47:34 +00001382 }
1383#endif
1384 }
1385
1386 layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info);
1387
1388#if 0 /* BOGUS: do we need the channels?? */
1389 while (MagickTrue)
1390 {
1391 /* read in the offset of the next channel */
1392 info->cp += xcf_read_int32 (info->fp, &offset, 1);
1393
1394 /* if the offset is 0 then we are at the end
1395 * of the channel list.
1396 */
1397 if (offset == 0)
1398 break;
1399
1400 /* save the current position as it is where the
1401 * next channel offset is stored.
1402 */
1403 saved_pos = info->cp;
1404
1405 /* seek to the channel offset */
1406 xcf_seek_pos (info, offset);
1407
1408 /* read in the layer */
1409 channel = xcf_load_channel (info, gimage);
1410 if (channel == 0)
1411 goto error;
1412
1413 num_successful_elements++;
1414
1415 /* add the channel to the image if its not the selection */
1416 if (channel != gimage->selection_mask)
1417 gimp_image_add_channel (gimage, channel, -1);
1418
1419 /* restore the saved position so we'll be ready to
1420 * read the next offset.
1421 */
1422 xcf_seek_pos (info, saved_pos);
1423 }
1424#endif
1425 }
1426
1427 (void) CloseBlob(image);
1428 if (image_type == GIMP_GRAY)
1429 image->type=GrayscaleType;
1430 return(GetFirstImageInList(image));
1431}
1432
1433/*
1434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435% %
1436% %
1437% %
1438% R e g i s t e r X C F I m a g e %
1439% %
1440% %
1441% %
1442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443%
1444% RegisterXCFImage() adds attributes for the XCF image format to
1445% the list of supported formats. The attributes include the image format
1446% tag, a method to read and/or write the format, whether the format
1447% supports the saving of more than one frame to the same file or blob,
1448% whether the format supports native in-memory I/O, and a brief
1449% description of the format.
1450%
1451% The format of the RegisterXCFImage method is:
1452%
cristybb503372010-05-27 20:51:26 +00001453% size_t RegisterXCFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001454%
1455*/
cristybb503372010-05-27 20:51:26 +00001456ModuleExport size_t RegisterXCFImage(void)
cristy3ed852e2009-09-05 21:47:34 +00001457{
1458 MagickInfo
1459 *entry;
1460
1461 entry=SetMagickInfo("XCF");
1462 entry->decoder=(DecodeImageHandler *) ReadXCFImage;
1463 entry->magick=(IsImageFormatHandler *) IsXCF;
1464 entry->description=ConstantString("GIMP image");
1465 entry->module=ConstantString("XCF");
1466 entry->seekable_stream=MagickTrue;
1467 (void) RegisterMagickInfo(entry);
1468 return(MagickImageCoderSignature);
1469}
1470
1471/*
1472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473% %
1474% %
1475% %
1476% U n r e g i s t e r X C F I m a g e %
1477% %
1478% %
1479% %
1480%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1481%
1482% UnregisterXCFImage() removes format registrations made by the
1483% XCF module from the list of supported formats.
1484%
1485% The format of the UnregisterXCFImage method is:
1486%
1487% UnregisterXCFImage(void)
1488%
1489*/
1490ModuleExport void UnregisterXCFImage(void)
1491{
1492 (void) UnregisterMagickInfo("XCF");
1493}