/src/imagemagick/coders/xcf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
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 | | % % |
20 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
21 | | % 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 | | % https://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 | | */ |
42 | | #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/property.h" |
58 | | #include "MagickCore/quantize.h" |
59 | | #include "MagickCore/quantum-private.h" |
60 | | #include "MagickCore/resource_.h" |
61 | | #include "MagickCore/static.h" |
62 | | #include "MagickCore/string_.h" |
63 | | #include "MagickCore/string-private.h" |
64 | | #include "MagickCore/module.h" |
65 | | |
66 | | /* |
67 | | Typedef declarations. |
68 | | */ |
69 | | typedef enum |
70 | | { |
71 | | GIMP_RGB, |
72 | | GIMP_GRAY, |
73 | | GIMP_INDEXED |
74 | | } GimpImageBaseType; |
75 | | |
76 | | typedef enum |
77 | | { |
78 | | PROP_END = 0, |
79 | | PROP_COLORMAP = 1, |
80 | | PROP_ACTIVE_LAYER = 2, |
81 | | PROP_ACTIVE_CHANNEL = 3, |
82 | | PROP_SELECTION = 4, |
83 | | PROP_FLOATING_SELECTION = 5, |
84 | | PROP_OPACITY = 6, |
85 | | PROP_MODE = 7, |
86 | | PROP_VISIBLE = 8, |
87 | | PROP_LINKED = 9, |
88 | | PROP_PRESERVE_TRANSPARENCY = 10, |
89 | | PROP_APPLY_MASK = 11, |
90 | | PROP_EDIT_MASK = 12, |
91 | | PROP_SHOW_MASK = 13, |
92 | | PROP_SHOW_MASKED = 14, |
93 | | PROP_OFFSETS = 15, |
94 | | PROP_COLOR = 16, |
95 | | PROP_COMPRESSION = 17, |
96 | | PROP_GUIDES = 18, |
97 | | PROP_RESOLUTION = 19, |
98 | | PROP_TATTOO = 20, |
99 | | PROP_PARASITES = 21, |
100 | | PROP_UNIT = 22, |
101 | | PROP_PATHS = 23, |
102 | | PROP_USER_UNIT = 24 |
103 | | } PropType; |
104 | | |
105 | | typedef enum |
106 | | { |
107 | | COMPRESS_NONE = 0, |
108 | | COMPRESS_RLE = 1, |
109 | | COMPRESS_ZLIB = 2, /* unused */ |
110 | | COMPRESS_FRACTAL = 3 /* unused */ |
111 | | } XcfCompressionType; |
112 | | |
113 | | typedef struct |
114 | | { |
115 | | size_t |
116 | | version, |
117 | | width, |
118 | | height, |
119 | | image_type, |
120 | | bytes_per_pixel; |
121 | | |
122 | | int |
123 | | compression; |
124 | | |
125 | | size_t |
126 | | file_size; |
127 | | |
128 | | size_t |
129 | | number_layers; |
130 | | } XCFDocInfo; |
131 | | |
132 | | typedef struct |
133 | | { |
134 | | char |
135 | | name[1024]; |
136 | | |
137 | | unsigned int |
138 | | active; |
139 | | |
140 | | size_t |
141 | | width, |
142 | | height, |
143 | | type, |
144 | | alpha, |
145 | | visible, |
146 | | linked, |
147 | | preserve_trans, |
148 | | apply_mask, |
149 | | show_mask, |
150 | | edit_mask, |
151 | | floating_offset; |
152 | | |
153 | | ssize_t |
154 | | offset_x, |
155 | | offset_y; |
156 | | |
157 | | size_t |
158 | | mode, |
159 | | tattoo; |
160 | | |
161 | | Image |
162 | | *image; |
163 | | } XCFLayerInfo; |
164 | | |
165 | 20.0k | #define TILE_WIDTH 64 |
166 | 15.5k | #define TILE_HEIGHT 64 |
167 | 1.75k | #define GIMP_MIN_RESOLUTION 5e-3f |
168 | 1.20k | #define GIMP_MAX_RESOLUTION 65536.0f |
169 | | |
170 | | typedef struct |
171 | | { |
172 | | unsigned char |
173 | | red, |
174 | | green, |
175 | | blue, |
176 | | alpha; |
177 | | } XCFPixelInfo; |
178 | | |
179 | | /* |
180 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
181 | | % % |
182 | | % % |
183 | | % % |
184 | | % I s X C F % |
185 | | % % |
186 | | % % |
187 | | % % |
188 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
189 | | % |
190 | | % IsXCF() returns MagickTrue if the image format type, identified by the |
191 | | % magick string, is XCF (GIMP native format). |
192 | | % |
193 | | % The format of the IsXCF method is: |
194 | | % |
195 | | % MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) |
196 | | % |
197 | | % A description of each parameter follows: |
198 | | % |
199 | | % o magick: compare image format pattern against these bytes. |
200 | | % |
201 | | % o length: Specifies the length of the magick string. |
202 | | % |
203 | | % |
204 | | */ |
205 | | static MagickBooleanType IsXCF(const unsigned char *magick,const size_t length) |
206 | 0 | { |
207 | 0 | if (length < 8) |
208 | 0 | return(MagickFalse); |
209 | 0 | if (LocaleNCompare((char *) magick,"gimp xcf",8) == 0) |
210 | 0 | return(MagickTrue); |
211 | 0 | return(MagickFalse); |
212 | 0 | } |
213 | | |
214 | | typedef enum |
215 | | { |
216 | | GIMP_LAYER_MODE_NORMAL_LEGACY, |
217 | | GIMP_LAYER_MODE_DISSOLVE, |
218 | | GIMP_LAYER_MODE_BEHIND_LEGACY, |
219 | | GIMP_LAYER_MODE_MULTIPLY_LEGACY, |
220 | | GIMP_LAYER_MODE_SCREEN_LEGACY, |
221 | | GIMP_LAYER_MODE_OVERLAY_LEGACY, |
222 | | GIMP_LAYER_MODE_DIFFERENCE_LEGACY, |
223 | | GIMP_LAYER_MODE_ADDITION_LEGACY, |
224 | | GIMP_LAYER_MODE_SUBTRACT_LEGACY, |
225 | | GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY, |
226 | | GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY, |
227 | | GIMP_LAYER_MODE_HSV_HUE_LEGACY, |
228 | | GIMP_LAYER_MODE_HSV_SATURATION_LEGACY, |
229 | | GIMP_LAYER_MODE_HSL_COLOR_LEGACY, |
230 | | GIMP_LAYER_MODE_HSV_VALUE_LEGACY, |
231 | | GIMP_LAYER_MODE_DIVIDE_LEGACY, |
232 | | GIMP_LAYER_MODE_DODGE_LEGACY, |
233 | | GIMP_LAYER_MODE_BURN_LEGACY, |
234 | | GIMP_LAYER_MODE_HARDLIGHT_LEGACY, |
235 | | GIMP_LAYER_MODE_SOFTLIGHT_LEGACY, |
236 | | GIMP_LAYER_MODE_GRAIN_EXTRACT_LEGACY, |
237 | | GIMP_LAYER_MODE_GRAIN_MERGE_LEGACY, |
238 | | GIMP_LAYER_MODE_COLOR_ERASE_LEGACY, |
239 | | GIMP_LAYER_MODE_OVERLAY, |
240 | | GIMP_LAYER_MODE_LCH_HUE, |
241 | | GIMP_LAYER_MODE_LCH_CHROMA, |
242 | | GIMP_LAYER_MODE_LCH_COLOR, |
243 | | GIMP_LAYER_MODE_LCH_LIGHTNESS, |
244 | | GIMP_LAYER_MODE_NORMAL, |
245 | | GIMP_LAYER_MODE_BEHIND, |
246 | | GIMP_LAYER_MODE_MULTIPLY, |
247 | | GIMP_LAYER_MODE_SCREEN, |
248 | | GIMP_LAYER_MODE_DIFFERENCE, |
249 | | GIMP_LAYER_MODE_ADDITION, |
250 | | GIMP_LAYER_MODE_SUBTRACT, |
251 | | GIMP_LAYER_MODE_DARKEN_ONLY, |
252 | | GIMP_LAYER_MODE_LIGHTEN_ONLY, |
253 | | GIMP_LAYER_MODE_HSV_HUE, |
254 | | GIMP_LAYER_MODE_HSV_SATURATION, |
255 | | GIMP_LAYER_MODE_HSL_COLOR, |
256 | | GIMP_LAYER_MODE_HSV_VALUE, |
257 | | GIMP_LAYER_MODE_DIVIDE, |
258 | | GIMP_LAYER_MODE_DODGE, |
259 | | GIMP_LAYER_MODE_BURN, |
260 | | GIMP_LAYER_MODE_HARDLIGHT, |
261 | | GIMP_LAYER_MODE_SOFTLIGHT, |
262 | | GIMP_LAYER_MODE_GRAIN_EXTRACT, |
263 | | GIMP_LAYER_MODE_GRAIN_MERGE, |
264 | | GIMP_LAYER_MODE_VIVID_LIGHT, |
265 | | GIMP_LAYER_MODE_PIN_LIGHT, |
266 | | GIMP_LAYER_MODE_LINEAR_LIGHT, |
267 | | GIMP_LAYER_MODE_HARD_MIX, |
268 | | GIMP_LAYER_MODE_EXCLUSION, |
269 | | GIMP_LAYER_MODE_LINEAR_BURN, |
270 | | GIMP_LAYER_MODE_LUMA_DARKEN_ONLY, |
271 | | GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY, |
272 | | GIMP_LAYER_MODE_LUMINANCE, |
273 | | GIMP_LAYER_MODE_COLOR_ERASE, |
274 | | GIMP_LAYER_MODE_ERASE, |
275 | | GIMP_LAYER_MODE_MERGE, |
276 | | GIMP_LAYER_MODE_SPLIT, |
277 | | GIMP_LAYER_MODE_PASS_THROUGH, |
278 | | } GimpLayerMode; |
279 | | |
280 | | /* |
281 | | Simple utility routine to convert between PSD blending modes and |
282 | | ImageMagick compositing operators |
283 | | */ |
284 | | static CompositeOperator GIMPBlendModeToCompositeOperator( |
285 | | size_t blendMode) |
286 | 4.89k | { |
287 | 4.89k | switch ( blendMode ) |
288 | 4.89k | { |
289 | 4.79k | case GIMP_LAYER_MODE_NORMAL_LEGACY: |
290 | 4.79k | case GIMP_LAYER_MODE_NORMAL: |
291 | 4.79k | return(OverCompositeOp); |
292 | 17 | case GIMP_LAYER_MODE_DISSOLVE: |
293 | 17 | return(DissolveCompositeOp); |
294 | 4 | case GIMP_LAYER_MODE_MULTIPLY_LEGACY: |
295 | 4 | case GIMP_LAYER_MODE_MULTIPLY: |
296 | 4 | return(MultiplyCompositeOp); |
297 | 0 | case GIMP_LAYER_MODE_SCREEN_LEGACY: |
298 | 0 | case GIMP_LAYER_MODE_SCREEN: |
299 | 0 | return(ScreenCompositeOp); |
300 | 1 | case GIMP_LAYER_MODE_OVERLAY_LEGACY: |
301 | 1 | case GIMP_LAYER_MODE_OVERLAY: |
302 | 1 | return(OverlayCompositeOp); |
303 | 0 | case GIMP_LAYER_MODE_DIFFERENCE_LEGACY: |
304 | 1 | case GIMP_LAYER_MODE_DIFFERENCE: |
305 | 1 | return(DifferenceCompositeOp); |
306 | 0 | case GIMP_LAYER_MODE_ADDITION_LEGACY: |
307 | 0 | case GIMP_LAYER_MODE_ADDITION: |
308 | 0 | return(ModulusAddCompositeOp); |
309 | 0 | case GIMP_LAYER_MODE_SUBTRACT_LEGACY: |
310 | 0 | case GIMP_LAYER_MODE_SUBTRACT: |
311 | 0 | return(ModulusSubtractCompositeOp); |
312 | 1 | case GIMP_LAYER_MODE_DARKEN_ONLY_LEGACY: |
313 | 1 | case GIMP_LAYER_MODE_DARKEN_ONLY: |
314 | 10 | case GIMP_LAYER_MODE_LUMA_DARKEN_ONLY: |
315 | 10 | return(DarkenCompositeOp); |
316 | 0 | case GIMP_LAYER_MODE_LIGHTEN_ONLY_LEGACY: |
317 | 0 | case GIMP_LAYER_MODE_LIGHTEN_ONLY: |
318 | 0 | return(LightenCompositeOp); |
319 | 0 | case GIMP_LAYER_MODE_LUMA_LIGHTEN_ONLY: |
320 | 0 | return(LightenCompositeOp); |
321 | 0 | case GIMP_LAYER_MODE_HSV_HUE_LEGACY: |
322 | 0 | case GIMP_LAYER_MODE_HSV_HUE: |
323 | 0 | return(HueCompositeOp); |
324 | 0 | case GIMP_LAYER_MODE_HSV_SATURATION_LEGACY: |
325 | 0 | case GIMP_LAYER_MODE_HSV_SATURATION: |
326 | 0 | return(SaturateCompositeOp); |
327 | 1 | case GIMP_LAYER_MODE_HSL_COLOR_LEGACY: |
328 | 4 | case GIMP_LAYER_MODE_HSL_COLOR: |
329 | 4 | return(ColorizeCompositeOp); |
330 | 1 | case GIMP_LAYER_MODE_DODGE_LEGACY: |
331 | 1 | case GIMP_LAYER_MODE_DODGE: |
332 | 1 | return(ColorDodgeCompositeOp); |
333 | 0 | case GIMP_LAYER_MODE_BURN_LEGACY: |
334 | 1 | case GIMP_LAYER_MODE_BURN: |
335 | 1 | return(ColorBurnCompositeOp); |
336 | 0 | case GIMP_LAYER_MODE_HARDLIGHT_LEGACY: |
337 | 0 | case GIMP_LAYER_MODE_HARDLIGHT: |
338 | 0 | return(HardLightCompositeOp); |
339 | 0 | case GIMP_LAYER_MODE_DIVIDE_LEGACY: |
340 | 0 | case GIMP_LAYER_MODE_DIVIDE: |
341 | 0 | return(DivideDstCompositeOp); |
342 | | /* these are the ones we don't support...yet */ |
343 | 61 | default: |
344 | 61 | return(OverCompositeOp); |
345 | 4.89k | } |
346 | 4.89k | } |
347 | | |
348 | | /* |
349 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
350 | | % % |
351 | | % % |
352 | | % % |
353 | | + 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 % |
354 | | % % |
355 | | % % |
356 | | % % |
357 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
358 | | % |
359 | | % ReadBlobStringWithLongSize reads characters from a blob or file |
360 | | % starting with a ssize_t length byte and then characters to that length |
361 | | % |
362 | | % The format of the ReadBlobStringWithLongSize method is: |
363 | | % |
364 | | % char *ReadBlobStringWithLongSize(Image *image,char *string, |
365 | | % ExceptionInfo *exception) |
366 | | % |
367 | | % A description of each parameter follows: |
368 | | % |
369 | | % o image: the image. |
370 | | % |
371 | | % o string: the address of a character buffer. |
372 | | % |
373 | | % o exception: return any errors or warnings in this structure. |
374 | | % |
375 | | */ |
376 | | |
377 | | static char *ReadBlobStringWithLongSize(Image *image,char *string,size_t max, |
378 | | ExceptionInfo *exception) |
379 | 6.35k | { |
380 | 6.35k | int |
381 | 6.35k | c; |
382 | | |
383 | 6.35k | MagickOffsetType |
384 | 6.35k | offset; |
385 | | |
386 | 6.35k | ssize_t |
387 | 6.35k | i; |
388 | | |
389 | 6.35k | size_t |
390 | 6.35k | length; |
391 | | |
392 | 6.35k | assert(image != (Image *) NULL); |
393 | 6.35k | assert(image->signature == MagickCoreSignature); |
394 | 6.35k | assert(max != 0); |
395 | 6.35k | if (IsEventLogging() != MagickFalse) |
396 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
397 | 6.35k | length=ReadBlobMSBLong(image); |
398 | 304k | for (i=0; i < (ssize_t) MagickMin(length,max-1); i++) |
399 | 297k | { |
400 | 297k | c=ReadBlobByte(image); |
401 | 297k | if (c == EOF) |
402 | 29 | return((char *) NULL); |
403 | 297k | string[i]=(char) c; |
404 | 297k | } |
405 | 6.32k | string[i]='\0'; |
406 | 6.32k | offset=SeekBlob(image,((MagickOffsetType) length-i),SEEK_CUR); |
407 | 6.32k | if (offset < 0) |
408 | 0 | (void) ThrowMagickException(exception,GetMagickModule(), |
409 | 0 | CorruptImageError,"ImproperImageHeader","`%s'",image->filename); |
410 | 6.32k | return(string); |
411 | 6.35k | } |
412 | | |
413 | | static MagickOffsetType GetXCFOffset(Image *image, XCFDocInfo *inDocInfo) |
414 | 46.1k | { |
415 | 46.1k | if (inDocInfo->version >= 4) |
416 | 5.81k | return (MagickOffsetType) ReadBlobMSBLongLong(image); |
417 | 40.3k | else |
418 | 40.3k | return (MagickOffsetType) ReadBlobMSBLong(image); |
419 | 46.1k | } |
420 | | |
421 | | static MagickBooleanType load_tile(Image *image,Image *tile_image, |
422 | | XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, |
423 | | ExceptionInfo *exception) |
424 | 270 | { |
425 | 270 | ssize_t |
426 | 270 | y; |
427 | | |
428 | 270 | ssize_t |
429 | 270 | x; |
430 | | |
431 | 270 | Quantum |
432 | 270 | *q; |
433 | | |
434 | 270 | size_t |
435 | 270 | extent; |
436 | | |
437 | 270 | ssize_t |
438 | 270 | count; |
439 | | |
440 | 270 | unsigned char |
441 | 270 | *graydata; |
442 | | |
443 | 270 | XCFPixelInfo |
444 | 270 | *xcfdata, |
445 | 270 | *xcfodata; |
446 | | |
447 | 270 | extent=0; |
448 | 270 | if (inDocInfo->image_type == GIMP_GRAY) |
449 | 109 | extent=tile_image->columns*tile_image->rows*sizeof(*graydata); |
450 | 161 | else |
451 | 161 | if (inDocInfo->image_type == GIMP_RGB) |
452 | 161 | extent=tile_image->columns*tile_image->rows*sizeof(*xcfdata); |
453 | 270 | if (extent > data_length) |
454 | 26 | ThrowBinaryException(CorruptImageError,"NotEnoughPixelData", |
455 | 270 | image->filename); |
456 | 244 | xcfdata=(XCFPixelInfo *) AcquireQuantumMemory(MagickMax(data_length, |
457 | 244 | tile_image->columns*tile_image->rows),sizeof(*xcfdata)); |
458 | 244 | if (xcfdata == (XCFPixelInfo *) NULL) |
459 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
460 | 244 | image->filename); |
461 | 244 | xcfodata=xcfdata; |
462 | 244 | graydata=(unsigned char *) xcfdata; /* used by gray and indexed */ |
463 | 244 | count=ReadBlob(image,data_length,(unsigned char *) xcfdata); |
464 | 244 | if (count != (ssize_t) data_length) |
465 | 169 | { |
466 | 169 | xcfodata=(XCFPixelInfo *) RelinquishMagickMemory(xcfodata); |
467 | 169 | ThrowBinaryException(CorruptImageError,"NotEnoughPixelData", |
468 | 169 | image->filename); |
469 | 0 | } |
470 | 1.98k | for (y=0; y < (ssize_t) tile_image->rows; y++) |
471 | 1.90k | { |
472 | 1.90k | q=GetAuthenticPixels(tile_image,0,y,tile_image->columns,1,exception); |
473 | 1.90k | if (q == (Quantum *) NULL) |
474 | 0 | break; |
475 | 1.90k | if (inDocInfo->image_type == GIMP_GRAY) |
476 | 1.11k | { |
477 | 3.30k | for (x=0; x < (ssize_t) tile_image->columns; x++) |
478 | 2.18k | { |
479 | 2.18k | SetPixelGray(tile_image,ScaleCharToQuantum(*graydata),q); |
480 | 2.18k | SetPixelAlpha(tile_image,ScaleCharToQuantum((unsigned char) |
481 | 2.18k | inLayerInfo->alpha),q); |
482 | 2.18k | graydata++; |
483 | 2.18k | q+=(ptrdiff_t) GetPixelChannels(tile_image); |
484 | 2.18k | } |
485 | 1.11k | } |
486 | 792 | else |
487 | 792 | if (inDocInfo->image_type == GIMP_RGB) |
488 | 792 | { |
489 | 2.09k | for (x=0; x < (ssize_t) tile_image->columns; x++) |
490 | 1.30k | { |
491 | 1.30k | SetPixelRed(tile_image,ScaleCharToQuantum(xcfdata->red),q); |
492 | 1.30k | SetPixelGreen(tile_image,ScaleCharToQuantum(xcfdata->green),q); |
493 | 1.30k | SetPixelBlue(tile_image,ScaleCharToQuantum(xcfdata->blue),q); |
494 | 1.30k | SetPixelAlpha(tile_image,xcfdata->alpha == 255U ? TransparentAlpha : |
495 | 1.30k | ScaleCharToQuantum((unsigned char) inLayerInfo->alpha),q); |
496 | 1.30k | xcfdata++; |
497 | 1.30k | q+=(ptrdiff_t) GetPixelChannels(tile_image); |
498 | 1.30k | } |
499 | 792 | } |
500 | 1.90k | if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) |
501 | 0 | break; |
502 | 1.90k | } |
503 | 75 | xcfodata=(XCFPixelInfo *) RelinquishMagickMemory(xcfodata); |
504 | 75 | return MagickTrue; |
505 | 244 | } |
506 | | |
507 | | static MagickBooleanType load_tile_rle(Image *image,Image *tile_image, |
508 | | XCFDocInfo *inDocInfo,XCFLayerInfo *inLayerInfo,size_t data_length, |
509 | | ExceptionInfo *exception) |
510 | 5.14k | { |
511 | 5.14k | MagickOffsetType |
512 | 5.14k | size; |
513 | | |
514 | 5.14k | Quantum |
515 | 5.14k | alpha, |
516 | 5.14k | data; |
517 | | |
518 | 5.14k | Quantum |
519 | 5.14k | *q; |
520 | | |
521 | 5.14k | size_t |
522 | 5.14k | length; |
523 | | |
524 | 5.14k | ssize_t |
525 | 5.14k | bytes_per_pixel, |
526 | 5.14k | count, |
527 | 5.14k | i, |
528 | 5.14k | j; |
529 | | |
530 | 5.14k | unsigned char |
531 | 5.14k | pixel, |
532 | 5.14k | *xcfdata, |
533 | 5.14k | *xcfodata, |
534 | 5.14k | *xcfdatalimit; |
535 | | |
536 | 5.14k | bytes_per_pixel=(ssize_t) inDocInfo->bytes_per_pixel; |
537 | 5.14k | xcfdata=(unsigned char *) AcquireQuantumMemory(data_length,sizeof(*xcfdata)); |
538 | 5.14k | if (xcfdata == (unsigned char *) NULL) |
539 | 4 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
540 | 5.14k | image->filename); |
541 | 5.14k | (void) memset(xcfdata,0,(size_t) data_length*sizeof(*xcfdata)); |
542 | 5.14k | xcfodata=xcfdata; |
543 | 5.14k | count=ReadBlob(image, (size_t) data_length, xcfdata); |
544 | 5.14k | xcfdatalimit = xcfodata+count-1; |
545 | 5.14k | alpha=ScaleCharToQuantum((unsigned char) inLayerInfo->alpha); |
546 | 6.71k | for (i=0; i < bytes_per_pixel; i++) |
547 | 1.88k | { |
548 | 1.88k | q=GetAuthenticPixels(tile_image,0,0,tile_image->columns,tile_image->rows, |
549 | 1.88k | exception); |
550 | 1.88k | if (q == (Quantum *) NULL) |
551 | 0 | continue; |
552 | 1.88k | size=(MagickOffsetType) (tile_image->rows*tile_image->columns); |
553 | 34.6k | while (size > 0) |
554 | 33.0k | { |
555 | 33.0k | if (xcfdata > xcfdatalimit) |
556 | 116 | goto bogus_rle; |
557 | 32.9k | pixel=(*xcfdata++); |
558 | 32.9k | length=(size_t) pixel; |
559 | 32.9k | if (length >= 128) |
560 | 4.60k | { |
561 | 4.60k | length=255-(length-1); |
562 | 4.60k | if (length == 128) |
563 | 446 | { |
564 | 446 | if (xcfdata >= xcfdatalimit) |
565 | 7 | goto bogus_rle; |
566 | 439 | length=(size_t) ((*xcfdata << 8) + xcfdata[1]) & 0xffff; |
567 | 439 | xcfdata+=2; |
568 | 439 | } |
569 | 4.60k | size-=(MagickOffsetType) length; |
570 | 4.60k | if (size < 0) |
571 | 32 | goto bogus_rle; |
572 | 4.57k | if ((length < 1) || (length > data_length)) |
573 | 21 | goto bogus_rle; |
574 | 4.54k | if (&xcfdata[length-1] > xcfdatalimit) |
575 | 66 | goto bogus_rle; |
576 | 45.9k | while (length-- > 0) |
577 | 41.4k | { |
578 | 41.4k | data=ScaleCharToQuantum(*xcfdata++); |
579 | 41.4k | switch (i) |
580 | 41.4k | { |
581 | 24.3k | case 0: |
582 | 24.3k | { |
583 | 24.3k | if (inDocInfo->image_type == GIMP_GRAY) |
584 | 16.7k | SetPixelGray(tile_image,data,q); |
585 | 7.61k | else |
586 | 7.61k | { |
587 | 7.61k | SetPixelRed(tile_image,data,q); |
588 | 7.61k | SetPixelGreen(tile_image,data,q); |
589 | 7.61k | SetPixelBlue(tile_image,data,q); |
590 | 7.61k | } |
591 | 24.3k | SetPixelAlpha(tile_image,alpha,q); |
592 | 24.3k | break; |
593 | 0 | } |
594 | 10.0k | case 1: |
595 | 10.0k | { |
596 | 10.0k | if (inDocInfo->image_type == GIMP_GRAY) |
597 | 7.72k | SetPixelAlpha(tile_image,data,q); |
598 | 2.33k | else |
599 | 2.33k | SetPixelGreen(tile_image,data,q); |
600 | 10.0k | break; |
601 | 0 | } |
602 | 1.99k | case 2: |
603 | 1.99k | { |
604 | 1.99k | SetPixelBlue(tile_image,data,q); |
605 | 1.99k | break; |
606 | 0 | } |
607 | 3.41k | case 3: |
608 | 3.41k | { |
609 | 3.41k | SetPixelAlpha(tile_image,data,q); |
610 | 3.41k | break; |
611 | 0 | } |
612 | 41.4k | } |
613 | 41.4k | q+=(ptrdiff_t) GetPixelChannels(tile_image); |
614 | 41.4k | } |
615 | 4.48k | } |
616 | 28.3k | else |
617 | 28.3k | { |
618 | 28.3k | length+=1; |
619 | 28.3k | if (length == 128) |
620 | 467 | { |
621 | 467 | if (xcfdata >= xcfdatalimit) |
622 | 1 | goto bogus_rle; |
623 | 466 | length=(size_t) ((*xcfdata << 8) + xcfdata[1]) & 0xffff; |
624 | 466 | xcfdata+=2; |
625 | 466 | } |
626 | 28.3k | size-=(MagickOffsetType) length; |
627 | 28.3k | if (size < 0) |
628 | 43 | goto bogus_rle; |
629 | 28.3k | if (xcfdata > xcfdatalimit) |
630 | 29 | goto bogus_rle; |
631 | 28.2k | pixel=(*xcfdata++); |
632 | 1.02M | for (j=0; j < (ssize_t) length; j++) |
633 | 996k | { |
634 | 996k | data=ScaleCharToQuantum(pixel); |
635 | 996k | switch (i) |
636 | 996k | { |
637 | 549k | case 0: |
638 | 549k | { |
639 | 549k | if (inDocInfo->image_type == GIMP_GRAY) |
640 | 320k | SetPixelGray(tile_image,data,q); |
641 | 229k | else |
642 | 229k | { |
643 | 229k | SetPixelRed(tile_image,data,q); |
644 | 229k | SetPixelGreen(tile_image,data,q); |
645 | 229k | SetPixelBlue(tile_image,data,q); |
646 | 229k | } |
647 | 549k | SetPixelAlpha(tile_image,alpha,q); |
648 | 549k | break; |
649 | 0 | } |
650 | 242k | case 1: |
651 | 242k | { |
652 | 242k | if (inDocInfo->image_type == GIMP_GRAY) |
653 | 145k | SetPixelAlpha(tile_image,data,q); |
654 | 96.5k | else |
655 | 96.5k | SetPixelGreen(tile_image,data,q); |
656 | 242k | break; |
657 | 0 | } |
658 | 132k | case 2: |
659 | 132k | { |
660 | 132k | SetPixelBlue(tile_image,data,q); |
661 | 132k | break; |
662 | 0 | } |
663 | 50.8k | case 3: |
664 | 50.8k | { |
665 | 50.8k | SetPixelAlpha(tile_image,data,q); |
666 | 50.8k | break; |
667 | 0 | } |
668 | 996k | } |
669 | 996k | q+=(ptrdiff_t) GetPixelChannels(tile_image); |
670 | 996k | } |
671 | 28.2k | } |
672 | 32.9k | } |
673 | 1.57k | if (SyncAuthenticPixels(tile_image,exception) == MagickFalse) |
674 | 0 | break; |
675 | 1.57k | } |
676 | 4.82k | xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); |
677 | 4.82k | return(MagickTrue); |
678 | | |
679 | 315 | bogus_rle: |
680 | 315 | if (xcfodata != (unsigned char *) NULL) |
681 | 315 | xcfodata=(unsigned char *) RelinquishMagickMemory(xcfodata); |
682 | 315 | return(MagickFalse); |
683 | 5.14k | } |
684 | | |
685 | | static MagickBooleanType load_level(Image *image,XCFDocInfo *inDocInfo, |
686 | | XCFLayerInfo *inLayerInfo,ExceptionInfo *exception) |
687 | 4.66k | { |
688 | 4.66k | int |
689 | 4.66k | destLeft = 0, |
690 | 4.66k | destTop = 0; |
691 | | |
692 | 4.66k | Image* |
693 | 4.66k | tile_image; |
694 | | |
695 | 4.66k | MagickBooleanType |
696 | 4.66k | status; |
697 | | |
698 | 4.66k | MagickOffsetType |
699 | 4.66k | saved_pos, |
700 | 4.66k | offset, |
701 | 4.66k | offset2; |
702 | | |
703 | 4.66k | ssize_t |
704 | 4.66k | i; |
705 | | |
706 | 4.66k | size_t |
707 | 4.66k | width, |
708 | 4.66k | height, |
709 | 4.66k | ntiles, |
710 | 4.66k | ntile_rows, |
711 | 4.66k | ntile_cols, |
712 | 4.66k | tile_image_width, |
713 | 4.66k | tile_image_height; |
714 | | |
715 | | /* start reading the data */ |
716 | 4.66k | width=ReadBlobMSBLong(image); |
717 | 4.66k | height=ReadBlobMSBLong(image); |
718 | | |
719 | | /* |
720 | | Read in the first tile offset. If it is '0', then this tile level is empty |
721 | | and we can simply return. |
722 | | */ |
723 | 4.66k | offset=GetXCFOffset(image,inDocInfo); |
724 | 4.66k | if (EOFBlob(image) != MagickFalse) |
725 | 1 | ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile", |
726 | 4.66k | image->filename); |
727 | 4.66k | if (offset == 0) |
728 | 2.10k | { |
729 | 2.10k | (void) SetImageBackgroundColor(image,exception); |
730 | 2.10k | return(MagickTrue); |
731 | 2.10k | } |
732 | | /* |
733 | | Initialise the reference for the in-memory tile-compression |
734 | | */ |
735 | 2.55k | ntile_rows=(height+TILE_HEIGHT-1)/TILE_HEIGHT; |
736 | 2.55k | ntile_cols=(width+TILE_WIDTH-1)/TILE_WIDTH; |
737 | 2.55k | ntiles=ntile_rows*ntile_cols; |
738 | 7.45k | for (i = 0; i < (ssize_t) ntiles; i++) |
739 | 5.87k | { |
740 | 5.87k | status=MagickFalse; |
741 | 5.87k | if (offset == 0) |
742 | 5.74k | ThrowBinaryException(CorruptImageError,"NotEnoughTiles",image->filename); |
743 | | /* |
744 | | Save the current position as it is where the next tile offset is stored. |
745 | | */ |
746 | 5.74k | saved_pos=TellBlob(image); |
747 | | /* read in the offset of the next tile so we can calculate the amount |
748 | | of data needed for this tile*/ |
749 | 5.74k | offset2=GetXCFOffset(image,inDocInfo); |
750 | 5.74k | if ((MagickSizeType) offset2 >= inDocInfo->file_size) |
751 | 211 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
752 | 5.74k | image->filename); |
753 | | /* if the offset is 0 then we need to read in the maximum possible |
754 | | allowing for negative compression */ |
755 | 5.53k | if (offset2 == 0) |
756 | 2.18k | offset2=(MagickOffsetType) (offset + TILE_WIDTH * TILE_WIDTH * 4* 1.5); |
757 | | /* seek to the tile offset */ |
758 | 5.53k | if ((offset > offset2) || (SeekBlob(image, offset, SEEK_SET) != offset)) |
759 | 120 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
760 | 5.53k | image->filename); |
761 | | |
762 | | /* |
763 | | Allocate the image for the tile. NOTE: the last tile in a row or |
764 | | column may not be a full tile! |
765 | | */ |
766 | 5.41k | tile_image_width=(size_t) (destLeft == (int) ntile_cols-1 ? |
767 | 3.48k | (int) width % TILE_WIDTH : TILE_WIDTH); |
768 | 5.41k | if (tile_image_width == 0) |
769 | 239 | tile_image_width=TILE_WIDTH; |
770 | 5.41k | tile_image_height = (size_t) (destTop == (int) ntile_rows-1 ? |
771 | 4.56k | (int) height % TILE_HEIGHT : TILE_HEIGHT); |
772 | 5.41k | if (tile_image_height == 0) |
773 | 163 | tile_image_height=TILE_HEIGHT; |
774 | 5.41k | tile_image=CloneImage(inLayerInfo->image,tile_image_width, |
775 | 5.41k | tile_image_height,MagickTrue,exception); |
776 | 5.41k | if (tile_image == (Image *) NULL) |
777 | 0 | ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed", |
778 | 5.41k | image->filename); |
779 | 5.41k | (void) SetImageBackgroundColor(tile_image,exception); |
780 | | |
781 | | /* read in the tile */ |
782 | 5.41k | switch (inDocInfo->compression) |
783 | 5.41k | { |
784 | 270 | case COMPRESS_NONE: |
785 | 270 | status=load_tile(image,tile_image,inDocInfo,inLayerInfo,(size_t) |
786 | 270 | (offset2-offset),exception); |
787 | 270 | break; |
788 | 5.14k | case COMPRESS_RLE: |
789 | 5.14k | status=load_tile_rle(image,tile_image,inDocInfo,inLayerInfo,(size_t) |
790 | 5.14k | (offset2-offset),exception); |
791 | 5.14k | break; |
792 | 2 | case COMPRESS_ZLIB: |
793 | 2 | tile_image=DestroyImage(tile_image); |
794 | 2 | ThrowBinaryException(CoderError,"ZipCompressNotSupported", |
795 | 0 | image->filename) |
796 | 2 | case COMPRESS_FRACTAL: |
797 | 2 | tile_image=DestroyImage(tile_image); |
798 | 2 | ThrowBinaryException(CoderError,"FractalCompressNotSupported", |
799 | 5.41k | image->filename) |
800 | 5.41k | } |
801 | | |
802 | | /* composite the tile onto the layer's image, and then destroy it */ |
803 | 5.41k | if (status != MagickFalse) |
804 | 4.90k | (void) CompositeImage(inLayerInfo->image,tile_image,CopyCompositeOp, |
805 | 4.90k | MagickTrue,destLeft * TILE_WIDTH,destTop*TILE_HEIGHT,exception); |
806 | 5.41k | tile_image=DestroyImage(tile_image); |
807 | | |
808 | 5.41k | if (status == MagickFalse) |
809 | 514 | return(MagickFalse); |
810 | | /* adjust tile position */ |
811 | 4.90k | destLeft++; |
812 | 4.90k | if (destLeft >= (int) ntile_cols) |
813 | 1.85k | { |
814 | 1.85k | destLeft = 0; |
815 | 1.85k | destTop++; |
816 | 1.85k | } |
817 | | /* restore the saved position so we'll be ready to |
818 | | * read the next offset. |
819 | | */ |
820 | 4.90k | offset=SeekBlob(image, saved_pos, SEEK_SET); |
821 | | /* read in the offset of the next tile */ |
822 | 4.90k | offset=GetXCFOffset(image,inDocInfo); |
823 | 4.90k | } |
824 | 1.58k | if (offset != 0) |
825 | 76 | ThrowBinaryException(CorruptImageError,"CorruptImage",image->filename) |
826 | 1.51k | return(MagickTrue); |
827 | 1.58k | } |
828 | | |
829 | | static MagickBooleanType load_hierarchy(Image *image,XCFDocInfo *inDocInfo, |
830 | | XCFLayerInfo *inLayer, ExceptionInfo *exception) |
831 | 4.81k | { |
832 | 4.81k | MagickOffsetType |
833 | 4.81k | saved_pos, |
834 | 4.81k | offset, |
835 | 4.81k | junk; |
836 | | |
837 | 4.81k | (void) ReadBlobMSBLong(image); /* width */ |
838 | 4.81k | (void) ReadBlobMSBLong(image); /* height */ |
839 | 4.81k | inDocInfo->bytes_per_pixel=ReadBlobMSBLong(image); |
840 | | |
841 | | /* load in the levels...we make sure that the number of levels |
842 | | * calculated when the TileManager was created is the same |
843 | | * as the number of levels found in the file. |
844 | | */ |
845 | 4.81k | offset=GetXCFOffset(image,inDocInfo); /* top level */ |
846 | 4.81k | if ((MagickSizeType) offset >= GetBlobSize(image)) |
847 | 150 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
848 | 4.81k | image->filename); |
849 | | |
850 | | /* discard offsets for layers below first, if any. |
851 | | */ |
852 | 4.66k | do |
853 | 12.7k | { |
854 | 12.7k | junk=(MagickOffsetType) ReadBlobMSBLong(image); |
855 | 12.7k | } |
856 | 12.7k | while (junk != 0); |
857 | | |
858 | | /* save the current position as it is where the |
859 | | * next level offset is stored. |
860 | | */ |
861 | 4.66k | saved_pos=TellBlob(image); |
862 | | |
863 | | /* seek to the level offset */ |
864 | 4.66k | if (SeekBlob(image, offset, SEEK_SET) != offset) |
865 | 0 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
866 | 4.66k | image->filename); |
867 | | |
868 | | /* read in the level */ |
869 | 4.66k | if (load_level (image, inDocInfo, inLayer, exception) == 0) |
870 | 1.04k | return(MagickFalse); |
871 | | /* restore the saved position so we'll be ready to |
872 | | * read the next offset. |
873 | | */ |
874 | 3.61k | offset=SeekBlob(image, saved_pos, SEEK_SET); |
875 | 3.61k | return(MagickTrue); |
876 | 4.66k | } |
877 | | |
878 | | static void InitXCFImage(XCFLayerInfo *outLayer,ExceptionInfo *exception) |
879 | 4.89k | { |
880 | 4.89k | outLayer->image->page.x=outLayer->offset_x; |
881 | 4.89k | outLayer->image->page.y=outLayer->offset_y; |
882 | 4.89k | outLayer->image->page.width=outLayer->width; |
883 | 4.89k | outLayer->image->page.height=outLayer->height; |
884 | 4.89k | (void) SetImageProperty(outLayer->image,"label",(char *) outLayer->name, |
885 | 4.89k | exception); |
886 | 4.89k | } |
887 | | |
888 | | static MagickBooleanType ReadOneLayer(const ImageInfo *image_info,Image* image, |
889 | | XCFDocInfo* inDocInfo,XCFLayerInfo *outLayer,const ssize_t layer, |
890 | | ExceptionInfo *exception) |
891 | 5.46k | { |
892 | 5.46k | MagickBooleanType |
893 | 5.46k | status; |
894 | | |
895 | 5.46k | MagickOffsetType |
896 | 5.46k | offset; |
897 | | |
898 | 5.46k | unsigned int |
899 | 5.46k | foundPropEnd = 0; |
900 | | |
901 | 5.46k | MagickOffsetType |
902 | 5.46k | hierarchy_offset, |
903 | 5.46k | layer_mask_offset; |
904 | | |
905 | | /* clear the block! */ |
906 | 5.46k | (void) memset( outLayer, 0, sizeof( XCFLayerInfo ) ); |
907 | | /* read in the layer width, height, type and name */ |
908 | 5.46k | outLayer->width = ReadBlobMSBLong(image); |
909 | 5.46k | outLayer->height = ReadBlobMSBLong(image); |
910 | 5.46k | outLayer->type = ReadBlobMSBLong(image); |
911 | 5.46k | (void) ReadBlobStringWithLongSize(image, outLayer->name, |
912 | 5.46k | sizeof(outLayer->name),exception); |
913 | 5.46k | if (EOFBlob(image) != MagickFalse) |
914 | 288 | ThrowBinaryException(CorruptImageError,"InsufficientImageDataInFile", |
915 | 5.46k | image->filename); |
916 | 5.17k | if ((outLayer->width == 0) || (outLayer->height == 0)) |
917 | 7 | ThrowBinaryException(CorruptImageError,"ImproperImageHeader", |
918 | 5.17k | image->filename); |
919 | | /* read the layer properties! */ |
920 | 5.16k | foundPropEnd = 0; |
921 | 16.8k | while ( (foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse) ) { |
922 | 11.6k | PropType prop_type = (PropType) ReadBlobMSBLong(image); |
923 | 11.6k | size_t prop_size = ReadBlobMSBLong(image); |
924 | 11.6k | switch (prop_type) |
925 | 11.6k | { |
926 | 5.02k | case PROP_END: |
927 | 5.02k | foundPropEnd = 1; |
928 | 5.02k | break; |
929 | 209 | case PROP_ACTIVE_LAYER: |
930 | 209 | outLayer->active = 1; |
931 | 209 | break; |
932 | 226 | case PROP_FLOATING_SELECTION: |
933 | 226 | outLayer->floating_offset = ReadBlobMSBLong(image); |
934 | 226 | break; |
935 | 294 | case PROP_OPACITY: |
936 | 294 | outLayer->alpha = ReadBlobMSBLong(image); |
937 | 294 | break; |
938 | 561 | case PROP_VISIBLE: |
939 | 561 | outLayer->visible = ReadBlobMSBLong(image); |
940 | 561 | break; |
941 | 280 | case PROP_LINKED: |
942 | 280 | outLayer->linked = ReadBlobMSBLong(image); |
943 | 280 | break; |
944 | 520 | case PROP_PRESERVE_TRANSPARENCY: |
945 | 520 | outLayer->preserve_trans = ReadBlobMSBLong(image); |
946 | 520 | break; |
947 | 230 | case PROP_APPLY_MASK: |
948 | 230 | outLayer->apply_mask = ReadBlobMSBLong(image); |
949 | 230 | break; |
950 | 223 | case PROP_EDIT_MASK: |
951 | 223 | outLayer->edit_mask = ReadBlobMSBLong(image); |
952 | 223 | break; |
953 | 246 | case PROP_SHOW_MASK: |
954 | 246 | outLayer->show_mask = ReadBlobMSBLong(image); |
955 | 246 | break; |
956 | 465 | case PROP_OFFSETS: |
957 | 465 | outLayer->offset_x = ReadBlobMSBSignedLong(image); |
958 | 465 | outLayer->offset_y = ReadBlobMSBSignedLong(image); |
959 | 465 | break; |
960 | 305 | case PROP_MODE: |
961 | 305 | outLayer->mode = ReadBlobMSBLong(image); |
962 | 305 | break; |
963 | 238 | case PROP_TATTOO: |
964 | 238 | outLayer->preserve_trans = ReadBlobMSBLong(image); |
965 | 238 | break; |
966 | 555 | case PROP_PARASITES: |
967 | 555 | { |
968 | 555 | if (DiscardBlobBytes(image,prop_size) == MagickFalse) |
969 | 3 | ThrowFileException(exception,CorruptImageError, |
970 | 555 | "UnexpectedEndOfFile",image->filename); |
971 | | |
972 | | /* |
973 | | ssize_t base = info->cp; |
974 | | GimpParasite *p; |
975 | | while (info->cp - base < prop_size) |
976 | | { |
977 | | p = xcf_load_parasite(info); |
978 | | gimp_drawable_parasite_attach(GIMP_DRAWABLE(layer), p); |
979 | | gimp_parasite_free(p); |
980 | | } |
981 | | if (info->cp - base != prop_size) |
982 | | g_message ("Error detected while loading a layer's parasites"); |
983 | | */ |
984 | 555 | } |
985 | 555 | break; |
986 | 2.31k | default: |
987 | | /* g_message ("unexpected/unknown layer property: %d (skipping)", |
988 | | prop_type); */ |
989 | | |
990 | 2.31k | { |
991 | 2.31k | int buf[16]; |
992 | 2.31k | ssize_t amount; |
993 | | |
994 | | /* read over it... */ |
995 | 4.55k | while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) |
996 | 2.26k | { |
997 | 2.26k | amount = (ssize_t) MagickMin(16, prop_size); |
998 | 2.26k | amount = ReadBlob(image, (size_t) amount, (unsigned char *) &buf); |
999 | 2.26k | if (!amount) |
1000 | 18 | ThrowBinaryException(CorruptImageError,"CorruptImage", |
1001 | 2.26k | image->filename); |
1002 | 2.24k | prop_size -= (size_t) MagickMin(16, (size_t) amount); |
1003 | 2.24k | } |
1004 | 2.31k | } |
1005 | 2.29k | break; |
1006 | 11.6k | } |
1007 | 11.6k | } |
1008 | 5.14k | if (EOFBlob(image) != MagickFalse) |
1009 | 151 | ThrowBinaryException(CorruptImageError,"UnexpectedEndOfFile", |
1010 | 5.14k | image->filename); |
1011 | 4.99k | if (foundPropEnd == MagickFalse) |
1012 | 0 | return(MagickFalse); |
1013 | | /* allocate the image for this layer */ |
1014 | 4.99k | if (image_info->number_scenes != 0) |
1015 | 0 | { |
1016 | 0 | ssize_t |
1017 | 0 | scene; |
1018 | |
|
1019 | 0 | scene=(ssize_t) inDocInfo->number_layers-layer-1; |
1020 | 0 | if (scene > (ssize_t) (image_info->scene+image_info->number_scenes-1)) |
1021 | 0 | { |
1022 | 0 | outLayer->image=CloneImage(image,0,0,MagickTrue,exception); |
1023 | 0 | if (outLayer->image == (Image *) NULL) |
1024 | 0 | return(MagickFalse); |
1025 | 0 | InitXCFImage(outLayer,exception); |
1026 | 0 | return(MagickTrue); |
1027 | 0 | } |
1028 | 0 | } |
1029 | 4.99k | outLayer->image=CloneImage(image,outLayer->width, outLayer->height,MagickTrue, |
1030 | 4.99k | exception); |
1031 | 4.99k | if (outLayer->image == (Image *) NULL) |
1032 | 99 | return(MagickFalse); |
1033 | 4.89k | outLayer->width=outLayer->image->columns; |
1034 | 4.89k | status=SetImageExtent(outLayer->image,outLayer->image->columns, |
1035 | 4.89k | outLayer->image->rows,exception); |
1036 | 4.89k | if (status != MagickFalse) |
1037 | 4.89k | status=ResetImagePixels(outLayer->image,exception); |
1038 | 4.89k | if (status == MagickFalse) |
1039 | 0 | { |
1040 | 0 | outLayer->image=DestroyImageList(outLayer->image); |
1041 | 0 | return(MagickFalse); |
1042 | 0 | } |
1043 | | /* clear the image based on the layer opacity */ |
1044 | 4.89k | outLayer->image->background_color.alpha= |
1045 | 4.89k | ScaleCharToQuantum((unsigned char) outLayer->alpha); |
1046 | 4.89k | if (outLayer->alpha != 255U) |
1047 | 4.88k | { |
1048 | 4.88k | outLayer->image->background_color.alpha_trait=BlendPixelTrait; |
1049 | 4.88k | outLayer->image->alpha_trait=BlendPixelTrait; |
1050 | 4.88k | (void) SetImageBackgroundColor(outLayer->image,exception); |
1051 | 4.88k | } |
1052 | | |
1053 | 4.89k | InitXCFImage(outLayer,exception); |
1054 | | |
1055 | | /* set the compositing mode */ |
1056 | 4.89k | outLayer->image->compose = GIMPBlendModeToCompositeOperator( outLayer->mode ); |
1057 | 4.89k | if ( outLayer->visible == MagickFalse ) |
1058 | 4.76k | { |
1059 | | /* BOGUS: should really be separate member var! */ |
1060 | 4.76k | outLayer->image->compose = NoCompositeOp; |
1061 | 4.76k | } |
1062 | | |
1063 | | /* read the hierarchy and layer mask offsets */ |
1064 | 4.89k | hierarchy_offset = GetXCFOffset(image,inDocInfo); |
1065 | 4.89k | layer_mask_offset = GetXCFOffset(image,inDocInfo); |
1066 | | |
1067 | | /* read in the hierarchy */ |
1068 | 4.89k | offset=SeekBlob(image, hierarchy_offset, SEEK_SET); |
1069 | 4.89k | if (offset != hierarchy_offset) |
1070 | 85 | ThrowBinaryException(CorruptImageError,"InvalidImageHeader", |
1071 | 4.89k | image->filename); |
1072 | 4.81k | if (load_hierarchy (image, inDocInfo, outLayer, exception) == 0) |
1073 | 1.19k | return(MagickFalse); |
1074 | | |
1075 | | /* read in the layer mask */ |
1076 | 3.61k | if (layer_mask_offset != 0) |
1077 | 2.33k | { |
1078 | 2.33k | offset=SeekBlob(image, (MagickOffsetType) layer_mask_offset, SEEK_SET); |
1079 | | |
1080 | | #if 0 /* BOGUS: support layer masks! */ |
1081 | | layer_mask = xcf_load_layer_mask (info, gimage); |
1082 | | if (layer_mask == 0) |
1083 | | goto error; |
1084 | | |
1085 | | /* set the offsets of the layer_mask */ |
1086 | | GIMP_DRAWABLE (layer_mask)->offset_x = GIMP_DRAWABLE (layer)->offset_x; |
1087 | | GIMP_DRAWABLE (layer_mask)->offset_y = GIMP_DRAWABLE (layer)->offset_y; |
1088 | | |
1089 | | gimp_layer_add_mask (layer, layer_mask, MagickFalse); |
1090 | | |
1091 | | layer->mask->apply_mask = apply_mask; |
1092 | | layer->mask->edit_mask = edit_mask; |
1093 | | layer->mask->show_mask = show_mask; |
1094 | | #endif |
1095 | 2.33k | } |
1096 | | |
1097 | | /* attach the floating selection... */ |
1098 | | #if 0 /* BOGUS: we may need to read this, even if we don't support it! */ |
1099 | | if (add_floating_sel) |
1100 | | { |
1101 | | GimpLayer *floating_sel; |
1102 | | |
1103 | | floating_sel = info->floating_sel; |
1104 | | floating_sel_attach (floating_sel, GIMP_DRAWABLE (layer)); |
1105 | | } |
1106 | | #endif |
1107 | | |
1108 | 3.61k | return MagickTrue; |
1109 | 4.81k | } |
1110 | | |
1111 | | /* |
1112 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1113 | | % % |
1114 | | % % |
1115 | | % % |
1116 | | % R e a d X C F I m a g e % |
1117 | | % % |
1118 | | % % |
1119 | | % % |
1120 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1121 | | % |
1122 | | % ReadXCFImage() reads a GIMP (GNU Image Manipulation Program) image |
1123 | | % file and returns it. It allocates the memory necessary for the new Image |
1124 | | % structure and returns a pointer to the new image. |
1125 | | % |
1126 | | % The format of the ReadXCFImage method is: |
1127 | | % |
1128 | | % image=ReadXCFImage(image_info) |
1129 | | % |
1130 | | % A description of each parameter follows: |
1131 | | % |
1132 | | % o image_info: the image info. |
1133 | | % |
1134 | | % o exception: return any errors or warnings in this structure. |
1135 | | % |
1136 | | */ |
1137 | | static Image *ReadXCFImage(const ImageInfo *image_info,ExceptionInfo *exception) |
1138 | 3.25k | { |
1139 | 3.25k | char |
1140 | 3.25k | magick[14]; |
1141 | | |
1142 | 3.25k | Image |
1143 | 3.25k | *image; |
1144 | | |
1145 | 3.25k | int |
1146 | 3.25k | foundPropEnd = 0; |
1147 | | |
1148 | 3.25k | MagickBooleanType |
1149 | 3.25k | status; |
1150 | | |
1151 | 3.25k | MagickOffsetType |
1152 | 3.25k | offset; |
1153 | | |
1154 | 3.25k | ssize_t |
1155 | 3.25k | i; |
1156 | | |
1157 | 3.25k | size_t |
1158 | 3.25k | image_type, |
1159 | 3.25k | length; |
1160 | | |
1161 | 3.25k | ssize_t |
1162 | 3.25k | count; |
1163 | | |
1164 | 3.25k | XCFDocInfo |
1165 | 3.25k | doc_info; |
1166 | | |
1167 | | /* |
1168 | | Open image file. |
1169 | | */ |
1170 | 3.25k | assert(image_info != (const ImageInfo *) NULL); |
1171 | 3.25k | assert(image_info->signature == MagickCoreSignature); |
1172 | 3.25k | assert(exception != (ExceptionInfo *) NULL); |
1173 | 3.25k | assert(exception->signature == MagickCoreSignature); |
1174 | 3.25k | if (IsEventLogging() != MagickFalse) |
1175 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
1176 | 0 | image_info->filename); |
1177 | 3.25k | image=AcquireImage(image_info,exception); |
1178 | 3.25k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
1179 | 3.25k | if (status == MagickFalse) |
1180 | 0 | { |
1181 | 0 | image=DestroyImageList(image); |
1182 | 0 | return((Image *) NULL); |
1183 | 0 | } |
1184 | 3.25k | count=ReadBlob(image,sizeof(magick),(unsigned char *) magick); |
1185 | 3.25k | if ((count != sizeof(magick)) || |
1186 | 3.25k | (LocaleNCompare((char *) magick,"gimp xcf",8) != 0)) |
1187 | 3.20k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
1188 | 3.20k | (void) memset(&doc_info,0,sizeof(XCFDocInfo)); |
1189 | 3.20k | doc_info.version=StringToUnsignedLong(magick+10); |
1190 | 3.20k | doc_info.width=ReadBlobMSBLong(image); |
1191 | 3.20k | doc_info.height=ReadBlobMSBLong(image); |
1192 | 3.20k | if ((doc_info.width > 262144) || (doc_info.height > 262144)) |
1193 | 3.13k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
1194 | 3.13k | doc_info.image_type=ReadBlobMSBLong(image); |
1195 | 3.13k | if (doc_info.version >= 4) |
1196 | 936 | { |
1197 | 936 | size_t |
1198 | 936 | precision; |
1199 | | |
1200 | 936 | precision=ReadBlobMSBLong(image); |
1201 | 936 | if ((precision == 0) && (doc_info.version == 4)) |
1202 | 724 | precision=150; |
1203 | 936 | if ((precision == 100) && (doc_info.version < 7)) |
1204 | 2 | precision=150; |
1205 | | /* we only support 8-bit gamma integer */ |
1206 | 936 | if (precision != 150) |
1207 | 814 | ThrowReaderException(CoderError,"DataStorageTypeIsNotSupported"); |
1208 | 814 | } |
1209 | | /* |
1210 | | Initialize image attributes. |
1211 | | */ |
1212 | 3.01k | image->columns=doc_info.width; |
1213 | 3.01k | image->rows=doc_info.height; |
1214 | 3.01k | image_type=doc_info.image_type; |
1215 | 3.01k | doc_info.file_size=(size_t) GetBlobSize(image); |
1216 | 3.01k | image->compression=NoCompression; |
1217 | 3.01k | image->depth=8; |
1218 | 3.01k | status=SetImageExtent(image,image->columns,image->rows,exception); |
1219 | 3.01k | if (status == MagickFalse) |
1220 | 86 | return(DestroyImageList(image)); |
1221 | 2.93k | if (status != MagickFalse) |
1222 | 2.93k | status=ResetImagePixels(image,exception); |
1223 | 2.93k | if (image_type == GIMP_INDEXED) |
1224 | 2.92k | ThrowReaderException(CoderError,"ColormapTypeNotSupported"); |
1225 | 2.92k | if (image_type == GIMP_RGB) |
1226 | 2.14k | SetImageColorspace(image,sRGBColorspace,exception); |
1227 | 781 | else if (image_type == GIMP_GRAY) |
1228 | 712 | SetImageColorspace(image,GRAYColorspace,exception); |
1229 | 69 | else |
1230 | 2.86k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
1231 | 2.86k | (void) SetImageBackgroundColor(image,exception); |
1232 | 2.86k | (void) SetImageAlpha(image,OpaqueAlpha,exception); |
1233 | | /* |
1234 | | Read properties. |
1235 | | */ |
1236 | 12.4k | while ((foundPropEnd == MagickFalse) && (EOFBlob(image) == MagickFalse)) |
1237 | 9.63k | { |
1238 | 9.63k | PropType prop_type = (PropType) ReadBlobMSBLong(image); |
1239 | 9.63k | size_t prop_size = ReadBlobMSBLong(image); |
1240 | | |
1241 | 9.63k | switch (prop_type) |
1242 | 9.63k | { |
1243 | 2.45k | case PROP_END: |
1244 | 2.45k | foundPropEnd=1; |
1245 | 2.45k | break; |
1246 | 214 | case PROP_COLORMAP: |
1247 | 214 | { |
1248 | | /* Cannot rely on prop_size here--the value is set incorrectly |
1249 | | by some Gimp versions. |
1250 | | */ |
1251 | 214 | size_t num_colors = ReadBlobMSBLong(image); |
1252 | 214 | if (DiscardBlobBytes(image,3*num_colors) == MagickFalse) |
1253 | 8 | ThrowFileException(exception,CorruptImageError, |
1254 | 214 | "UnexpectedEndOfFile",image->filename); |
1255 | | /* |
1256 | | if (info->file_version == 0) |
1257 | | { |
1258 | | gint i; |
1259 | | |
1260 | | g_message (_("XCF warning: version 0 of XCF file format\n" |
1261 | | "did not save indexed colormaps correctly.\n" |
1262 | | "Substituting grayscale map.")); |
1263 | | info->cp += |
1264 | | xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); |
1265 | | gimage->cmap = g_new (guchar, gimage->num_cols*3); |
1266 | | xcf_seek_pos (info, info->cp + gimage->num_cols); |
1267 | | for (i = 0; i<gimage->num_cols; i++) |
1268 | | { |
1269 | | gimage->cmap[i*3+0] = i; |
1270 | | gimage->cmap[i*3+1] = i; |
1271 | | gimage->cmap[i*3+2] = i; |
1272 | | } |
1273 | | } |
1274 | | else |
1275 | | { |
1276 | | info->cp += |
1277 | | xcf_read_int32 (info->fp, (guint32*) &gimage->num_cols, 1); |
1278 | | gimage->cmap = g_new (guchar, gimage->num_cols*3); |
1279 | | info->cp += |
1280 | | xcf_read_int8 (info->fp, |
1281 | | (guint8*) gimage->cmap, gimage->num_cols*3); |
1282 | | } |
1283 | | */ |
1284 | 214 | break; |
1285 | 0 | } |
1286 | 1.85k | case PROP_COMPRESSION: |
1287 | 1.85k | { |
1288 | 1.85k | doc_info.compression = ReadBlobByte(image); |
1289 | 1.85k | if ((doc_info.compression != COMPRESS_NONE) && |
1290 | 1.85k | (doc_info.compression != COMPRESS_RLE) && |
1291 | 1.85k | (doc_info.compression != COMPRESS_ZLIB) && |
1292 | 1.85k | (doc_info.compression != COMPRESS_FRACTAL)) |
1293 | 1.83k | ThrowReaderException(CorruptImageError,"UnrecognizedImageCompression"); |
1294 | 1.83k | } |
1295 | 0 | break; |
1296 | | |
1297 | 246 | case PROP_GUIDES: |
1298 | 246 | { |
1299 | | /* just skip it - we don't care about guides */ |
1300 | 246 | if (DiscardBlobBytes(image,prop_size) == MagickFalse) |
1301 | 13 | ThrowFileException(exception,CorruptImageError, |
1302 | 246 | "UnexpectedEndOfFile",image->filename); |
1303 | 246 | } |
1304 | 246 | break; |
1305 | | |
1306 | 1.14k | case PROP_RESOLUTION: |
1307 | 1.14k | { |
1308 | 1.14k | float |
1309 | 1.14k | x, |
1310 | 1.14k | y; |
1311 | | |
1312 | 1.14k | x=ReadBlobFloat(image); |
1313 | 1.14k | y=ReadBlobFloat(image); |
1314 | 1.14k | if ((x >= GIMP_MIN_RESOLUTION) && (x <= GIMP_MAX_RESOLUTION) && |
1315 | 1.14k | (y >= GIMP_MIN_RESOLUTION) && (y <= GIMP_MAX_RESOLUTION)) |
1316 | 198 | { |
1317 | 198 | image->resolution.x=(double) x; |
1318 | 198 | image->resolution.y=(double) y; |
1319 | 198 | image->units=PixelsPerInchResolution; |
1320 | 198 | } |
1321 | 1.14k | } |
1322 | 1.14k | break; |
1323 | | |
1324 | 212 | case PROP_TATTOO: |
1325 | 212 | { |
1326 | | /* we need to read it, even if we ignore it */ |
1327 | 212 | /*size_t tattoo_state = */ (void) ReadBlobMSBLong(image); |
1328 | 212 | } |
1329 | 212 | break; |
1330 | | |
1331 | 444 | case PROP_PARASITES: |
1332 | 444 | { |
1333 | | /* BOGUS: we may need these for IPTC stuff */ |
1334 | 444 | if (DiscardBlobBytes(image,prop_size) == MagickFalse) |
1335 | 40 | ThrowFileException(exception,CorruptImageError, |
1336 | 444 | "UnexpectedEndOfFile",image->filename); |
1337 | | /* |
1338 | | gssize_t base = info->cp; |
1339 | | GimpParasite *p; |
1340 | | |
1341 | | while (info->cp - base < prop_size) |
1342 | | { |
1343 | | p = xcf_load_parasite (info); |
1344 | | gimp_image_parasite_attach (gimage, p); |
1345 | | gimp_parasite_free (p); |
1346 | | } |
1347 | | if (info->cp - base != prop_size) |
1348 | | g_message ("Error detected while loading an image's parasites"); |
1349 | | */ |
1350 | 444 | } |
1351 | 444 | break; |
1352 | | |
1353 | 198 | case PROP_UNIT: |
1354 | 198 | { |
1355 | | /* BOGUS: ignore for now... */ |
1356 | 198 | /*size_t unit = */ (void) ReadBlobMSBLong(image); |
1357 | 198 | } |
1358 | 198 | break; |
1359 | | |
1360 | 250 | case PROP_PATHS: |
1361 | 250 | { |
1362 | | /* BOGUS: just skip it for now */ |
1363 | 250 | if (DiscardBlobBytes(image,prop_size) == MagickFalse) |
1364 | 24 | ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", |
1365 | 250 | image->filename); |
1366 | | |
1367 | | /* |
1368 | | PathList *paths = xcf_load_bzpaths (gimage, info); |
1369 | | gimp_image_set_paths (gimage, paths); |
1370 | | */ |
1371 | 250 | } |
1372 | 250 | break; |
1373 | | |
1374 | 178 | case PROP_USER_UNIT: |
1375 | 178 | { |
1376 | 178 | char unit_string[1000]; |
1377 | | /*BOGUS: ignored for now */ |
1378 | 178 | /*float factor = (float) */ (void) ReadBlobMSBLong(image); |
1379 | 178 | /* size_t digits = */ (void) ReadBlobMSBLong(image); |
1380 | 1.06k | for (i=0; i<5; i++) |
1381 | 890 | (void) ReadBlobStringWithLongSize(image, unit_string, |
1382 | 890 | sizeof(unit_string),exception); |
1383 | 178 | } |
1384 | 178 | break; |
1385 | | |
1386 | 2.43k | default: |
1387 | 2.43k | { |
1388 | 2.43k | int buf[16]; |
1389 | 2.43k | ssize_t amount; |
1390 | | |
1391 | | /* read over it... */ |
1392 | 6.79k | while ((prop_size > 0) && (EOFBlob(image) == MagickFalse)) |
1393 | 4.42k | { |
1394 | 4.42k | amount=(ssize_t) MagickMin(16, prop_size); |
1395 | 4.42k | amount=(ssize_t) ReadBlob(image,(size_t) amount,(unsigned char *) &buf); |
1396 | 4.42k | if (!amount) |
1397 | 4.36k | ThrowReaderException(CorruptImageError,"CorruptImage"); |
1398 | 4.36k | prop_size -= (size_t) MagickMin(16,(size_t) amount); |
1399 | 4.36k | } |
1400 | 2.43k | } |
1401 | 2.37k | break; |
1402 | 9.63k | } |
1403 | 9.63k | } |
1404 | 2.78k | if (foundPropEnd == MagickFalse) |
1405 | 2.45k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
1406 | | |
1407 | 2.45k | if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0)) |
1408 | 0 | { |
1409 | 0 | ; /* do nothing, were just pinging! */ |
1410 | 0 | } |
1411 | 2.45k | else |
1412 | 2.45k | { |
1413 | 2.45k | MagickBooleanType |
1414 | 2.45k | foundAllLayers = MagickFalse; |
1415 | | |
1416 | 2.45k | MagickOffsetType |
1417 | 2.45k | oldPos = TellBlob(image); |
1418 | | |
1419 | 2.45k | size_t |
1420 | 2.45k | number_layers = 0; |
1421 | | |
1422 | 2.45k | ssize_t |
1423 | 2.45k | current_layer = 0; |
1424 | | |
1425 | 2.45k | XCFLayerInfo |
1426 | 2.45k | *layer_info; |
1427 | | |
1428 | | /* |
1429 | | The read pointer. |
1430 | | */ |
1431 | 2.45k | do |
1432 | 10.3k | { |
1433 | 10.3k | offset=GetXCFOffset(image,&doc_info); |
1434 | 10.3k | if (offset == 0) |
1435 | 2.45k | foundAllLayers=MagickTrue; |
1436 | 7.84k | else |
1437 | 7.84k | { |
1438 | 7.84k | number_layers++; |
1439 | 7.84k | if (AcquireMagickResource(ListLengthResource,number_layers) == MagickFalse) |
1440 | 7.84k | ThrowReaderException(ResourceLimitError,"ListLengthExceedsLimit"); |
1441 | 7.84k | } |
1442 | 10.2k | if (EOFBlob(image) != MagickFalse) |
1443 | 1.66k | { |
1444 | 1.66k | ThrowFileException(exception,CorruptImageError, |
1445 | 1.66k | "UnexpectedEndOfFile",image->filename); |
1446 | 1.66k | break; |
1447 | 1.66k | } |
1448 | 10.2k | } while (foundAllLayers == MagickFalse); |
1449 | 2.45k | doc_info.number_layers=number_layers; |
1450 | 2.45k | offset=SeekBlob(image,oldPos,SEEK_SET); /* restore the position! */ |
1451 | 2.45k | if (offset < 0) |
1452 | 2.45k | ThrowReaderException(CorruptImageError,"ImproperImageHeader"); |
1453 | | /* allocate our array of layer info blocks */ |
1454 | 2.45k | length=(size_t) number_layers; |
1455 | 2.45k | layer_info=(XCFLayerInfo *) AcquireQuantumMemory(length, |
1456 | 2.45k | sizeof(*layer_info)); |
1457 | 2.45k | if (layer_info == (XCFLayerInfo *) NULL) |
1458 | 2.31k | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
1459 | 2.31k | (void) memset(layer_info,0,number_layers*sizeof(XCFLayerInfo)); |
1460 | 2.31k | for ( ; ; ) |
1461 | 5.93k | { |
1462 | 5.93k | MagickBooleanType |
1463 | 5.93k | layer_ok; |
1464 | | |
1465 | 5.93k | MagickOffsetType |
1466 | 5.93k | saved_pos; |
1467 | | |
1468 | | /* read in the offset of the next layer */ |
1469 | 5.93k | offset=GetXCFOffset(image,&doc_info); |
1470 | | /* if the offset is 0 then we are at the end |
1471 | | * of the layer list. |
1472 | | */ |
1473 | 5.93k | if (offset == 0) |
1474 | 285 | break; |
1475 | | /* save the current position as it is where the |
1476 | | * next layer offset is stored. |
1477 | | */ |
1478 | 5.65k | saved_pos=TellBlob(image); |
1479 | | /* seek to the layer offset */ |
1480 | 5.65k | layer_ok=MagickFalse; |
1481 | 5.65k | if (SeekBlob(image,offset,SEEK_SET) == offset) |
1482 | 5.46k | { |
1483 | | /* read in the layer */ |
1484 | 5.46k | layer_ok=ReadOneLayer(image_info,image,&doc_info, |
1485 | 5.46k | &layer_info[current_layer],current_layer,exception); |
1486 | 5.46k | } |
1487 | 5.65k | if (layer_ok == MagickFalse) |
1488 | 2.03k | { |
1489 | 2.03k | ssize_t j; |
1490 | | |
1491 | 5.34k | for (j=0; j <= current_layer; j++) |
1492 | 3.31k | if (layer_info[j].image != (Image *) NULL) |
1493 | 2.55k | layer_info[j].image=DestroyImage(layer_info[j].image); |
1494 | 2.03k | layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info); |
1495 | 2.03k | ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); |
1496 | 0 | } |
1497 | | /* restore the saved position so we'll be ready to |
1498 | | * read the next offset. |
1499 | | */ |
1500 | 3.61k | offset=SeekBlob(image, saved_pos, SEEK_SET); |
1501 | 3.61k | current_layer++; |
1502 | 3.61k | } |
1503 | | #if 0 |
1504 | | { |
1505 | | /* NOTE: XCF layers are REVERSED from composite order! */ |
1506 | | signed int j; |
1507 | | for (j=number_layers-1; j>=0; j--) { |
1508 | | /* BOGUS: need to consider layer blending modes!! */ |
1509 | | |
1510 | | if ( layer_info[j].visible ) { /* only visible ones, please! */ |
1511 | | CompositeImage(image, OverCompositeOp, layer_info[j].image, |
1512 | | layer_info[j].offset_x, layer_info[j].offset_y ); |
1513 | | layer_info[j].image =DestroyImage( layer_info[j].image ); |
1514 | | |
1515 | | /* If we do this, we'll get REAL gray images! */ |
1516 | | if ( image_type == GIMP_GRAY ) { |
1517 | | QuantizeInfo qi; |
1518 | | GetQuantizeInfo(&qi); |
1519 | | qi.colorspace = GRAYColorspace; |
1520 | | QuantizeImage( &qi, layer_info[j].image ); |
1521 | | } |
1522 | | } |
1523 | | } |
1524 | | } |
1525 | | #else |
1526 | 285 | { |
1527 | | /* NOTE: XCF layers are REVERSED from composite order! */ |
1528 | 285 | ssize_t j; |
1529 | | |
1530 | | /* now reverse the order of the layers as they are put |
1531 | | into subimages |
1532 | | */ |
1533 | 2.62k | for (j=(ssize_t) number_layers-1; j >= 0; j--) |
1534 | 2.34k | AppendImageToList(&image,layer_info[j].image); |
1535 | 285 | } |
1536 | 285 | #endif |
1537 | | |
1538 | 285 | layer_info=(XCFLayerInfo *) RelinquishMagickMemory(layer_info); |
1539 | | |
1540 | | #if 0 /* BOGUS: do we need the channels?? */ |
1541 | | while (MagickTrue) |
1542 | | { |
1543 | | /* read in the offset of the next channel */ |
1544 | | info->cp += xcf_read_int32 (info->fp, &offset, 1); |
1545 | | |
1546 | | /* if the offset is 0 then we are at the end |
1547 | | * of the channel list. |
1548 | | */ |
1549 | | if (offset == 0) |
1550 | | break; |
1551 | | |
1552 | | /* save the current position as it is where the |
1553 | | * next channel offset is stored. |
1554 | | */ |
1555 | | saved_pos = info->cp; |
1556 | | |
1557 | | /* seek to the channel offset */ |
1558 | | xcf_seek_pos (info, offset); |
1559 | | |
1560 | | /* read in the layer */ |
1561 | | channel = xcf_load_channel (info, gimage); |
1562 | | if (channel == 0) |
1563 | | goto error; |
1564 | | |
1565 | | num_successful_elements++; |
1566 | | |
1567 | | /* add the channel to the image if its not the selection */ |
1568 | | if (channel != gimage->selection_mask) |
1569 | | gimp_image_add_channel (gimage, channel, -1); |
1570 | | |
1571 | | /* restore the saved position so we'll be ready to |
1572 | | * read the next offset. |
1573 | | */ |
1574 | | xcf_seek_pos (info, saved_pos); |
1575 | | } |
1576 | | #endif |
1577 | 285 | } |
1578 | | |
1579 | 285 | (void) CloseBlob(image); |
1580 | 285 | if (GetNextImageInList(image) != (Image *) NULL) |
1581 | 285 | DestroyImage(RemoveFirstImageFromList(&image)); |
1582 | 285 | if (image_type == GIMP_GRAY) |
1583 | 35 | image->type=GrayscaleType; |
1584 | 285 | return(GetFirstImageInList(image)); |
1585 | 2.45k | } |
1586 | | |
1587 | | /* |
1588 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1589 | | % % |
1590 | | % % |
1591 | | % % |
1592 | | % R e g i s t e r X C F I m a g e % |
1593 | | % % |
1594 | | % % |
1595 | | % % |
1596 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1597 | | % |
1598 | | % RegisterXCFImage() adds attributes for the XCF image format to |
1599 | | % the list of supported formats. The attributes include the image format |
1600 | | % tag, a method to read and/or write the format, whether the format |
1601 | | % supports the saving of more than one frame to the same file or blob, |
1602 | | % whether the format supports native in-memory I/O, and a brief |
1603 | | % description of the format. |
1604 | | % |
1605 | | % The format of the RegisterXCFImage method is: |
1606 | | % |
1607 | | % size_t RegisterXCFImage(void) |
1608 | | % |
1609 | | */ |
1610 | | ModuleExport size_t RegisterXCFImage(void) |
1611 | 10 | { |
1612 | 10 | MagickInfo |
1613 | 10 | *entry; |
1614 | | |
1615 | 10 | entry=AcquireMagickInfo("XCF","XCF","GIMP image"); |
1616 | 10 | entry->decoder=(DecodeImageHandler *) ReadXCFImage; |
1617 | 10 | entry->magick=(IsImageFormatHandler *) IsXCF; |
1618 | 10 | entry->flags|=CoderDecoderSeekableStreamFlag; |
1619 | 10 | (void) RegisterMagickInfo(entry); |
1620 | 10 | return(MagickImageCoderSignature); |
1621 | 10 | } |
1622 | | |
1623 | | /* |
1624 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1625 | | % % |
1626 | | % % |
1627 | | % % |
1628 | | % U n r e g i s t e r X C F I m a g e % |
1629 | | % % |
1630 | | % % |
1631 | | % % |
1632 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1633 | | % |
1634 | | % UnregisterXCFImage() removes format registrations made by the |
1635 | | % XCF module from the list of supported formats. |
1636 | | % |
1637 | | % The format of the UnregisterXCFImage method is: |
1638 | | % |
1639 | | % UnregisterXCFImage(void) |
1640 | | % |
1641 | | */ |
1642 | | ModuleExport void UnregisterXCFImage(void) |
1643 | 0 | { |
1644 | 0 | (void) UnregisterMagickInfo("XCF"); |
1645 | 0 | } |