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