/src/graphicsmagick/coders/tiff.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2026 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % TTTTT IIIII FFFFF FFFFF % |
15 | | % T I F F % |
16 | | % T I FFF FFF % |
17 | | % T I F F % |
18 | | % T IIIII F F % |
19 | | % % |
20 | | % % |
21 | | % Read/Write TIFF Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Original Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % Re-Written For GraphicsMagick % |
28 | | % Bob Friesenhahn % |
29 | | % 2002-2015 % |
30 | | % Jaroslav Fojtik % |
31 | | % 2023-2024 % |
32 | | % % |
33 | | % % |
34 | | % % |
35 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "magick/studio.h" |
44 | | #include "magick/analyze.h" |
45 | | #include "magick/attribute.h" |
46 | | #include "magick/bit_stream.h" |
47 | | #include "magick/blob.h" |
48 | | #include "magick/colormap.h" |
49 | | #include "magick/constitute.h" |
50 | | #include "magick/log.h" |
51 | | #include "magick/magick.h" |
52 | | #include "magick/monitor.h" |
53 | | #include "magick/pixel_cache.h" |
54 | | #include "magick/profile.h" |
55 | | #include "magick/quantize.h" |
56 | | #include "magick/resize.h" |
57 | | #include "magick/resource.h" |
58 | | #include "magick/tempfile.h" |
59 | | #include "magick/tsd.h" |
60 | | #include "magick/utility.h" |
61 | | #include "magick/version.h" |
62 | | #include "magick/static.h" |
63 | | #if defined(HasTIFF) |
64 | | # if defined(HAVE_TIFFCONF_H) |
65 | | # include "tiffconf.h" |
66 | | # endif |
67 | | # include "tiff.h" |
68 | | # include "tiffio.h" |
69 | | # if !defined(COMPRESSION_ADOBE_DEFLATE) |
70 | | # define COMPRESSION_ADOBE_DEFLATE 8 |
71 | | # endif /* !defined(COMPRESSION_ADOBE_DEFLATE) */ |
72 | | # if defined(COMPRESSION_ZSTD) && defined(HasZSTD) |
73 | | # include "zstd.h" |
74 | | # endif /* if defined(COMPRESSION_ZSTD) && defined(HasZSTD) */ |
75 | | |
76 | | #if !defined(EXPERIMENTAL_EXIF_TAGS) |
77 | | # define EXPERIMENTAL_EXIF_TAGS 1 |
78 | | #endif /* if !defined(EXPERIMENTAL_EXIF_TAGS) */ |
79 | | |
80 | | /* |
81 | | JPEG headers are needed in order to obtain BITS_IN_JSAMPLE |
82 | | */ |
83 | | # if !defined(_VISUALC_) |
84 | | # if defined(HasJPEG) |
85 | | # if defined(__MINGW32__) |
86 | | # define XMD_H 1 |
87 | | # endif |
88 | | # undef HAVE_STDLIB_H |
89 | | # include <jpeglib.h> |
90 | | # endif /* defined(HasJPEG) */ |
91 | | # endif /* !defined(_VISUALC_) */ |
92 | | |
93 | | #if defined(TIFF_VERSION_BIG) |
94 | | # define HasBigTIFF 1 |
95 | | #endif /* defined(TIFF_BIGTIFF_VERSION) */ |
96 | | |
97 | | #if (TIFFLIB_VERSION >= 20201219) |
98 | | # undef uint16 |
99 | 1.04M | # define uint16 uint16_t |
100 | | # undef uint32 |
101 | 996k | # define uint32 uint32_t |
102 | | #endif /* TIFFLIB_VERSION */ |
103 | | |
104 | | /* |
105 | | Set to 1 in order to log low-level BLOB I/O at "coder" level. |
106 | | */ |
107 | | #if !defined(LOG_TIFF_BLOB_IO) |
108 | | # define LOG_TIFF_BLOB_IO 0 |
109 | | #endif /* !defined(LOG_TIFF_BLOB_IO) */ |
110 | | |
111 | | /* |
112 | | The number of bytes to try to allocate per uncompressed strip by default. |
113 | | This is used to determine the default number of rows per strip. |
114 | | |
115 | | Target that each uncompressed strip is ~1MB. |
116 | | */ |
117 | | #if !defined(TIFF_BYTES_PER_STRIP) |
118 | 34.0k | # define TIFF_BYTES_PER_STRIP 1048576 |
119 | | #endif /* !defined(TIFF_BYTES_PER_STRIP) */ |
120 | | |
121 | | #if !defined(PREDICTOR_NONE) |
122 | | #define PREDICTOR_NONE 1 |
123 | | #endif |
124 | | #if !defined(PREDICTOR_HORIZONTAL) |
125 | | #define PREDICTOR_HORIZONTAL 2 |
126 | | #endif |
127 | | #if !defined(PREDICTOR_FLOATINGPOINT) |
128 | | #define PREDICTOR_FLOATINGPOINT 3 |
129 | | #endif |
130 | | #if !defined(SAMPLEFORMAT_COMPLEXINT) |
131 | | #define SAMPLEFORMAT_COMPLEXINT 5 |
132 | | #endif |
133 | | #if !defined(SAMPLEFORMAT_COMPLEXIEEEFP) |
134 | | #define SAMPLEFORMAT_COMPLEXIEEEFP 6 |
135 | | #endif |
136 | | |
137 | | #if !defined(TIFFTAG_COPYRIGHT) |
138 | | #define TIFFTAG_COPYRIGHT 33432 |
139 | | #endif |
140 | | #if !defined(TIFFTAG_OPIIMAGEID) |
141 | | #define TIFFTAG_OPIIMAGEID 32781 |
142 | | #endif |
143 | | |
144 | | /* |
145 | | Global declarations. |
146 | | */ |
147 | | static MagickTsdKey_t tsd_key = (MagickTsdKey_t) 0; |
148 | | |
149 | | /* static ExceptionInfo */ |
150 | | /* *tiff_exception; */ |
151 | | |
152 | | typedef struct _Magick_TIFF_ClientData |
153 | | { |
154 | | Image |
155 | | *image; |
156 | | |
157 | | const ImageInfo |
158 | | *image_info; |
159 | | } Magick_TIFF_ClientData; |
160 | | |
161 | | /* |
162 | | Forward declarations. |
163 | | */ |
164 | | #if defined(__cplusplus) || defined(c_plusplus) |
165 | | extern "C" { |
166 | | #endif |
167 | | |
168 | | static tsize_t |
169 | | TIFFReadBlob(thandle_t,tdata_t,tsize_t); |
170 | | |
171 | | static MagickPassFail |
172 | | WriteGROUP4RAWImage(const ImageInfo *image_info,Image *image), |
173 | | WritePTIFImage(const ImageInfo *,Image *), |
174 | | WriteTIFFImage(const ImageInfo *,Image *); |
175 | | |
176 | | #if defined(__cplusplus) || defined(c_plusplus) |
177 | | } |
178 | | #endif |
179 | | |
180 | | /* |
181 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
182 | | % % |
183 | | % % |
184 | | % % |
185 | | % I s T I F F % |
186 | | % % |
187 | | % % |
188 | | % % |
189 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
190 | | % |
191 | | % Method IsTIFF returns True if the image format type, identified by the |
192 | | % magick string, is TIFF. |
193 | | % |
194 | | % The format of the IsTIFF method is: |
195 | | % |
196 | | % MagickBool IsTIFF(const unsigned char *magick,const size_t length) |
197 | | % |
198 | | % A description of each parameter follows: |
199 | | % |
200 | | % o status: Method IsTIFF returns MagickTrue if the image format type |
201 | | % is TIFF. |
202 | | % |
203 | | % o magick: This string is generally the first few bytes of an image file |
204 | | % or blob. |
205 | | % |
206 | | % o length: Specifies the length of the magick string. |
207 | | % |
208 | | % |
209 | | */ |
210 | | static MagickBool |
211 | | IsTIFF(const unsigned char *magick,const size_t length) |
212 | 0 | { |
213 | 0 | if (length < 8) |
214 | 0 | return(False); |
215 | | |
216 | | /* Big endian Classic TIFF*/ |
217 | 0 | if (memcmp(magick,"\115\115\000\052",4) == 0) |
218 | 0 | return(True); |
219 | | |
220 | | /* Little endian Classic TIFF */ |
221 | 0 | if (memcmp(magick,"\111\111\052\000",4) == 0) |
222 | 0 | return(True); |
223 | | |
224 | 0 | #if defined(HasBigTIFF) |
225 | | /* |
226 | | * From http://www.remotesensing.org/libtiff/bigtiffdesign.html |
227 | | * |
228 | | * * The Version ID, in header bytes 2-3, formerly decimal 42, now changes to 43 |
229 | | * * Header bytes 4-5 contain the decimal number 8. |
230 | | * - If there is some other number here, a reader should give up. |
231 | | * - This is to provide a nice way to move to 16-byte pointers some day. |
232 | | * |
233 | | * * Header bytes 6-7 are reserved and must be zero. |
234 | | * - If they're not, a reader should give up. |
235 | | * |
236 | | * Also http://www.awaresystems.be/imaging/tiff/bigtiff.html |
237 | | */ |
238 | | |
239 | | /* Big endian BigTIFF*/ |
240 | 0 | if (memcmp(magick,"\115\115\000\053\000\010\000\000",8) == 0) |
241 | 0 | return(True); |
242 | | |
243 | | /* Little endian BigTIFF */ |
244 | 0 | if (memcmp(magick,"\111\111\053\000\010\000\000\000",8) == 0) |
245 | 0 | return(True); |
246 | 0 | #endif |
247 | | |
248 | 0 | return(False); |
249 | 0 | } |
250 | | |
251 | | /* |
252 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
253 | | % % |
254 | | % % |
255 | | % % |
256 | | % R e a d T I F F I m a g e % |
257 | | % % |
258 | | % % |
259 | | % % |
260 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
261 | | % |
262 | | % Method ReadTIFFImage reads a Tagged image file and returns it. It |
263 | | % allocates the memory necessary for the new Image structure and returns a |
264 | | % pointer to the new image. |
265 | | % |
266 | | % The format of the ReadTIFFImage method is: |
267 | | % |
268 | | % Image *ReadTIFFImage(const ImageInfo *image_info, |
269 | | % ExceptionInfo *exception) |
270 | | % |
271 | | % A description of each parameter follows: |
272 | | % |
273 | | % o image: Method ReadTIFFImage returns a pointer to the image after |
274 | | % reading. A null image is returned if there is a memory shortage or |
275 | | % if the image cannot be read. |
276 | | % |
277 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
278 | | % |
279 | | % o exception: return any errors or warnings in this structure. |
280 | | % |
281 | | % |
282 | | */ |
283 | | #if defined(HAVE_TIFFMERGEFIELDINFO) && defined(HAVE_TIFFSETTAGEXTENDER) |
284 | | # define EXTEND_TIFF_TAGS 1 |
285 | | # if !defined(TIFFTAG_EXIFIFD) |
286 | | # define TIFFTAG_EXIFIFD 34665 |
287 | | # endif |
288 | | # if TIFFLIB_VERSION < 20050704 |
289 | | /* It seems that support was added for the EXIFIFDOffset tag in |
290 | | libtiff release 3-7-3 which corresponds with TIFFLIB_VERSION |
291 | | 20050704 */ |
292 | | static const TIFFFieldInfo |
293 | | ExtensionTiffFieldInfo[] = |
294 | | { |
295 | | { |
296 | | TIFFTAG_EXIFIFD, -1, -1, TIFF_LONG, FIELD_CUSTOM, |
297 | | MagickFalse, MagickTrue, "EXIFIFDOffset" |
298 | | } |
299 | | }; |
300 | | # endif |
301 | | |
302 | | /* |
303 | | Ignore specific tags |
304 | | */ |
305 | | static void |
306 | | TIFFIgnoreTags(TIFF *tiff) |
307 | 847k | { |
308 | 847k | char |
309 | 847k | *q; |
310 | | |
311 | 847k | const char |
312 | 847k | *p, |
313 | 847k | *tags; |
314 | | |
315 | 847k | Magick_TIFF_ClientData |
316 | 847k | *client_data; |
317 | | |
318 | 847k | register ssize_t |
319 | 847k | i; |
320 | | |
321 | 847k | size_t |
322 | 847k | count; |
323 | | |
324 | 847k | TIFFFieldInfo |
325 | 847k | *ignore; |
326 | | |
327 | 847k | if (TIFFGetReadProc(tiff) != TIFFReadBlob) |
328 | 1.67k | return; |
329 | | |
330 | 846k | client_data=((Magick_TIFF_ClientData *) TIFFClientdata(tiff)); |
331 | 846k | if (client_data == (Magick_TIFF_ClientData *) NULL) |
332 | 0 | return; |
333 | 846k | tags=AccessDefinition(client_data->image_info,"tiff","ignore-tags"); |
334 | 846k | if (tags == (const char *) NULL) |
335 | 846k | return; |
336 | 0 | count=0; |
337 | 0 | p=tags; |
338 | 0 | while (*p != '\0') |
339 | 0 | { |
340 | 0 | while ((isspace((int) ((unsigned char) *p)) != 0)) |
341 | 0 | p++; |
342 | |
|
343 | 0 | { |
344 | 0 | (void) strtol(p,&q,10); |
345 | 0 | } |
346 | 0 | if (p == q) |
347 | 0 | return; |
348 | | |
349 | 0 | p=q; |
350 | 0 | count++; |
351 | |
|
352 | 0 | while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) |
353 | 0 | p++; |
354 | 0 | } |
355 | 0 | if (count == 0) |
356 | 0 | return; |
357 | 0 | i=0; |
358 | 0 | p=tags; |
359 | 0 | ignore=MagickAllocateResourceLimitedArray(TIFFFieldInfo*,count,sizeof(*ignore)); |
360 | 0 | if (ignore == (TIFFFieldInfo*) NULL) |
361 | 0 | { |
362 | 0 | ThrowException(&client_data->image->exception,ResourceLimitError, |
363 | 0 | MemoryAllocationFailed,client_data->image->filename); |
364 | 0 | return; |
365 | 0 | } |
366 | | /* This also sets field_bit to 0 (FIELD_IGNORE) */ |
367 | 0 | (void) memset(ignore,0,count*sizeof(*ignore)); |
368 | 0 | while (*p != '\0') |
369 | 0 | { |
370 | 0 | while ((isspace((int) ((unsigned char) *p)) != 0)) |
371 | 0 | p++; |
372 | |
|
373 | 0 | ignore[i].field_tag=(ttag_t) strtol(p,&q,10); |
374 | |
|
375 | 0 | p=q; |
376 | 0 | i++; |
377 | |
|
378 | 0 | while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == ',')) |
379 | 0 | p++; |
380 | 0 | } |
381 | 0 | (void) TIFFMergeFieldInfo(tiff,ignore,(uint32) count); |
382 | 0 | MagickFreeResourceLimitedMemory(TIFFFieldInfo*,ignore); |
383 | 0 | } |
384 | | |
385 | | /* |
386 | | Merge in our new fields and then call the next extender if there is |
387 | | one in effect. |
388 | | */ |
389 | | static TIFFExtendProc _ParentExtender = NULL; |
390 | | static void |
391 | | ExtensionTagsDefaultDirectory(TIFF *tif) |
392 | 847k | { |
393 | | # if TIFFLIB_VERSION < 20050704 |
394 | | /* Install the extended Tag field info */ |
395 | | TIFFMergeFieldInfo(tif, ExtensionTiffFieldInfo, |
396 | | sizeof(ExtensionTiffFieldInfo)/ |
397 | | sizeof(ExtensionTiffFieldInfo[0])); |
398 | | # endif |
399 | | |
400 | | /* Since an XTIFF client module may have overridden |
401 | | * the default directory method, we call it now to |
402 | | * allow it to set up the rest of its own methods. |
403 | | */ |
404 | 847k | if (_ParentExtender) |
405 | 0 | (*_ParentExtender)(tif); |
406 | 847k | TIFFIgnoreTags(tif); |
407 | 847k | } |
408 | | |
409 | | /* |
410 | | Obtain the current handler at the front of the chain, and register |
411 | | ourselves as the new first handler. |
412 | | */ |
413 | | static |
414 | | void ExtensionTagsInitialize(void) |
415 | 7 | { |
416 | 7 | static int |
417 | 7 | not_first_time=0; |
418 | | |
419 | 7 | if (not_first_time) return; /* Been there. Done that. */ |
420 | 7 | not_first_time = 1; |
421 | | |
422 | | /* Grab the inherited method and install */ |
423 | 7 | _ParentExtender = TIFFSetTagExtender(ExtensionTagsDefaultDirectory); |
424 | 7 | } |
425 | | |
426 | | #endif /* defined(HAVE_TIFFMERGEFIELDINFO) && defined(HAVE_TIFFSETTAGEXTENDER) */ |
427 | | |
428 | | /* |
429 | | Return MagickTrue if libtiff supports the indicated compression type. |
430 | | Sets buffer pointed to by 'compression_name' to the name of the compression. |
431 | | */ |
432 | | static MagickBool |
433 | | CompressionSupported(const CompressionType compression, |
434 | | char *compression_name, |
435 | | const size_t sizeof_compression_name) |
436 | 34.3k | { |
437 | 34.3k | uint16 |
438 | 34.3k | compress_tag; |
439 | | |
440 | 34.3k | MagickBool |
441 | 34.3k | status; |
442 | | |
443 | 34.3k | status = MagickFalse; |
444 | 34.3k | compress_tag=COMPRESSION_NONE; |
445 | 34.3k | strlcpy(compression_name,"Undefined",sizeof_compression_name); |
446 | | |
447 | | /* |
448 | | This switch statement should match all the values of CompressionType. |
449 | | */ |
450 | 34.3k | switch (compression) |
451 | 34.3k | { |
452 | 0 | case UndefinedCompression: |
453 | 0 | { |
454 | 0 | strlcpy(compression_name,"Undefined",sizeof_compression_name); |
455 | 0 | break; |
456 | 0 | } |
457 | 9.85k | case NoCompression: |
458 | 9.85k | { |
459 | 9.85k | strlcpy(compression_name,"No",sizeof_compression_name); |
460 | 9.85k | compress_tag=COMPRESSION_NONE; |
461 | 9.85k | status=MagickTrue; |
462 | 9.85k | break; |
463 | 0 | } |
464 | 0 | case BZipCompression: |
465 | 0 | { |
466 | 0 | strlcpy(compression_name,"BZip",sizeof_compression_name); |
467 | 0 | break; |
468 | 0 | } |
469 | 1.58k | case FaxCompression: |
470 | 1.58k | { |
471 | 1.58k | strlcpy(compression_name,"Group3 FAX",sizeof_compression_name); |
472 | 1.58k | #if defined(COMPRESSION_CCITTFAX3) |
473 | 1.58k | compress_tag=COMPRESSION_CCITTFAX3; |
474 | 1.58k | status=MagickTrue; |
475 | 1.58k | #endif |
476 | 1.58k | break; |
477 | 0 | } |
478 | 4.95k | case Group4Compression: |
479 | 4.95k | { |
480 | 4.95k | strlcpy(compression_name,"Group4 FAX",sizeof_compression_name); |
481 | 4.95k | #if defined(COMPRESSION_CCITTFAX4) |
482 | 4.95k | compress_tag=COMPRESSION_CCITTFAX4; |
483 | 4.95k | status=MagickTrue; |
484 | 4.95k | #endif |
485 | 4.95k | break; |
486 | 0 | } |
487 | 0 | case JBIG1Compression: |
488 | 0 | { |
489 | 0 | strlcpy(compression_name,"JBIG",sizeof_compression_name); |
490 | 0 | #if defined(COMPRESSION_JBIG) |
491 | 0 | compress_tag=COMPRESSION_JBIG; |
492 | 0 | status=MagickTrue; |
493 | 0 | #endif |
494 | 0 | break; |
495 | 0 | } |
496 | 0 | case JBIG2Compression: |
497 | 0 | { |
498 | 0 | strlcpy(compression_name,"JBIG2",sizeof_compression_name); |
499 | 0 | break; |
500 | 0 | } |
501 | 8.15k | case JPEGCompression: |
502 | 8.15k | { |
503 | 8.15k | strlcpy(compression_name,"JPEG",sizeof_compression_name); |
504 | 8.15k | #if defined(COMPRESSION_JPEG) |
505 | 8.15k | compress_tag=COMPRESSION_JPEG; |
506 | 8.15k | status=MagickTrue; |
507 | 8.15k | #endif |
508 | 8.15k | break; |
509 | 0 | } |
510 | 0 | case JPEG2000Compression: |
511 | 0 | { |
512 | 0 | strlcpy(compression_name,"JPEG2000",sizeof_compression_name); |
513 | 0 | break; |
514 | 0 | } |
515 | 0 | case LERCCompression: |
516 | 0 | { |
517 | 0 | strlcpy(compression_name,"LERC",sizeof_compression_name); |
518 | 0 | #if defined(COMPRESSION_LERC) |
519 | 0 | compress_tag=COMPRESSION_LERC; |
520 | 0 | status=MagickTrue; |
521 | 0 | #endif |
522 | 0 | break; |
523 | 0 | } |
524 | 0 | case LosslessJPEGCompression: |
525 | 0 | { |
526 | 0 | strlcpy(compression_name,"Lossless JPEG",sizeof_compression_name); |
527 | 0 | break; |
528 | 0 | } |
529 | 0 | case LZMACompression: |
530 | 0 | { |
531 | 0 | strlcpy(compression_name,"LZMA",sizeof_compression_name); |
532 | 0 | #if defined(COMPRESSION_LZMA) |
533 | 0 | compress_tag=COMPRESSION_LZMA; |
534 | 0 | status=MagickTrue; |
535 | 0 | #endif |
536 | 0 | break; |
537 | 0 | } |
538 | 750 | case LZWCompression: |
539 | 750 | { |
540 | 750 | strlcpy(compression_name,"LZW",sizeof_compression_name); |
541 | 750 | #if defined(COMPRESSION_LZW) |
542 | 750 | compress_tag=COMPRESSION_LZW; |
543 | 750 | status=MagickTrue; |
544 | 750 | #endif |
545 | 750 | break; |
546 | 0 | } |
547 | 0 | case RLECompression: |
548 | 0 | { |
549 | 0 | strlcpy(compression_name,"Macintosh RLE (Packbits)",sizeof_compression_name); |
550 | 0 | #if defined(COMPRESSION_PACKBITS) |
551 | 0 | compress_tag=COMPRESSION_PACKBITS; |
552 | 0 | status=MagickTrue; |
553 | 0 | #endif |
554 | 0 | break; |
555 | 0 | } |
556 | 3.12k | case WebPCompression: |
557 | 3.12k | { |
558 | 3.12k | strlcpy(compression_name,"WebP",sizeof_compression_name); |
559 | 3.12k | #if defined(COMPRESSION_WEBP) |
560 | 3.12k | compress_tag=COMPRESSION_WEBP; |
561 | 3.12k | status=MagickTrue; |
562 | 3.12k | #endif |
563 | 3.12k | break; |
564 | 0 | } |
565 | 3.52k | case ZipCompression: |
566 | 3.52k | { |
567 | 3.52k | strlcpy(compression_name,"Adobe Deflate",sizeof_compression_name); |
568 | 3.52k | #if defined(COMPRESSION_ADOBE_DEFLATE) |
569 | 3.52k | compress_tag=COMPRESSION_ADOBE_DEFLATE; |
570 | 3.52k | status=MagickTrue; |
571 | 3.52k | #endif |
572 | 3.52k | break; |
573 | 0 | } |
574 | 2.42k | case ZSTDCompression: |
575 | 2.42k | { |
576 | 2.42k | strlcpy(compression_name,"Zstandard",sizeof_compression_name); |
577 | 2.42k | #if defined(COMPRESSION_ZSTD) |
578 | 2.42k | compress_tag=COMPRESSION_ZSTD; |
579 | 2.42k | status=MagickTrue; |
580 | 2.42k | #endif |
581 | 2.42k | break; |
582 | 0 | } |
583 | 34.3k | } |
584 | | |
585 | 34.3k | if (MagickTrue == status) |
586 | 34.3k | { |
587 | 34.3k | #if defined(HAVE_TIFFISCODECCONFIGURED) || (TIFFLIB_VERSION > 20040919) |
588 | 34.3k | if (compress_tag != COMPRESSION_NONE) |
589 | 24.5k | { |
590 | | /* |
591 | | Returns 1 if the codec is configured and |
592 | | working. Otherwise 0 will be returned. |
593 | | */ |
594 | 24.5k | if (!TIFFIsCODECConfigured(compress_tag)) |
595 | 0 | status = MagickFalse; |
596 | 24.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
597 | 24.5k | "TIFFIsCODECConfigured says support for %s " |
598 | 24.5k | "compression %s configured.", |
599 | 24.5k | compression_name, |
600 | 24.5k | (status == MagickTrue ? "is" : "is not")); |
601 | 24.5k | } |
602 | | #else |
603 | | switch (compress_tag) |
604 | | { |
605 | | # if defined(CCITT_SUPPORT) |
606 | | case COMPRESSION_CCITTFAX3: |
607 | | case COMPRESSION_CCITTFAX4: |
608 | | # endif |
609 | | # if defined(YCBCR_SUPPORT) && defined(JPEG_SUPPORT) |
610 | | case COMPRESSION_JPEG: |
611 | | # endif |
612 | | # if defined(LZW_SUPPORT) |
613 | | case COMPRESSION_LZW: |
614 | | # endif |
615 | | # if defined(PACKBITS_SUPPORT) |
616 | | case COMPRESSION_PACKBITS: |
617 | | # endif |
618 | | # if defined(ZIP_SUPPORT) |
619 | | case COMPRESSION_ADOBE_DEFLATE: |
620 | | # endif |
621 | | case COMPRESSION_NONE: |
622 | | { |
623 | | status = MagickTrue; |
624 | | break; |
625 | | } |
626 | | default: |
627 | | { |
628 | | status = MagickFalse; |
629 | | break; |
630 | | } |
631 | | } |
632 | | #endif |
633 | 34.3k | } |
634 | 34.3k | return status; |
635 | 34.3k | } |
636 | | |
637 | | /* |
638 | | Convert a libtiff compression tag to a human readable string. |
639 | | */ |
640 | | static const char * |
641 | | CompressionTagToString(unsigned int compress_tag) |
642 | 72 | { |
643 | 72 | const char |
644 | 72 | *result = "Unknown"; |
645 | | |
646 | 72 | switch (compress_tag) |
647 | 72 | { |
648 | 0 | case COMPRESSION_ADOBE_DEFLATE: |
649 | 0 | result="ZIP deflate (Adobe)"; |
650 | 0 | break; |
651 | 0 | #if defined(COMPRESSION_DEFLATE) |
652 | 0 | case COMPRESSION_DEFLATE: |
653 | 0 | result="ZIP deflate (Pixar)"; |
654 | 0 | break; |
655 | 0 | #endif |
656 | 28 | case COMPRESSION_CCITTFAX3: |
657 | 28 | result="CCITT Group 3 fax"; |
658 | 28 | break; |
659 | 22 | case COMPRESSION_CCITTFAX4: |
660 | 22 | result="CCITT Group 4 fax"; |
661 | 22 | break; |
662 | 0 | case COMPRESSION_CCITTRLE: |
663 | 0 | result="CCITT modified Huffman RLE"; |
664 | 0 | break; |
665 | 0 | case COMPRESSION_CCITTRLEW: |
666 | 0 | result="CCITT modified Huffman RLE (Word aligned)"; |
667 | 0 | break; |
668 | 0 | #if defined(COMPRESSION_OJPEG) |
669 | 0 | case COMPRESSION_OJPEG: |
670 | 0 | result="JPEG DCT (Old)"; |
671 | 0 | break; |
672 | 0 | #endif |
673 | 0 | case COMPRESSION_JPEG: |
674 | 0 | result="JPEG DCT"; |
675 | 0 | break; |
676 | 0 | #if defined(COMPRESSION_JBIG) |
677 | 0 | case COMPRESSION_JBIG: |
678 | 0 | result="JBIG"; |
679 | 0 | break; |
680 | 0 | #endif |
681 | 0 | case COMPRESSION_LZW: |
682 | 0 | result="LZW"; |
683 | 0 | break; |
684 | 0 | #if defined(COMPRESSION_NEXT) |
685 | 0 | case COMPRESSION_NEXT: |
686 | 0 | result="NeXT 2-bit RLE"; |
687 | 0 | break; |
688 | 0 | #endif |
689 | 0 | case COMPRESSION_NONE: |
690 | 0 | result="not compressed"; |
691 | 0 | break; |
692 | 0 | case COMPRESSION_PACKBITS: |
693 | 0 | result="Macintosh RLE (Packbits)"; |
694 | 0 | break; |
695 | 0 | #if defined(COMPRESSION_THUNDERSCAN) |
696 | 0 | case COMPRESSION_THUNDERSCAN: |
697 | 0 | result="ThunderScan RLE"; |
698 | 0 | break; |
699 | 0 | #endif |
700 | 0 | #if defined(COMPRESSION_LZMA) |
701 | 0 | case COMPRESSION_LZMA: |
702 | 0 | result="LZMA"; |
703 | 0 | break; |
704 | 0 | #endif |
705 | 0 | #if defined(COMPRESSION_ZSTD) |
706 | 0 | case COMPRESSION_ZSTD: |
707 | 0 | result="Zstandard"; |
708 | 0 | break; |
709 | 0 | #endif |
710 | 0 | #if defined(COMPRESSION_WEBP) |
711 | 22 | case COMPRESSION_WEBP: |
712 | 22 | result="WebP"; |
713 | 22 | break; |
714 | 0 | #endif |
715 | 0 | #if defined(COMPRESSION_LERC) |
716 | 0 | case COMPRESSION_LERC: |
717 | 0 | result="LERC"; |
718 | 0 | break; |
719 | 72 | #endif |
720 | 72 | } |
721 | 72 | return result; |
722 | 72 | } |
723 | | |
724 | | static const char * |
725 | | PhotometricTagToString(unsigned int photometric) |
726 | 2.38k | { |
727 | 2.38k | const char |
728 | 2.38k | *result = "Unknown"; |
729 | | |
730 | 2.38k | switch (photometric) |
731 | 2.38k | { |
732 | 0 | case PHOTOMETRIC_CIELAB: |
733 | 0 | result="CIELAB"; |
734 | 0 | break; |
735 | 0 | case PHOTOMETRIC_LOGL: |
736 | 0 | result="CIE Log2(L)"; |
737 | 0 | break; |
738 | 0 | case PHOTOMETRIC_LOGLUV: |
739 | 0 | result="LOGLUV"; |
740 | 0 | break; |
741 | 0 | #if defined(PHOTOMETRIC_MASK) |
742 | 0 | case PHOTOMETRIC_MASK: |
743 | 0 | result="MASK"; |
744 | 0 | break; |
745 | 0 | #endif |
746 | 347 | case PHOTOMETRIC_MINISBLACK: |
747 | 347 | result="MINISBLACK"; |
748 | 347 | break; |
749 | 110 | case PHOTOMETRIC_MINISWHITE: |
750 | 110 | result="MINISWHITE"; |
751 | 110 | break; |
752 | 0 | case PHOTOMETRIC_PALETTE: |
753 | 0 | result="PALETTE"; |
754 | 0 | break; |
755 | 1.93k | case PHOTOMETRIC_RGB: |
756 | 1.93k | result="RGB"; |
757 | 1.93k | break; |
758 | 0 | case PHOTOMETRIC_SEPARATED: |
759 | 0 | result="SEPARATED"; |
760 | 0 | break; |
761 | 0 | case PHOTOMETRIC_YCBCR: |
762 | 0 | result="YCBCR"; |
763 | 0 | break; |
764 | 2.38k | } |
765 | | |
766 | 2.38k | return result; |
767 | 2.38k | } |
768 | | |
769 | | static const char *ExtraSampleToString(const unsigned int sample_info) |
770 | 786k | { |
771 | 786k | const char |
772 | 786k | *result = "Unknown"; |
773 | | |
774 | 786k | switch (sample_info) |
775 | 786k | { |
776 | 778k | case EXTRASAMPLE_UNSPECIFIED: |
777 | 778k | result="Unspecified data"; |
778 | 778k | break; |
779 | 4.22k | case EXTRASAMPLE_ASSOCALPHA: |
780 | 4.22k | result="Associated alpha data (with pre-multiplied color)"; |
781 | 4.22k | break; |
782 | 3.63k | case EXTRASAMPLE_UNASSALPHA: |
783 | 3.63k | result="Unassociated alpha data"; |
784 | 3.63k | break; |
785 | 786k | } |
786 | | |
787 | 786k | return result; |
788 | 786k | } |
789 | | |
790 | | /* |
791 | | Locate and store Photoshop or IPTC profiles. |
792 | | |
793 | | Arguments: |
794 | | |
795 | | text - Pointer to octet buffer |
796 | | length - Number of bytes or 32-bit words in buffer |
797 | | image - Image to store into |
798 | | type - TIFF tag (TIFFTAG_PHOTOSHOP or TIFFTAG_RICHTIFFIPTC) |
799 | | |
800 | | If the tag is TIFFTAG_RICHTIFFIPTC then it appears that the length |
801 | | represents the number of 32-bit words. If the tag is |
802 | | TIFFTAG_PHOTOSHOP then the length is in bytes, but the underlying |
803 | | data is stored in units of 16-bit words. |
804 | | */ |
805 | 27.2k | #define NEWSP_REMAINING(base_p,current_p,length) ((ssize_t) length-(current_p-base_p)) |
806 | | static unsigned int |
807 | | ReadNewsProfile(const unsigned char *text,const size_t length,Image *image,const int type) |
808 | 98 | { |
809 | 98 | register const unsigned char |
810 | 98 | *p; |
811 | | |
812 | | #if defined(GET_ONLY_IPTC_DATA) |
813 | | static const char tag_header [] = "8BIM44"; |
814 | | #else |
815 | 98 | static const char tag_header [] = "8BIM"; |
816 | 98 | #endif |
817 | | |
818 | 98 | MagickBool found_header=MagickFalse; |
819 | | |
820 | 98 | if ((length == 0) || ((ssize_t) length < 0)) |
821 | 3 | return MagickFail; |
822 | | |
823 | 95 | p=(unsigned char *) text; |
824 | 95 | if (type == TIFFTAG_RICHTIFFIPTC) |
825 | 0 | { |
826 | | /* |
827 | | Handle IPTC tag (length is number of 32-bit words). |
828 | | */ |
829 | 0 | return SetImageProfile(image,"IPTC",p,(size_t) length*4U); |
830 | 0 | } |
831 | | /* |
832 | | Handle PHOTOSHOP tag (length is in bytes, but data is organized as |
833 | | array of 16-bit values. |
834 | | */ |
835 | 27.1k | while (NEWSP_REMAINING(text,p,length) > (ssize_t) sizeof(tag_header)) |
836 | 27.1k | { |
837 | 27.1k | if (LocaleNCompare((char *) p,tag_header,sizeof(tag_header)-1) == 0) |
838 | 73 | { |
839 | 73 | found_header=MagickTrue; |
840 | 73 | break; |
841 | 73 | } |
842 | 27.0k | p+=2; |
843 | 27.0k | } |
844 | 95 | if (!found_header) |
845 | 22 | { |
846 | 22 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
847 | 22 | "Failed to find %s header, ignoring profile.", |
848 | 22 | tag_header); |
849 | 22 | return MagickFail; |
850 | 22 | } |
851 | | #if defined(GET_ONLY_IPTC_DATA) |
852 | | /* |
853 | | Eat OSType, IPTC ID code, and Pascal string length bytes. |
854 | | */ |
855 | | do |
856 | | { |
857 | | magick_uint32_t |
858 | | hdr_length; |
859 | | |
860 | | p+=sizeof(tag_header)-1; |
861 | | if (NEWSP_REMAINING(text,p,length) < 8) |
862 | | { |
863 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
864 | | "Truncated profile: %" MAGICK_SIZE_T_F "u" |
865 | | " bytes, %" MAGICK_SSIZE_T_F "u remaining" |
866 | | ", ignoring profile.", |
867 | | (MAGICK_SIZE_T) length, |
868 | | (MAGICK_SSIZE_T) NEWSP_REMAINING(text,p,length)); |
869 | | break; |
870 | | } |
871 | | hdr_length=(*p++); |
872 | | p+=hdr_length; |
873 | | if (NEWSP_REMAINING(text,p,length) < 8) |
874 | | { |
875 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
876 | | "Truncated profile: %" MAGICK_SIZE_T_F "u" |
877 | | " bytes, %" MAGICK_SSIZE_T_F "u remaining" |
878 | | ", ignoring profile.", |
879 | | (MAGICK_SIZE_T) length, |
880 | | (MAGICK_SSIZE_T) NEWSP_REMAINING(text,p,length)); |
881 | | break; |
882 | | } |
883 | | if ((hdr_length & 0x01) == 0) |
884 | | p++; /* align to an even byte boundary */ |
885 | | hdr_length=(p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; |
886 | | p+=4; |
887 | | if (((ssize_t) hdr_length <= 0) || |
888 | | ((ssize_t) hdr_length > NEWSP_REMAINING(text,p,length))) |
889 | | { |
890 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
891 | | "Truncated profile: %" MAGICK_SIZE_T_F "u" |
892 | | " bytes, %" MAGICK_SSIZE_T_F "u " |
893 | | "remaining (need %u more bytes)" |
894 | | ", ignoring profile.", |
895 | | (MAGICK_SIZE_T) length, |
896 | | (MAGICK_SSIZE_T) NEWSP_REMAINING(text,p,length), |
897 | | hdr_length); |
898 | | return MagickFail; |
899 | | } |
900 | | return SetImageProfile(image,"8BIM",p,hdr_length); |
901 | | } while (0); |
902 | | return MagickFail; |
903 | | #else |
904 | 73 | return SetImageProfile(image,"8BIM",p,(size_t) NEWSP_REMAINING(text,p,length)); |
905 | 95 | #endif |
906 | 95 | } |
907 | | |
908 | | /* |
909 | | Return MagickTrue if TIFF warnings should be thrown as a warning |
910 | | exception rather than logged. |
911 | | */ |
912 | | static MagickBool |
913 | | CheckThrowWarnings(const ImageInfo *image_info) |
914 | 1.82M | { |
915 | 1.82M | const char * |
916 | 1.82M | definition_value; |
917 | | |
918 | 1.82M | MagickBool |
919 | 1.82M | report_warnings=MagickFalse; |
920 | | |
921 | 1.82M | if ((definition_value=AccessDefinition(image_info,"tiff","report-warnings"))) |
922 | 0 | { |
923 | 0 | if (LocaleCompare(definition_value,"TRUE") == 0) |
924 | 0 | report_warnings=MagickTrue; |
925 | 0 | } |
926 | | |
927 | 1.82M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
928 | 1.82M | "Reporting TIFF warnings via %s", |
929 | 1.82M | (report_warnings ? "exception" : |
930 | 1.82M | "log message")); |
931 | | |
932 | 1.82M | return report_warnings; |
933 | 1.82M | } |
934 | | |
935 | | |
936 | | #if defined(__cplusplus) || defined(c_plusplus) |
937 | | extern "C" { |
938 | | #endif |
939 | | |
940 | | /* Close BLOB */ |
941 | | static int |
942 | | TIFFCloseBlob(thandle_t image_handle) |
943 | 136k | { |
944 | 136k | Image |
945 | 136k | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
946 | | |
947 | 136k | int |
948 | 136k | status = MagickPass; |
949 | | |
950 | | #if LOG_TIFF_BLOB_IO |
951 | | if (image->logging) |
952 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"TIFF close blob"); |
953 | | #endif /* LOG_TIFF_BLOB_IO */ |
954 | 136k | while (image->previous != (Image *) NULL) |
955 | 0 | image=image->previous; |
956 | 136k | status &= CloseBlob(image); |
957 | 136k | return(status); |
958 | 136k | } |
959 | | |
960 | | /* Report errors. */ |
961 | | static void |
962 | | TIFFReadErrorsHandler(const char *module,const char *format, |
963 | | va_list warning) |
964 | | MAGICK_FUNC_PRINTF_FORMAT(2,0); |
965 | | #if defined(HAVE_TIFFERROREXTR) |
966 | | static int |
967 | | TIFFReadErrorsHandlerExtR(TIFF *tif, void *user_data, |
968 | | const char *module, const char *format, va_list warning) |
969 | | MAGICK_FUNC_PRINTF_FORMAT(4,0); |
970 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
971 | | static void |
972 | | TIFFWriteErrorsHandler(const char *module,const char *format, |
973 | | va_list warning) MAGICK_FUNC_PRINTF_FORMAT(2,0); |
974 | | #if defined(HAVE_TIFFERROREXTR) |
975 | | static int |
976 | | TIFFWriteErrorsHandlerExtR(TIFF *tif, void *user_data, |
977 | | const char *module, const char *format, va_list warning) |
978 | | MAGICK_FUNC_PRINTF_FORMAT(4,0); |
979 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
980 | | static void |
981 | | TIFFReadErrorsHandler(const char *module,const char *format, |
982 | | va_list warning) |
983 | 0 | { |
984 | 0 | ExceptionInfo |
985 | 0 | *tiff_exception; |
986 | |
|
987 | 0 | char |
988 | 0 | message[MaxTextExtent]; |
989 | |
|
990 | 0 | errno=0; |
991 | 0 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
992 | 0 | message[MaxTextExtent-2]='\0'; |
993 | 0 | #define UNKNOWN_TAG_ERROR "Internal error, unknown tag" |
994 | 0 | if (LocaleNCompare(message,UNKNOWN_TAG_ERROR,sizeof(UNKNOWN_TAG_ERROR)-1) == 0) |
995 | 0 | return; |
996 | 0 | (void) strlcat(message,".",MaxTextExtent); |
997 | 0 | tiff_exception=(ExceptionInfo *) MagickTsdGetSpecific(tsd_key); |
998 | 0 | ThrowException2(tiff_exception,CorruptImageError,message,module); |
999 | 0 | } |
1000 | | |
1001 | | #if defined(HAVE_TIFFERROREXTR) |
1002 | | static int TIFFReadErrorsHandlerExtR(TIFF *tif, void *user_data, |
1003 | | const char *module, const char *format, |
1004 | | va_list warning) |
1005 | 4.17M | { |
1006 | 4.17M | ExceptionInfo |
1007 | 4.17M | *tiff_exception; |
1008 | | |
1009 | 4.17M | char |
1010 | 4.17M | message[MaxTextExtent]; |
1011 | | |
1012 | 4.17M | (void) tif; |
1013 | 4.17M | errno=0; |
1014 | 4.17M | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1015 | 4.17M | message[MaxTextExtent-2]='\0'; |
1016 | 8.35M | #define UNKNOWN_TAG_ERROR "Internal error, unknown tag" |
1017 | 4.17M | if (LocaleNCompare(message,UNKNOWN_TAG_ERROR,sizeof(UNKNOWN_TAG_ERROR)-1) == 0) |
1018 | 0 | return 0; |
1019 | 4.17M | (void) strlcat(message,".",MaxTextExtent); |
1020 | 4.17M | tiff_exception=(ExceptionInfo *) user_data; |
1021 | 4.17M | ThrowException2(tiff_exception,CorruptImageError,message,module); |
1022 | | /* |
1023 | | 0: both functions TIFFErrorHandler() and TIFFErrorHandlerExt() are called. |
1024 | | non-zero: no further error message function is called. |
1025 | | */ |
1026 | 4.17M | return 1; |
1027 | 4.17M | } |
1028 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
1029 | | |
1030 | | static void |
1031 | | TIFFWriteErrorsHandler(const char *module,const char *format, |
1032 | | va_list warning) |
1033 | 36 | { |
1034 | 36 | ExceptionInfo |
1035 | 36 | *tiff_exception; |
1036 | | |
1037 | 36 | char |
1038 | 36 | message[MaxTextExtent]; |
1039 | | |
1040 | 36 | errno=0; |
1041 | 36 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1042 | 36 | message[MaxTextExtent-2]='\0'; |
1043 | 36 | (void) strlcat(message,".",MaxTextExtent); |
1044 | 36 | tiff_exception=(ExceptionInfo *) MagickTsdGetSpecific(tsd_key); |
1045 | 36 | ThrowException2(tiff_exception,CoderError,message,module); |
1046 | 36 | } |
1047 | | |
1048 | | #if defined(HAVE_TIFFERROREXTR) |
1049 | | static int |
1050 | | TIFFWriteErrorsHandlerExtR(TIFF *tif, void *user_data, |
1051 | | const char *module, const char *format, va_list warning) |
1052 | 369 | { |
1053 | 369 | ExceptionInfo |
1054 | 369 | *tiff_exception; |
1055 | | |
1056 | 369 | char |
1057 | 369 | message[MaxTextExtent]; |
1058 | | |
1059 | 369 | (void) tif; |
1060 | 369 | errno=0; |
1061 | 369 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1062 | 369 | message[MaxTextExtent-2]='\0'; |
1063 | 369 | (void) strlcat(message,".",MaxTextExtent); |
1064 | 369 | tiff_exception=(ExceptionInfo *) user_data; |
1065 | 369 | ThrowException2(tiff_exception,CoderError,message,module); |
1066 | | |
1067 | | /* |
1068 | | 0: both functions TIFFWarningHandler() and TIFFWarningHandlerExt() are called. |
1069 | | non-zero: no further warning message function is called. |
1070 | | */ |
1071 | 369 | return 1; |
1072 | 369 | } |
1073 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
1074 | | |
1075 | | /* Memory map entire input file in read-only mode. */ |
1076 | | static int |
1077 | | TIFFMapBlob(thandle_t image_handle,tdata_t *base,toff_t *size) |
1078 | 893k | { |
1079 | 893k | Image |
1080 | 893k | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1081 | | |
1082 | 893k | *base = (tdata_t *) GetBlobStreamData(image); |
1083 | 893k | if (*base) |
1084 | 891k | *size = (toff_t) GetBlobSize(image); |
1085 | | |
1086 | 893k | if (*base) |
1087 | 891k | { |
1088 | | #if LOG_TIFF_BLOB_IO |
1089 | | if (image->logging) |
1090 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1091 | | "TIFF mapped blob: base=0x%p size=%" MAGICK_OFF_F |
1092 | | "d",*base, (magick_off_t) *size); |
1093 | | #endif |
1094 | 891k | return 1; |
1095 | 891k | } |
1096 | 2.46k | return(0); |
1097 | 893k | } |
1098 | | |
1099 | | /* Read BLOB data at current offset */ |
1100 | | static tsize_t |
1101 | | TIFFReadBlob(thandle_t image_handle,tdata_t data,tsize_t size) |
1102 | 1.73M | { |
1103 | 1.73M | Image |
1104 | 1.73M | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1105 | | |
1106 | 1.73M | tsize_t |
1107 | 1.73M | result; |
1108 | | |
1109 | 1.73M | result=(tsize_t) ReadBlob(image,(size_t) size,data); |
1110 | | |
1111 | | #if LOG_TIFF_BLOB_IO |
1112 | | if (image->logging) |
1113 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1114 | | "TIFF read blob: data=0x%p size=" |
1115 | | "%" MAGICK_SIZE_T_F "u, returns " |
1116 | | "%" MAGICK_SIZE_T_F "u", |
1117 | | data, (MAGICK_SIZE_T) size, |
1118 | | (MAGICK_SIZE_T) result); |
1119 | | #endif /* LOG_TIFF_BLOB_IO */ |
1120 | | |
1121 | 1.73M | return result; |
1122 | 1.73M | } |
1123 | | |
1124 | | /* Seek to BLOB offset */ |
1125 | | static toff_t |
1126 | | TIFFSeekBlob(thandle_t image_handle,toff_t offset,int whence) |
1127 | 902k | { |
1128 | 902k | Image |
1129 | 902k | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1130 | | |
1131 | 902k | toff_t |
1132 | 902k | result; |
1133 | | |
1134 | 902k | result=SeekBlob(image,offset,whence); |
1135 | | #if LOG_TIFF_BLOB_IO |
1136 | | if (image->logging) |
1137 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1138 | | "TIFF seek blob: offset=%" MAGICK_OFF_F |
1139 | | "u whence=%d (%s), returns %" MAGICK_OFF_F "d", |
1140 | | (magick_off_t) offset, |
1141 | | whence, |
1142 | | (whence == SEEK_SET ? "SET" : |
1143 | | (whence == SEEK_CUR ? "CUR" : |
1144 | | (whence == SEEK_END ? "END" : "unknown"))), |
1145 | | (magick_off_t) result); |
1146 | | #endif /* LOG_TIFF_BLOB_IO */ |
1147 | 902k | return result; |
1148 | 902k | } |
1149 | | |
1150 | | /* Obtain BLOB size */ |
1151 | | static toff_t |
1152 | | TIFFGetBlobSize(thandle_t image_handle) |
1153 | 889k | { |
1154 | 889k | Image |
1155 | 889k | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1156 | | |
1157 | 889k | toff_t |
1158 | 889k | result; |
1159 | | |
1160 | 889k | result=(toff_t) GetBlobSize(image); |
1161 | | |
1162 | | #if LOG_TIFF_BLOB_IO |
1163 | | if (image->logging) |
1164 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1165 | | "TIFF get blob size returns %" MAGICK_OFF_F "d", |
1166 | | (magick_off_t) result); |
1167 | | #endif /* LOG_TIFF_BLOB_IO */ |
1168 | | |
1169 | 889k | return result; |
1170 | 889k | } |
1171 | | |
1172 | | /* Unmap BLOB memory */ |
1173 | | static void |
1174 | | TIFFUnmapBlob(thandle_t image_handle, |
1175 | | tdata_t base, |
1176 | | toff_t size) |
1177 | 897k | { |
1178 | | #if LOG_TIFF_BLOB_IO |
1179 | | Image |
1180 | | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1181 | | |
1182 | | if (image->logging) |
1183 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1184 | | "TIFF unmap blob: base=0x%p size=%" MAGICK_OFF_F "d", |
1185 | | base,(magick_off_t) size); |
1186 | | #else |
1187 | 897k | ARG_NOT_USED(image_handle); |
1188 | 897k | ARG_NOT_USED(base); |
1189 | 897k | ARG_NOT_USED(size); |
1190 | 897k | #endif /* LOG_TIFF_BLOB_IO */ |
1191 | 897k | } |
1192 | | |
1193 | | #if defined(HAVE_TIFFERROREXTR) |
1194 | | static int |
1195 | | TIFFReadWarningHandlerExtRLogOnly(TIFF *tif, void *user_data, |
1196 | | const char *module, const char *format, va_list warning) |
1197 | | MAGICK_FUNC_PRINTF_FORMAT(4,0); |
1198 | | static int TIFFReadWarningHandlerExtRLogOnly(TIFF *tif, void *user_data, |
1199 | | const char *module, const char *format, |
1200 | | va_list warning) |
1201 | 5.52M | { |
1202 | 5.52M | char |
1203 | 5.52M | message[MaxTextExtent]; |
1204 | | |
1205 | 5.52M | (void) tif; |
1206 | 5.52M | (void) user_data; |
1207 | 5.52M | (void) module; |
1208 | 5.52M | errno=0; |
1209 | 5.52M | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1210 | 5.52M | message[MaxTextExtent-2]='\0'; |
1211 | 5.52M | (void) strlcat(message,".",MaxTextExtent); |
1212 | 5.52M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1213 | 5.52M | "TIFF Warning: %s",message); |
1214 | | /* |
1215 | | 0: both functions TIFFErrorHandler() and TIFFErrorHandlerExt() are called. |
1216 | | non-zero: no further error message function is called. |
1217 | | */ |
1218 | 5.52M | return 1; |
1219 | 5.52M | } |
1220 | | |
1221 | | static int |
1222 | | TIFFReadWarningHandlerExtRThrowException(TIFF *tif, void *user_data, |
1223 | | const char *module, const char *format, va_list warning) |
1224 | | MAGICK_FUNC_PRINTF_FORMAT(4,0); |
1225 | | static int TIFFReadWarningHandlerExtRThrowException(TIFF *tif, void *user_data, |
1226 | | const char *module, const char *format, |
1227 | | va_list warning) |
1228 | 0 | { |
1229 | 0 | ExceptionInfo |
1230 | 0 | *tiff_exception; |
1231 | |
|
1232 | 0 | char |
1233 | 0 | message[MaxTextExtent]; |
1234 | |
|
1235 | 0 | (void) tif; |
1236 | 0 | errno=0; |
1237 | 0 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1238 | 0 | message[MaxTextExtent-2]='\0'; |
1239 | 0 | #define UNKNOWN_TAG_ERROR "Internal error, unknown tag" |
1240 | 0 | if (LocaleNCompare(message,UNKNOWN_TAG_ERROR,sizeof(UNKNOWN_TAG_ERROR)-1) == 0) |
1241 | 0 | return 0; |
1242 | 0 | (void) strlcat(message,".",MaxTextExtent); |
1243 | 0 | tiff_exception=(ExceptionInfo *) user_data; |
1244 | 0 | ThrowException2(tiff_exception,CoderWarning,message,module); |
1245 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1246 | 0 | "TIFF Warning: %s",message); |
1247 | | /* |
1248 | | 0: both functions TIFFWarningHandler() and then TIFFWarningHandlerExt() are called. |
1249 | | non-zero: no further error message function is called. |
1250 | | */ |
1251 | 0 | return 1; |
1252 | 0 | } |
1253 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
1254 | | |
1255 | | /* Report warnings as a coder log message. */ |
1256 | | static void |
1257 | | TIFFWarningsLogOnly(const char *module,const char *format,va_list warning) |
1258 | | MAGICK_FUNC_PRINTF_FORMAT(2,0); |
1259 | | static void |
1260 | | TIFFWarningsLogOnly(const char *module,const char *format,va_list warning) |
1261 | 0 | { |
1262 | | /* ExceptionInfo */ |
1263 | | /* *tiff_exception; */ |
1264 | |
|
1265 | 0 | char |
1266 | 0 | message[MaxTextExtent]; |
1267 | |
|
1268 | 0 | (void) module; |
1269 | 0 | errno=0; |
1270 | 0 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1271 | 0 | message[MaxTextExtent-2]='\0'; |
1272 | 0 | (void) strlcat(message,".",MaxTextExtent); |
1273 | | /* tiff_exception=(ExceptionInfo *) MagickTsdGetSpecific(tsd_key); */ |
1274 | | /* ThrowException2(tiff_exception,CoderWarning,message,module); */ |
1275 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1276 | 0 | "TIFF Warning: %s",message); |
1277 | 0 | } |
1278 | | |
1279 | | /* Report warnings as exception in thread-specific ExceptionInfo */ |
1280 | | static void |
1281 | | TIFFWarningsThrowException(const char *module,const char *format,va_list warning) |
1282 | | MAGICK_FUNC_PRINTF_FORMAT(2,0); |
1283 | | static void |
1284 | | TIFFWarningsThrowException(const char *module,const char *format,va_list warning) |
1285 | 0 | { |
1286 | 0 | ExceptionInfo |
1287 | 0 | *tiff_exception; |
1288 | |
|
1289 | 0 | char |
1290 | 0 | message[MaxTextExtent]; |
1291 | |
|
1292 | 0 | (void) module; |
1293 | 0 | errno=0; |
1294 | 0 | (void) vsnprintf(message,MaxTextExtent-2,format,warning); |
1295 | 0 | message[MaxTextExtent-2]='\0'; |
1296 | 0 | (void) strlcat(message,".",MaxTextExtent); |
1297 | 0 | tiff_exception=(ExceptionInfo *) MagickTsdGetSpecific(tsd_key); |
1298 | 0 | ThrowException2(tiff_exception,CoderWarning,message,module); |
1299 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1300 | 0 | "TIFF Warning: %s",message); |
1301 | 0 | } |
1302 | | |
1303 | | /* Write data at current offset */ |
1304 | | static tsize_t |
1305 | | TIFFWriteBlob(thandle_t image_handle,tdata_t data,tsize_t size) |
1306 | 234k | { |
1307 | 234k | Image |
1308 | 234k | *image = ((Magick_TIFF_ClientData *) image_handle)->image; |
1309 | | |
1310 | 234k | tsize_t |
1311 | 234k | result; |
1312 | | |
1313 | 234k | result=(tsize_t) WriteBlob(image,(size_t) size,data); |
1314 | | |
1315 | | #if LOG_TIFF_BLOB_IO |
1316 | | if (image->logging) |
1317 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1318 | | "TIFF write blob: data=0x%p size=" |
1319 | | "%" MAGICK_SIZE_T_F "u, returns " |
1320 | | "%" MAGICK_SIZE_T_F "u", |
1321 | | data, (MAGICK_SIZE_T) size, |
1322 | | (MAGICK_SIZE_T) result); |
1323 | | #endif /* LOG_TIFF_BLOB_IO */ |
1324 | | |
1325 | 234k | return result; |
1326 | 234k | } |
1327 | | |
1328 | | /* |
1329 | | Convert TIFF data from libtiff "native" format to byte-parsable big endian |
1330 | | */ |
1331 | | #if !defined(WORDS_BIGENDIAN) |
1332 | | static void |
1333 | | SwabDataToBigEndian(const uint16 bits_per_sample, tdata_t data, |
1334 | | const tsize_t size) |
1335 | 3.75k | { |
1336 | 3.75k | if (bits_per_sample == 64U) |
1337 | 0 | { |
1338 | 0 | TIFFSwabArrayOfDouble((double*) data, |
1339 | 0 | NumberOfObjectsInArray(size,sizeof(double))); |
1340 | 0 | } |
1341 | 3.75k | else if (bits_per_sample == 32U) |
1342 | 0 | { |
1343 | 0 | TIFFSwabArrayOfLong((uint32*) data, |
1344 | 0 | NumberOfObjectsInArray(size,sizeof(uint32))); |
1345 | 0 | } |
1346 | 3.75k | #if defined(HAVE_TIFFSWABARRAYOFTRIPLES) |
1347 | | /* New libtiff function to swap 24 bit values. Grumble ... */ |
1348 | 3.75k | else if (bits_per_sample == 24U) |
1349 | 3.75k | { |
1350 | 3.75k | TIFFSwabArrayOfTriples((uint8_t*) data, |
1351 | 3.75k | NumberOfObjectsInArray(size,3)); |
1352 | 3.75k | } |
1353 | 0 | #endif |
1354 | 0 | else if (bits_per_sample == 16U) |
1355 | 0 | { |
1356 | 0 | TIFFSwabArrayOfShort((uint16*) data, |
1357 | 0 | NumberOfObjectsInArray(size,sizeof(uint16))); |
1358 | 0 | } |
1359 | 3.75k | } |
1360 | | #endif |
1361 | | |
1362 | | /* |
1363 | | Convert TIFF data from byte-parsable big endian to libtiff "native" format. |
1364 | | */ |
1365 | | #if !defined(WORDS_BIGENDIAN) |
1366 | | static void |
1367 | | SwabDataToNativeEndian(const uint16 bits_per_sample, tdata_t data, |
1368 | | const tsize_t size) |
1369 | 0 | { |
1370 | 0 | if (bits_per_sample == 64) |
1371 | 0 | { |
1372 | 0 | TIFFSwabArrayOfDouble((double*) data, |
1373 | 0 | NumberOfObjectsInArray(size,sizeof(double))); |
1374 | 0 | } |
1375 | 0 | else if (bits_per_sample == 32) |
1376 | 0 | { |
1377 | 0 | TIFFSwabArrayOfLong((uint32*) data, |
1378 | 0 | NumberOfObjectsInArray(size,sizeof(uint32))); |
1379 | 0 | } |
1380 | 0 | #if defined(HAVE_TIFFSWABARRAYOFTRIPLES) |
1381 | | /* New libtiff function to swap 24 bit values. Grumble ... */ |
1382 | 0 | else if (bits_per_sample == 24U) |
1383 | 0 | { |
1384 | 0 | TIFFSwabArrayOfTriples((uint8_t*) data, |
1385 | 0 | NumberOfObjectsInArray(size,3)); |
1386 | 0 | } |
1387 | 0 | #endif |
1388 | 0 | else if (bits_per_sample == 16) |
1389 | 0 | { |
1390 | 0 | TIFFSwabArrayOfShort((uint16*) data, |
1391 | 0 | NumberOfObjectsInArray(size,sizeof(uint16))); |
1392 | 0 | } |
1393 | 0 | } |
1394 | | #endif |
1395 | | |
1396 | | /* |
1397 | | Initialize the image colormap. |
1398 | | */ |
1399 | | static MagickPassFail |
1400 | | InitializeImageColormap(Image *image, TIFF *tiff) |
1401 | 2.25k | { |
1402 | 2.25k | uint16 |
1403 | 2.25k | bits_per_sample, |
1404 | 2.25k | photometric; |
1405 | | |
1406 | 2.25k | register unsigned int |
1407 | 2.25k | i; |
1408 | | |
1409 | 2.25k | unsigned int |
1410 | 2.25k | max_sample_value, |
1411 | 2.25k | status = MagickFail; |
1412 | | |
1413 | 2.25k | if (TIFFGetFieldDefaulted(tiff,TIFFTAG_BITSPERSAMPLE,&bits_per_sample) != 1) |
1414 | 0 | return status; |
1415 | 2.25k | if (TIFFGetFieldDefaulted(tiff,TIFFTAG_PHOTOMETRIC,&photometric) != 1) |
1416 | 0 | return status; |
1417 | | |
1418 | | /* |
1419 | | Compute colormap size |
1420 | | */ |
1421 | 2.25k | if (bits_per_sample > 64) |
1422 | 0 | { |
1423 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1424 | 0 | "Bits per sample (%u) is out of range!", |
1425 | 0 | (unsigned) bits_per_sample); |
1426 | 0 | ThrowException(&image->exception,CorruptImageError,ImproperImageHeader, |
1427 | 0 | image->filename); |
1428 | 0 | return status; |
1429 | 0 | } |
1430 | 2.25k | max_sample_value=MaxValueGivenBits(bits_per_sample); |
1431 | | |
1432 | 2.25k | image->colors=0; |
1433 | 2.25k | if (MaxColormapSize > max_sample_value) |
1434 | 2.25k | image->colors=max_sample_value+1; |
1435 | 0 | else if (MaxColormapSize > MaxRGB) |
1436 | 0 | { |
1437 | 0 | if (photometric == PHOTOMETRIC_PALETTE) |
1438 | 0 | return status; |
1439 | 0 | else |
1440 | 0 | image->colors=MaxColormapSize; |
1441 | 0 | } |
1442 | | |
1443 | 2.25k | if (image->colors > 0) |
1444 | 2.25k | { |
1445 | 2.25k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1446 | 2.25k | "Allocating colormap with %u colors", |
1447 | 2.25k | image->colors); |
1448 | | |
1449 | | /* |
1450 | | Allocate colormap. |
1451 | | */ |
1452 | 2.25k | if (AllocateImageColormap(image,image->colors) == MagickFail) |
1453 | 0 | return status; |
1454 | | |
1455 | | /* |
1456 | | Create colormap. |
1457 | | */ |
1458 | 2.25k | switch (photometric) |
1459 | 2.25k | { |
1460 | 0 | case PHOTOMETRIC_MINISBLACK: |
1461 | 0 | case PHOTOMETRIC_MINISWHITE: |
1462 | 0 | default: |
1463 | 0 | { |
1464 | | /* |
1465 | | Ascending order produced by AllocateImageColormap() is sufficient. |
1466 | | */ |
1467 | 0 | status = MagickPass; |
1468 | 0 | break; |
1469 | 0 | } |
1470 | 2.25k | case PHOTOMETRIC_PALETTE: |
1471 | 2.25k | { |
1472 | 2.25k | long |
1473 | 2.25k | range; |
1474 | | |
1475 | 2.25k | uint16 |
1476 | 2.25k | *blue_colormap, |
1477 | 2.25k | *green_colormap, |
1478 | 2.25k | *red_colormap; |
1479 | | |
1480 | 2.25k | (void) TIFFGetField(tiff,TIFFTAG_COLORMAP,&red_colormap, |
1481 | 2.25k | &green_colormap,&blue_colormap); |
1482 | 2.25k | range=256L; /* might be old style 8-bit colormap */ |
1483 | 36.1k | for (i=0; i < image->colors; i++) |
1484 | 35.8k | if ((red_colormap[i] >= 256) || (green_colormap[i] >= 256) || |
1485 | 34.2k | (blue_colormap[i] >= 256)) |
1486 | 1.95k | { |
1487 | 1.95k | range=65535L; |
1488 | 1.95k | break; |
1489 | 1.95k | } |
1490 | 398k | for (i=0; i < image->colors; i++) |
1491 | 396k | { |
1492 | 396k | image->colormap[i].red=(Quantum) |
1493 | 396k | (((double) MaxRGB*red_colormap[i])/range+0.5); |
1494 | 396k | image->colormap[i].green=(Quantum) |
1495 | 396k | (((double) MaxRGB*green_colormap[i])/range+0.5); |
1496 | 396k | image->colormap[i].blue=(Quantum) |
1497 | 396k | (((double) MaxRGB*blue_colormap[i])/range+0.5); |
1498 | 396k | } |
1499 | 2.25k | status = MagickPass; |
1500 | 2.25k | break; |
1501 | 0 | } |
1502 | 2.25k | } |
1503 | 2.25k | if (status == MagickPass) |
1504 | 2.25k | { |
1505 | 2.25k | register const PixelPacket |
1506 | 2.25k | *p; |
1507 | | |
1508 | 2.25k | register unsigned int |
1509 | 2.25k | scale; |
1510 | | |
1511 | 2.25k | unsigned int |
1512 | 2.25k | depth=1; |
1513 | | |
1514 | | /* |
1515 | | Evaluate colormap depth. |
1516 | | */ |
1517 | 2.25k | p=image->colormap; |
1518 | 2.25k | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); |
1519 | 44.6k | for (i=image->colors; i != 0; i--) |
1520 | 44.0k | { |
1521 | 44.0k | if ((p->red != scale*(p->red/scale)) || |
1522 | 30.5k | (p->green != scale*(p->green/scale)) || |
1523 | 23.0k | (p->blue != scale*(p->blue/scale))) |
1524 | 26.6k | { |
1525 | 26.6k | depth++; |
1526 | 26.6k | if (depth == QuantumDepth) |
1527 | 1.63k | break; |
1528 | 25.0k | scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth)); |
1529 | 25.0k | continue; |
1530 | 26.6k | } |
1531 | 17.3k | p++; |
1532 | 17.3k | } |
1533 | 2.25k | if (depth < 8) |
1534 | 571 | depth=8; |
1535 | 1.68k | else |
1536 | 1.68k | depth=16; |
1537 | 2.25k | image->depth=depth; |
1538 | 2.25k | } |
1539 | 2.25k | } |
1540 | | |
1541 | 2.25k | return status; |
1542 | 2.25k | } |
1543 | | |
1544 | | /* |
1545 | | Determine the quauntum import/export method to use with |
1546 | | ImportImagePixelArea() and ExportImagePixelArea() based on the |
1547 | | nature of the image, the TIFF photometric, sample format, samples |
1548 | | per pixel, the desired planar configuration, and the specified plane |
1549 | | (0 for contiguous planar configuration). Updates quantum_type with |
1550 | | the import/export method, and quantum_samples with the number of |
1551 | | samples consumed by one pixel. Returns MagickPass if the photometric |
1552 | | is supported. |
1553 | | */ |
1554 | | static MagickPassFail |
1555 | | QuantumTransferMode(const Image *image, |
1556 | | const uint16 photometric, |
1557 | | const uint16 compress_tag, |
1558 | | const uint16 sample_format, |
1559 | | const unsigned int samples_per_pixel, |
1560 | | const uint16 planar_config, |
1561 | | const unsigned int plane, |
1562 | | QuantumType *quantum_type, |
1563 | | int *quantum_samples, |
1564 | | ExceptionInfo *exception) |
1565 | 1.69M | { |
1566 | 1.69M | ARG_NOT_USED(exception); |
1567 | | |
1568 | 1.69M | *quantum_type=UndefinedQuantum; |
1569 | 1.69M | *quantum_samples=0; |
1570 | 1.69M | if ((sample_format == SAMPLEFORMAT_INT) || |
1571 | 1.69M | (sample_format == SAMPLEFORMAT_UINT) || |
1572 | 99.3k | (sample_format == SAMPLEFORMAT_VOID) || |
1573 | 95.6k | (sample_format == SAMPLEFORMAT_IEEEFP)) |
1574 | 1.68M | { |
1575 | 1.68M | switch (photometric) |
1576 | 1.68M | { |
1577 | 82.7k | case PHOTOMETRIC_CIELAB: |
1578 | 82.7k | { |
1579 | | /* samples_per_pixel may be 1 or 3 */ |
1580 | 82.7k | if (planar_config == PLANARCONFIG_SEPARATE) |
1581 | 10.2k | { |
1582 | 10.2k | switch (plane) |
1583 | 10.2k | { |
1584 | 10.2k | case 0: |
1585 | 10.2k | if (samples_per_pixel == 1) |
1586 | 10.1k | *quantum_type=GrayQuantum; |
1587 | 39 | else |
1588 | 39 | *quantum_type=RedQuantum; |
1589 | 10.2k | break; |
1590 | 16 | case 1: |
1591 | 16 | *quantum_type=GreenQuantum; |
1592 | 16 | break; |
1593 | 16 | case 2: |
1594 | 16 | *quantum_type=BlueQuantum; |
1595 | 16 | break; |
1596 | 8 | case 3: |
1597 | 8 | *quantum_type=AlphaQuantum; |
1598 | 8 | break; |
1599 | 10.2k | } |
1600 | 10.2k | *quantum_samples=1; |
1601 | 10.2k | } |
1602 | 72.4k | else |
1603 | 72.4k | { |
1604 | 72.4k | if (samples_per_pixel == 1) |
1605 | 45.7k | { |
1606 | 45.7k | if (image->matte) |
1607 | 5 | { |
1608 | 5 | *quantum_type=GrayAlphaQuantum; |
1609 | 5 | *quantum_samples=2; |
1610 | 5 | } |
1611 | 45.7k | else |
1612 | 45.7k | { |
1613 | 45.7k | *quantum_type=GrayQuantum; |
1614 | 45.7k | *quantum_samples=1; |
1615 | 45.7k | } |
1616 | 45.7k | } |
1617 | 26.7k | else |
1618 | 26.7k | { |
1619 | 26.7k | if (image->matte) |
1620 | 2.63k | { |
1621 | 2.63k | *quantum_type=RGBAQuantum; |
1622 | 2.63k | *quantum_samples=4; |
1623 | 2.63k | } |
1624 | 24.1k | else |
1625 | 24.1k | { |
1626 | 24.1k | *quantum_type=RGBQuantum; |
1627 | 24.1k | *quantum_samples=3; |
1628 | 24.1k | } |
1629 | 26.7k | } |
1630 | 72.4k | } |
1631 | 82.7k | break; |
1632 | 82.7k | } |
1633 | 82.7k | case PHOTOMETRIC_LOGL: |
1634 | 5.48k | { |
1635 | 5.48k | if (((compress_tag == COMPRESSION_SGILOG) || |
1636 | 4.27k | (compress_tag == COMPRESSION_SGILOG24)) && |
1637 | 5.48k | (planar_config == PLANARCONFIG_CONTIG) && |
1638 | 5.48k | (samples_per_pixel == 1) && |
1639 | 5.47k | (!image->matte)) |
1640 | 5.47k | { |
1641 | 5.47k | *quantum_type=CIEYQuantum; |
1642 | 5.47k | *quantum_samples=1; |
1643 | 5.47k | } |
1644 | 5.48k | break; |
1645 | 82.7k | } |
1646 | 6.24k | case PHOTOMETRIC_LOGLUV: |
1647 | 6.24k | { |
1648 | 6.24k | if (((compress_tag == COMPRESSION_SGILOG) || |
1649 | 1.56k | (compress_tag == COMPRESSION_SGILOG24)) && |
1650 | 6.24k | (planar_config == PLANARCONFIG_CONTIG) && |
1651 | 6.24k | (!image->matte)) |
1652 | 6.23k | { |
1653 | 6.23k | if (samples_per_pixel == 1) |
1654 | 4.10k | { |
1655 | | /* FIXME: this might not work. */ |
1656 | 4.10k | *quantum_type=CIEYQuantum; |
1657 | 4.10k | *quantum_samples=1; |
1658 | 4.10k | } |
1659 | 2.12k | else if (samples_per_pixel == 3) |
1660 | 2.12k | { |
1661 | 2.12k | *quantum_type=CIEXYZQuantum; |
1662 | 2.12k | *quantum_samples=3; |
1663 | 2.12k | } |
1664 | 6.23k | } |
1665 | 6.24k | break; |
1666 | 82.7k | } |
1667 | 644k | case PHOTOMETRIC_MINISBLACK: |
1668 | 1.32M | case PHOTOMETRIC_MINISWHITE: |
1669 | 1.32M | { |
1670 | 1.32M | if (planar_config == PLANARCONFIG_SEPARATE) |
1671 | 10.0k | { |
1672 | 10.0k | switch (plane) |
1673 | 10.0k | { |
1674 | 9.39k | case 0: |
1675 | 9.39k | *quantum_type=GrayQuantum; |
1676 | 9.39k | *quantum_samples=1; |
1677 | 9.39k | break; |
1678 | 674 | case 1: |
1679 | 674 | *quantum_type=AlphaQuantum; |
1680 | 674 | *quantum_samples=1; |
1681 | 674 | break; |
1682 | 10.0k | } |
1683 | 10.0k | } |
1684 | 1.31M | else |
1685 | 1.31M | { |
1686 | 1.31M | if (image->matte) |
1687 | 4.50k | { |
1688 | 4.50k | *quantum_type=GrayAlphaQuantum; |
1689 | 4.50k | *quantum_samples=2; |
1690 | 4.50k | } |
1691 | 1.30M | else |
1692 | 1.30M | { |
1693 | 1.30M | *quantum_type=GrayQuantum; |
1694 | 1.30M | *quantum_samples=1; |
1695 | 1.30M | } |
1696 | 1.31M | } |
1697 | 1.32M | break; |
1698 | 1.32M | } |
1699 | 1.32M | case PHOTOMETRIC_PALETTE: |
1700 | 45.0k | { |
1701 | 45.0k | if (sample_format == SAMPLEFORMAT_UINT) |
1702 | 44.0k | { |
1703 | 44.0k | if (image->matte) |
1704 | 270 | { |
1705 | 270 | *quantum_type=IndexAlphaQuantum; |
1706 | 270 | *quantum_samples=2; |
1707 | 270 | } |
1708 | 43.7k | else |
1709 | 43.7k | { |
1710 | 43.7k | *quantum_type=IndexQuantum; |
1711 | 43.7k | *quantum_samples=1; |
1712 | 43.7k | } |
1713 | 44.0k | } |
1714 | 45.0k | break; |
1715 | 1.32M | } |
1716 | 109k | case PHOTOMETRIC_RGB: |
1717 | 109k | { |
1718 | 109k | if (compress_tag != COMPRESSION_OJPEG) |
1719 | 109k | { |
1720 | 109k | if (planar_config == PLANARCONFIG_SEPARATE) |
1721 | 5.62k | { |
1722 | 5.62k | switch (plane) |
1723 | 5.62k | { |
1724 | 2.07k | case 0: |
1725 | 2.07k | *quantum_type=RedQuantum; |
1726 | 2.07k | break; |
1727 | 1.56k | case 1: |
1728 | 1.56k | *quantum_type=GreenQuantum; |
1729 | 1.56k | break; |
1730 | 1.53k | case 2: |
1731 | 1.53k | *quantum_type=BlueQuantum; |
1732 | 1.53k | break; |
1733 | 461 | case 3: |
1734 | 461 | *quantum_type=AlphaQuantum; |
1735 | 461 | break; |
1736 | 5.62k | } |
1737 | 5.62k | *quantum_samples=1; |
1738 | 5.62k | } |
1739 | 104k | else |
1740 | 104k | { |
1741 | 104k | if (image->matte) |
1742 | 24.0k | { |
1743 | 24.0k | *quantum_type=RGBAQuantum; |
1744 | 24.0k | *quantum_samples=4; |
1745 | 24.0k | } |
1746 | 80.2k | else |
1747 | 80.2k | { |
1748 | 80.2k | *quantum_type=RGBQuantum; |
1749 | 80.2k | *quantum_samples=3; |
1750 | 80.2k | } |
1751 | 104k | } |
1752 | 109k | } |
1753 | 109k | break; |
1754 | 109k | } |
1755 | 113k | case PHOTOMETRIC_SEPARATED: |
1756 | 113k | { |
1757 | 113k | if (planar_config == PLANARCONFIG_SEPARATE) |
1758 | 4.93k | { |
1759 | 4.93k | switch (plane) |
1760 | 4.93k | { |
1761 | 1.65k | case 0: |
1762 | 1.65k | *quantum_type=CyanQuantum; |
1763 | 1.65k | break; |
1764 | 1.04k | case 1: |
1765 | 1.04k | *quantum_type=MagentaQuantum; |
1766 | 1.04k | break; |
1767 | 1.04k | case 2: |
1768 | 1.04k | *quantum_type=YellowQuantum; |
1769 | 1.04k | break; |
1770 | 1.04k | case 3: |
1771 | 1.04k | *quantum_type=BlackQuantum; |
1772 | 1.04k | break; |
1773 | 132 | case 4: |
1774 | 132 | *quantum_type=AlphaQuantum; |
1775 | 132 | break; |
1776 | 4.93k | } |
1777 | 4.93k | *quantum_samples=1; |
1778 | 4.93k | } |
1779 | 108k | else |
1780 | 108k | { |
1781 | 108k | if (image->matte) |
1782 | 1.69k | { |
1783 | 1.69k | *quantum_type=CMYKAQuantum; |
1784 | 1.69k | *quantum_samples=5; |
1785 | 1.69k | } |
1786 | 106k | else |
1787 | 106k | { |
1788 | 106k | *quantum_type=CMYKQuantum; |
1789 | 106k | *quantum_samples=4; |
1790 | 106k | } |
1791 | 108k | } |
1792 | 113k | break; |
1793 | 113k | } |
1794 | 113k | case PHOTOMETRIC_YCBCR: |
1795 | 2.72k | { |
1796 | | /* |
1797 | | This is here to support JPEGCOLORMODE_RGB which claims a |
1798 | | YCbCr photometric, but passes RGB to libtiff. |
1799 | | */ |
1800 | 2.72k | if (compress_tag == COMPRESSION_JPEG) |
1801 | 1.51k | { |
1802 | 1.51k | *quantum_type=RGBQuantum; |
1803 | 1.51k | *quantum_samples=3; |
1804 | 1.51k | } |
1805 | 2.72k | break; |
1806 | 113k | } |
1807 | 1.68M | } |
1808 | 1.68M | } |
1809 | 1.69M | if (image->logging) |
1810 | 1.65M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1811 | 1.65M | "Quantum Type: %s, Quantum Samples: %d", |
1812 | 1.65M | QuantumTypeToString(*quantum_type),*quantum_samples); |
1813 | | /* FIXME: We do need to support YCbCr! */ |
1814 | | |
1815 | 1.69M | if (*quantum_samples != 0) |
1816 | 1.68M | { |
1817 | | /* |
1818 | | Enforce that there are no buffer-overruns. |
1819 | | */ |
1820 | 1.68M | if (((planar_config == PLANARCONFIG_SEPARATE) && (*quantum_samples != 1)) || |
1821 | 1.68M | ((unsigned int) (*quantum_samples) > samples_per_pixel)) |
1822 | 60 | { |
1823 | 60 | if (image->logging) |
1824 | 60 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1825 | 60 | "Expected >= %u samples per pixel, have only %d!", |
1826 | 60 | *quantum_samples, samples_per_pixel); |
1827 | 60 | ThrowException(exception,CorruptImageError,ImproperImageHeader, |
1828 | 60 | image->filename); |
1829 | 60 | *quantum_type=UndefinedQuantum; |
1830 | 60 | *quantum_samples=0; |
1831 | 60 | } |
1832 | 1.68M | } |
1833 | | |
1834 | 1.69M | if ((image->logging) && (*quantum_samples == 0)) |
1835 | 7.47k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1836 | 7.47k | "QuantumTransferMode reports failure"); |
1837 | | |
1838 | 1.69M | return (*quantum_samples != 0 ? MagickPass : MagickFail); |
1839 | 1.69M | } |
1840 | | |
1841 | | /* |
1842 | | Compact samples to only contain raster data. This may seem |
1843 | | inefficient, but it allows us to easily deal with contiguous images |
1844 | | which contain extra samples while optimizing performance for images |
1845 | | without extra samples. |
1846 | | */ |
1847 | | static void |
1848 | | CompactSamples( const unsigned long total_pixels, |
1849 | | const unsigned int bits_per_sample, |
1850 | | const unsigned int samples_per_pixel, |
1851 | | const unsigned int quantum_samples, |
1852 | | unsigned char *samples) |
1853 | 2.56M | { |
1854 | 2.56M | if ((samples_per_pixel > quantum_samples) && |
1855 | 2.56M | (bits_per_sample > 0) && bits_per_sample <= 32) |
1856 | 2.40M | { |
1857 | | /* |
1858 | | Compact scanline to only contain raster data. |
1859 | | */ |
1860 | | |
1861 | 2.40M | BitStreamReadHandle |
1862 | 2.40M | read_stream; |
1863 | | |
1864 | 2.40M | BitStreamWriteHandle |
1865 | 2.40M | write_stream; |
1866 | | |
1867 | 2.40M | unsigned long |
1868 | 2.40M | pixels; |
1869 | | |
1870 | 2.40M | unsigned int |
1871 | 2.40M | count, |
1872 | 2.40M | quantum_value; |
1873 | | |
1874 | 2.40M | MagickBitStreamInitializeRead(&read_stream,samples); |
1875 | 2.40M | MagickBitStreamInitializeWrite(&write_stream,samples); |
1876 | | |
1877 | 114M | for (pixels = total_pixels; pixels != 0 ; pixels--) |
1878 | 111M | { |
1879 | 523M | for (count = quantum_samples; count != 0 ; count--) |
1880 | 411M | { |
1881 | 411M | quantum_value=MagickBitStreamMSBRead(&read_stream,bits_per_sample); |
1882 | 411M | MagickBitStreamMSBWrite(&write_stream,bits_per_sample,quantum_value); |
1883 | 411M | } |
1884 | 277M | for (count = samples_per_pixel-quantum_samples; count != 0 ; count--) |
1885 | 165M | { |
1886 | 165M | (void) MagickBitStreamMSBRead(&read_stream,bits_per_sample); |
1887 | 165M | } |
1888 | 111M | } |
1889 | 2.40M | } |
1890 | 2.56M | } |
1891 | | |
1892 | | |
1893 | | /* |
1894 | | Convert selected pixel area to associated alpha representation. |
1895 | | */ |
1896 | | static void |
1897 | | AssociateAlphaRegion(Image *image) |
1898 | 611k | { |
1899 | 611k | register PixelPacket |
1900 | 611k | *q; |
1901 | | |
1902 | 611k | register long |
1903 | 611k | x; |
1904 | | |
1905 | 611k | register double |
1906 | 611k | alpha, |
1907 | 611k | value; |
1908 | | |
1909 | 611k | long |
1910 | 611k | number_pixels; |
1911 | | |
1912 | 611k | number_pixels=(long) GetPixelCacheArea(image); |
1913 | 611k | q=AccessMutablePixels(image); |
1914 | | |
1915 | 28.8M | for (x = number_pixels; x > 0; --x) |
1916 | 28.2M | { |
1917 | 28.2M | alpha=((double) MaxRGB-q->opacity)/MaxRGB; |
1918 | 28.2M | value=(double) q->red*alpha; |
1919 | 28.2M | q->red=RoundDoubleToQuantum(value); |
1920 | 28.2M | value=(double) q->green*alpha; |
1921 | 28.2M | q->green=RoundDoubleToQuantum(value); |
1922 | 28.2M | value=(double) q->blue*alpha; |
1923 | 28.2M | q->blue=RoundDoubleToQuantum(value); |
1924 | 28.2M | q++; |
1925 | 28.2M | } |
1926 | 611k | } |
1927 | | |
1928 | | /* |
1929 | | Convert associated alpha to internal representation for selected |
1930 | | pixel area. |
1931 | | */ |
1932 | | static void |
1933 | | DisassociateAlphaRegion(Image *image) |
1934 | 498k | { |
1935 | 498k | register PixelPacket |
1936 | 498k | *q; |
1937 | | |
1938 | 498k | register long |
1939 | 498k | x; |
1940 | | |
1941 | 498k | register double |
1942 | 498k | alpha, |
1943 | 498k | value; |
1944 | | |
1945 | 498k | long |
1946 | 498k | number_pixels; |
1947 | | |
1948 | 498k | number_pixels=(long) GetPixelCacheArea(image); |
1949 | 498k | q=AccessMutablePixels(image); |
1950 | | |
1951 | 11.1M | for (x = number_pixels; x > 0; --x) |
1952 | 10.6M | { |
1953 | 10.6M | if (q->opacity != (Quantum) MaxRGB) |
1954 | 9.83M | { |
1955 | 9.83M | alpha=((double) MaxRGB-q->opacity)/MaxRGB; |
1956 | 9.83M | value=(double) q->red/alpha; |
1957 | 9.83M | q->red=RoundDoubleToQuantum(value); |
1958 | 9.83M | value=(double) q->green/alpha; |
1959 | 9.83M | q->green=RoundDoubleToQuantum(value); |
1960 | 9.83M | value=(double) q->blue/alpha; |
1961 | 9.83M | q->blue=RoundDoubleToQuantum(value); |
1962 | 9.83M | } |
1963 | 10.6M | q++; |
1964 | 10.6M | } |
1965 | 498k | } |
1966 | | |
1967 | | #if defined(__cplusplus) || defined(c_plusplus) |
1968 | | } |
1969 | | #endif |
1970 | | |
1971 | | /* |
1972 | | Copy a possibly unterminated sized string to an image attribute. |
1973 | | */ |
1974 | | #define CopySizedFieldToAttribute(key,count,text) \ |
1975 | 34 | do \ |
1976 | 34 | { \ |
1977 | 34 | char _attribute[MaxTextExtent]; \ |
1978 | 34 | (void) memcpy(_attribute,text,Min(sizeof(_attribute),count)); \ |
1979 | 34 | _attribute[Min(sizeof(_attribute)-1,count)]='\0'; \ |
1980 | 34 | (void) SetImageAttribute(image,key,_attribute); \ |
1981 | 34 | } while(0); |
1982 | | |
1983 | | |
1984 | | typedef enum |
1985 | | { |
1986 | | ScanLineMethod, /* Scanline method */ |
1987 | | StrippedMethod, /* Stripped method */ |
1988 | | TiledMethod, /* Tiled method */ |
1989 | | RGBAStrippedMethod, /* RGBA stripped method */ |
1990 | | RGBATiledMethod, /* RGBA tiled method */ |
1991 | | RGBAPuntMethod /* RGBA whole-image method (last resort) */ |
1992 | | } TIFFMethod; |
1993 | | |
1994 | 3.31k | #define ThrowTIFFReaderException(code_,reason_,image_) \ |
1995 | 3.31k | { \ |
1996 | 3.31k | if (tiff != (TIFF *) NULL) \ |
1997 | 3.31k | TIFFClose(tiff); \ |
1998 | 3.31k | ThrowReaderException(code_,reason_,image_); \ |
1999 | 0 | } |
2000 | | |
2001 | | static Image * |
2002 | | ReadTIFFImage(const ImageInfo *image_info,ExceptionInfo *exception) |
2003 | 899k | { |
2004 | 899k | char |
2005 | 899k | *text; |
2006 | | |
2007 | 899k | const char * |
2008 | 899k | definition_value; |
2009 | | |
2010 | 899k | float |
2011 | 899k | *chromaticity, |
2012 | 899k | x_resolution, |
2013 | 899k | y_resolution; |
2014 | | |
2015 | 899k | Image |
2016 | 899k | *image; |
2017 | | |
2018 | 899k | unsigned int |
2019 | 899k | y; |
2020 | | |
2021 | 899k | register unsigned int |
2022 | 899k | x; |
2023 | | |
2024 | 899k | register PixelPacket |
2025 | 899k | *q; |
2026 | | |
2027 | 899k | register unsigned int |
2028 | 899k | i; |
2029 | | |
2030 | 899k | TIFF |
2031 | 899k | *tiff = (TIFF *) NULL; |
2032 | | |
2033 | 899k | magick_off_t |
2034 | 899k | file_size, |
2035 | 899k | max_compress_ratio=1000; /* Maximum compression ratio */ |
2036 | | |
2037 | 899k | uint16 |
2038 | 899k | compress_tag, |
2039 | 899k | bits_per_sample, |
2040 | 899k | extra_samples, |
2041 | 899k | fill_order, |
2042 | 899k | max_sample_value, |
2043 | 899k | min_sample_value, |
2044 | 899k | orientation, |
2045 | 899k | pages, |
2046 | 899k | photometric, |
2047 | 899k | planar_config, |
2048 | 899k | *sample_info, |
2049 | 899k | sample_format, |
2050 | 899k | samples_per_pixel, |
2051 | 899k | units; |
2052 | | |
2053 | 899k | uint32 |
2054 | 899k | count, |
2055 | 899k | height, |
2056 | 899k | rows_per_strip, |
2057 | 899k | width; |
2058 | | |
2059 | 899k | TIFFMethod |
2060 | 899k | method; |
2061 | | |
2062 | 899k | Magick_TIFF_ClientData |
2063 | 899k | client_data; |
2064 | | |
2065 | 899k | ImportPixelAreaOptions |
2066 | 899k | import_options; |
2067 | | |
2068 | 899k | AlphaType |
2069 | 899k | alpha_type=UnspecifiedAlpha; |
2070 | | |
2071 | 899k | MagickBool |
2072 | 899k | logging, |
2073 | 899k | more_frames; |
2074 | | |
2075 | 899k | MagickPassFail |
2076 | 899k | status; |
2077 | | |
2078 | | /* |
2079 | | Open image. |
2080 | | */ |
2081 | 899k | assert(image_info != (const ImageInfo *) NULL); |
2082 | 899k | assert(image_info->signature == MagickSignature); |
2083 | 899k | assert(exception != (ExceptionInfo *) NULL); |
2084 | 899k | assert(exception->signature == MagickSignature); |
2085 | | |
2086 | 899k | logging=IsEventLogged(CoderEvent); |
2087 | 899k | image=AllocateImage(image_info); |
2088 | 899k | more_frames=MagickFalse; |
2089 | 899k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
2090 | 899k | if (status == MagickFail) |
2091 | 899k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
2092 | 899k | file_size = GetBlobSize(image); |
2093 | 899k | (void) MagickTsdSetSpecific(tsd_key,(void *) exception); |
2094 | | |
2095 | | /* |
2096 | | Set global thread-specific reader errors and warnings handlers. |
2097 | | */ |
2098 | 899k | (void) TIFFSetErrorHandler((TIFFErrorHandler) TIFFReadErrorsHandler); |
2099 | 899k | (void) TIFFSetWarningHandler((TIFFErrorHandler) (CheckThrowWarnings(image_info) ? |
2100 | 0 | TIFFWarningsThrowException : |
2101 | 899k | TIFFWarningsLogOnly)); |
2102 | 899k | client_data.image=image; |
2103 | 899k | client_data.image_info=image_info; |
2104 | 899k | #if defined(HAVE_TIFFERROREXTR) |
2105 | 899k | { |
2106 | 899k | magick_int64_t |
2107 | 899k | memory_consumed, |
2108 | 899k | memory_maximum_limit; |
2109 | | |
2110 | 899k | tmsize_t |
2111 | 899k | max_single_mem_alloc; |
2112 | | |
2113 | 899k | TIFFOpenOptions *open_options = TIFFOpenOptionsAlloc(); |
2114 | | /* |
2115 | | TIFFOpenOptionsSetMaxSingleMemAlloc(TIFFOpenOptions *opts, |
2116 | | tmsize_t max_single_mem_alloc); |
2117 | | TIFFOpenOptionsSetMaxCumulatedMemAlloc(TIFFOpenOptions *opts, |
2118 | | tmsize_t max_cumulated_mem_alloc); |
2119 | | TIFFOpenOptionsSetWarnAboutUnknownTags(TIFFOpenOptions *opts, |
2120 | | int warn_about_unknown_tags); |
2121 | | TIFFOpenOptionsSetErrorHandlerExtR(TIFFOpenOptions *opts, |
2122 | | TIFFErrorHandlerExtR handler, |
2123 | | void *errorhandler_user_data); |
2124 | | TIFFOpenOptionsSetWarningHandlerExtR(TIFFOpenOptions *opts, |
2125 | | TIFFErrorHandlerExtR handler, |
2126 | | void *warnhandler_user_data); |
2127 | | */ |
2128 | 899k | TIFFOpenOptionsSetErrorHandlerExtR(open_options, TIFFReadErrorsHandlerExtR, (void *) exception); |
2129 | 899k | TIFFOpenOptionsSetWarningHandlerExtR(open_options, |
2130 | 899k | (CheckThrowWarnings(image_info) ? |
2131 | 899k | TIFFReadWarningHandlerExtRThrowException : TIFFReadWarningHandlerExtRLogOnly), |
2132 | 899k | (void *) exception); /* FIXME */ |
2133 | 899k | memory_maximum_limit=GetMagickResourceLimit(MemoryResource); |
2134 | 899k | memory_consumed=GetMagickResource(MemoryResource); |
2135 | 899k | max_single_mem_alloc=((memory_maximum_limit-memory_consumed)/8); /* FIXME: Support configuring ratio? */ |
2136 | 899k | TIFFOpenOptionsSetMaxSingleMemAlloc(open_options, max_single_mem_alloc); /* Added in v4.5.0 */ |
2137 | 899k | #if defined(HAVE_TIFFOPENOPTIONSSETMAXCUMULATEDMEMALLOC) |
2138 | 899k | { |
2139 | 899k | tmsize_t |
2140 | 899k | max_cumulated_mem_alloc; |
2141 | | |
2142 | 899k | max_cumulated_mem_alloc=((memory_maximum_limit-memory_consumed)/4); /* FIXME: Support configuring ratio? */ |
2143 | 899k | TIFFOpenOptionsSetMaxCumulatedMemAlloc(open_options, max_cumulated_mem_alloc); /* Added in v4.7.0 */ |
2144 | 899k | } |
2145 | 899k | #endif /* if defined(HAVE_TIFFOPENOPTIONSSETMAXCUMULATEDMEMALLOC) */ |
2146 | 899k | tiff=TIFFClientOpenExt(image->filename,"rb",(thandle_t) &client_data, |
2147 | 899k | TIFFReadBlob,TIFFWriteBlob,TIFFSeekBlob,TIFFCloseBlob, |
2148 | 899k | TIFFGetBlobSize,TIFFMapBlob,TIFFUnmapBlob, |
2149 | 899k | open_options); |
2150 | 899k | TIFFOpenOptionsFree(open_options); |
2151 | 899k | } |
2152 | | #else |
2153 | | tiff=TIFFClientOpen(image->filename,"rb",(thandle_t) &client_data, |
2154 | | TIFFReadBlob, |
2155 | | TIFFWriteBlob,TIFFSeekBlob,TIFFCloseBlob, |
2156 | | TIFFGetBlobSize,TIFFMapBlob,TIFFUnmapBlob); |
2157 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
2158 | 899k | if (tiff == (TIFF *) NULL) |
2159 | 791k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
2160 | | |
2161 | | /* |
2162 | | Report error if error was reported via TIFFReadErrorsHandler() while in |
2163 | | TIFFClientOpen(). |
2164 | | */ |
2165 | 108k | if (exception->severity > ErrorException) |
2166 | 51.4k | { |
2167 | 51.4k | TIFFClose(tiff); |
2168 | 51.4k | DestroyImageList(image); |
2169 | 51.4k | return (Image *) NULL; |
2170 | 51.4k | } |
2171 | | |
2172 | 57.3k | if (image_info->subrange != 0) |
2173 | 55.0k | while (image->scene < image_info->subimage) |
2174 | 0 | { |
2175 | | /* |
2176 | | Skip to next image. |
2177 | | */ |
2178 | 0 | image->scene++; |
2179 | 0 | status=TIFFReadDirectory(tiff); |
2180 | 0 | if (status == False) |
2181 | 0 | { |
2182 | 0 | ThrowTIFFReaderException(CorruptImageError,UnableToReadSubImageData, |
2183 | 0 | image); |
2184 | 0 | } |
2185 | 0 | } |
2186 | 57.3k | do |
2187 | 57.3k | { |
2188 | 57.3k | if (image_info->verbose > 1) |
2189 | 0 | TIFFPrintDirectory(tiff,stdout,False); |
2190 | | |
2191 | | /* |
2192 | | Read critical tags. We need a value for these tags in order to proceed. |
2193 | | */ |
2194 | 57.3k | status = 1; |
2195 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_PHOTOMETRIC,&photometric) == 1); |
2196 | 57.3k | if ((status == 1) && |
2197 | 56.0k | ((photometric == PHOTOMETRIC_LOGL) || (photometric == PHOTOMETRIC_LOGLUV))) |
2198 | 1.15k | status &= (TIFFSetField(tiff,TIFFTAG_SGILOGDATAFMT,SGILOGDATAFMT_FLOAT) == 1); |
2199 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_COMPRESSION,&compress_tag) == 1); |
2200 | 57.3k | status &= (TIFFGetField(tiff,TIFFTAG_IMAGEWIDTH,&width) == 1); |
2201 | 57.3k | status &= (TIFFGetField(tiff,TIFFTAG_IMAGELENGTH,&height) == 1); |
2202 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_PLANARCONFIG,&planar_config) == 1); |
2203 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLESPERPIXEL,&samples_per_pixel) == 1); |
2204 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_BITSPERSAMPLE,&bits_per_sample) == 1); |
2205 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLEFORMAT,&sample_format) == 1); |
2206 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_MINSAMPLEVALUE,&min_sample_value) == 1); |
2207 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_MAXSAMPLEVALUE,&max_sample_value) == 1); |
2208 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_ROWSPERSTRIP,&rows_per_strip) == 1); |
2209 | 57.3k | status &= (TIFFGetFieldDefaulted(tiff,TIFFTAG_FILLORDER,&fill_order) == 1); |
2210 | 57.3k | if (status == 0) |
2211 | 1.32k | { |
2212 | | /* |
2213 | | Promote TIFF warnings to errors for these critical tags. |
2214 | | */ |
2215 | 1.32k | if ((exception->severity > WarningException) && |
2216 | 6 | (exception->severity < ErrorException)) |
2217 | 0 | exception->severity = (ExceptionType) ((int) exception->severity + |
2218 | 0 | ((int) ErrorException - (int)WarningException)); |
2219 | 1.32k | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader, |
2220 | 0 | image); |
2221 | 0 | } |
2222 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_ORIENTATION,&orientation) == 1) |
2223 | 4.96k | image->orientation=(OrientationType) orientation; |
2224 | 56.0k | if (logging) |
2225 | 0 | { |
2226 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %ux%u", |
2227 | 0 | (unsigned int) width,(unsigned int) height); |
2228 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"PlanarConfiguration: %s", |
2229 | 0 | planar_config == PLANARCONFIG_CONTIG ? "contiguous" : |
2230 | 0 | planar_config == PLANARCONFIG_SEPARATE ? "separate" : |
2231 | 0 | "UNKNOWN"); |
2232 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2233 | 0 | "Samples per pixel: %u", samples_per_pixel); |
2234 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2235 | 0 | "Sample format: %s", |
2236 | 0 | sample_format == SAMPLEFORMAT_UINT ? "Unsigned integer" : |
2237 | 0 | sample_format == SAMPLEFORMAT_INT ? "Signed integer" : |
2238 | 0 | sample_format == SAMPLEFORMAT_IEEEFP ? "IEEE floating point" : |
2239 | 0 | sample_format == SAMPLEFORMAT_VOID ? "Untyped data" : |
2240 | 0 | sample_format == SAMPLEFORMAT_COMPLEXINT ? "Complex signed int" : |
2241 | 0 | sample_format == SAMPLEFORMAT_COMPLEXIEEEFP ? "Complex IEEE floating point" : |
2242 | 0 | "UNKNOWN"); |
2243 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2244 | 0 | "Bits per sample: %u",bits_per_sample); |
2245 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2246 | 0 | "Min sample value: %u",min_sample_value); |
2247 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2248 | 0 | "Max sample value: %u",max_sample_value); |
2249 | 0 | if (sample_format == SAMPLEFORMAT_IEEEFP) |
2250 | 0 | { |
2251 | 0 | double |
2252 | 0 | value; |
2253 | |
|
2254 | 0 | if (TIFFGetField(tiff,TIFFTAG_SMINSAMPLEVALUE,&value) == 1) |
2255 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2256 | 0 | "Special min sample value: %g", value); |
2257 | 0 | if (TIFFGetField(tiff,TIFFTAG_SMAXSAMPLEVALUE,&value) == 1) |
2258 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2259 | 0 | "Special max sample value: %g", value); |
2260 | 0 | } |
2261 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2262 | 0 | "Photometric: %s", PhotometricTagToString(photometric)); |
2263 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2264 | 0 | "Compression: %s", CompressionTagToString(compress_tag)); |
2265 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2266 | 0 | "Byte swapped: %s",TIFFIsByteSwapped(tiff) ? |
2267 | 0 | "true" : "false"); |
2268 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2269 | 0 | "Bit fill order: %s", |
2270 | 0 | fill_order == FILLORDER_LSB2MSB ? "LSB2MSB" : |
2271 | 0 | fill_order == FILLORDER_MSB2LSB ? "MSB2LSB" : |
2272 | 0 | "unknown"); |
2273 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2274 | 0 | "Rows per strip: %u",(unsigned int) rows_per_strip); |
2275 | 0 | } |
2276 | 56.0k | ImportPixelAreaOptionsInit(&import_options); |
2277 | 56.0k | if (photometric == PHOTOMETRIC_CIELAB) |
2278 | 3.58k | { |
2279 | 3.58k | #if 1 |
2280 | 3.58k | image->colorspace=LABColorspace; |
2281 | | #else |
2282 | | ThrowTIFFReaderException(CoderError,UnableToReadCIELABImages,image); |
2283 | | #endif |
2284 | 3.58k | } |
2285 | 56.0k | if (photometric == PHOTOMETRIC_SEPARATED) |
2286 | 2.65k | image->colorspace=CMYKColorspace; |
2287 | 56.0k | if (planar_config == PLANARCONFIG_SEPARATE) |
2288 | 3.66k | image->interlace=PlaneInterlace; |
2289 | 56.0k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_RESOLUTIONUNIT,&units); |
2290 | 56.0k | x_resolution=(float) image->x_resolution; |
2291 | 56.0k | y_resolution=(float) image->y_resolution; |
2292 | 56.0k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_XRESOLUTION,&x_resolution); |
2293 | 56.0k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_YRESOLUTION,&y_resolution); |
2294 | 56.0k | image->x_resolution=x_resolution; |
2295 | 56.0k | image->y_resolution=y_resolution; |
2296 | 56.0k | chromaticity=(float *) NULL; |
2297 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_WHITEPOINT,&chromaticity) == 1) |
2298 | 402 | { |
2299 | 402 | if (chromaticity != (float *) NULL) |
2300 | 402 | { |
2301 | 402 | if (logging) |
2302 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2303 | 0 | "White Point: %gx%g", |
2304 | 0 | chromaticity[0] /* white_point.x */, |
2305 | 0 | chromaticity[1] /* white_point.y */); |
2306 | 402 | if (MAGICK_ISNAN(chromaticity[0]) || (chromaticity[0] < 0.0) || |
2307 | 402 | MAGICK_ISNAN(chromaticity[1]) || (chromaticity[1] < 0.0)) |
2308 | 19 | { |
2309 | 19 | if (logging) |
2310 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2311 | 0 | "White Point: Is corrupt!"); |
2312 | 19 | } |
2313 | 383 | else |
2314 | 383 | { |
2315 | 383 | image->chromaticity.white_point.x=chromaticity[0]; |
2316 | 383 | image->chromaticity.white_point.y=chromaticity[1]; |
2317 | 383 | } |
2318 | 402 | } |
2319 | 402 | } |
2320 | 56.0k | chromaticity=(float *) NULL; |
2321 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_PRIMARYCHROMATICITIES,&chromaticity) == 1) |
2322 | 236 | { |
2323 | 236 | if (chromaticity != (float *) NULL) |
2324 | 236 | { |
2325 | 236 | if (logging) |
2326 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2327 | 0 | "Primary Chromaticities: " |
2328 | 0 | "r=%gx%g g=%gx%g b=%gx%g", |
2329 | 0 | chromaticity[0] /* red_primary.x */, |
2330 | 0 | chromaticity[1] /* red_primary.y */, |
2331 | 0 | chromaticity[2] /* green_primary.x */, |
2332 | 0 | chromaticity[3] /* green_primary.y */, |
2333 | 0 | chromaticity[4] /* blue_primary.x */, |
2334 | 0 | chromaticity[5] /* blue_primary.y */); |
2335 | 236 | if (MAGICK_ISNAN(chromaticity[0]) || (chromaticity[0] < 0.0) || |
2336 | 236 | MAGICK_ISNAN(chromaticity[1]) || (chromaticity[1] < 0.0) || |
2337 | 236 | MAGICK_ISNAN(chromaticity[2]) || (chromaticity[2] < 0.0) || |
2338 | 236 | MAGICK_ISNAN(chromaticity[3]) || (chromaticity[3] < 0.0) || |
2339 | 236 | MAGICK_ISNAN(chromaticity[4]) || (chromaticity[4] < 0.0) || |
2340 | 236 | MAGICK_ISNAN(chromaticity[5]) || (chromaticity[5] < 0.0)) |
2341 | 64 | { |
2342 | 64 | if (logging) |
2343 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2344 | 0 | "Primary Chromaticities: Is corrupt!"); |
2345 | 64 | } |
2346 | 172 | else |
2347 | 172 | { |
2348 | 172 | image->chromaticity.red_primary.x=chromaticity[0]; |
2349 | 172 | image->chromaticity.red_primary.y=chromaticity[1]; |
2350 | 172 | image->chromaticity.green_primary.x=chromaticity[2]; |
2351 | 172 | image->chromaticity.green_primary.y=chromaticity[3]; |
2352 | 172 | image->chromaticity.blue_primary.x=chromaticity[4]; |
2353 | 172 | image->chromaticity.blue_primary.y=chromaticity[5]; |
2354 | 172 | } |
2355 | 236 | } |
2356 | 236 | } |
2357 | 56.0k | { |
2358 | | /* |
2359 | | Retrieve embedded profiles. |
2360 | | */ |
2361 | 56.0k | uint32 |
2362 | 56.0k | length; |
2363 | | /* |
2364 | | ICC ICM color profile. |
2365 | | */ |
2366 | 56.0k | #if defined(TIFFTAG_ICCPROFILE) |
2367 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_ICCPROFILE,&length,&text) == 1) |
2368 | 38 | { |
2369 | 38 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2370 | 38 | "ICC ICM embedded profile with length %lu bytes", |
2371 | 38 | (unsigned long)length); |
2372 | 38 | (void) SetImageProfile(image,"ICM",(unsigned char *) text,(size_t) length); |
2373 | 38 | } |
2374 | 56.0k | #endif /* defined(TIFFTAG_ICCPROFILE) */ |
2375 | | /* |
2376 | | IPTC/Photoshop profile. |
2377 | | */ |
2378 | 56.0k | #if defined(TIFFTAG_PHOTOSHOP) |
2379 | | /* Photoshop profile (with embedded IPTC profile) */ |
2380 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_PHOTOSHOP,&length,&text) == 1) |
2381 | 98 | { |
2382 | 98 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2383 | 98 | "Photoshop embedded profile with length %lu bytes", |
2384 | 98 | (unsigned long) length); |
2385 | 98 | (void) ReadNewsProfile((unsigned char *) text,(long) length,image,TIFFTAG_PHOTOSHOP); |
2386 | 98 | } |
2387 | | #elif defined(TIFFTAG_RICHTIFFIPTC) |
2388 | | /* IPTC TAG from RichTIFF specifications */ |
2389 | | if (TIFFGetField(tiff,TIFFTAG_RICHTIFFIPTC,&length,&text) == 1) |
2390 | | { |
2391 | | if (TIFFIsByteSwapped(tiff)) |
2392 | | TIFFSwabArrayOfLong((uint32 *) text,length); |
2393 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2394 | | "IPTC Newsphoto embedded profile with length %u bytes",length); |
2395 | | ReadNewsProfile((unsigned char *) text,length,image,TIFFTAG_RICHTIFFIPTC); |
2396 | | } |
2397 | | #endif |
2398 | | /* |
2399 | | XML XMP profile. |
2400 | | */ |
2401 | 56.0k | #if defined(TIFFTAG_XMLPACKET) |
2402 | | /* %XML packet [Adobe XMP Specification, Janary 2004] */ |
2403 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_XMLPACKET,&length,&text) == 1) |
2404 | 34 | { |
2405 | 34 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2406 | 34 | "XMP embedded profile with length %lu bytes", |
2407 | 34 | (unsigned long) length); |
2408 | 34 | SetImageProfile(image,"XMP",(unsigned char *) text,(size_t) length); |
2409 | 34 | } |
2410 | 56.0k | #endif |
2411 | 56.0k | } |
2412 | | /* |
2413 | | Map from TIFF compression tags to GraphicsMagick equivalents |
2414 | | as well as estimate a maximum compression ratio (for |
2415 | | validating scanline/strip/tile allocation requests). |
2416 | | */ |
2417 | 56.0k | switch (compress_tag) |
2418 | 56.0k | { |
2419 | 8.88k | case COMPRESSION_NONE: |
2420 | 8.88k | image->compression=NoCompression; |
2421 | 8.88k | break; |
2422 | 3.87k | case COMPRESSION_CCITTFAX3: |
2423 | 3.87k | image->compression=FaxCompression; |
2424 | 3.87k | break; |
2425 | 4.92k | case COMPRESSION_CCITTFAX4: |
2426 | 4.92k | image->compression=Group4Compression; |
2427 | 4.92k | break; |
2428 | 12.3k | case COMPRESSION_JPEG: |
2429 | 12.3k | image->compression=JPEGCompression; |
2430 | 12.3k | break; |
2431 | 0 | case COMPRESSION_OJPEG: |
2432 | 0 | image->compression=JPEGCompression; |
2433 | 0 | break; |
2434 | 1.70k | case COMPRESSION_LZW: |
2435 | 1.70k | image->compression=LZWCompression; |
2436 | 1.70k | break; |
2437 | 0 | #if defined(COMPRESSION_LZMA) |
2438 | 75 | case COMPRESSION_LZMA: |
2439 | 75 | image->compression=LZMACompression; |
2440 | 75 | break; |
2441 | 0 | #endif /* defined(COMPRESSION_LZMA) */ |
2442 | 863 | case COMPRESSION_DEFLATE: |
2443 | 863 | image->compression=ZipCompression; |
2444 | 863 | break; |
2445 | 4.24k | case COMPRESSION_ADOBE_DEFLATE: |
2446 | 4.24k | image->compression=ZipCompression; |
2447 | 4.24k | break; |
2448 | 0 | #if defined(COMPRESSION_ZSTD) |
2449 | 5.60k | case COMPRESSION_ZSTD: |
2450 | 5.60k | image->compression=ZSTDCompression; |
2451 | 5.60k | break; |
2452 | 0 | #endif /* defined(COMPRESSION_ZSTD) */ |
2453 | 0 | #if defined(COMPRESSION_WEBP) |
2454 | 7.43k | case COMPRESSION_WEBP: |
2455 | 7.43k | image->compression=WebPCompression; |
2456 | 7.43k | break; |
2457 | 0 | #endif /* if defined(COMPRESSION_WEBP) */ |
2458 | 6.13k | default: |
2459 | 6.13k | image->compression=NoCompression; |
2460 | 6.13k | break; |
2461 | 56.0k | } |
2462 | 56.0k | image->columns=width; |
2463 | 56.0k | image->rows=height; |
2464 | 56.0k | image->depth=bits_per_sample; |
2465 | | |
2466 | 56.0k | if (image->scene != 0) |
2467 | 0 | status=MagickMonitorFormatted(image->scene-1,image->scene, |
2468 | 0 | &image->exception, |
2469 | 0 | LoadImageText,image->filename, |
2470 | 0 | image->columns,image->rows); |
2471 | | |
2472 | | /* |
2473 | | Obtain information about any extra samples. |
2474 | | FIXME samples_per_pixel = 4 and 1 extra sample which is 'unspecified' |
2475 | | */ |
2476 | 56.0k | extra_samples=0; |
2477 | 56.0k | if (TIFFGetField(tiff,TIFFTAG_EXTRASAMPLES,&extra_samples, |
2478 | 56.0k | &sample_info) == 1) |
2479 | 6.96k | { |
2480 | 6.96k | int |
2481 | 6.96k | sample_index; |
2482 | | |
2483 | 6.96k | if (extra_samples != 0) |
2484 | 6.87k | { |
2485 | 6.87k | if (sample_info[0] == EXTRASAMPLE_UNSPECIFIED) |
2486 | 303 | { |
2487 | 303 | alpha_type=UnspecifiedAlpha; |
2488 | 303 | } |
2489 | 6.57k | else if (sample_info[0] == EXTRASAMPLE_UNASSALPHA) |
2490 | 3.63k | { |
2491 | 3.63k | alpha_type=UnassociatedAlpha; |
2492 | 3.63k | } |
2493 | 2.93k | else if (sample_info[0] == EXTRASAMPLE_ASSOCALPHA) |
2494 | 2.93k | { |
2495 | 2.93k | alpha_type=AssociatedAlpha; |
2496 | 2.93k | } |
2497 | 6.87k | } |
2498 | 6.96k | if (image->logging) |
2499 | 793k | for (sample_index=0 ; sample_index < extra_samples; sample_index++) |
2500 | 786k | { |
2501 | 786k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2502 | 786k | "Extra sample %u contains %s",sample_index+1, |
2503 | 786k | ExtraSampleToString(sample_info[sample_index])); |
2504 | 786k | } |
2505 | 6.96k | } |
2506 | | /* |
2507 | | Report RGBA images which may be improperly marked. |
2508 | | */ |
2509 | 56.0k | if ((image->logging) && (extra_samples == 0)) |
2510 | 49.1k | if (((PHOTOMETRIC_RGB == photometric) && (samples_per_pixel == 4)) || |
2511 | 47.2k | (((PHOTOMETRIC_MINISWHITE == photometric) || (PHOTOMETRIC_MINISBLACK == photometric)) && (samples_per_pixel == 2))) |
2512 | 2.38k | { |
2513 | 2.38k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2514 | 2.38k | "Photometric is %s but %u samples/pixel and %u extra_samples provided!", |
2515 | 2.38k | PhotometricTagToString(photometric),samples_per_pixel, extra_samples); |
2516 | 2.38k | } |
2517 | | /* |
2518 | | Deal with files which are marked as unspecified alpha, but |
2519 | | they should be unassociated alpha. |
2520 | | */ |
2521 | 56.0k | if (((extra_samples == 1) && (sample_info[0] == EXTRASAMPLE_UNSPECIFIED)) && |
2522 | 255 | ( |
2523 | 255 | ((samples_per_pixel == 2) && ((PHOTOMETRIC_MINISWHITE == photometric) || (PHOTOMETRIC_MINISBLACK == photometric))) || |
2524 | 152 | ((samples_per_pixel == 4) && (PHOTOMETRIC_RGB == photometric)) || |
2525 | 63 | ((samples_per_pixel == 5) && (PHOTOMETRIC_SEPARATED == photometric)) |
2526 | 255 | ) |
2527 | 56.0k | ) |
2528 | 213 | { |
2529 | 213 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2530 | 213 | "Promoting UnspecifiedAlpha to UnassociatedAlpha"); |
2531 | 213 | alpha_type=UnassociatedAlpha; |
2532 | 213 | image->matte=True; |
2533 | 213 | } |
2534 | | /* |
2535 | | Allow the user to over-ride the alpha channel type. |
2536 | | */ |
2537 | 56.0k | if (image->matte) |
2538 | 213 | { |
2539 | 213 | if ((definition_value=AccessDefinition(image_info,"tiff","alpha"))) |
2540 | 0 | { |
2541 | 0 | if (LocaleCompare(definition_value,"unspecified") == 0) |
2542 | 0 | alpha_type=UnspecifiedAlpha; |
2543 | 0 | else if (LocaleCompare(definition_value,"associated") == 0) |
2544 | 0 | alpha_type=AssociatedAlpha; |
2545 | 0 | else if (LocaleCompare(definition_value,"unassociated") == 0) |
2546 | 0 | alpha_type=UnassociatedAlpha; |
2547 | 0 | } |
2548 | 213 | } |
2549 | | |
2550 | | /* |
2551 | | Does alpha type deserve a matte channel? |
2552 | | */ |
2553 | 56.0k | switch (alpha_type) |
2554 | 56.0k | { |
2555 | 49.2k | case UnspecifiedAlpha: |
2556 | 49.2k | break; |
2557 | 3.85k | case UnassociatedAlpha: |
2558 | 3.85k | image->matte=True; |
2559 | 3.85k | break; |
2560 | 2.93k | case AssociatedAlpha: |
2561 | 2.93k | image->matte=True; |
2562 | 2.93k | break; |
2563 | 0 | default: |
2564 | 0 | break; |
2565 | 56.0k | } |
2566 | | |
2567 | | /* |
2568 | | Describe how the alpha channel will be treated. |
2569 | | */ |
2570 | 56.0k | if (image->matte) |
2571 | 6.78k | { |
2572 | 6.78k | char |
2573 | 6.78k | alpha_string[MaxTextExtent]; |
2574 | | |
2575 | 6.78k | switch (alpha_type) |
2576 | 6.78k | { |
2577 | 0 | default: |
2578 | 0 | case UnspecifiedAlpha: |
2579 | 0 | (void) strlcpy(alpha_string,"Unspecified",MaxTextExtent); |
2580 | 0 | break; |
2581 | 3.85k | case UnassociatedAlpha: |
2582 | 3.85k | (void) strlcpy(alpha_string,"Unassociated",MaxTextExtent); |
2583 | 3.85k | break; |
2584 | 2.93k | case AssociatedAlpha: |
2585 | 2.93k | (void) strlcpy(alpha_string,"Associated",MaxTextExtent); |
2586 | 2.93k | break; |
2587 | 6.78k | } |
2588 | 6.78k | (void) SetImageAttribute(image,"alpha",alpha_string); |
2589 | 6.78k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2590 | 6.78k | "Image has a matte channel of type: %s", |
2591 | 6.78k | alpha_string); |
2592 | 6.78k | } |
2593 | | |
2594 | 56.0k | if (units == RESUNIT_INCH) |
2595 | 55.4k | image->units=PixelsPerInchResolution; |
2596 | 56.0k | if (units == RESUNIT_CENTIMETER) |
2597 | 93 | image->units=PixelsPerCentimeterResolution; |
2598 | 56.0k | { |
2599 | 56.0k | uint16 |
2600 | 56.0k | pagenumber; |
2601 | | |
2602 | 56.0k | pagenumber=(unsigned short) image->scene; |
2603 | 56.0k | if (TIFFGetFieldDefaulted(tiff,TIFFTAG_PAGENUMBER,&pagenumber,&pages) == 1) |
2604 | 628 | image->scene=pagenumber; |
2605 | 56.0k | } |
2606 | | |
2607 | | /* |
2608 | | Convert TIFF tags to text attributes |
2609 | | https://gitlab.com/libtiff/libtiff/-/issues/575 |
2610 | | */ |
2611 | 56.0k | { |
2612 | 56.0k | static const struct |
2613 | 56.0k | { |
2614 | 56.0k | uint32 tag; |
2615 | 56.0k | const char name[14]; |
2616 | 56.0k | } text_tags[] = |
2617 | 56.0k | { |
2618 | 56.0k | { TIFFTAG_ARTIST, "artist" }, |
2619 | 56.0k | { TIFFTAG_COPYRIGHT, "copyright" }, |
2620 | 56.0k | { TIFFTAG_DATETIME, "timestamp" }, |
2621 | 56.0k | { TIFFTAG_DOCUMENTNAME, "document" }, |
2622 | 56.0k | { TIFFTAG_HOSTCOMPUTER, "hostcomputer" }, |
2623 | 56.0k | { TIFFTAG_IMAGEDESCRIPTION, "comment" }, |
2624 | 56.0k | { TIFFTAG_MAKE, "make" }, |
2625 | 56.0k | { TIFFTAG_MODEL, "model" }, |
2626 | 56.0k | { TIFFTAG_PAGENAME, "label" }, |
2627 | 56.0k | { TIFFTAG_SOFTWARE, "software" }, |
2628 | 56.0k | #if 1 |
2629 | | /* |
2630 | | These three cause TIFFFieldWithTag() to return NULL and |
2631 | | therefore produce "TIFF Warning: Warning, unknown tag" exceptions |
2632 | | while reading! |
2633 | | */ |
2634 | 56.0k | { TIFFTAG_OPIIMAGEID, "imageid" }, |
2635 | 56.0k | { 33423, "kodak-33423" }, |
2636 | 56.0k | { 36867, "kodak-36867" } |
2637 | 56.0k | #endif |
2638 | 56.0k | }; |
2639 | | |
2640 | 784k | for (i = 0; i < ArraySize(text_tags); i++) |
2641 | 728k | { |
2642 | 728k | const uint32 tag = text_tags[i].tag; |
2643 | 728k | const char *tag_name = text_tags[i].name; |
2644 | 728k | int field_passcount=1; /* "Unsupported" tags return two arguments */ |
2645 | | #if TIFFLIB_VERSION <= 20111221 |
2646 | | /* |
2647 | | Before tiff 4.0.0 (20111221), TIFFFieldWithTag returned |
2648 | | TIFFFieldInfo * which provides field_passcount |
2649 | | */ |
2650 | | const TIFFFieldInfo* tiff_field=TIFFFieldWithTag(tiff,tag); |
2651 | | if (tiff_field != (const TIFFFieldInfo *)) |
2652 | | field_passcount=tiff_field->field_passcount; |
2653 | | else |
2654 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2655 | | "TIFFFieldWithTag() returns NULL for tag %u \"%s\"", tag, tag_name); |
2656 | | #else |
2657 | 728k | const TIFFField *tiff_field=TIFFFieldWithTag(tiff,tag); |
2658 | 728k | if (tiff_field != (const TIFFField *) NULL) |
2659 | 560k | field_passcount=TIFFFieldPassCount(tiff_field); |
2660 | 168k | else |
2661 | 168k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2662 | 168k | "TIFFFieldWithTag() returns NULL for tag %u \"%s\"", tag, tag_name); |
2663 | 728k | #endif |
2664 | | #if 0 |
2665 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2666 | | "Field pass count for tag %u \"%s\" is %d", tag, tag_name, field_passcount); |
2667 | | #endif |
2668 | 728k | if (field_passcount) |
2669 | 168k | { |
2670 | 168k | if ((TIFFGetField(tiff,tag,&count,&text) == 1) && (count) && (text != (const char*) NULL)) |
2671 | 34 | CopySizedFieldToAttribute(tag_name,count,text); |
2672 | 168k | } |
2673 | 560k | else |
2674 | 560k | { |
2675 | 560k | if ((TIFFGetField(tiff,tag,&text) == 1) && (text != (const char*) NULL)) |
2676 | 10.5k | (void) SetImageAttribute(image,tag_name,text); |
2677 | 560k | } |
2678 | 728k | } |
2679 | 56.0k | } |
2680 | | |
2681 | 56.0k | if ((photometric == PHOTOMETRIC_PALETTE) || |
2682 | 53.8k | ((photometric == PHOTOMETRIC_MINISWHITE || |
2683 | 37.2k | photometric == PHOTOMETRIC_MINISBLACK) && |
2684 | 31.5k | ((image_info->type == PaletteType) || |
2685 | 31.5k | (image_info->type == PaletteMatteType)) && |
2686 | 0 | (MaxColormapSize > MaxValueGivenBits(bits_per_sample)) |
2687 | 53.8k | ) |
2688 | 56.0k | ) |
2689 | 2.25k | { |
2690 | | /* |
2691 | | Palette image |
2692 | | */ |
2693 | 2.25k | if (MaxColormapSize > MaxValueGivenBits(bits_per_sample)) |
2694 | 2.25k | { |
2695 | 2.25k | (void) InitializeImageColormap(image,tiff); |
2696 | 2.25k | } |
2697 | 0 | else |
2698 | 0 | { |
2699 | 0 | ThrowTIFFReaderException(CoderError,ColormapTooLarge,image); |
2700 | 0 | } |
2701 | 2.25k | } |
2702 | | |
2703 | | /* |
2704 | | Quit if in "ping" mode and we are outside of requested range, |
2705 | | otherwise continue to next frame. |
2706 | | */ |
2707 | 56.0k | if (image_info->ping) |
2708 | 0 | { |
2709 | 0 | if (image_info->subrange != 0) |
2710 | 0 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
2711 | 0 | break; |
2712 | | |
2713 | 0 | goto read_next_frame; |
2714 | 0 | } |
2715 | | |
2716 | 56.0k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
2717 | 1.44k | { |
2718 | 1.44k | ThrowTIFFReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
2719 | 0 | } |
2720 | | |
2721 | | /* |
2722 | | Verify that the bits per sample, samples per pixel, and |
2723 | | photometric are suitable for the claimed compressor |
2724 | | */ |
2725 | 54.6k | #if defined(COMPRESSION_CCITTFAX3) |
2726 | 54.6k | if (COMPRESSION_CCITTFAX3 == compress_tag) |
2727 | 3.85k | { |
2728 | 3.85k | if (1 != bits_per_sample) |
2729 | 5 | { |
2730 | 5 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2731 | 5 | "%s compression requires 1 bits per sample!", |
2732 | 5 | CompressionTagToString(compress_tag)); |
2733 | 5 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2734 | 0 | } |
2735 | 3.85k | if ((PHOTOMETRIC_MINISBLACK != photometric) && (PHOTOMETRIC_MINISWHITE != photometric)) |
2736 | 23 | { |
2737 | 23 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2738 | 23 | "%s compression requires photometric of minisblack or miniswhite!", |
2739 | 23 | CompressionTagToString(compress_tag)); |
2740 | 23 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2741 | 0 | } |
2742 | 3.85k | } |
2743 | 54.5k | #endif /* if defined(COMPRESSION_CCITTFAX3) */ |
2744 | 54.5k | #if defined(COMPRESSION_CCITTFAX4) |
2745 | 54.5k | if (COMPRESSION_CCITTFAX4 == compress_tag) |
2746 | 4.78k | { |
2747 | 4.78k | if (1 != bits_per_sample) |
2748 | 4 | { |
2749 | 4 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2750 | 4 | "%s compression requires 1 bits per sample!", |
2751 | 4 | CompressionTagToString(compress_tag)); |
2752 | 4 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2753 | 0 | } |
2754 | 4.77k | if ((PHOTOMETRIC_MINISBLACK != photometric) && (PHOTOMETRIC_MINISWHITE != photometric)) |
2755 | 18 | { |
2756 | 18 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2757 | 18 | "%s compression requires photometric of minisblack or miniswhite!", |
2758 | 18 | CompressionTagToString(compress_tag)); |
2759 | 18 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2760 | 0 | } |
2761 | 4.77k | } |
2762 | 54.5k | #endif /* if defined(COMPRESSION_CCITTFAX4) */ |
2763 | 54.5k | #if defined(COMPRESSION_JBIG) |
2764 | 54.5k | if (COMPRESSION_JBIG == compress_tag) |
2765 | 0 | { |
2766 | 0 | if (1 != bits_per_sample) |
2767 | 0 | { |
2768 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2769 | 0 | "%s compression requires 1 bits per sample!", |
2770 | 0 | CompressionTagToString(compress_tag)); |
2771 | 0 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2772 | 0 | } |
2773 | 0 | if ((PHOTOMETRIC_MINISBLACK != photometric) && (PHOTOMETRIC_MINISWHITE != photometric)) |
2774 | 0 | { |
2775 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2776 | 0 | "%s compression requires photometric of minisblack or miniswhite!", |
2777 | 0 | CompressionTagToString(compress_tag)); |
2778 | 0 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2779 | 0 | } |
2780 | 0 | } |
2781 | 54.5k | #endif /* if defined(COMPRESSION_JBIG) */ |
2782 | 54.5k | #if defined(COMPRESSION_WEBP) |
2783 | 54.5k | if (COMPRESSION_WEBP == compress_tag) |
2784 | 7.41k | { |
2785 | 7.41k | if (8 != bits_per_sample) |
2786 | 10 | { |
2787 | 10 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2788 | 10 | "%s compression requires 8 bits per sample!", |
2789 | 10 | CompressionTagToString(compress_tag)); |
2790 | 10 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2791 | 0 | } |
2792 | 7.40k | if ((3 != samples_per_pixel) && (4 != samples_per_pixel)) |
2793 | 8 | { |
2794 | 8 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2795 | 8 | "%s compression requires 3 or 4 samples per pixel!", |
2796 | 8 | CompressionTagToString(compress_tag)); |
2797 | 8 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2798 | 0 | } |
2799 | 7.39k | if (PHOTOMETRIC_RGB != photometric) |
2800 | 4 | { |
2801 | 4 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2802 | 4 | "%s compression requires photometric RGB!", |
2803 | 4 | CompressionTagToString(compress_tag)); |
2804 | 4 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2805 | 0 | } |
2806 | 7.39k | } |
2807 | 54.5k | #endif /* if defined(COMPRESSION_WEBP) */ |
2808 | | |
2809 | | /* |
2810 | | Check if the bits-per-sample value is supported by the |
2811 | | implementation before proceeding. |
2812 | | |
2813 | | Currently we support a range of 1-32, and 64 bits if the |
2814 | | samples are float. |
2815 | | */ |
2816 | 54.5k | if (!((sample_format == SAMPLEFORMAT_IEEEFP && bits_per_sample == 64) || |
2817 | 53.6k | ((bits_per_sample > 0) && (bits_per_sample <= 32)))) |
2818 | 44 | { |
2819 | 44 | ThrowTIFFReaderException(CoderError,UnsupportedBitsPerSample,image); |
2820 | 0 | } |
2821 | | |
2822 | | /* |
2823 | | Check for excessive samples per pixel or excessive extra samples. |
2824 | | */ |
2825 | 54.4k | if ((samples_per_pixel > 8U) || (extra_samples > 8U)) |
2826 | 47 | { |
2827 | 47 | ThrowTIFFReaderException(CoderError,UnsupportedSamplesPerPixel,image); |
2828 | 0 | } |
2829 | | |
2830 | | /* |
2831 | | Determine which method to use for reading pixels. |
2832 | | */ |
2833 | 54.4k | { |
2834 | 54.4k | int |
2835 | 54.4k | quantum_samples; |
2836 | | |
2837 | 54.4k | QuantumType |
2838 | 54.4k | quantum_type; |
2839 | | |
2840 | 54.4k | if ((samples_per_pixel > 1) && |
2841 | 21.6k | (compress_tag == COMPRESSION_JPEG) && |
2842 | 1.79k | (photometric == PHOTOMETRIC_YCBCR)) |
2843 | 1.29k | { |
2844 | | /* Following hack avoids the error message "Application |
2845 | | transferred too many scanlines. (JPEGLib)." caused by |
2846 | | YCbCr subsampling, but it returns data in RGB rather |
2847 | | than YCbCr. */ |
2848 | 1.29k | if (logging) |
2849 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2850 | 0 | "Resetting photometric from %s to %s for JPEG RGB", |
2851 | 0 | PhotometricTagToString(photometric), |
2852 | 0 | PhotometricTagToString(PHOTOMETRIC_RGB)); |
2853 | 1.29k | (void) TIFFSetField( tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB ); |
2854 | 1.29k | photometric=PHOTOMETRIC_RGB; |
2855 | 1.29k | } |
2856 | 54.4k | method=RGBAPuntMethod; |
2857 | 54.4k | quantum_type=UndefinedQuantum; |
2858 | 54.4k | quantum_samples=0; |
2859 | 54.4k | if (QuantumTransferMode(image,photometric,compress_tag,sample_format, |
2860 | 54.4k | samples_per_pixel,planar_config,0,&quantum_type, |
2861 | 54.4k | &quantum_samples,exception) |
2862 | 54.4k | == MagickPass) |
2863 | 46.9k | { |
2864 | 46.9k | method=ScanLineMethod; |
2865 | 46.9k | if ((compress_tag == COMPRESSION_JPEG) || |
2866 | 34.8k | (compress_tag == COMPRESSION_OJPEG)) |
2867 | 12.1k | { |
2868 | 12.1k | if (TIFFIsTiled(tiff)) |
2869 | 486 | method=TiledMethod; |
2870 | 11.6k | else |
2871 | 11.6k | method=StrippedMethod; |
2872 | 12.1k | } |
2873 | 34.8k | #if defined(COMPRESSION_JBIG) |
2874 | 34.8k | else if (compress_tag == COMPRESSION_JBIG) |
2875 | | /* libtiff jbig coder only handles strips */ |
2876 | 0 | method=StrippedMethod; |
2877 | 34.8k | #endif |
2878 | 34.8k | else if (TIFFIsTiled(tiff)) |
2879 | 3.89k | method=TiledMethod; |
2880 | 30.9k | else if (TIFFStripSize(tiff) <= 1024*256) |
2881 | 27.1k | method=StrippedMethod; |
2882 | 46.9k | if (photometric == PHOTOMETRIC_MINISWHITE) |
2883 | 15.3k | import_options.grayscale_miniswhite=MagickTrue; |
2884 | 46.9k | } |
2885 | 7.45k | else if (exception->severity < ErrorException) |
2886 | 7.41k | { |
2887 | 7.41k | if (TIFFIsTiled(tiff)) |
2888 | 1.20k | method=RGBATiledMethod; |
2889 | 6.20k | else if (TIFFGetField(tiff,TIFFTAG_ROWSPERSTRIP,&rows_per_strip) == 1) |
2890 | 3.70k | method=RGBAStrippedMethod; |
2891 | 7.41k | } |
2892 | 39 | else |
2893 | 39 | { |
2894 | | /* |
2895 | | QuantumTransferMode reported an error |
2896 | | */ |
2897 | 39 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader,image); |
2898 | 0 | } |
2899 | 54.4k | } |
2900 | | |
2901 | | /* |
2902 | | Set extra import options for floating point. |
2903 | | */ |
2904 | 54.4k | if (sample_format == SAMPLEFORMAT_IEEEFP) |
2905 | 4.81k | { |
2906 | 4.81k | double |
2907 | 4.81k | value; |
2908 | | |
2909 | 4.81k | import_options.sample_type=FloatQuantumSampleType; |
2910 | 4.81k | if (TIFFGetField(tiff,TIFFTAG_SMINSAMPLEVALUE,&value) == 1) |
2911 | 58 | import_options.double_minvalue=value; |
2912 | 4.81k | if (TIFFGetField(tiff,TIFFTAG_SMAXSAMPLEVALUE,&value) == 1) |
2913 | 112 | import_options.double_maxvalue=value; |
2914 | 4.81k | if ((definition_value=AccessDefinition(image_info,"tiff","min-sample-value"))) |
2915 | 0 | import_options.double_minvalue=strtod(definition_value,(char **)NULL); |
2916 | 4.81k | if ((definition_value=AccessDefinition(image_info,"tiff","max-sample-value"))) |
2917 | 0 | import_options.double_maxvalue=strtod(definition_value,(char **)NULL); |
2918 | 4.81k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2919 | 4.81k | "Using min sample value %g, max sample value %g", |
2920 | 4.81k | import_options.double_minvalue, |
2921 | 4.81k | import_options.double_maxvalue); |
2922 | 4.81k | } |
2923 | | |
2924 | | /* |
2925 | | For sample sizes matching a CPU native word, use native endian |
2926 | | order for import. |
2927 | | */ |
2928 | 54.4k | if ((16 == bits_per_sample) || (32 == bits_per_sample) || (64 == bits_per_sample)) |
2929 | 7.05k | import_options.endian=NativeEndian; |
2930 | | |
2931 | 54.4k | switch (method) |
2932 | 54.4k | { |
2933 | 3.81k | case ScanLineMethod: |
2934 | 3.81k | { |
2935 | | /* |
2936 | | Read TIFF image as scanlines. |
2937 | | */ |
2938 | 3.81k | unsigned char |
2939 | 3.81k | *scanline = (unsigned char *) NULL; |
2940 | | |
2941 | 3.81k | int |
2942 | 3.81k | max_sample, |
2943 | 3.81k | quantum_samples, |
2944 | 3.81k | sample; |
2945 | | |
2946 | 3.81k | tsize_t |
2947 | 3.81k | scanline_size = 0; |
2948 | | |
2949 | 3.81k | QuantumType |
2950 | 3.81k | quantum_type; |
2951 | | |
2952 | 3.81k | if (logging) |
2953 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2954 | 0 | "Using scanline %s read method with %u bits per sample", |
2955 | 0 | PhotometricTagToString(photometric),bits_per_sample); |
2956 | | /* |
2957 | | Prepare for separate/contiguous retrieval. |
2958 | | */ |
2959 | 3.81k | max_sample=1; |
2960 | 3.81k | if (planar_config == PLANARCONFIG_SEPARATE) |
2961 | 67 | { |
2962 | 67 | if (QuantumTransferMode(image,photometric,compress_tag, |
2963 | 67 | sample_format,samples_per_pixel, |
2964 | 67 | PLANARCONFIG_CONTIG,0, |
2965 | 67 | &quantum_type,&quantum_samples, |
2966 | 67 | exception) |
2967 | 67 | != MagickPass) |
2968 | 3 | ThrowTIFFReaderException(CorruptImageError, |
2969 | 67 | ImproperImageHeader,image); |
2970 | 64 | max_sample=quantum_samples; |
2971 | 64 | } |
2972 | | /* |
2973 | | Allocate memory for one scanline. |
2974 | | */ |
2975 | 3.80k | scanline_size=TIFFScanlineSize(tiff); |
2976 | 3.80k | if (scanline_size <= 0) |
2977 | 0 | { |
2978 | 0 | status=MagickFail; |
2979 | 0 | break; |
2980 | 0 | } |
2981 | | /* |
2982 | | Scale up to size of 32-bit word. |
2983 | | */ |
2984 | 3.80k | scanline_size=RoundUpToAlignment(scanline_size,sizeof(magick_int32_t)); |
2985 | | |
2986 | 3.80k | if (logging) |
2987 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2988 | 0 | "Request to allocate scanline buffer of " |
2989 | 0 | "%" MAGICK_SIZE_T_F "u bytes", |
2990 | 0 | (MAGICK_SIZE_T) scanline_size); |
2991 | | |
2992 | | /* |
2993 | | Rationalize memory request based on file size |
2994 | | */ |
2995 | 3.80k | if (scanline_size > file_size*max_compress_ratio) |
2996 | 0 | { |
2997 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2998 | 0 | "Unreasonable allocation size " |
2999 | 0 | "(ratio of alloc to file size %g)", |
3000 | 0 | (double) scanline_size/file_size); |
3001 | 0 | ThrowTIFFReaderException(CorruptImageError,InsufficientImageDataInFile, |
3002 | 0 | image); |
3003 | 0 | } |
3004 | | |
3005 | 3.80k | scanline=MagickAllocateResourceLimitedClearedMemory(unsigned char *,(size_t) scanline_size); |
3006 | 3.80k | if (scanline == (unsigned char *) NULL) |
3007 | 0 | { |
3008 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3009 | 0 | image); |
3010 | 0 | } |
3011 | 7.61k | for (sample=0; (status != MagickFail) && (sample < max_sample); sample++) |
3012 | 3.80k | { |
3013 | 1.55M | for (y=0; (status != MagickFail) && (y < image->rows); y++) |
3014 | 1.55M | { |
3015 | | /* |
3016 | | Obtain a scanline |
3017 | | */ |
3018 | 1.55M | if (TIFFReadScanline(tiff,(char *) scanline,(uint32) y,sample) == -1) |
3019 | 2.70k | { |
3020 | 2.70k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3021 | 2.70k | "TIFFReadScanline() failed!"); |
3022 | 2.70k | status=MagickFail; |
3023 | 2.70k | break; |
3024 | 2.70k | } |
3025 | 1.54M | if (image->exception.severity >= ErrorException) |
3026 | 0 | { |
3027 | 0 | status=MagickFail; |
3028 | 0 | break; |
3029 | 0 | } |
3030 | 1.54M | #if !defined(WORDS_BIGENDIAN) |
3031 | 1.54M | if (24 == bits_per_sample) |
3032 | 757 | SwabDataToBigEndian(bits_per_sample,scanline,scanline_size); |
3033 | 1.54M | #endif |
3034 | | /* |
3035 | | Determine quantum parse method. |
3036 | | */ |
3037 | 1.54M | if (QuantumTransferMode(image,photometric,compress_tag, |
3038 | 1.54M | sample_format, |
3039 | 1.54M | samples_per_pixel,planar_config, |
3040 | 1.54M | sample, |
3041 | 1.54M | &quantum_type,&quantum_samples, |
3042 | 1.54M | exception) |
3043 | 1.54M | == MagickFail) |
3044 | 0 | { |
3045 | 0 | status=MagickFail; |
3046 | 0 | break; |
3047 | 0 | } |
3048 | | /* |
3049 | | Compact scanline to only contain raster data. |
3050 | | */ |
3051 | 1.54M | if ((samples_per_pixel > quantum_samples) && |
3052 | 184k | (planar_config == PLANARCONFIG_CONTIG)) |
3053 | 184k | CompactSamples(image->columns, bits_per_sample, |
3054 | 184k | samples_per_pixel, quantum_samples, scanline); |
3055 | | /* |
3056 | | Import scanline into image. |
3057 | | */ |
3058 | 1.54M | if (sample == 0) |
3059 | 1.54M | q=SetImagePixelsEx(image,0,y,image->columns,1,exception); |
3060 | 0 | else |
3061 | 0 | q=GetImagePixelsEx(image,0,y,image->columns,1,exception); |
3062 | 1.54M | if (q == (PixelPacket *) NULL) |
3063 | 0 | { |
3064 | 0 | status=MagickFail; |
3065 | 0 | break; |
3066 | 0 | } |
3067 | 1.54M | if ((sample == 0) && (max_sample > 1)) |
3068 | 69 | (void) memset(q,0,image->columns*sizeof(PixelPacket)); |
3069 | 1.54M | if (ImportImagePixelArea(image,quantum_type,bits_per_sample,scanline, |
3070 | 1.54M | &import_options,0) == MagickFail) |
3071 | 3 | { |
3072 | 3 | CopyException(exception,&image->exception); |
3073 | 3 | status=MagickFail; |
3074 | 3 | break; |
3075 | 3 | } |
3076 | | /* |
3077 | | Disassociate alpha from pixels if necessary. |
3078 | | */ |
3079 | 1.54M | if ((PHOTOMETRIC_RGB == photometric) && |
3080 | 71.3k | (image->matte) && (alpha_type == AssociatedAlpha) && |
3081 | 12.5k | (sample == (max_sample-1))) |
3082 | 12.5k | DisassociateAlphaRegion(image); |
3083 | | /* |
3084 | | Save our updates. |
3085 | | */ |
3086 | 1.54M | if (!SyncImagePixelsEx(image,exception)) |
3087 | 0 | { |
3088 | 0 | status=MagickFail; |
3089 | 0 | break; |
3090 | 0 | } |
3091 | 1.54M | if (image->previous == (Image *) NULL) |
3092 | 1.54M | if (QuantumTick(y+(magick_int64_t)sample*image->rows, (magick_int64_t)image->rows*max_sample)) |
3093 | 137k | if (!MagickMonitorFormatted(y+ (magick_int64_t)sample*image->rows, |
3094 | 137k | (magick_int64_t)image->rows*max_sample,exception, |
3095 | 137k | LoadImageText,image->filename, |
3096 | 137k | image->columns,image->rows)) |
3097 | 0 | break; |
3098 | 1.54M | } |
3099 | 3.80k | } |
3100 | 3.80k | MagickFreeResourceLimitedMemory(unsigned char *,scanline); |
3101 | 3.80k | break; |
3102 | 3.80k | } |
3103 | 38.8k | case StrippedMethod: |
3104 | 38.8k | { |
3105 | | /* |
3106 | | Read TIFF image using multi-row strip storage. |
3107 | | */ |
3108 | 38.8k | unsigned char |
3109 | 38.8k | *p, |
3110 | 38.8k | *strip = (unsigned char *) NULL; |
3111 | | |
3112 | 38.8k | long |
3113 | | /* pixels_per_strip, */ |
3114 | 38.8k | stride, |
3115 | 38.8k | rows_remaining; |
3116 | | |
3117 | 38.8k | int |
3118 | 38.8k | max_sample, |
3119 | 38.8k | quantum_samples, |
3120 | 38.8k | sample; |
3121 | | |
3122 | 38.8k | tsize_t |
3123 | 38.8k | strip_size, |
3124 | 38.8k | strip_size_max = 0; |
3125 | | |
3126 | 38.8k | tstrip_t |
3127 | 38.8k | strip_id; |
3128 | | |
3129 | 38.8k | QuantumType |
3130 | 38.8k | quantum_type; |
3131 | | |
3132 | 38.8k | ImportPixelAreaInfo |
3133 | 38.8k | import_info; |
3134 | | |
3135 | 38.8k | if (logging) |
3136 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3137 | 0 | "Using stripped read method with %u bits per sample", |
3138 | 0 | bits_per_sample); |
3139 | | /* |
3140 | | Prepare for separate/contiguous retrieval. |
3141 | | */ |
3142 | 38.8k | max_sample=1; |
3143 | 38.8k | if (planar_config == PLANARCONFIG_SEPARATE) |
3144 | 2.63k | { |
3145 | 2.63k | if (QuantumTransferMode(image,photometric,compress_tag, |
3146 | 2.63k | sample_format, |
3147 | 2.63k | samples_per_pixel,PLANARCONFIG_CONTIG, |
3148 | 2.63k | 0,&quantum_type,&quantum_samples, |
3149 | 2.63k | exception) |
3150 | 2.63k | != MagickPass) |
3151 | 14 | ThrowTIFFReaderException(CorruptImageError, |
3152 | 2.63k | ImproperImageHeader,image); |
3153 | 2.61k | max_sample=quantum_samples; |
3154 | 2.61k | } |
3155 | | |
3156 | | /* pixels_per_strip=rows_per_strip*image->columns; */ |
3157 | 38.7k | p=0; |
3158 | 38.7k | strip_size=0; |
3159 | 38.7k | strip_id=0; |
3160 | | /* |
3161 | | Allocate memory for one strip. |
3162 | | */ |
3163 | 38.7k | strip_size_max=TIFFStripSize(tiff); |
3164 | 38.7k | if (strip_size_max <= 0) |
3165 | 0 | { |
3166 | 0 | status=MagickFail; |
3167 | 0 | break; |
3168 | 0 | } |
3169 | | /* |
3170 | | Scale up to size of 32-bit word. |
3171 | | */ |
3172 | 38.7k | strip_size_max=RoundUpToAlignment(strip_size_max,sizeof(magick_int32_t)); |
3173 | 38.7k | if (logging) |
3174 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3175 | 0 | "Maximum strip size %" MAGICK_SIZE_T_F "u", |
3176 | 0 | (MAGICK_SSIZE_T) strip_size_max); |
3177 | 38.7k | if (strip_size_max <= 0) |
3178 | 0 | { |
3179 | 0 | status=MagickFail; |
3180 | 0 | break; |
3181 | 0 | } |
3182 | | |
3183 | | /* |
3184 | | Rationalize memory request based on file size |
3185 | | */ |
3186 | 38.7k | if (strip_size_max > file_size*max_compress_ratio) |
3187 | 75 | { |
3188 | 75 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3189 | 75 | "Unreasonable allocation size " |
3190 | 75 | "(ratio of alloc to file size %g)", |
3191 | 75 | (double) strip_size_max/file_size); |
3192 | 75 | ThrowTIFFReaderException(CorruptImageError,InsufficientImageDataInFile, |
3193 | 0 | image); |
3194 | 0 | } |
3195 | | |
3196 | 38.7k | strip=MagickAllocateResourceLimitedClearedMemory(unsigned char *,(size_t) strip_size_max); |
3197 | 38.7k | if (strip == (unsigned char *) NULL) |
3198 | 0 | { |
3199 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3200 | 0 | image); |
3201 | 0 | } |
3202 | | /* |
3203 | | Compute per-row stride. |
3204 | | */ |
3205 | 38.7k | stride=TIFFVStripSize(tiff,1); |
3206 | | /* |
3207 | | Process each plane |
3208 | | */ |
3209 | 65.6k | for (sample=0; (status != MagickFail) && (sample < max_sample); sample++) |
3210 | 41.5k | { |
3211 | 41.5k | rows_remaining=0; |
3212 | | /* |
3213 | | Determine quantum parse method. |
3214 | | */ |
3215 | 41.5k | if (QuantumTransferMode(image,photometric,compress_tag, |
3216 | 41.5k | sample_format,samples_per_pixel, |
3217 | 41.5k | planar_config,sample, |
3218 | 41.5k | &quantum_type,&quantum_samples, |
3219 | 41.5k | exception) |
3220 | 41.5k | == MagickFail) |
3221 | 0 | { |
3222 | 0 | status=MagickFail; |
3223 | 0 | break; |
3224 | 0 | } |
3225 | 11.7M | for (y=0; (status != MagickFail) && (y < image->rows); y++) |
3226 | 11.7M | { |
3227 | 11.7M | if (rows_remaining == 0) |
3228 | 124k | { |
3229 | | /* |
3230 | | Obtain a strip |
3231 | | */ |
3232 | 124k | if (((strip_size=TIFFReadEncodedStrip(tiff,strip_id,strip, |
3233 | 124k | strip_size_max)) == -1)) |
3234 | 14.5k | { |
3235 | 14.5k | status=MagickFail; |
3236 | 14.5k | break; |
3237 | 14.5k | } |
3238 | 110k | #if !defined(WORDS_BIGENDIAN) |
3239 | 110k | if (24 == bits_per_sample) |
3240 | 2.68k | SwabDataToBigEndian(bits_per_sample,strip,strip_size); |
3241 | 110k | #endif |
3242 | 110k | rows_remaining=rows_per_strip; |
3243 | 110k | if (y+rows_per_strip > image->rows) |
3244 | 9.14k | rows_remaining=(rows_per_strip-(y+rows_per_strip-image->rows)); |
3245 | 110k | p=strip; |
3246 | 110k | strip_id++; |
3247 | 110k | } |
3248 | | /* |
3249 | | Compact strip row to only contain raster data. |
3250 | | */ |
3251 | 11.7M | if ((samples_per_pixel > quantum_samples) && |
3252 | 705k | (planar_config == PLANARCONFIG_CONTIG)) |
3253 | 591k | CompactSamples(image->columns, bits_per_sample, |
3254 | 591k | samples_per_pixel, quantum_samples, p); |
3255 | | /* |
3256 | | Access Magick pixels. |
3257 | | */ |
3258 | 11.7M | if (sample == 0) |
3259 | 11.6M | q=SetImagePixelsEx(image,0,y,image->columns,1,exception); |
3260 | 63.8k | else |
3261 | 63.8k | q=GetImagePixelsEx(image,0,y,image->columns,1,exception); |
3262 | 11.7M | if (q == (PixelPacket *) NULL) |
3263 | 11 | { |
3264 | 11 | status=MagickFail; |
3265 | 11 | break; |
3266 | 11 | } |
3267 | 11.7M | if ((sample == 0) && (max_sample > 1)) |
3268 | 29.6k | (void) memset(q,0,image->columns*sizeof(PixelPacket)); |
3269 | | /* |
3270 | | Import strip row into image. |
3271 | | */ |
3272 | 11.7M | if (ImportImagePixelArea(image,quantum_type,bits_per_sample,p, |
3273 | 11.7M | &import_options,&import_info) == MagickFail) |
3274 | 11 | { |
3275 | 11 | CopyException(exception,&image->exception); |
3276 | 11 | status=MagickFail; |
3277 | 11 | break; |
3278 | 11 | } |
3279 | | /* |
3280 | | Disassociate alpha from pixels if necessary. |
3281 | | */ |
3282 | 11.7M | if ((PHOTOMETRIC_RGB == photometric) && |
3283 | 2.49M | (image->matte) && (alpha_type == AssociatedAlpha) |
3284 | 485k | && (sample == (max_sample-1))) |
3285 | 483k | DisassociateAlphaRegion(image); |
3286 | | /* |
3287 | | Save our updates. |
3288 | | */ |
3289 | 11.7M | if (!SyncImagePixelsEx(image,exception)) |
3290 | 0 | { |
3291 | 0 | status=MagickFail; |
3292 | 0 | break; |
3293 | 0 | } |
3294 | | /* |
3295 | | Advance to next row |
3296 | | */ |
3297 | 11.7M | p += stride; |
3298 | 11.7M | rows_remaining--; |
3299 | | |
3300 | 11.7M | if (image->previous == (Image *) NULL) |
3301 | 11.7M | if (QuantumTick(y+(magick_int64_t)image->rows*sample, (magick_int64_t)image->rows*max_sample)) |
3302 | 1.59M | if (!MagickMonitorFormatted(y+ (magick_int64_t)image->rows*sample, (magick_int64_t)image->rows*max_sample,exception, |
3303 | 1.59M | LoadImageText,image->filename, |
3304 | 1.59M | image->columns,image->rows)) |
3305 | 0 | { |
3306 | 0 | status=MagickFail; |
3307 | 0 | break; |
3308 | 0 | } |
3309 | 11.7M | } |
3310 | 41.5k | if (status == MagickFail) |
3311 | 14.5k | break; |
3312 | 41.5k | } |
3313 | 38.7k | MagickFreeResourceLimitedMemory(unsigned char *,strip); |
3314 | 38.7k | break; |
3315 | 38.7k | } |
3316 | 4.38k | case TiledMethod: |
3317 | 4.38k | { |
3318 | | /* |
3319 | | Read TIFF using tiled storage. |
3320 | | */ |
3321 | 4.38k | unsigned char |
3322 | 4.38k | *tile = (unsigned char *) NULL; |
3323 | | |
3324 | 4.38k | uint32 |
3325 | 4.38k | tile_columns, |
3326 | 4.38k | tile_rows; |
3327 | | |
3328 | 4.38k | tsize_t |
3329 | 4.38k | stride, |
3330 | 4.38k | tile_size, |
3331 | 4.38k | tile_size_max = 0; |
3332 | | |
3333 | 4.38k | int |
3334 | 4.38k | max_sample, |
3335 | 4.38k | quantum_samples, |
3336 | 4.38k | sample; |
3337 | | |
3338 | 4.38k | QuantumType |
3339 | 4.38k | quantum_type; |
3340 | | |
3341 | 4.38k | size_t |
3342 | 4.38k | tile_total_pixels, |
3343 | 4.38k | tile_num=0, |
3344 | 4.38k | tiles_total; |
3345 | | |
3346 | 4.38k | if (logging) |
3347 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3348 | 0 | "Using tiled %s read method with %u bits per sample", |
3349 | 0 | PhotometricTagToString(photometric), bits_per_sample); |
3350 | | /* |
3351 | | Prepare for separate/contiguous retrieval. |
3352 | | */ |
3353 | 4.38k | max_sample=1; |
3354 | 4.38k | if (planar_config == PLANARCONFIG_SEPARATE) |
3355 | 99 | { |
3356 | 99 | if (QuantumTransferMode(image,photometric,compress_tag, |
3357 | 99 | sample_format,samples_per_pixel, |
3358 | 99 | PLANARCONFIG_CONTIG,0,&quantum_type, |
3359 | 99 | &quantum_samples, |
3360 | 99 | exception) |
3361 | 99 | != MagickPass) |
3362 | 4 | ThrowTIFFReaderException(CorruptImageError, |
3363 | 99 | ImproperImageHeader,image); |
3364 | 95 | max_sample=quantum_samples; |
3365 | 95 | } |
3366 | | /* |
3367 | | Obtain tile geometry |
3368 | | */ |
3369 | 4.38k | if (!(TIFFGetField(tiff,TIFFTAG_TILEWIDTH,&tile_columns) == 1) || |
3370 | 4.38k | !(TIFFGetField(tiff,TIFFTAG_TILELENGTH,&tile_rows) == 1)) |
3371 | 0 | { |
3372 | 0 | ThrowTIFFReaderException(CoderError,ImageIsNotTiled,image); |
3373 | 0 | } |
3374 | | /* |
3375 | | Obtain the maximum number of bytes required to contain a tile. |
3376 | | */ |
3377 | 4.38k | tile_size_max=TIFFTileSize(tiff); |
3378 | 4.38k | if (tile_size_max <= 0) |
3379 | 1 | { |
3380 | 1 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3381 | 0 | image); |
3382 | 0 | } |
3383 | | /* |
3384 | | Scale up to size of 32-bit word. |
3385 | | */ |
3386 | 4.37k | tile_size_max=RoundUpToAlignment(tile_size_max,sizeof(magick_int32_t)); |
3387 | 4.37k | if (0 == tile_size_max) |
3388 | 0 | { |
3389 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3390 | 0 | image); |
3391 | 0 | } |
3392 | | /* |
3393 | | Compute the total number of pixels in one tile |
3394 | | */ |
3395 | 4.37k | tile_total_pixels=MagickArraySize(tile_columns,tile_rows); |
3396 | 4.37k | if (logging) |
3397 | 0 | { |
3398 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3399 | 0 | "TIFF tile geometry %ux%u, " |
3400 | 0 | "%" MAGICK_SIZE_T_F "u pixels" |
3401 | 0 | " (%" MAGICK_SIZE_T_F "u bytes max)", |
3402 | 0 | (unsigned int)tile_columns, |
3403 | 0 | (unsigned int)tile_rows, |
3404 | 0 | (MAGICK_SIZE_T) tile_total_pixels, |
3405 | 0 | (MAGICK_SIZE_T) tile_size_max); |
3406 | 0 | } |
3407 | | |
3408 | | /* |
3409 | | Rationalize memory request based on file size |
3410 | | */ |
3411 | 4.37k | if (tile_size_max > file_size*max_compress_ratio) |
3412 | 111 | { |
3413 | 111 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3414 | 111 | "Unreasonable tile allocation size " |
3415 | 111 | "(ratio of alloc to file size %g)", |
3416 | 111 | (double) tile_size_max/file_size); |
3417 | 111 | ThrowTIFFReaderException(CorruptImageError,InsufficientImageDataInFile, |
3418 | 0 | image); |
3419 | 0 | } |
3420 | | |
3421 | 4.26k | if ((tile_columns > 1024) && (tile_columns > image->columns*10)) |
3422 | 5 | { |
3423 | 5 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3424 | 5 | "Unreasonable tile columns %u", |
3425 | 5 | (unsigned) tile_columns); |
3426 | 5 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader, |
3427 | 0 | image); |
3428 | 0 | } |
3429 | | |
3430 | 4.26k | if ((tile_rows > 1024) && (tile_rows > image->rows*10)) |
3431 | 6 | { |
3432 | 6 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3433 | 6 | "Unreasonable tile rows %u", |
3434 | 6 | (unsigned) tile_rows); |
3435 | 6 | ThrowTIFFReaderException(CorruptImageError,ImproperImageHeader, |
3436 | 0 | image); |
3437 | 0 | } |
3438 | | |
3439 | | /* |
3440 | | Allocate tile buffer |
3441 | | */ |
3442 | 4.25k | tile=MagickAllocateResourceLimitedClearedMemory(unsigned char *, (size_t) tile_size_max); |
3443 | 4.25k | if (tile == (unsigned char *) NULL) |
3444 | 0 | { |
3445 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3446 | 0 | image); |
3447 | 0 | } |
3448 | 4.25k | tiles_total=((((size_t) image->columns/tile_columns)+((image->columns % tile_columns) ? 1 : 0)) |
3449 | 4.25k | *(((size_t) image->rows/tile_rows)+((image->rows % tile_rows) ? 1 : 0)))*max_sample; |
3450 | | /* |
3451 | | Compute per-row stride. |
3452 | | */ |
3453 | 4.25k | stride=TIFFTileRowSize(tiff); |
3454 | | |
3455 | | /* |
3456 | | Process each plane. |
3457 | | */ |
3458 | 5.81k | for (sample=0; (status != MagickFail) && (sample < max_sample); sample++) |
3459 | 4.32k | { |
3460 | | /* |
3461 | | Determine quantum parse method. |
3462 | | */ |
3463 | 4.32k | if (QuantumTransferMode(image,photometric,compress_tag, |
3464 | 4.32k | sample_format,samples_per_pixel, |
3465 | 4.32k | planar_config,sample,&quantum_type, |
3466 | 4.32k | &quantum_samples,exception) |
3467 | 4.32k | == MagickFail) |
3468 | 0 | { |
3469 | 0 | status=MagickFail; |
3470 | 0 | break; |
3471 | 0 | } |
3472 | 23.6k | for (y=0; (status != MagickFail) && (y < image->rows); y+=tile_rows) |
3473 | 22.0k | { |
3474 | 1.64M | for (x=0; (status != MagickFail) && (x < image->columns); x+=tile_columns) |
3475 | 1.62M | { |
3476 | 1.62M | long |
3477 | 1.62M | tile_set_columns, |
3478 | 1.62M | tile_set_rows; |
3479 | | |
3480 | 1.62M | unsigned char |
3481 | 1.62M | *p; |
3482 | | |
3483 | 1.62M | register long |
3484 | 1.62M | yy; |
3485 | | |
3486 | | /* |
3487 | | Compute image region corresponding to tile. |
3488 | | */ |
3489 | 1.62M | if (x+tile_columns > image->columns) |
3490 | 11.1k | tile_set_columns=(tile_columns-(x+tile_columns-image->columns)); |
3491 | 1.61M | else |
3492 | 1.61M | tile_set_columns=tile_columns; |
3493 | 1.62M | if (y+tile_rows > image->rows) |
3494 | 17.8k | tile_set_rows=(tile_rows-(y+tile_rows-image->rows)); |
3495 | 1.61M | else |
3496 | 1.61M | tile_set_rows=tile_rows; |
3497 | | |
3498 | | /* |
3499 | | Read a tile. |
3500 | | */ |
3501 | 1.62M | tile_num++; |
3502 | 1.62M | if ((tile_size=TIFFReadTile(tiff,tile,x,y,0,sample)) == -1) |
3503 | 2.39k | { |
3504 | 2.39k | status=MagickFail; |
3505 | 2.39k | break; |
3506 | 2.39k | } |
3507 | 1.62M | #if !defined(WORDS_BIGENDIAN) |
3508 | 1.62M | if (24 == bits_per_sample) |
3509 | 316 | SwabDataToBigEndian(bits_per_sample,tile,tile_size); |
3510 | 1.62M | #endif |
3511 | 1.62M | p=tile; |
3512 | 11.1M | for (yy=y; (status != MagickFail) && (yy < (long) y+tile_set_rows); yy++) |
3513 | 9.48M | { |
3514 | | /* |
3515 | | Compact tile row to only contain raster data. |
3516 | | */ |
3517 | 9.48M | if ((samples_per_pixel > quantum_samples) && |
3518 | 1.79M | (planar_config == PLANARCONFIG_CONTIG)) |
3519 | 1.79M | CompactSamples(tile_set_columns, bits_per_sample, |
3520 | 1.79M | samples_per_pixel, quantum_samples, p); |
3521 | | /* |
3522 | | Obtain pixel region corresponding to tile row. |
3523 | | */ |
3524 | 9.48M | if (sample == 0) |
3525 | 9.48M | q=SetImagePixelsEx(image,x,yy,tile_set_columns,1,exception); |
3526 | 1.67k | else |
3527 | 1.67k | q=GetImagePixelsEx(image,x,yy,tile_set_columns,1,exception); |
3528 | 9.48M | if (q == (PixelPacket *) NULL) |
3529 | 377 | { |
3530 | 377 | status=MagickFail; |
3531 | 377 | break; |
3532 | 377 | } |
3533 | 9.48M | if ((sample == 0) && (max_sample > 1)) |
3534 | 1.45k | (void) memset(q,0,tile_set_columns*sizeof(PixelPacket)); |
3535 | | /* |
3536 | | Import tile row |
3537 | | */ |
3538 | 9.48M | if (ImportImagePixelArea(image,quantum_type, |
3539 | 9.48M | bits_per_sample,p, |
3540 | 9.48M | &import_options,0) |
3541 | 9.48M | == MagickFail) |
3542 | 5 | { |
3543 | 5 | CopyException(exception,&image->exception); |
3544 | 5 | status=MagickFail; |
3545 | 5 | break; |
3546 | 5 | } |
3547 | | /* |
3548 | | Disassociate alpha from pixels if necessary. |
3549 | | */ |
3550 | 9.48M | if ((PHOTOMETRIC_RGB == photometric) && |
3551 | 58.1k | (image->matte) && (alpha_type == AssociatedAlpha) |
3552 | 1.45k | && (sample == (max_sample-1))) |
3553 | 1.18k | DisassociateAlphaRegion(image); |
3554 | | /* |
3555 | | Save our updates. |
3556 | | */ |
3557 | 9.48M | if (!SyncImagePixelsEx(image,exception)) |
3558 | 0 | { |
3559 | 0 | status=MagickFail; |
3560 | 0 | break; |
3561 | 0 | } |
3562 | 9.48M | p += stride; |
3563 | 9.48M | } |
3564 | 1.62M | if (image->previous == (Image *) NULL) |
3565 | 1.62M | if (QuantumTick(tile_num,tiles_total)) |
3566 | 21.6k | if (!MagickMonitorFormatted(tile_num, |
3567 | 21.6k | tiles_total, |
3568 | 21.6k | exception, |
3569 | 21.6k | LoadImageText,image->filename, |
3570 | 21.6k | image->columns,image->rows)) |
3571 | 0 | { |
3572 | 0 | status=MagickFail; |
3573 | 0 | } |
3574 | 1.62M | if (status == MagickFail) |
3575 | 382 | break; |
3576 | 1.62M | } |
3577 | 22.0k | if (status == MagickFail) |
3578 | 2.77k | break; |
3579 | 22.0k | } |
3580 | 4.32k | if (status == MagickFail) |
3581 | 2.77k | break; |
3582 | 4.32k | } |
3583 | | |
3584 | 4.25k | MagickFreeResourceLimitedMemory(unsigned char *,tile); |
3585 | 4.25k | break; |
3586 | 4.25k | } |
3587 | 3.70k | case RGBAStrippedMethod: |
3588 | 3.70k | { |
3589 | 3.70k | size_t |
3590 | 3.70k | number_pixels, |
3591 | 3.70k | strip_pixels_size; |
3592 | | |
3593 | 3.70k | uint32 |
3594 | 3.70k | *strip_pixels; |
3595 | | |
3596 | 3.70k | register uint32 |
3597 | 3.70k | *p; |
3598 | | |
3599 | 3.70k | if (logging) |
3600 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3601 | 0 | "Using RGB stripped read method with %u bits per sample", |
3602 | 0 | bits_per_sample); |
3603 | | /* |
3604 | | Convert stripped TIFF image to DirectClass MIFF image. |
3605 | | */ |
3606 | 3.70k | image->storage_class=DirectClass; |
3607 | 3.70k | number_pixels=MagickArraySize(image->columns,rows_per_strip); |
3608 | 3.70k | if (0 == number_pixels) |
3609 | 0 | { |
3610 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3611 | 0 | image); |
3612 | 0 | } |
3613 | | /* |
3614 | | Rationalize memory request based on file size |
3615 | | */ |
3616 | 3.70k | if ((magick_off_t) (number_pixels*sizeof(uint32)) > |
3617 | 3.70k | file_size*max_compress_ratio) |
3618 | 49 | { |
3619 | 49 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3620 | 49 | "Unreasonable allocation size " |
3621 | 49 | "(ratio of alloc to file size %g)", |
3622 | 49 | (double) (number_pixels*sizeof(uint32))/file_size); |
3623 | 49 | ThrowTIFFReaderException(CorruptImageError,InsufficientImageDataInFile, |
3624 | 0 | image); |
3625 | 0 | } |
3626 | 3.65k | strip_pixels_size=MagickArraySize(number_pixels,sizeof(uint32)); |
3627 | 3.65k | if (strip_pixels_size == 0) |
3628 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3629 | 3.65k | image); |
3630 | 3.65k | strip_pixels=MagickAllocateResourceLimitedClearedMemory(uint32 *,strip_pixels_size); |
3631 | 3.65k | if (strip_pixels == (uint32 *) NULL) |
3632 | 3 | { |
3633 | 3 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3634 | 0 | image); |
3635 | 0 | } |
3636 | 3.65k | if (logging) |
3637 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3638 | 0 | "Allocated %" MAGICK_SIZE_T_F "u bytes for RGBA strip", |
3639 | 0 | (MAGICK_SIZE_T) number_pixels*sizeof(uint32)); |
3640 | | /* |
3641 | | Convert image to DirectClass pixel packets. |
3642 | | */ |
3643 | 3.65k | i=0; |
3644 | 3.65k | p=0; |
3645 | 458k | for (y=0; (status != MagickFail) && (y < image->rows); y++) |
3646 | 457k | { |
3647 | 457k | q=SetImagePixelsEx(image,0,y,image->columns,1,exception); |
3648 | 457k | if (q == (PixelPacket *) NULL) |
3649 | 0 | { |
3650 | 0 | status=MagickFail; |
3651 | 0 | break; |
3652 | 0 | } |
3653 | 457k | if (0 == i) |
3654 | 68.5k | { |
3655 | 68.5k | if (!TIFFReadRGBAStrip(tiff,y,strip_pixels)) |
3656 | 2.52k | { |
3657 | 2.52k | if (logging) |
3658 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3659 | 0 | "TIFFReadRGBAStrip reports failure"); |
3660 | 2.52k | status=MagickFail; |
3661 | 2.52k | break; |
3662 | 2.52k | } |
3663 | 65.9k | i=(long) Min(rows_per_strip,image->rows-y); |
3664 | 65.9k | } |
3665 | 454k | i--; |
3666 | 454k | p=strip_pixels+(size_t) image->columns*i; |
3667 | 209M | for (x=0; (status != MagickFail) && (x < image->columns); x++) |
3668 | 208M | { |
3669 | 208M | q->red=ScaleCharToQuantum(TIFFGetR(*p)); |
3670 | 208M | q->green=ScaleCharToQuantum(TIFFGetG(*p)); |
3671 | 208M | q->blue=ScaleCharToQuantum(TIFFGetB(*p)); |
3672 | 208M | if (image->matte) |
3673 | 1.33M | q->opacity=(Quantum) ScaleCharToQuantum(TIFFGetA(*p)); |
3674 | 207M | else |
3675 | 207M | q->opacity=OpaqueOpacity; |
3676 | 208M | p++; |
3677 | 208M | q++; |
3678 | 208M | } |
3679 | | /* |
3680 | | Disassociate alpha from pixels if necessary. |
3681 | | */ |
3682 | 454k | if ((PHOTOMETRIC_RGB == photometric) && |
3683 | 19.7k | (image->matte) && (alpha_type == AssociatedAlpha)) |
3684 | 775 | DisassociateAlphaRegion(image); |
3685 | 454k | if (!SyncImagePixelsEx(image,exception)) |
3686 | 0 | { |
3687 | 0 | status=MagickFail; |
3688 | 0 | break; |
3689 | 0 | } |
3690 | 454k | if (image->previous == (Image *) NULL) |
3691 | 454k | if (QuantumTick(y,image->rows)) |
3692 | 145k | if (!MagickMonitorFormatted(y,image->rows,exception, |
3693 | 145k | LoadImageText,image->filename, |
3694 | 145k | image->columns,image->rows)) |
3695 | 0 | { |
3696 | 0 | status=MagickFail; |
3697 | 0 | break; |
3698 | 0 | } |
3699 | 454k | } |
3700 | 3.65k | MagickFreeResourceLimitedMemory(uint32 *,strip_pixels); |
3701 | 3.65k | break; |
3702 | 3.65k | } |
3703 | 1.20k | case RGBATiledMethod: |
3704 | 1.20k | { |
3705 | | /* |
3706 | | Convert tiled TIFF image to DirectClass MIFF image. |
3707 | | */ |
3708 | 1.20k | register uint32 |
3709 | 1.20k | *p; |
3710 | | |
3711 | 1.20k | uint32 |
3712 | 1.20k | *tile_pixels, |
3713 | 1.20k | tile_columns, |
3714 | 1.20k | tile_rows; |
3715 | | |
3716 | 1.20k | tsize_t |
3717 | 1.20k | tile_size_max; |
3718 | | |
3719 | 1.20k | size_t |
3720 | 1.20k | tile_pixels_size, |
3721 | 1.20k | tile_total_pixels, |
3722 | 1.20k | tile_num=0, |
3723 | 1.20k | tiles_total; |
3724 | | |
3725 | 1.20k | if (logging) |
3726 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3727 | 0 | "Using RGB tiled read method with %u bits per sample", |
3728 | 0 | bits_per_sample); |
3729 | 1.20k | image->storage_class=DirectClass; |
3730 | | /* |
3731 | | Obtain tile geometry |
3732 | | */ |
3733 | 1.20k | if (!(TIFFGetField(tiff,TIFFTAG_TILEWIDTH,&tile_columns) == 1) || |
3734 | 1.20k | !(TIFFGetField(tiff,TIFFTAG_TILELENGTH,&tile_rows) == 1)) |
3735 | 0 | { |
3736 | 0 | ThrowTIFFReaderException(CoderError,ImageIsNotTiled,image); |
3737 | 0 | } |
3738 | 1.20k | tile_total_pixels=MagickArraySize(tile_columns,tile_rows); |
3739 | 1.20k | tiles_total=((((size_t) image->columns/tile_columns)+((image->columns % tile_columns) ? 1 : 0)) |
3740 | 1.20k | *(((size_t) image->rows/tile_rows)+((image->rows % tile_rows) ? 1 : 0))); |
3741 | 1.20k | if (logging) |
3742 | 0 | { |
3743 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Reading TIFF tiles ..."); |
3744 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3745 | 0 | "TIFF tile geometry %ux%u, %" MAGICK_SIZE_T_F "u pixels/tile" |
3746 | 0 | ", %" MAGICK_SIZE_T_F "u tiles", |
3747 | 0 | (unsigned int)tile_columns, |
3748 | 0 | (unsigned int)tile_rows, |
3749 | 0 | (MAGICK_SIZE_T) tile_total_pixels, |
3750 | 0 | (MAGICK_SIZE_T) tiles_total); |
3751 | 0 | } |
3752 | | /* |
3753 | | Obtain the maximum number of bytes required to contain a tile. |
3754 | | */ |
3755 | 1.20k | tile_size_max=TIFFTileSize(tiff); |
3756 | 1.20k | if (tile_size_max <= 0) |
3757 | 0 | { |
3758 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3759 | 0 | image); |
3760 | 0 | } |
3761 | | /* |
3762 | | Apply image resource limits to tile width, height, and pixels. |
3763 | | */ |
3764 | 1.20k | errno=0; |
3765 | 1.20k | if (AcquireMagickResource(WidthResource,tile_columns) != MagickPass) |
3766 | 54 | { |
3767 | 54 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3768 | 54 | "TIFF tile width %u exceeds limit!",tile_columns); |
3769 | 54 | ThrowTIFFReaderException(ResourceLimitError,ImagePixelWidthLimitExceeded, |
3770 | 0 | image); |
3771 | 0 | } |
3772 | 1.15k | if (AcquireMagickResource(HeightResource,tile_rows) != MagickPass) |
3773 | 6 | { |
3774 | 6 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3775 | 6 | "TIFF tile width %u exceeds limit!",tile_rows); |
3776 | 6 | ThrowTIFFReaderException(ResourceLimitError,ImagePixelHeightLimitExceeded, |
3777 | 0 | image); |
3778 | 0 | } |
3779 | 1.14k | if (AcquireMagickResource(PixelsResource,tile_total_pixels) != MagickPass) |
3780 | 0 | { |
3781 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3782 | 0 | "TIFF tile pixels %" MAGICK_SIZE_T_F "u exceeds limit!", |
3783 | 0 | (MAGICK_SIZE_T) tile_total_pixels); |
3784 | 0 | ThrowTIFFReaderException(ResourceLimitError,ImagePixelLimitExceeded, |
3785 | 0 | image); |
3786 | 0 | } |
3787 | | /* |
3788 | | Rationalize memory request based on file size |
3789 | | */ |
3790 | 1.14k | if (tile_size_max > file_size*max_compress_ratio) |
3791 | 5 | { |
3792 | 5 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3793 | 5 | "Unreasonable tile allocation size " |
3794 | 5 | "(ratio of alloc to file size %g)", |
3795 | 5 | (double) tile_size_max/file_size); |
3796 | 5 | ThrowTIFFReaderException(CorruptImageError,InsufficientImageDataInFile, |
3797 | 0 | image); |
3798 | 0 | } |
3799 | | /* |
3800 | | Allocate tile buffer |
3801 | | */ |
3802 | 1.14k | tile_pixels_size=MagickArraySize(MagickArraySize(tile_columns,tile_rows), |
3803 | 1.14k | sizeof (uint32)); |
3804 | 1.14k | if (tile_pixels_size == 0) |
3805 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3806 | 1.14k | image); |
3807 | 1.14k | tile_pixels=MagickAllocateResourceLimitedMemory(uint32*,tile_pixels_size); |
3808 | 1.14k | if (tile_pixels == (uint32 *) NULL) |
3809 | 0 | { |
3810 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3811 | 0 | image); |
3812 | 0 | } |
3813 | 17.9k | for (y=0; (status != MagickFail) && (y < image->rows); y+=tile_rows) |
3814 | 17.6k | { |
3815 | | /* |
3816 | | Retrieve a tile height's worth of rows |
3817 | | */ |
3818 | 17.6k | PixelPacket |
3819 | 17.6k | *strip; |
3820 | | |
3821 | 17.6k | unsigned int |
3822 | 17.6k | tile_columns_remaining, |
3823 | 17.6k | tile_rows_remaining; |
3824 | | |
3825 | | /* Compute remaining tile rows */ |
3826 | 17.6k | if (y+tile_rows < image->rows) |
3827 | 17.1k | tile_rows_remaining=tile_rows; |
3828 | 526 | else |
3829 | 526 | tile_rows_remaining=image->rows-y; |
3830 | | /* |
3831 | | Obtain a row of pixels |
3832 | | */ |
3833 | 17.6k | strip=SetImagePixelsEx(image,0,y,image->columns,tile_rows_remaining,exception); |
3834 | 17.6k | if (strip == (PixelPacket *) NULL) |
3835 | 0 | { |
3836 | 0 | status=MagickFail; |
3837 | 0 | break; |
3838 | 0 | } |
3839 | 753k | for (x=0; (status != MagickFail) && (x < image->columns); x+=tile_columns) |
3840 | 736k | { |
3841 | 736k | register unsigned int |
3842 | 736k | tile_column, |
3843 | 736k | tile_row; |
3844 | | |
3845 | | /* |
3846 | | Obtain one tile. Origin is bottom left of tile. |
3847 | | */ |
3848 | 736k | tile_num++; |
3849 | 736k | if (!TIFFReadRGBATile(tiff,x,y,tile_pixels)) |
3850 | 838 | { |
3851 | 838 | status=MagickFail; |
3852 | 838 | break; |
3853 | 838 | } |
3854 | | /* |
3855 | | Compute remaining tile columns |
3856 | | */ |
3857 | 735k | if (x+tile_columns < image->columns) |
3858 | 719k | tile_columns_remaining=tile_columns; |
3859 | 16.8k | else |
3860 | 16.8k | tile_columns_remaining=image->columns-x; |
3861 | | /* |
3862 | | Transfer tile to image |
3863 | | */ |
3864 | 735k | p=tile_pixels+(size_t)(tile_rows-tile_rows_remaining)*tile_columns; |
3865 | 735k | q=strip+(x+(size_t)(tile_rows_remaining-1)*image->columns); |
3866 | 16.2M | for ( tile_row=tile_rows_remaining; tile_row != 0; tile_row--) |
3867 | 15.5M | { |
3868 | 15.5M | if (image->matte) |
3869 | 268k | for (tile_column=tile_columns_remaining; tile_column != 0; |
3870 | 262k | tile_column--) |
3871 | 262k | { |
3872 | 262k | q->red=ScaleCharToQuantum(TIFFGetR(*p)); |
3873 | 262k | q->green=ScaleCharToQuantum(TIFFGetG(*p)); |
3874 | 262k | q->blue=ScaleCharToQuantum(TIFFGetB(*p)); |
3875 | 262k | q->opacity=(Quantum) ScaleCharToQuantum(TIFFGetA(*p)); |
3876 | 262k | q++; |
3877 | 262k | p++; |
3878 | 262k | } |
3879 | 15.5M | else |
3880 | 40.0M | for (tile_column=tile_columns_remaining; tile_column != 0; |
3881 | 24.4M | tile_column--) |
3882 | 24.4M | { |
3883 | 24.4M | q->red=ScaleCharToQuantum(TIFFGetR(*p)); |
3884 | 24.4M | q->green=ScaleCharToQuantum(TIFFGetG(*p)); |
3885 | 24.4M | q->blue=ScaleCharToQuantum(TIFFGetB(*p)); |
3886 | 24.4M | q->opacity=OpaqueOpacity; |
3887 | 24.4M | q++; |
3888 | 24.4M | p++; |
3889 | 24.4M | } |
3890 | 15.5M | p+=tile_columns-tile_columns_remaining; |
3891 | 15.5M | q-=((size_t) image->columns+tile_columns_remaining); |
3892 | 15.5M | } |
3893 | 735k | if (image->previous == (Image *) NULL) |
3894 | 735k | if (QuantumTick(tile_num,tiles_total)) |
3895 | 18.6k | if (!MagickMonitorFormatted(tile_num, |
3896 | 18.6k | tiles_total, |
3897 | 18.6k | exception, |
3898 | 18.6k | LoadImageText,image->filename, |
3899 | 18.6k | image->columns,image->rows)) |
3900 | 0 | { |
3901 | 0 | status=MagickFail; |
3902 | 0 | } |
3903 | 735k | if (status == MagickFail) |
3904 | 0 | break; |
3905 | 735k | } |
3906 | 17.6k | if (status == MagickFail) |
3907 | 838 | break; |
3908 | | /* |
3909 | | Disassociate alpha from pixels if necessary. |
3910 | | */ |
3911 | 16.8k | if ((PHOTOMETRIC_RGB == photometric) && |
3912 | 253 | (image->matte) && (alpha_type == AssociatedAlpha)) |
3913 | 3 | DisassociateAlphaRegion(image); |
3914 | 16.8k | if (!SyncImagePixelsEx(image,exception)) |
3915 | 0 | { |
3916 | 0 | status=MagickFail; |
3917 | 0 | break; |
3918 | 0 | } |
3919 | 16.8k | } |
3920 | 1.14k | MagickFreeResourceLimitedMemory(uint32 *,tile_pixels); |
3921 | 1.14k | break; |
3922 | 1.14k | } |
3923 | 2.50k | case RGBAPuntMethod: |
3924 | 2.50k | default: |
3925 | 2.50k | { |
3926 | 2.50k | register uint32 |
3927 | 2.50k | *p; |
3928 | | |
3929 | 2.50k | uint32 |
3930 | 2.50k | *pixels; |
3931 | | |
3932 | 2.50k | size_t |
3933 | 2.50k | number_pixels, |
3934 | 2.50k | pixels_size; |
3935 | | |
3936 | 2.50k | if (logging) |
3937 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3938 | 0 | "Using RGB punt read method with %u bits per sample", |
3939 | 0 | bits_per_sample); |
3940 | | |
3941 | | |
3942 | | /* |
3943 | | TIFFReadRGBAImage() does not handle more than 4 samples per pixel. |
3944 | | */ |
3945 | 2.50k | if (samples_per_pixel > 4) |
3946 | 10 | { |
3947 | 10 | ThrowTIFFReaderException(CoderError,UnsupportedSamplesPerPixel,image); |
3948 | 0 | } |
3949 | | |
3950 | | /* |
3951 | | Convert TIFF image to DirectClass MIFF image. |
3952 | | */ |
3953 | 2.49k | image->storage_class=DirectClass; |
3954 | 2.49k | number_pixels=MagickArraySize(image->columns,image->rows); |
3955 | 2.49k | if (number_pixels == 0) |
3956 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3957 | 2.49k | image); |
3958 | | |
3959 | | |
3960 | | /* |
3961 | | TIFFReadRGBAImage reads a strip- or tile-based image |
3962 | | into memory, storing the result in the user supplied |
3963 | | raster. The raster is assumed to be an array of width |
3964 | | times height 32-bit entries, where width must be less |
3965 | | than or equal to the width of the image (height may be |
3966 | | any non-zero size). If the raster dimensions are smaller |
3967 | | than the image, the image data is cropped to the raster |
3968 | | bounds. If the raster height is greater than that of |
3969 | | the image, then the image data are placed in the lower |
3970 | | part of the raster. (Note that the raster is assumed to |
3971 | | be organized such that the pixel at location (x,y) is |
3972 | | raster[y*width+x]; with the raster origin in the |
3973 | | lower-left hand corner.) |
3974 | | |
3975 | | Please note that this allocation used to be |
3976 | | (number_pixels+6*image->columns)*sizeof(uint32) for |
3977 | | unknown reasons. |
3978 | | */ |
3979 | 2.49k | pixels_size=MagickArraySize(number_pixels,sizeof(uint32)); |
3980 | 2.49k | if (pixels_size == 0) |
3981 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3982 | 2.49k | image); |
3983 | 2.49k | pixels=MagickAllocateResourceLimitedMemory(uint32 *,pixels_size); |
3984 | 2.49k | if (pixels == (uint32 *) NULL) |
3985 | 0 | { |
3986 | 0 | ThrowTIFFReaderException(ResourceLimitError,MemoryAllocationFailed, |
3987 | 0 | image); |
3988 | 0 | } |
3989 | 2.49k | if (logging) |
3990 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3991 | 0 | "Dispatching to TIFFReadRGBAImage()"); |
3992 | 2.49k | if (!TIFFReadRGBAImage(tiff,(uint32) image->columns, |
3993 | 2.49k | (uint32) image->rows, |
3994 | 2.49k | pixels,0)) |
3995 | 430 | { |
3996 | 430 | if (logging) |
3997 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3998 | 0 | "TIFFReadRGBAImage() returns failure!"); |
3999 | 430 | MagickFreeResourceLimitedMemory(uint32 *,pixels); |
4000 | 430 | status=MagickFail; |
4001 | 430 | break; |
4002 | 430 | } |
4003 | | /* |
4004 | | Convert image to DirectClass pixel packets. |
4005 | | */ |
4006 | 2.06k | p=pixels+number_pixels-1; |
4007 | 446k | for (y=0; y < image->rows; y++) |
4008 | 444k | { |
4009 | 444k | q=SetImagePixelsEx(image,0,y,image->columns,1,exception); |
4010 | 444k | if (q == (PixelPacket *) NULL) |
4011 | 0 | { |
4012 | 0 | status=MagickFail; |
4013 | 0 | break; |
4014 | 0 | } |
4015 | 444k | q+=image->columns-1; |
4016 | 444k | if (image->matte) |
4017 | 3.19M | for (x=(long) image->columns; x != 0; x--) |
4018 | 3.17M | { |
4019 | 3.17M | q->red=ScaleCharToQuantum(TIFFGetR(*p)); |
4020 | 3.17M | q->green=ScaleCharToQuantum(TIFFGetG(*p)); |
4021 | 3.17M | q->blue=ScaleCharToQuantum(TIFFGetB(*p)); |
4022 | 3.17M | q->opacity=(Quantum) ScaleCharToQuantum(TIFFGetA(*p)); |
4023 | 3.17M | p--; |
4024 | 3.17M | q--; |
4025 | 3.17M | } |
4026 | 428k | else |
4027 | 297M | for (x=(long) image->columns; x != 0; x--) |
4028 | 297M | { |
4029 | 297M | q->red=ScaleCharToQuantum(TIFFGetR(*p)); |
4030 | 297M | q->green=ScaleCharToQuantum(TIFFGetG(*p)); |
4031 | 297M | q->blue=ScaleCharToQuantum(TIFFGetB(*p)); |
4032 | 297M | q->opacity=OpaqueOpacity; |
4033 | 297M | p--; |
4034 | 297M | q--; |
4035 | 297M | } |
4036 | | /* |
4037 | | Disassociate alpha from pixels if necessary. |
4038 | | */ |
4039 | 444k | if ((PHOTOMETRIC_RGB == photometric) && |
4040 | 25.9k | (image->matte) && (alpha_type == AssociatedAlpha)) |
4041 | 1.26k | DisassociateAlphaRegion(image); |
4042 | 444k | if (!SyncImagePixelsEx(image,exception)) |
4043 | 0 | { |
4044 | 0 | status=MagickFail; |
4045 | 0 | break; |
4046 | 0 | } |
4047 | 444k | if (image->previous == (Image *) NULL) |
4048 | 444k | if (QuantumTick(y,image->rows)) |
4049 | 155k | if (!MagickMonitorFormatted(y,image->rows,exception, |
4050 | 155k | LoadImageText,image->filename, |
4051 | 155k | image->columns,image->rows)) |
4052 | 0 | { |
4053 | 0 | status=MagickFail; |
4054 | 0 | break; |
4055 | 0 | } |
4056 | 444k | if (status == MagickFail) |
4057 | 0 | break; |
4058 | 444k | } |
4059 | 2.06k | MagickFreeResourceLimitedMemory(uint32 *,pixels); |
4060 | 2.06k | break; |
4061 | 2.49k | } |
4062 | 54.4k | } |
4063 | | |
4064 | 54.0k | read_next_frame: |
4065 | 54.0k | if (status == MagickPass) |
4066 | 30.1k | { |
4067 | 30.1k | StopTimer(&image->timer); |
4068 | 30.1k | if (image->depth > QuantumDepth) |
4069 | 3.26k | image->depth=QuantumDepth; |
4070 | 30.1k | if ((photometric == PHOTOMETRIC_LOGL) || |
4071 | 30.1k | (photometric == PHOTOMETRIC_MINISBLACK) || |
4072 | 24.1k | (photometric == PHOTOMETRIC_MINISWHITE)) |
4073 | 17.3k | image->is_grayscale=MagickTrue; |
4074 | 30.1k | if ((image->is_grayscale == MagickTrue) && (bits_per_sample == 1)) |
4075 | 7.64k | image->is_monochrome=MagickTrue; |
4076 | | /* |
4077 | | Proceed to next image. |
4078 | | */ |
4079 | 30.1k | if (image_info->subrange != 0) |
4080 | 28.3k | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
4081 | 28.3k | break; |
4082 | 1.88k | more_frames=TIFFReadDirectory(tiff); |
4083 | 1.88k | if (logging) |
4084 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4085 | 0 | "TIFFReadDirectory() returned %d",more_frames); |
4086 | 1.88k | if ((more_frames == 0) && (exception->severity == CorruptImageError)) |
4087 | 212 | { |
4088 | 212 | if (logging) |
4089 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4090 | 0 | "Re-casting 'CorruptImageError' to" |
4091 | 0 | " 'CorruptImageWarning' due to" |
4092 | 0 | " TIFFReadDirectory() error"); |
4093 | 212 | exception->severity=CorruptImageWarning; |
4094 | 212 | } |
4095 | 1.88k | if (more_frames) |
4096 | 0 | { |
4097 | | /* |
4098 | | Allocate next image structure. |
4099 | | */ |
4100 | 0 | AllocateNextImage(image_info,image); |
4101 | 0 | if (image->next == (Image *) NULL) |
4102 | 0 | { |
4103 | 0 | DestroyImageList(image); |
4104 | 0 | return((Image *) NULL); |
4105 | 0 | } |
4106 | 0 | image=SyncNextImageInList(image); |
4107 | 0 | } |
4108 | 1.88k | } |
4109 | | |
4110 | 25.7k | if (status == MagickFail) |
4111 | 23.8k | break; |
4112 | | |
4113 | 25.7k | } while ((status == MagickPass) && (more_frames)); |
4114 | 54.0k | TIFFClose(tiff); |
4115 | 54.0k | if (status == MagickFail) |
4116 | 23.8k | { |
4117 | 23.8k | if (logging) |
4118 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4119 | 0 | "Delete image %ld from list due to error", |
4120 | 0 | image->scene); |
4121 | 23.8k | DeleteImageFromList(&image); |
4122 | 23.8k | } |
4123 | 54.0k | return GetFirstImageInList(image); |
4124 | 57.3k | } |
4125 | | #endif |
4126 | | |
4127 | | /* |
4128 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4129 | | % % |
4130 | | % % |
4131 | | % % |
4132 | | % R e g i s t e r T I F F I m a g e % |
4133 | | % % |
4134 | | % % |
4135 | | % % |
4136 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4137 | | % |
4138 | | % Method RegisterTIFFImage adds attributes for the TIFF image format to |
4139 | | % the list of supported formats. The attributes include the image format |
4140 | | % tag, a method to read and/or write the format, whether the format |
4141 | | % supports the saving of more than one frame to the same file or blob, |
4142 | | % whether the format supports native in-memory I/O, and a brief |
4143 | | % description of the format. |
4144 | | % |
4145 | | % The format of the RegisterTIFFImage method is: |
4146 | | % |
4147 | | % void RegisterTIFFImage(void) |
4148 | | % |
4149 | | */ |
4150 | | ModuleExport void |
4151 | | RegisterTIFFImage(void) |
4152 | 7 | { |
4153 | 7 | #define BIGTIFFDescription "Tagged Image File Format (64-bit offsets)" |
4154 | 7 | #define GROUP4RAWDescription "CCITT Group4 RAW" |
4155 | 7 | #define PTIFDescription "Pyramid encoded TIFF" |
4156 | 14 | #define TIFFDescription "Tagged Image File Format" |
4157 | 7 | #if defined(HasTIFF) |
4158 | | |
4159 | 7 | static char |
4160 | 7 | version[32]; |
4161 | | |
4162 | 7 | MagickInfo |
4163 | 7 | *entry; |
4164 | | |
4165 | 7 | static const char |
4166 | 7 | TIFFNote[] = |
4167 | 7 | "Supported Compressions: " |
4168 | 7 | #if defined(COMPRESSION_NONE) |
4169 | 7 | "None" |
4170 | 7 | #endif |
4171 | 7 | #if defined(COMPRESSION_CCITTFAX3) |
4172 | 7 | ", Fax/Group3" |
4173 | 7 | #endif |
4174 | 7 | #if defined(COMPRESSION_CCITTFAX4) |
4175 | 7 | ", Group4" |
4176 | 7 | #endif |
4177 | 7 | #if defined(COMPRESSION_JBIG) |
4178 | 7 | ", JBIG" |
4179 | 7 | #endif |
4180 | 7 | #if defined(COMPRESSION_JPEG) |
4181 | 7 | ", JPEG" |
4182 | 7 | #endif |
4183 | 7 | #if defined(COMPRESSION_LZW) |
4184 | 7 | ", LZW" |
4185 | 7 | #endif |
4186 | 7 | #if defined(COMPRESSION_LZMA) |
4187 | 7 | ", LZMA" |
4188 | 7 | #endif |
4189 | 7 | #if defined(COMPRESSION_PACKBITS) |
4190 | 7 | ", RLE" |
4191 | 7 | #endif |
4192 | 7 | #if defined(COMPRESSION_ADOBE_DEFLATE) |
4193 | 7 | ", ZIP" |
4194 | 7 | #endif |
4195 | 7 | #if defined(COMPRESSION_ZSTD) |
4196 | 7 | ", ZSTD" |
4197 | 7 | #endif |
4198 | 7 | #if defined(COMPRESSION_WEBP) |
4199 | 7 | ", WEBP" |
4200 | 7 | #endif |
4201 | 7 | ; |
4202 | | |
4203 | | /* |
4204 | | Initialize thread specific data key. |
4205 | | */ |
4206 | 7 | if (tsd_key == (MagickTsdKey_t) 0) |
4207 | 7 | (void) MagickTsdKeyCreate(&tsd_key); |
4208 | | |
4209 | 7 | version[0]='\0'; |
4210 | 7 | { |
4211 | 7 | unsigned int |
4212 | 7 | i; |
4213 | | |
4214 | 7 | const char |
4215 | 7 | *p; |
4216 | | |
4217 | | /* TIFFGetVersion() is in libtiff 3.5.3 and later */ |
4218 | 7 | for (p=TIFFGetVersion(), i=0; |
4219 | 161 | (i < sizeof(version)-1) && (*p != 0) && (*p != '\n'); |
4220 | 154 | p++, i++) |
4221 | 154 | version[i] = *p; |
4222 | 7 | version[i]=0; |
4223 | 7 | } |
4224 | | |
4225 | | /* |
4226 | | Big TIFF (64-bit offsets) |
4227 | | */ |
4228 | 7 | #if defined(HasBigTIFF) |
4229 | 7 | entry=SetMagickInfo("BIGTIFF"); |
4230 | 7 | entry->thread_support=MagickFalse; /* libtiff uses libjpeg which is not thread safe */ |
4231 | 7 | entry->decoder=(DecoderHandler) ReadTIFFImage; |
4232 | 7 | entry->encoder=(EncoderHandler) WriteTIFFImage; |
4233 | 7 | entry->seekable_stream=MagickTrue; |
4234 | 7 | entry->description=BIGTIFFDescription; |
4235 | 7 | entry->note=TIFFNote; |
4236 | 7 | entry->module="TIFF"; |
4237 | 7 | entry->coder_class=PrimaryCoderClass; |
4238 | 7 | (void) RegisterMagickInfo(entry); |
4239 | 7 | #endif /* defined(HasBigTIFF) */ |
4240 | | |
4241 | | /* |
4242 | | CCITT Group4 RAW encoded page. |
4243 | | */ |
4244 | 7 | entry=SetMagickInfo("GROUP4RAW"); |
4245 | 7 | entry->thread_support=MagickTrue; |
4246 | | /* entry->decoder=(DecoderHandler) ReadGROUP4RAWImage; */ |
4247 | 7 | entry->encoder=(EncoderHandler) WriteGROUP4RAWImage; |
4248 | 7 | entry->raw=MagickTrue; |
4249 | 7 | entry->adjoin=MagickFalse; |
4250 | 7 | entry->seekable_stream=MagickFalse; |
4251 | 7 | entry->extension_treatment=IgnoreExtensionTreatment; |
4252 | 7 | entry->stealth=MagickTrue; /* Don't list in '-list format' output */ |
4253 | 7 | entry->description=GROUP4RAWDescription; |
4254 | 7 | entry->module="TIFF"; |
4255 | 7 | (void) RegisterMagickInfo(entry); |
4256 | | |
4257 | | /* |
4258 | | Pyramid TIFF (sequence of successively smaller versions of the same image) |
4259 | | */ |
4260 | 7 | entry=SetMagickInfo("PTIF"); |
4261 | 7 | entry->thread_support=MagickFalse; /* libtiff uses libjpeg which is not thread safe */ |
4262 | 7 | entry->decoder=(DecoderHandler) ReadTIFFImage; |
4263 | 7 | entry->encoder=(EncoderHandler) WritePTIFImage; |
4264 | 7 | entry->seekable_stream=MagickTrue; |
4265 | 7 | entry->description=PTIFDescription; |
4266 | 7 | entry->note=TIFFNote; |
4267 | 7 | entry->module="TIFF"; |
4268 | 7 | (void) RegisterMagickInfo(entry); |
4269 | | |
4270 | | /* |
4271 | | Another name for 32-bit TIFF |
4272 | | */ |
4273 | 7 | entry=SetMagickInfo("TIF"); |
4274 | 7 | entry->thread_support=MagickFalse; /* libtiff uses libjpeg which is not thread safe */ |
4275 | 7 | entry->decoder=(DecoderHandler) ReadTIFFImage; |
4276 | 7 | entry->encoder=(EncoderHandler) WriteTIFFImage; |
4277 | 7 | entry->seekable_stream=MagickTrue; |
4278 | 7 | entry->description=TIFFDescription; |
4279 | 7 | if (*version != '\0') |
4280 | 7 | entry->version=version; |
4281 | 7 | entry->note=TIFFNote; |
4282 | 7 | entry->stealth=MagickTrue; /* Don't list in '-list format' output */ |
4283 | 7 | entry->module="TIFF"; |
4284 | 7 | entry->coder_class=PrimaryCoderClass; |
4285 | 7 | (void) RegisterMagickInfo(entry); |
4286 | | |
4287 | | /* |
4288 | | Traditional 32-bit TIFF |
4289 | | */ |
4290 | 7 | entry=SetMagickInfo("TIFF"); |
4291 | 7 | entry->thread_support=MagickFalse; /* libtiff uses libjpeg which is not thread safe */ |
4292 | 7 | entry->decoder=(DecoderHandler) ReadTIFFImage; |
4293 | 7 | entry->encoder=(EncoderHandler) WriteTIFFImage; |
4294 | 7 | entry->magick=(MagickHandler) IsTIFF; |
4295 | 7 | entry->seekable_stream=MagickTrue; |
4296 | 7 | entry->description=TIFFDescription; |
4297 | 7 | if (*version != '\0') |
4298 | 7 | entry->version=version; |
4299 | 7 | entry->note=TIFFNote; |
4300 | 7 | entry->module="TIFF"; |
4301 | 7 | entry->coder_class=PrimaryCoderClass; |
4302 | 7 | (void) RegisterMagickInfo(entry); |
4303 | | |
4304 | 7 | #if defined(EXTEND_TIFF_TAGS) |
4305 | | /* |
4306 | | Add our own TIFF tag extensions. |
4307 | | */ |
4308 | 7 | ExtensionTagsInitialize(); |
4309 | 7 | #endif /* defined(EXTEND_TIFF_TAGS) */ |
4310 | | |
4311 | | |
4312 | 7 | #endif /* if defined(HasTIFF) */ |
4313 | 7 | } |
4314 | | |
4315 | | /* |
4316 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4317 | | % % |
4318 | | % % |
4319 | | % % |
4320 | | % U n r e g i s t e r T I F F I m a g e % |
4321 | | % % |
4322 | | % % |
4323 | | % % |
4324 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4325 | | % |
4326 | | % Method UnregisterTIFFImage removes format registrations made by the |
4327 | | % TIFF module from the list of supported formats. |
4328 | | % |
4329 | | % The format of the UnregisterTIFFImage method is: |
4330 | | % |
4331 | | % void UnregisterTIFFImage(void) |
4332 | | % |
4333 | | */ |
4334 | | ModuleExport void |
4335 | | UnregisterTIFFImage(void) |
4336 | 0 | { |
4337 | 0 | #if defined(HasTIFF) |
4338 | 0 | #if defined(HasBigTIFF) |
4339 | 0 | (void) UnregisterMagickInfo("BIGTIFF"); |
4340 | 0 | #endif /* defined(HasBigTIFF) */ |
4341 | 0 | (void) UnregisterMagickInfo("GROUP4RAW"); |
4342 | 0 | (void) UnregisterMagickInfo("PTIF"); |
4343 | 0 | (void) UnregisterMagickInfo("TIF"); |
4344 | 0 | (void) UnregisterMagickInfo("TIFF"); |
4345 | | |
4346 | | /* |
4347 | | Destroy thread specific data key. |
4348 | | */ |
4349 | 0 | if (tsd_key != (MagickTsdKey_t) 0) |
4350 | 0 | { |
4351 | 0 | (void) MagickTsdKeyDelete(tsd_key); |
4352 | 0 | tsd_key = (MagickTsdKey_t) 0; |
4353 | 0 | } |
4354 | 0 | #endif |
4355 | 0 | } |
4356 | | |
4357 | | #if defined(HasTIFF) |
4358 | | /* |
4359 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4360 | | % % |
4361 | | % % |
4362 | | % % |
4363 | | % W r i t e G R O U P 4 R A W I m a g e % |
4364 | | % % |
4365 | | % % |
4366 | | % % |
4367 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4368 | | % |
4369 | | % Method WriteGROUP4RAWImage writes an image as raw Group4 compressed data. |
4370 | | % |
4371 | | % The format of the WriteGROUP4RAWImage method is: |
4372 | | % |
4373 | | % MagickPassFail WriteGROUP4RAWImage(const ImageInfo *image_info,Image *image) |
4374 | | % |
4375 | | % A description of each parameter follows: |
4376 | | % |
4377 | | % o status: Method WriteGROUP4RAWImage return True if the image is written. |
4378 | | % False is returned is there is of a memory shortage or if the image |
4379 | | % file cannot be opened for writing. |
4380 | | % |
4381 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
4382 | | % |
4383 | | % o image: A pointer to an Image structure. |
4384 | | % |
4385 | | % |
4386 | | */ |
4387 | | static MagickPassFail |
4388 | | WriteGROUP4RAWImage(const ImageInfo *image_info,Image *image) |
4389 | 1.67k | { |
4390 | 1.67k | char |
4391 | 1.67k | temporary_filename[MaxTextExtent]; |
4392 | | |
4393 | 1.67k | Image |
4394 | 1.67k | *huffman_image; |
4395 | | |
4396 | 1.67k | ImageInfo |
4397 | 1.67k | *clone_info; |
4398 | | |
4399 | 1.67k | TIFF |
4400 | 1.67k | *tiff; |
4401 | | |
4402 | 1.67k | toff_t |
4403 | 1.67k | *byte_counts, |
4404 | 1.67k | count, |
4405 | 1.67k | strip_size; |
4406 | | |
4407 | 1.67k | unsigned char |
4408 | 1.67k | *strip; |
4409 | | |
4410 | 1.67k | unsigned int |
4411 | 1.67k | i; |
4412 | | |
4413 | 1.67k | MagickPassFail |
4414 | 1.67k | status; |
4415 | | |
4416 | | /* |
4417 | | Write image as CCITTFax4 TIFF image. |
4418 | | */ |
4419 | 1.67k | assert(image_info != (ImageInfo *) NULL); |
4420 | 1.67k | assert(image_info->signature == MagickSignature); |
4421 | 1.67k | assert(image != (Image *) NULL); |
4422 | 1.67k | assert(image->signature == MagickSignature); |
4423 | | |
4424 | 1.67k | if (!AcquireTemporaryFileName(temporary_filename)) |
4425 | 1.67k | ThrowWriterException(FileOpenError,UnableToCreateTemporaryFile,image); |
4426 | | |
4427 | 1.67k | huffman_image=CloneImage(image,0,0,True,&image->exception); |
4428 | 1.67k | if (huffman_image == (Image *) NULL) |
4429 | 0 | return(MagickFail); |
4430 | | |
4431 | 1.67k | if (SetImageType(huffman_image,BilevelType) == MagickFail) |
4432 | 0 | { |
4433 | 0 | CopyException(&image->exception,&huffman_image->exception); |
4434 | 0 | DestroyImage(huffman_image); |
4435 | 0 | return MagickFail; |
4436 | 0 | } |
4437 | | |
4438 | 1.67k | MagickFormatString(huffman_image->filename,sizeof(huffman_image->filename),"tiff:%s",temporary_filename); |
4439 | 1.67k | clone_info=CloneImageInfo((const ImageInfo *) NULL); |
4440 | | /* clone_info->blob=0; */ |
4441 | 1.67k | clone_info->compression=Group4Compression; |
4442 | 1.67k | clone_info->type=BilevelType; |
4443 | 1.67k | (void) AddDefinitions(clone_info,"tiff:strip-per-page=TRUE", |
4444 | 1.67k | &image->exception); |
4445 | 1.67k | (void) AddDefinitions(clone_info,"tiff:fill-order=msb2lsb", |
4446 | 1.67k | &image->exception); |
4447 | 1.67k | status=WriteImage(clone_info,huffman_image); |
4448 | 1.67k | if (status == MagickFail) |
4449 | 0 | CopyException(&image->exception,&huffman_image->exception); |
4450 | 1.67k | DestroyImageInfo(clone_info); |
4451 | 1.67k | DestroyImage(huffman_image); |
4452 | 1.67k | if (status == MagickFail) |
4453 | 0 | { |
4454 | 0 | (void) LiberateTemporaryFile(temporary_filename); |
4455 | 0 | return MagickFail; |
4456 | 0 | } |
4457 | | |
4458 | 1.67k | (void) MagickTsdSetSpecific(tsd_key,(void *) (&image->exception)); |
4459 | 1.67k | (void) TIFFSetErrorHandler((TIFFErrorHandler) TIFFWriteErrorsHandler); |
4460 | 1.67k | (void) TIFFSetWarningHandler((TIFFErrorHandler) (CheckThrowWarnings(image_info) ? |
4461 | 0 | TIFFWarningsThrowException : |
4462 | 1.67k | TIFFWarningsLogOnly)); |
4463 | | |
4464 | 1.67k | tiff=TIFFOpen(temporary_filename,"rb"); |
4465 | 1.67k | if (tiff == (TIFF *) NULL) |
4466 | 0 | { |
4467 | 0 | (void) LiberateTemporaryFile(temporary_filename); |
4468 | 0 | return MagickFail; |
4469 | 0 | } |
4470 | | |
4471 | | /* |
4472 | | Allocate raw strip buffer. |
4473 | | */ |
4474 | 1.67k | if (TIFFGetField(tiff,TIFFTAG_STRIPBYTECOUNTS,&byte_counts) != 1) |
4475 | 0 | { |
4476 | 0 | TIFFClose(tiff); |
4477 | 0 | (void) LiberateTemporaryFile(temporary_filename); |
4478 | 0 | return MagickFail; |
4479 | 0 | } |
4480 | 1.67k | strip_size=byte_counts[0]; |
4481 | 1.67k | for (i=1; i < TIFFNumberOfStrips(tiff); i++) |
4482 | 0 | if (byte_counts[i] > strip_size) |
4483 | 0 | strip_size=byte_counts[i]; |
4484 | 1.67k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4485 | 1.67k | "Allocating %lu bytes of memory for TIFF strip", |
4486 | 1.67k | (unsigned long) strip_size); |
4487 | 1.67k | strip=MagickAllocateResourceLimitedMemory(unsigned char *,(size_t) strip_size); |
4488 | 1.67k | if (strip == (unsigned char *) NULL) |
4489 | 0 | { |
4490 | 0 | TIFFClose(tiff); |
4491 | 0 | (void) LiberateTemporaryFile(temporary_filename); |
4492 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
4493 | 0 | image); |
4494 | 0 | } |
4495 | | /* |
4496 | | Open blob for output |
4497 | | */ |
4498 | 1.67k | if ((status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception)) |
4499 | 1.67k | == MagickFail) |
4500 | 0 | { |
4501 | 0 | MagickFreeResourceLimitedMemory(unsigned char *,strip); |
4502 | 0 | TIFFClose(tiff); |
4503 | 0 | (void) LiberateTemporaryFile(temporary_filename); |
4504 | 0 | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
4505 | 0 | } |
4506 | | |
4507 | | /* |
4508 | | Compress runlength encoded to 2D Huffman pixels. |
4509 | | */ |
4510 | 1.67k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4511 | 1.67k | "Output 2D Huffman pixels."); |
4512 | 3.35k | for (i=0; i < TIFFNumberOfStrips(tiff); i++) |
4513 | 1.67k | { |
4514 | 1.67k | count=TIFFReadRawStrip(tiff,(uint32) i,strip,strip_size); |
4515 | 1.67k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4516 | 1.67k | "Writing strip %u (%lu bytes) to blob ...", |
4517 | 1.67k | i,(unsigned long) count); |
4518 | 1.67k | if ((toff_t) WriteBlob(image,count,strip) != count) |
4519 | 0 | status=MagickFail; |
4520 | 1.67k | } |
4521 | | |
4522 | 1.67k | MagickFreeResourceLimitedMemory(unsigned char *,strip); |
4523 | 1.67k | TIFFClose(tiff); |
4524 | | |
4525 | 1.67k | (void) LiberateTemporaryFile(temporary_filename); |
4526 | 1.67k | status &= CloseBlob(image); |
4527 | 1.67k | return status; |
4528 | 1.67k | } |
4529 | | #endif /* if defined(HasTIFF) */ |
4530 | | |
4531 | | #if defined(HasTIFF) |
4532 | | /* |
4533 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4534 | | % % |
4535 | | % % |
4536 | | % % |
4537 | | % W r i t e P T I F I m a g e % |
4538 | | % % |
4539 | | % % |
4540 | | % % |
4541 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
4542 | | % |
4543 | | % WritePTIFImage() writes an image in the pyramid-encoded Tagged image file |
4544 | | % format. |
4545 | | % |
4546 | | % The format of the WritePTIFImage method is: |
4547 | | % |
4548 | | % MagickPassFail WritePTIFImage(const ImageInfo *image_info,Image *image) |
4549 | | % |
4550 | | % A description of each parameter follows: |
4551 | | % |
4552 | | % o status: Method WritePTIFImage return MagickPass if the image is written. |
4553 | | % MagickFail is returned is there is of a memory shortage or if the image |
4554 | | % file cannot be opened for writing. |
4555 | | % |
4556 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
4557 | | % |
4558 | | % o image: A pointer to an Image structure. |
4559 | | % |
4560 | | % |
4561 | | */ |
4562 | | static MagickPassFail |
4563 | | WritePTIFImage(const ImageInfo *image_info,Image *image) |
4564 | 8.95k | { |
4565 | 8.95k | Image |
4566 | 8.95k | *pyramid_image; |
4567 | | |
4568 | 8.95k | ImageInfo |
4569 | 8.95k | *clone_info; |
4570 | | |
4571 | 8.95k | FilterTypes |
4572 | 8.95k | filter; |
4573 | | |
4574 | 8.95k | unsigned int |
4575 | 8.95k | status; |
4576 | | |
4577 | 8.95k | const char |
4578 | 8.95k | *def; |
4579 | | |
4580 | 8.95k | RectangleInfo |
4581 | 8.95k | min_geometry; |
4582 | | |
4583 | | /* |
4584 | | Create pyramid-encoded TIFF image. |
4585 | | */ |
4586 | 8.95k | assert(image_info != (const ImageInfo *) NULL); |
4587 | 8.95k | assert(image_info->signature == MagickSignature); |
4588 | 8.95k | assert(image != (Image *) NULL); |
4589 | 8.95k | assert(image->signature == MagickSignature); |
4590 | 8.95k | if (!(((def=AccessDefinition(image_info,"ptif","minimum-geometry")) != NULL) && |
4591 | 0 | (GetGeometry(def,&min_geometry.x,&min_geometry.y,&min_geometry.width, |
4592 | 0 | &min_geometry.height) & (WidthValue|HeightValue)))) |
4593 | 8.95k | { |
4594 | | /* |
4595 | | Minimum default subresolution frame is 32x32 |
4596 | | */ |
4597 | 8.95k | min_geometry.height=32; |
4598 | 8.95k | min_geometry.width=32; |
4599 | 8.95k | } |
4600 | 8.95k | if (image->logging) |
4601 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4602 | 0 | "PTIF minimum pyramid dimensions: %lux%lu", |
4603 | 0 | min_geometry.width, min_geometry.height); |
4604 | 8.95k | filter=TriangleFilter; |
4605 | 8.95k | if (image->is_monochrome) |
4606 | 1.40k | filter=PointFilter; |
4607 | 8.95k | pyramid_image=CloneImage(image,0,0,True,&image->exception); |
4608 | 8.95k | if (pyramid_image == (Image *) NULL) |
4609 | 8.95k | ThrowWriterException2(FileOpenError,image->exception.reason,image); |
4610 | 8.95k | DestroyBlob(pyramid_image); |
4611 | 8.95k | pyramid_image->blob=ReferenceBlob(image->blob); |
4612 | 8.95k | (void) SetImageAttribute(pyramid_image,"subfiletype","NONE"); |
4613 | 14.2k | while(1) |
4614 | 14.2k | { |
4615 | 14.2k | if ((pyramid_image->columns/2 < min_geometry.width) || |
4616 | 7.04k | (pyramid_image->rows/2 < min_geometry.height)) |
4617 | 8.95k | break; |
4618 | | |
4619 | 5.30k | pyramid_image->next=ResizeImage(image,pyramid_image->columns/2, |
4620 | 5.30k | pyramid_image->rows/2,filter, |
4621 | 5.30k | 1.0,&image->exception); |
4622 | 5.30k | if (pyramid_image->next == (Image *) NULL) |
4623 | 0 | { |
4624 | 0 | DestroyImageList(pyramid_image); |
4625 | 0 | ThrowWriterException2(FileOpenError,image->exception.reason,image); |
4626 | 0 | } |
4627 | 5.30k | DestroyBlob(pyramid_image->next); |
4628 | 5.30k | pyramid_image->next->blob=ReferenceBlob(image->blob); |
4629 | 5.30k | if ((!image->is_monochrome) && (image->storage_class == PseudoClass)) |
4630 | 510 | (void) MapImage(pyramid_image->next,image,False); |
4631 | 5.30k | pyramid_image->next->x_resolution=pyramid_image->x_resolution/2.0; |
4632 | 5.30k | pyramid_image->next->y_resolution=pyramid_image->y_resolution/2.0; |
4633 | 5.30k | (void) SetImageAttribute(pyramid_image->next,"subfiletype","REDUCEDIMAGE"); |
4634 | 5.30k | pyramid_image->next->previous=pyramid_image; |
4635 | 5.30k | pyramid_image=pyramid_image->next; |
4636 | 8.95k | }; |
4637 | 14.2k | while (pyramid_image->previous != (Image *) NULL) |
4638 | 5.30k | pyramid_image=pyramid_image->previous; |
4639 | | /* |
4640 | | Write pyramid-encoded TIFF image. |
4641 | | */ |
4642 | 8.95k | clone_info=CloneImageInfo(image_info); |
4643 | 8.95k | clone_info->adjoin=True; |
4644 | 8.95k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
4645 | 8.95k | "Invoking \"%.1024s\" encoder, monochrome=%s, grayscale=%s", |
4646 | 8.95k | "TIFF", |
4647 | 8.95k | MagickBoolToString(image->is_monochrome), |
4648 | 8.95k | MagickBoolToString(image->is_grayscale)); |
4649 | 8.95k | status=WriteTIFFImage(clone_info,pyramid_image); |
4650 | 8.95k | DestroyImageList(pyramid_image); |
4651 | 8.95k | DestroyImageInfo(clone_info); |
4652 | 8.95k | return(status); |
4653 | 8.95k | } |
4654 | | #endif /* defined(HasTIFF) */ |
4655 | | |
4656 | | #if defined(HasTIFF) |
4657 | | |
4658 | | #if EXPERIMENTAL_EXIF_TAGS |
4659 | | #if TIFFLIB_VERSION >= 20120922 |
4660 | | |
4661 | 77 | #define FLAG_EXIF 1 /* In (or have handled) EXIF IFD */ |
4662 | 420 | #define FLAG_GPS 2 /* In (or have handled) GPS IFD */ |
4663 | 288 | #define FLAG_BASE 4 /* Transfer tags. Used to be set globally, so primary IFD would be updated/trashed */ |
4664 | | |
4665 | | |
4666 | | /* |
4667 | | static TIFFField customFields[] = { |
4668 | | {544, -1, -1, TIFF_LONG, 0, TIFF_SETGET_UINT32, |
4669 | | TIFF_SETGET_UNDEFINED, FIELD_CUSTOM, 1, 0, "Custom1", NULL}, |
4670 | | }; |
4671 | | |
4672 | | static TIFFFieldArray customFieldArray = {tfiatOther, 0, 1, customFields}; |
4673 | | */ |
4674 | | |
4675 | | /* |
4676 | | EXIF data-marshalling functions. The "_LO" variants are for reading |
4677 | | little-endian EXIF and the "_HI" variants are for big-endian |
4678 | | EXIF. Reading is done in bytes in order to avoid address alignment |
4679 | | violations. |
4680 | | |
4681 | | The implementations are reversed from what may be intuitively |
4682 | | expected, since they also perform endian swapping. |
4683 | | */ |
4684 | | static magick_uint32_t LD_UINT32_LO(const unsigned char *Mem) |
4685 | 102 | { |
4686 | | #ifdef WORDS_BIGENDIAN |
4687 | | return (((magick_uint32_t)Mem[0]<<24) | ((magick_uint32_t)Mem[1] << 16) | ((magick_uint32_t)Mem[2] << 8) | ((magick_uint32_t)Mem[3])); |
4688 | | #else |
4689 | 102 | return ((magick_uint32_t)Mem[0] | ((magick_uint32_t)Mem[1] << 8) | ((magick_uint32_t)Mem[2] << 16) | ((magick_uint32_t)Mem[3] << 24)); |
4690 | 102 | #endif |
4691 | 102 | } |
4692 | | |
4693 | | static magick_uint32_t LD_UINT32_HI(const unsigned char *Mem) |
4694 | 7.96k | { |
4695 | | #ifdef WORDS_BIGENDIAN |
4696 | | return ((magick_uint32_t)Mem[0] | ((magick_uint32_t)Mem[1] << 8) | ((magick_uint32_t)Mem[2] << 16) | ((magick_uint32_t)Mem[3] << 24)); |
4697 | | #else |
4698 | 7.96k | return (((magick_uint32_t)Mem[0]<<24) | ((magick_uint32_t)Mem[1] << 16) | ((magick_uint32_t)Mem[2] << 8) | ((magick_uint32_t)Mem[3])); |
4699 | 7.96k | #endif |
4700 | 7.96k | } |
4701 | | |
4702 | | |
4703 | | static magick_uint16_t LD_UINT16_LO(const unsigned char *Mem) |
4704 | 90 | { |
4705 | | #ifdef WORDS_BIGENDIAN |
4706 | | return (((magick_uint16_t)Mem[0] << 8) | ((magick_uint16_t)Mem[1])); |
4707 | | #else |
4708 | 90 | return ((magick_uint16_t)Mem[0] | ((magick_uint16_t)Mem[1] << 8)); |
4709 | 90 | #endif |
4710 | 90 | } |
4711 | | |
4712 | | static magick_uint16_t LD_UINT16_HI(const unsigned char *Mem) |
4713 | 7.89k | { |
4714 | | #ifdef WORDS_BIGENDIAN |
4715 | | return ((magick_uint16_t)Mem[0] | ((magick_uint16_t)Mem[1] << 8)); |
4716 | | #else |
4717 | 7.89k | return (((magick_uint16_t)Mem[0] << 8) | ((magick_uint16_t)Mem[1])); |
4718 | 7.89k | #endif |
4719 | 7.89k | } |
4720 | | |
4721 | | |
4722 | | static const char *FipFieldName(const TIFFField *fip) |
4723 | 0 | { |
4724 | 0 | if (fip) |
4725 | 0 | { |
4726 | 0 | const char * const FName = TIFFFieldName(fip); |
4727 | 0 | if (FName==NULL) |
4728 | 0 | return "N/A"; |
4729 | 0 | return FName; |
4730 | 0 | } |
4731 | 0 | return "UNSUPPORTED"; |
4732 | 0 | } |
4733 | | |
4734 | | static const char *FipFieldTypeToStr(const TIFFDataType data_type) |
4735 | 0 | { |
4736 | 0 | const char *str = "UNKNOWN"; |
4737 | 0 | switch (data_type) |
4738 | 0 | { |
4739 | 0 | case TIFF_NOTYPE: /* placeholder */ |
4740 | 0 | str = "NOTYPE"; |
4741 | 0 | break; |
4742 | 0 | case TIFF_BYTE: /* 8-bit unsigned integer */ |
4743 | 0 | str = "BYTE"; |
4744 | 0 | break; |
4745 | 0 | case TIFF_ASCII: /* 8-bit bytes w/ last byte null */ |
4746 | 0 | str = "STRING"; /* vs "ASCII */ |
4747 | 0 | break; |
4748 | 0 | case TIFF_SHORT: /* 16-bit unsigned integer */ |
4749 | 0 | str = "USHORT"; |
4750 | 0 | break; |
4751 | 0 | case TIFF_LONG: /* 32-bit unsigned integer */ |
4752 | 0 | str = "ULONG"; |
4753 | 0 | break; |
4754 | 0 | case TIFF_RATIONAL: /* 64-bit unsigned fraction */ |
4755 | 0 | str = "URATIONAL"; |
4756 | 0 | break; |
4757 | 0 | case TIFF_SBYTE: /* !8-bit signed integer */ |
4758 | 0 | str = "SBYTE"; |
4759 | 0 | break; |
4760 | 0 | case TIFF_UNDEFINED: /* !8-bit untyped data */ |
4761 | 0 | str = "UNDEFINED"; |
4762 | 0 | break; |
4763 | 0 | case TIFF_SSHORT: /* !16-bit signed integer */ |
4764 | 0 | str = "SSHORT"; |
4765 | 0 | break; |
4766 | 0 | case TIFF_SLONG: /* !32-bit signed integer */ |
4767 | 0 | str = "SLONG"; |
4768 | 0 | break; |
4769 | 0 | case TIFF_SRATIONAL: /* !64-bit signed fraction */ |
4770 | 0 | str = "SRATIONAL"; |
4771 | 0 | break; |
4772 | 0 | case TIFF_FLOAT: /* !32-bit IEEE floating point */ |
4773 | 0 | str = "FLOAT"; |
4774 | 0 | break; |
4775 | 0 | case TIFF_DOUBLE: /* !64-bit IEEE floating point */ |
4776 | 0 | str = "DOUBLE"; |
4777 | 0 | break; |
4778 | 0 | case TIFF_IFD: /* %32-bit unsigned integer (offset) */ |
4779 | 0 | str = "IFD"; |
4780 | 0 | break; |
4781 | 0 | #if defined(HasBigTIFF) |
4782 | 0 | case TIFF_LONG8: /* BigTIFF 64-bit unsigned integer */ |
4783 | 0 | str = "LONG8"; |
4784 | 0 | break; |
4785 | 0 | case TIFF_SLONG8: /* BigTIFF 64-bit signed integer */ |
4786 | 0 | str = "SLONG8"; |
4787 | 0 | break; |
4788 | 0 | case TIFF_IFD8: /* BigTIFF 64-bit unsigned integer (offset) */ |
4789 | 0 | str = "IFD8"; |
4790 | 0 | break; |
4791 | 0 | #endif /* if defined(HasBigTIFF) */ |
4792 | 0 | } |
4793 | 0 | return str; |
4794 | 0 | } |
4795 | | |
4796 | | |
4797 | | static int CheckAndStoreStr(TIFF *tiff, const magick_uint16_t Tag, const char *String, const magick_uint32_t StrSize) |
4798 | 0 | { |
4799 | 0 | magick_uint32_t i = StrSize; |
4800 | |
|
4801 | 0 | if (Tag == TIFFTAG_INKNAMES) /* Variant of call is tag dependent, too bad. */ |
4802 | 0 | { |
4803 | 0 | if (StrSize > 0xFFFF) |
4804 | 0 | return 0; |
4805 | | /* TIFFTAG_INKNAMES needs only uint16_t https://libtiff.gitlab.io/libtiff/functions/TIFFSetField.html#c.TIFFSetField */ |
4806 | 0 | return TIFFSetField(tiff, Tag, (magick_uint16_t)StrSize, String); |
4807 | 0 | } |
4808 | | |
4809 | | /* Look for zero terminator. */ |
4810 | 0 | while (i > 0) |
4811 | 0 | { |
4812 | 0 | i--; |
4813 | 0 | if (String[i] == 0) |
4814 | 0 | { |
4815 | 0 | return TIFFSetField(tiff, Tag, String); |
4816 | 0 | } |
4817 | 0 | } |
4818 | | |
4819 | 0 | if (StrSize > 0) |
4820 | 0 | { |
4821 | | /* Try to duplicate unterminated string. */ |
4822 | 0 | char *StringDup = MagickAllocateResourceLimitedMemory(char *, StrSize+1); |
4823 | 0 | if (StringDup != NULL) |
4824 | 0 | { |
4825 | 0 | memcpy(StringDup,String,StrSize); |
4826 | 0 | StringDup[StrSize] = 0; |
4827 | 0 | i = TIFFSetField(tiff, Tag, String); |
4828 | 0 | MagickFreeResourceLimitedMemory(char *,StringDup); |
4829 | 0 | return i; |
4830 | 0 | } |
4831 | 0 | } |
4832 | 0 | return 0; |
4833 | 0 | } |
4834 | | |
4835 | | |
4836 | | static int AddIFDExifFields(TIFF *tiff, unsigned int *ifds_handled, |
4837 | | const unsigned char * const profile_data, |
4838 | | const unsigned char *IFD_data, |
4839 | | const size_t profile_length, |
4840 | | MagickBool logging, magick_uint16_t Flags) |
4841 | 245 | { |
4842 | 245 | magick_uint32_t(*LD_UINT32)(const unsigned char *Mem); |
4843 | 245 | magick_uint16_t(*LD_UINT16)(const unsigned char *Mem); |
4844 | 245 | const TIFFField *fip; |
4845 | 245 | magick_uint16_t EntryNum; |
4846 | 245 | magick_uint16_t Tag, Field; |
4847 | 245 | magick_uint32_t Long2, Value; |
4848 | 245 | const size_t Max_IFD_data_Loops = 4096; |
4849 | 245 | size_t IFD_data_Loops = 0; |
4850 | 245 | MagickBool continue_looping = MagickTrue; |
4851 | 245 | int FieldCount = 0; |
4852 | | |
4853 | 245 | if (*profile_data == 'M') |
4854 | 206 | { |
4855 | | /* "MM" is big-endian */ |
4856 | 206 | if (logging) |
4857 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4858 | 0 | "Big-endian IFD"); |
4859 | 206 | LD_UINT32 = LD_UINT32_HI; |
4860 | 206 | LD_UINT16 = LD_UINT16_HI; |
4861 | 206 | } |
4862 | 39 | else |
4863 | 39 | { |
4864 | | /* "II" is little-endian */ |
4865 | 39 | if (*profile_data != 'I') |
4866 | 3 | return 0; |
4867 | 36 | if (logging) |
4868 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4869 | 0 | "Little-endian IFD"); |
4870 | 36 | LD_UINT32 = LD_UINT32_LO; |
4871 | 36 | LD_UINT16 = LD_UINT16_LO; |
4872 | 36 | } |
4873 | | |
4874 | 242 | do |
4875 | 272 | { |
4876 | 272 | const unsigned char *Orig_IFD_data |
4877 | 272 | = IFD_data; |
4878 | | |
4879 | 272 | if (profile_length < (size_t)(IFD_data-profile_data)+2) |
4880 | 6 | return 0; |
4881 | 266 | if (logging) |
4882 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4883 | 0 | "Flags=0x%02x", (unsigned) Flags); |
4884 | 266 | EntryNum = LD_UINT16(IFD_data); /* Number of IFD directory entries */ |
4885 | 266 | if (logging /* && (Flags & FLAG_BASE) != 0 */) |
4886 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4887 | 0 | "IFD EntryNum %u", (unsigned) EntryNum); |
4888 | 266 | if (profile_length < (size_t)(IFD_data-profile_data)+(EntryNum*12)) |
4889 | 97 | return 0; |
4890 | 169 | IFD_data += 2; |
4891 | | |
4892 | 4.02k | while (EntryNum > 0) |
4893 | 3.86k | { |
4894 | | /* Directory Entry */ |
4895 | 3.86k | Tag = LD_UINT16(IFD_data); /* Tag */ |
4896 | 3.86k | Field = LD_UINT16(IFD_data+2); /* Tag Type Code */ |
4897 | 3.86k | Long2 = LD_UINT32(IFD_data+4); /* Count */ |
4898 | 3.86k | Value = LD_UINT32(IFD_data+8); /* Value or Offset */ |
4899 | | |
4900 | 3.86k | fip = TIFFFindField(tiff, Tag, TIFF_ANY); |
4901 | 3.86k | if (logging && (Flags & FLAG_BASE) != 0) |
4902 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4903 | 0 | "Extracted tag from EXIF %xh.H (%u), Field %u (%s), Long2 %u, val %u %s", |
4904 | 0 | (unsigned) Tag, (unsigned) Tag, (unsigned) Field, |
4905 | 0 | FipFieldTypeToStr((TIFFDataType) Field), |
4906 | 0 | (unsigned) Long2, (unsigned) Value, FipFieldName(fip)); |
4907 | | |
4908 | | /* |
4909 | | Skip EXIF tags which are redundant with the main IFD |
4910 | | (there are many more). |
4911 | | */ |
4912 | 3.86k | if (Tag == 0 || |
4913 | 3.44k | Tag == TIFFTAG_COLORMAP || /* 0x0140 */ |
4914 | 3.44k | Tag == TIFFTAG_COMPRESSION || /* 0x0103 */ |
4915 | 3.43k | Tag == TIFFTAG_IMAGELENGTH || /* 0x0101 */ |
4916 | 3.40k | Tag == TIFFTAG_IMAGEWIDTH || /* 0x0100 */ |
4917 | 3.32k | Tag == TIFFTAG_SAMPLESPERPIXEL || /* 0x0115 */ |
4918 | 3.32k | Tag == TIFFTAG_BITSPERSAMPLE || /* 0x0102 */ |
4919 | 3.31k | Tag == TIFFTAG_SAMPLEFORMAT || /* 0x0153 */ |
4920 | 3.31k | Tag == TIFFTAG_STRIPOFFSETS || /* 0x0111 */ |
4921 | 3.30k | Tag == TIFFTAG_ROWSPERSTRIP || /* 0x0116 */ |
4922 | 3.30k | Tag == TIFFTAG_STRIPBYTECOUNTS || /* 0x0117 */ |
4923 | 3.30k | Tag == TIFFTAG_ORIENTATION || /* 0x0112 */ /* Orientation is handled different way. */ |
4924 | 3.30k | Tag == TIFFTAG_XRESOLUTION || /* 0x011A */ |
4925 | 3.29k | Tag == TIFFTAG_YRESOLUTION || /* 0x011B */ |
4926 | 3.28k | Tag == TIFFTAG_PLANARCONFIG || /* 0x011C */ |
4927 | 3.28k | Tag == TIFFTAG_TRANSFERFUNCTION ||/* 0x012D */ /* Depends on TIFFTAG_BITSPERSAMPLE and TIFFTAG_SAMPLESPERPIXEL (above) */ |
4928 | 3.28k | Tag == TIFFTAG_EXTRASAMPLES /* 0x0152 */ |
4929 | 3.86k | ) |
4930 | 577 | { |
4931 | 577 | if (logging && (Flags & FLAG_BASE) != 0) |
4932 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4933 | 0 | " Skipping tag from EXIF %xh.H (%u), Field %u (%s), Long2 %u, val %u %s", |
4934 | 0 | (unsigned) Tag, (unsigned) Tag, (unsigned) Field, |
4935 | 0 | FipFieldTypeToStr((TIFFDataType) Field), |
4936 | 0 | (unsigned) Long2, (unsigned) Value, FipFieldName(fip)); |
4937 | 577 | goto NextItem; /* Banned TIFF tags that cannot be copied from EXIF. */ |
4938 | 577 | } |
4939 | | |
4940 | 3.28k | if (Tag == TIFFTAG_EXIFIFD) |
4941 | 0 | { |
4942 | 0 | if (Value >= profile_length) |
4943 | 0 | { |
4944 | 0 | if (logging) |
4945 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4946 | 0 | "EXIF IFD data length (%u) exceeds profile length (%zu)!", |
4947 | 0 | (unsigned) Value, profile_length); |
4948 | 0 | goto NextItem; |
4949 | 0 | } |
4950 | 0 | if (((Flags & FLAG_EXIF) != 0) && ((*ifds_handled & FLAG_EXIF) == 0)) |
4951 | 0 | { |
4952 | 0 | if (logging) |
4953 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"Recursing into EXIF IFD..."); |
4954 | 0 | *ifds_handled |= FLAG_EXIF; |
4955 | |
|
4956 | 0 | FieldCount += AddIFDExifFields(tiff, ifds_handled, profile_data, profile_data+Value, |
4957 | 0 | profile_length, logging, Flags|FLAG_BASE); |
4958 | 0 | if (logging) |
4959 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"Recursing out of EXIF IFD..."); |
4960 | 0 | } |
4961 | 0 | goto NextItem; |
4962 | 0 | } |
4963 | 3.28k | if (Tag == TIFFTAG_GPSIFD) |
4964 | 342 | { |
4965 | 342 | if (Value >= profile_length) |
4966 | 108 | { |
4967 | 108 | if (logging) |
4968 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
4969 | 0 | "GPS IFD data length (%u) exceeds profile length (%zu)!", |
4970 | 0 | (unsigned) Value, profile_length); |
4971 | 108 | goto NextItem; |
4972 | 108 | } |
4973 | 234 | if (((Flags & FLAG_GPS) != 0) && ((*ifds_handled & FLAG_GPS) == 0)) |
4974 | 29 | { |
4975 | 29 | if (logging) |
4976 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"Recursing into GPS IFD..."); |
4977 | 29 | *ifds_handled |= FLAG_GPS; |
4978 | | |
4979 | 29 | FieldCount += AddIFDExifFields(tiff, ifds_handled, profile_data, profile_data+Value, |
4980 | 29 | profile_length, logging, Flags|FLAG_BASE); |
4981 | 29 | if (logging) |
4982 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"Recursing out of GPS IFD..."); |
4983 | 29 | } |
4984 | 234 | goto NextItem; |
4985 | 342 | } |
4986 | | |
4987 | 2.94k | if (fip != NULL && (Flags & FLAG_BASE)!=0) /* libtiff doesn't understand these */ |
4988 | 20 | { |
4989 | 20 | const TIFFDataType FDT = TIFFFieldDataType(fip); |
4990 | 20 | const int WriteCount = TIFFFieldWriteCount(fip); |
4991 | 20 | switch (Field) |
4992 | 20 | { |
4993 | 0 | case TIFF_ASCII: |
4994 | 0 | if (FDT != TIFF_ASCII) |
4995 | 0 | break; /* Incompatible recipe.*/ |
4996 | 0 | if (Long2 <= 4) |
4997 | 0 | { |
4998 | 0 | if (CheckAndStoreStr(tiff, Tag, (const char *) IFD_data+8, Long2)) /* The short string is inside Value. */ |
4999 | 0 | FieldCount++; |
5000 | 0 | } |
5001 | 0 | else |
5002 | 0 | { |
5003 | 0 | if ((Value > (Value+Long2)) || (Value+Long2 >= profile_length-1)) |
5004 | 0 | break; /* String outside EXIF boundary. */ |
5005 | 0 | if (CheckAndStoreStr(tiff, Tag, (const char *) profile_data+Value, Long2)) |
5006 | 0 | FieldCount++; |
5007 | 0 | } |
5008 | 0 | break; |
5009 | | |
5010 | 0 | case TIFF_SHORT: |
5011 | 0 | if (WriteCount!=1) |
5012 | 0 | { |
5013 | 0 | magick_uint16_t *Array; |
5014 | 0 | magick_uint32_t i; |
5015 | 0 | if (FDT != Field) |
5016 | 0 | break; /* Incompatible array type, might be converted in future. */ |
5017 | 0 | if (WriteCount != TIFF_VARIABLE && WriteCount != TIFF_VARIABLE2) |
5018 | 0 | { |
5019 | 0 | if (logging && (Flags & FLAG_BASE) != 0) |
5020 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"%s fixed size arrays are not supported yet.", |
5021 | 0 | FipFieldTypeToStr((TIFFDataType) Field)); |
5022 | 0 | break; /* Fixed size arrays not handled. */ |
5023 | 0 | } |
5024 | 0 | if ((Value > (Value+2U*Long2)) || (Value+2U*Long2 >= profile_length-1)) |
5025 | 0 | break; |
5026 | 0 | if (Long2 == 0) |
5027 | 0 | break; |
5028 | 0 | Array = MagickAllocateResourceLimitedMemory(magick_uint16_t *, 2*Long2); |
5029 | 0 | if (Array == NULL) |
5030 | 0 | break; |
5031 | 0 | for (i=0; i < Long2; i++) |
5032 | 0 | Array[i] = LD_UINT16(profile_data+Value+2*i); |
5033 | 0 | if (WriteCount == TIFF_VARIABLE) |
5034 | 0 | { |
5035 | 0 | if (TIFFSetField(tiff, Tag, (int)Long2, Array)) /* Argument 3 type int, argument 4 uint16_t*. */ |
5036 | 0 | FieldCount++; |
5037 | 0 | } else if (WriteCount == TIFF_VARIABLE2) |
5038 | 0 | { |
5039 | 0 | if (TIFFSetField(tiff, Tag, Long2, Array)) /* Argument 3 type uint32_t, argument 4 uint16_t*.. */ |
5040 | 0 | FieldCount++; |
5041 | 0 | } |
5042 | 0 | MagickFreeResourceLimitedMemory(magick_uint16_t *,Array); |
5043 | 0 | break; |
5044 | 0 | } |
5045 | 0 | goto Scalar; |
5046 | | |
5047 | 0 | case TIFF_LONG: |
5048 | 0 | if (WriteCount != 1) |
5049 | 0 | { |
5050 | 0 | magick_uint32_t *Array; |
5051 | 0 | magick_uint32_t i; |
5052 | 0 | if (FDT != Field) |
5053 | 0 | break; /* Incompatible array type, might be converted in future. */ |
5054 | 0 | if (WriteCount != TIFF_VARIABLE && WriteCount != TIFF_VARIABLE2) |
5055 | 0 | { |
5056 | 0 | if (logging && (Flags & FLAG_BASE) != 0) |
5057 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"%s fixed size arrays are not supported yet.", |
5058 | 0 | FipFieldTypeToStr((TIFFDataType) Field)); |
5059 | 0 | break; /* Fixed size arrays not handled. */ |
5060 | 0 | } |
5061 | 0 | if ((Value > (Value+4U*Long2)) || (Value+4U*Long2 >= profile_length-1)) |
5062 | 0 | break; |
5063 | 0 | if (Long2==0) |
5064 | 0 | break; |
5065 | 0 | Array = MagickAllocateResourceLimitedMemory(magick_uint32_t *, 4*Long2); |
5066 | 0 | if (Array == NULL) |
5067 | 0 | break; |
5068 | 0 | for (i=0; i < Long2; i++) |
5069 | 0 | Array[i] = LD_UINT32(profile_data+Value+4*i); |
5070 | 0 | if (WriteCount == TIFF_VARIABLE) |
5071 | 0 | { |
5072 | 0 | if (TIFFSetField(tiff, Tag, (int)Long2, Array)) /* Argument 3 type int, argument 4 uint32_t*. */ |
5073 | 0 | FieldCount++; |
5074 | 0 | } else if (WriteCount == TIFF_VARIABLE2) |
5075 | 0 | { |
5076 | 0 | if (TIFFSetField(tiff, Tag, Long2, Array)) /* Argument 3 type uint32_t, argument 4 uint32_t*. */ |
5077 | 0 | FieldCount++; |
5078 | 0 | } |
5079 | 0 | MagickFreeResourceLimitedMemory(magick_uint32_t *,Array); |
5080 | 0 | break; |
5081 | 0 | } |
5082 | 0 | goto Scalar; |
5083 | | |
5084 | 0 | case TIFF_BYTE: |
5085 | 0 | if (WriteCount != 1) |
5086 | 0 | { |
5087 | 0 | if (FDT != Field) |
5088 | 0 | break; /* Incompatible array type, might be converted in future. */ |
5089 | 0 | if (WriteCount != TIFF_VARIABLE && WriteCount != TIFF_VARIABLE2) |
5090 | 0 | { |
5091 | 0 | if ((WriteCount <= 0) || (Long2 < (magick_uint32_t)WriteCount)) |
5092 | 0 | break; /* Too small amount of mandatory items. */ |
5093 | 0 | if (Long2 < (magick_uint32_t)WriteCount) |
5094 | 0 | break; /* Too small amount of mandatory items. */ |
5095 | 0 | if (Long2 <= 4) |
5096 | 0 | { |
5097 | 0 | if (TIFFSetField(tiff, Tag, IFD_data+8)) /* Argument 3 uint8_t[4]. */ |
5098 | 0 | FieldCount++; |
5099 | 0 | } |
5100 | 0 | else |
5101 | 0 | { |
5102 | 0 | if ((Value > (Value+Long2)) || (Value+Long2 >= profile_length-1)) |
5103 | 0 | break; |
5104 | 0 | if (TIFFSetField(tiff, Tag, profile_data+Value)) /* Argument 3 uint8_t[4]. */ |
5105 | 0 | FieldCount++; |
5106 | 0 | } |
5107 | 0 | break; /* Fixed size arrays not handled. */ |
5108 | 0 | } |
5109 | 0 | if ((Value > (Value+Long2)) || (Value+Long2 >= profile_length-1)) |
5110 | 0 | break; |
5111 | | /* No need to convert endianity for BYTES. */ |
5112 | 0 | if (WriteCount == TIFF_VARIABLE) |
5113 | 0 | { |
5114 | 0 | if (TIFFSetField(tiff, Tag, (int)Long2, profile_data+Value)) /* Argument 3 type int, argument 4 uint8_t*. */ |
5115 | 0 | FieldCount++; |
5116 | 0 | } else if (WriteCount == TIFF_VARIABLE2) |
5117 | 0 | { |
5118 | 0 | if (TIFFSetField(tiff, Tag, Long2, profile_data+Value)) /* Argument 3 type uint32_t, argument 4 uint8_t*. */ |
5119 | 0 | FieldCount++; |
5120 | 0 | } |
5121 | 0 | break; |
5122 | 0 | } |
5123 | | |
5124 | 0 | Scalar: |
5125 | 0 | if (FDT == TIFF_SHORT) |
5126 | 0 | { |
5127 | 0 | if (TIFFSetField(tiff, Tag, (unsigned)Value & 0xFFFF)) |
5128 | 0 | FieldCount++; |
5129 | 0 | } |
5130 | 0 | if (FDT != TIFF_BYTE && FDT != TIFF_LONG) |
5131 | 0 | break; |
5132 | 0 | if (TIFFSetField(tiff, Tag, Value)) |
5133 | 0 | FieldCount++; |
5134 | 0 | break; |
5135 | | |
5136 | 0 | case TIFF_SRATIONAL: |
5137 | 0 | if (logging && (Flags & FLAG_BASE) != 0) |
5138 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"TIFF_SRATIONAL type is not supported yet."); |
5139 | 0 | break; |
5140 | 19 | case TIFF_RATIONAL: |
5141 | 19 | if (FDT != TIFF_RATIONAL) |
5142 | 0 | break; |
5143 | 19 | if (WriteCount != 1) |
5144 | 19 | { |
5145 | 19 | if (WriteCount > 1) |
5146 | 19 | { |
5147 | 19 | if (Long2 < (magick_uint32_t)WriteCount) |
5148 | 16 | break; /* Too small amount of mandatory items. */ |
5149 | 3 | if ((Value > (Value+8*WriteCount)) || (Value+8*WriteCount >= profile_length-1)) |
5150 | 3 | break; /* Array falls over blob boundary. */ |
5151 | 0 | #if TIFFLIB_VERSION >= 20230609 |
5152 | 0 | { |
5153 | 0 | int i; |
5154 | 0 | switch (TIFFFieldSetGetSize(fip)) |
5155 | 0 | { |
5156 | 0 | case 8: /* double array is required in input. */ |
5157 | 0 | { |
5158 | 0 | double *ArrayD; |
5159 | 0 | ArrayD = MagickAllocateResourceLimitedMemory(double *, sizeof(double)*WriteCount); |
5160 | 0 | if (ArrayD == NULL) |
5161 | 0 | break; |
5162 | 0 | for (i=0; i < WriteCount; i++) |
5163 | 0 | { |
5164 | 0 | const magick_uint32_t val = LD_UINT32(profile_data+Value+4+8*i); |
5165 | 0 | ArrayD[i] = (val==0) ? 0.0 : (LD_UINT32(profile_data+Value+8*i) / (double)val); |
5166 | 0 | } |
5167 | 0 | if (TIFFSetField(tiff, Tag, ArrayD)) |
5168 | 0 | FieldCount++; |
5169 | 0 | MagickFreeResourceLimitedMemory(double *,ArrayD); |
5170 | 0 | } |
5171 | 0 | break; |
5172 | 0 | case 4: /* float array is required in input. */ |
5173 | 0 | { |
5174 | 0 | float *ArrayF; |
5175 | 0 | ArrayF = MagickAllocateResourceLimitedMemory(float *, sizeof(float)*WriteCount); |
5176 | 0 | if (ArrayF==NULL) |
5177 | 0 | break; |
5178 | 0 | for (i=0; i < WriteCount; i++) |
5179 | 0 | { |
5180 | 0 | const magick_uint32_t val = LD_UINT32(profile_data+Value+4+8*i); |
5181 | 0 | ArrayF[i] = (val==0) ? 0.0f : (LD_UINT32(profile_data+Value+8*i) / (float)val); |
5182 | 0 | } |
5183 | 0 | if (TIFFSetField(tiff, Tag, ArrayF)) |
5184 | 0 | FieldCount++; |
5185 | 0 | MagickFreeResourceLimitedMemory(float *,ArrayF); |
5186 | 0 | } |
5187 | 0 | break; |
5188 | 0 | } |
5189 | 0 | } |
5190 | 0 | #endif |
5191 | 0 | break; |
5192 | 0 | } |
5193 | | |
5194 | 0 | if (WriteCount == TIFF_VARIABLE || WriteCount == TIFF_VARIABLE2) |
5195 | 0 | { |
5196 | 0 | if (logging && (Flags & FLAG_BASE) != 0) |
5197 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(),"Variable size fractional arrays are not supported yet."); |
5198 | 0 | break; |
5199 | 0 | } |
5200 | 0 | break; |
5201 | 0 | } |
5202 | 0 | else /* Process as scalar. */ |
5203 | 0 | { |
5204 | 0 | double d; |
5205 | 0 | if ((Value > (Value+8U)) || (Value+8U >= profile_length)) |
5206 | 0 | break; |
5207 | 0 | d = LD_UINT32(profile_data+Value+4); |
5208 | 0 | if (d == 0) |
5209 | 0 | break; /* Prevent division by 0. */ |
5210 | 0 | d = LD_UINT32(profile_data+Value) / d; |
5211 | 0 | if (TIFFSetField(tiff, Tag, d)) |
5212 | 0 | FieldCount++; |
5213 | 0 | } |
5214 | 0 | break; |
5215 | 20 | } |
5216 | 20 | } |
5217 | | |
5218 | 3.86k | NextItem: |
5219 | 3.86k | if (profile_length <= 12) |
5220 | 0 | break; |
5221 | 3.86k | IFD_data += 12; |
5222 | 3.86k | EntryNum--; |
5223 | 3.86k | } |
5224 | | |
5225 | 169 | if (profile_length < (size_t)(IFD_data-profile_data)+4) |
5226 | 34 | break; |
5227 | 135 | Value = LD_UINT32(IFD_data); |
5228 | 135 | if (Value >= profile_length) |
5229 | 95 | break; |
5230 | 40 | IFD_data = profile_data+Value; |
5231 | | /* |
5232 | | It is possible for the calculated IFD_data to create a |
5233 | | non-terminal loop by computing the same IFD or one earlier in |
5234 | | IFD list. Defend against this by placing a limit on loops, as |
5235 | | well as detecting the immediate looping case. |
5236 | | */ |
5237 | 40 | IFD_data_Loops++; |
5238 | 40 | if (Orig_IFD_data == IFD_data) |
5239 | 2 | { |
5240 | 2 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
5241 | 2 | "IFD direct loop detected!"); |
5242 | 2 | continue_looping = MagickFalse; /* Break looping */ |
5243 | 2 | } |
5244 | 40 | if (IFD_data_Loops == Max_IFD_data_Loops) |
5245 | 0 | continue_looping = MagickFalse; |
5246 | 40 | } while ((continue_looping) && (Value > 8)); |
5247 | | |
5248 | 139 | if ((logging && (Flags & FLAG_BASE) != 0) && (!continue_looping)) |
5249 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
5250 | 0 | "IFD looping detected (%zu loops)!", IFD_data_Loops); |
5251 | | |
5252 | 139 | return FieldCount; |
5253 | 242 | } |
5254 | | |
5255 | | |
5256 | | static int AddExifFields(TIFF *tiff, const unsigned char *profile_data, |
5257 | | size_t profile_length, MagickBool logging, |
5258 | | magick_uint16_t Flags) |
5259 | 231 | { |
5260 | 231 | const char EXIF[6] |
5261 | 231 | = {'E','x','i','f',0,0}; |
5262 | | |
5263 | 231 | unsigned int |
5264 | 231 | ifds_handled = 0; |
5265 | | |
5266 | 231 | size_t |
5267 | 231 | profile_data_len; |
5268 | | |
5269 | 231 | if (profile_data==NULL || profile_length < 12+8) |
5270 | 3 | return 0; |
5271 | | |
5272 | 228 | if (memcmp(EXIF,profile_data,6)==0) |
5273 | 225 | { |
5274 | 225 | profile_data += 6; |
5275 | 225 | profile_length -= 6; |
5276 | 225 | if (profile_length < 12+8) |
5277 | 3 | return 0; |
5278 | 225 | } |
5279 | | |
5280 | 225 | if (profile_data[0] != profile_data[1]) |
5281 | 9 | return 0; |
5282 | | |
5283 | 216 | profile_data_len = (size_t)((profile_data[0]=='M') ? |
5284 | 177 | LD_UINT32_HI(profile_data+4) : |
5285 | 216 | LD_UINT32_LO(profile_data+4)); |
5286 | 216 | if (logging) |
5287 | 0 | (void)LogMagickEvent(CoderEvent,GetMagickModule(), |
5288 | 0 | "Profile data length %zu", profile_data_len); |
5289 | | |
5290 | 216 | return AddIFDExifFields(tiff, &ifds_handled, profile_data, |
5291 | 216 | profile_data + profile_data_len, |
5292 | 216 | profile_length-2, logging, Flags); |
5293 | 225 | } |
5294 | | |
5295 | | #endif /* if TIFFLIB_VERSION >= 20120922 */ |
5296 | | #endif /* if EXPERIMENTAL_EXIF_TAGS */ |
5297 | | |
5298 | | /* |
5299 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
5300 | | % % |
5301 | | % % |
5302 | | % % |
5303 | | % W r i t e T I F F I m a g e % |
5304 | | % % |
5305 | | % % |
5306 | | % % |
5307 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
5308 | | % |
5309 | | % Method WriteTIFFImage writes an image in the Tagged image file format. |
5310 | | % |
5311 | | % The format of the WriteTIFFImage method is: |
5312 | | % |
5313 | | % MagickPassFail WriteTIFFImage(const ImageInfo *image_info,Image *image) |
5314 | | % |
5315 | | % A description of each parameter follows: |
5316 | | % |
5317 | | % o status: Method WriteTIFFImage return True if the image is written. |
5318 | | % False is returned is there is of a memory shortage or if the image |
5319 | | % file cannot be opened for writing. |
5320 | | % |
5321 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
5322 | | % |
5323 | | % o image: A pointer to an Image structure. |
5324 | | % |
5325 | | % |
5326 | | */ |
5327 | | |
5328 | | static void |
5329 | | WriteNewsProfile(TIFF *tiff, |
5330 | | int profile_tag, |
5331 | | const unsigned char *profile_data, |
5332 | | const size_t profile_length) |
5333 | 370 | { |
5334 | 370 | unsigned char |
5335 | 370 | *profile=0; |
5336 | | |
5337 | 370 | size_t |
5338 | 370 | length; |
5339 | | |
5340 | 370 | assert(tiff != (TIFF *) NULL); |
5341 | 370 | assert(profile_tag != 0); |
5342 | 370 | assert(profile_data != (const unsigned char *) NULL); |
5343 | | |
5344 | 370 | length = (uint32) profile_length; |
5345 | 370 | if (length == 0) |
5346 | 0 | return; |
5347 | | |
5348 | 370 | if (profile_tag == TIFFTAG_RICHTIFFIPTC) |
5349 | 0 | { |
5350 | | /* |
5351 | | Handle TIFFTAG_RICHTIFFIPTC tag. |
5352 | | */ |
5353 | 0 | length += (4-(length & 0x03)); /* Round up for long word alignment */ |
5354 | 0 | profile=MagickAllocateResourceLimitedClearedMemory(unsigned char *,length); |
5355 | 0 | if (profile == (unsigned char *) NULL) |
5356 | 0 | return; |
5357 | 0 | (void) memcpy(profile,profile_data,profile_length); |
5358 | |
|
5359 | 0 | if (TIFFIsByteSwapped(tiff)) |
5360 | 0 | TIFFSwabArrayOfLong((uint32 *) profile,length/4); |
5361 | | |
5362 | | /* Tag is type TIFF_LONG so byte length is divided by four */ |
5363 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5364 | 0 | "TIFFSetField(tiff=0x%p,tag=%d," |
5365 | 0 | "length=%" MAGICK_SIZE_T_F "u,data=0x%p)", |
5366 | 0 | tiff,profile_tag,(MAGICK_SIZE_T) length/4,profile); |
5367 | 0 | (void) TIFFSetField(tiff,profile_tag,(uint32) length/4,(void *) profile); |
5368 | 0 | } |
5369 | 370 | else if (profile_tag == TIFFTAG_PHOTOSHOP) |
5370 | 370 | { |
5371 | | /* |
5372 | | Handle TIFFTAG_PHOTOSHOP tag. |
5373 | | */ |
5374 | 370 | length += (length & 0x01); /* Round up for Photoshop */ |
5375 | | #if defined(GET_ONLY_IPTC_DATA) |
5376 | | length += 12; /* Space for 8BIM header */ |
5377 | | profile=MagickAllocateResourceLimitedClearedMemory(unsigned char *,length); |
5378 | | if (profile == (unsigned char *) NULL) |
5379 | | return; |
5380 | | (void) memcpy(profile,"8BIM\04\04\0\0",8); |
5381 | | profile[8]=(length >> 24) & 0xff; |
5382 | | profile[9]=(length >> 16) & 0xff; |
5383 | | profile[10]=(length >> 8) & 0xff; |
5384 | | profile[11]=length & 0xff; |
5385 | | (void) memcpy(profile+12,profile_data,profile_length); |
5386 | | #else |
5387 | 370 | profile=MagickAllocateResourceLimitedClearedMemory(unsigned char *,length); |
5388 | 370 | if (profile == (unsigned char *) NULL) |
5389 | 0 | return; |
5390 | 370 | (void) memcpy(profile,profile_data,profile_length); |
5391 | 370 | #endif |
5392 | 370 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5393 | 370 | "TIFFSetField(tiff=0x%p,tag=%d," |
5394 | 370 | "length=%" MAGICK_SIZE_T_F "u,data=0x%p)", |
5395 | 370 | tiff,profile_tag,(MAGICK_SIZE_T) length,profile); |
5396 | 370 | (void) TIFFSetField(tiff,profile_tag,(uint32) length,(void *) profile); |
5397 | 370 | } |
5398 | | |
5399 | 370 | MagickFreeResourceLimitedMemory(unsigned char *,profile); |
5400 | 370 | } |
5401 | | |
5402 | 0 | #define ThrowTIFFWriterException(code_,reason_,image_) \ |
5403 | 0 | { \ |
5404 | 0 | if (tiff != (TIFF *) NULL) \ |
5405 | 0 | TIFFClose(tiff); \ |
5406 | 0 | ThrowWriterException(code_,reason_,image_); \ |
5407 | 0 | } |
5408 | | |
5409 | | static MagickPassFail |
5410 | | WriteTIFFImage(const ImageInfo *image_info,Image *image) |
5411 | 27.4k | { |
5412 | 27.4k | char |
5413 | 27.4k | filename[MaxTextExtent], |
5414 | 27.4k | open_flags[MaxTextExtent]; |
5415 | | |
5416 | 27.4k | const ImageAttribute |
5417 | 27.4k | *attribute; |
5418 | | |
5419 | 27.4k | unsigned int |
5420 | 27.4k | x, |
5421 | 27.4k | y; |
5422 | | |
5423 | 27.4k | register unsigned int |
5424 | 27.4k | i; |
5425 | | |
5426 | 27.4k | TIFF |
5427 | 27.4k | *tiff = (TIFF *) NULL; |
5428 | | |
5429 | 27.4k | size_t |
5430 | 27.4k | bytes_per_strip_target = TIFF_BYTES_PER_STRIP; |
5431 | | |
5432 | 27.4k | uint16 |
5433 | 27.4k | bits_per_sample, |
5434 | 27.4k | compress_tag, |
5435 | 27.4k | fill_order, |
5436 | 27.4k | photometric, |
5437 | 27.4k | planar_config, |
5438 | 27.4k | predictor, |
5439 | 27.4k | sample_format, |
5440 | 27.4k | samples_per_pixel; |
5441 | | |
5442 | 27.4k | uint32 |
5443 | 27.4k | rows_per_strip; |
5444 | | |
5445 | 27.4k | tsize_t |
5446 | 27.4k | scanline_size; |
5447 | | |
5448 | 27.4k | AlphaType |
5449 | 27.4k | alpha_type; |
5450 | | |
5451 | 27.4k | CompressionType |
5452 | 27.4k | compression=UndefinedCompression; |
5453 | | |
5454 | 27.4k | TIFFMethod |
5455 | 27.4k | method; |
5456 | | |
5457 | 27.4k | Magick_TIFF_ClientData |
5458 | 27.4k | client_data; |
5459 | | |
5460 | 27.4k | ExportPixelAreaOptions |
5461 | 27.4k | export_options; |
5462 | | |
5463 | 27.4k | ExportPixelAreaInfo |
5464 | 27.4k | export_info; |
5465 | | |
5466 | 27.4k | MagickBool |
5467 | 27.4k | logging=MagickFalse; |
5468 | | |
5469 | 27.4k | MagickPassFail |
5470 | 27.4k | status; |
5471 | | |
5472 | 27.4k | unsigned long |
5473 | 27.4k | depth, |
5474 | 27.4k | scene; |
5475 | | |
5476 | 27.4k | size_t |
5477 | 27.4k | image_list_length; |
5478 | | |
5479 | | |
5480 | | |
5481 | | /* |
5482 | | Open TIFF file. |
5483 | | */ |
5484 | 27.4k | assert(image_info != (const ImageInfo *) NULL); |
5485 | 27.4k | assert(image_info->signature == MagickSignature); |
5486 | 27.4k | assert(image != (Image *) NULL); |
5487 | 27.4k | assert(image->signature == MagickSignature); |
5488 | 27.4k | image_list_length=GetImageListLength(image); |
5489 | 27.4k | logging=IsEventLogged(CoderEvent); |
5490 | 27.4k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
5491 | 27.4k | if (status == MagickFail) |
5492 | 27.4k | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
5493 | 27.4k | (void) MagickTsdSetSpecific(tsd_key,(void *) (&image->exception)); |
5494 | 27.4k | (void) TIFFSetErrorHandler((TIFFErrorHandler) TIFFWriteErrorsHandler); |
5495 | 27.4k | (void) TIFFSetWarningHandler((TIFFErrorHandler) (CheckThrowWarnings(image_info) ? |
5496 | 0 | TIFFWarningsThrowException : |
5497 | 27.4k | TIFFWarningsLogOnly)); |
5498 | 27.4k | (void) strlcpy(filename,image->filename,MaxTextExtent); |
5499 | | /* |
5500 | | Open TIFF file |
5501 | | |
5502 | | 'w' open for write |
5503 | | 'l' force little-endian byte order |
5504 | | 'b' force big-endian byte order |
5505 | | 'L' force LSB to MSB bit order (weird) |
5506 | | 'B' force MSB to LSB bit order (normal) |
5507 | | '8' 64-bit offsets (BigTIFF) |
5508 | | */ |
5509 | 27.4k | (void) strlcpy(open_flags, "w", sizeof(open_flags)); |
5510 | 27.4k | switch (image_info->endian) |
5511 | 27.4k | { |
5512 | 0 | case LSBEndian: |
5513 | 0 | (void) strlcat(open_flags, "l", sizeof(open_flags)); |
5514 | 0 | if (logging) |
5515 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5516 | 0 | "Using little endian byte order"); |
5517 | 0 | break; |
5518 | 0 | case MSBEndian: |
5519 | 0 | (void) strlcat(open_flags, "b", sizeof(open_flags)); |
5520 | 0 | if (logging) |
5521 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5522 | 0 | "Using big endian byte order"); |
5523 | 0 | break; |
5524 | 0 | default: |
5525 | 27.4k | case UndefinedEndian: |
5526 | 27.4k | { |
5527 | | /* Default is native byte order */ |
5528 | 27.4k | if (logging) |
5529 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5530 | 0 | "Using native endian byte order"); |
5531 | 27.4k | } |
5532 | 27.4k | } |
5533 | | |
5534 | 27.4k | #if defined(HasBigTIFF) |
5535 | 27.4k | if (strcmp(image_info->magick,"BIGTIFF") == 0) |
5536 | 8.07k | { |
5537 | 8.07k | (void) strlcat(open_flags, "8", sizeof(open_flags)); |
5538 | 8.07k | if (logging) |
5539 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5540 | 0 | "Using 64-bit offsets (BigTIFF format)"); |
5541 | 8.07k | } |
5542 | 27.4k | #endif |
5543 | | |
5544 | 27.4k | if (logging) |
5545 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5546 | 0 | "Opening TIFF file \"%s\" using open flags \"%s\".", |
5547 | 0 | filename,open_flags); |
5548 | 27.4k | client_data.image=image; |
5549 | 27.4k | client_data.image_info=image_info; |
5550 | 27.4k | #if defined(HAVE_TIFFERROREXTR) |
5551 | 27.4k | { |
5552 | 27.4k | TIFFOpenOptions *open_options = TIFFOpenOptionsAlloc(); |
5553 | 27.4k | TIFFOpenOptionsSetErrorHandlerExtR(open_options, TIFFWriteErrorsHandlerExtR, (void *) &image->exception); |
5554 | 27.4k | TIFFOpenOptionsSetWarningHandlerExtR(open_options, TIFFWriteErrorsHandlerExtR, (void *) &image->exception); /* FIXME */ |
5555 | 27.4k | tiff=TIFFClientOpenExt(filename,open_flags,(thandle_t) &client_data, |
5556 | 27.4k | TIFFReadBlob,TIFFWriteBlob,TIFFSeekBlob, |
5557 | 27.4k | TIFFCloseBlob,TIFFGetBlobSize,TIFFMapBlob, |
5558 | 27.4k | TIFFUnmapBlob, |
5559 | 27.4k | open_options); |
5560 | 27.4k | TIFFOpenOptionsFree(open_options); |
5561 | 27.4k | } |
5562 | | #else |
5563 | | { |
5564 | | tiff=TIFFClientOpen(filename,open_flags,(thandle_t) &client_data, |
5565 | | TIFFReadBlob,TIFFWriteBlob,TIFFSeekBlob, |
5566 | | TIFFCloseBlob,TIFFGetBlobSize,TIFFMapBlob, |
5567 | | TIFFUnmapBlob); |
5568 | | } |
5569 | | #endif /* if defined(HAVE_TIFFERROREXTR) */ |
5570 | 27.4k | if (tiff == (TIFF *) NULL) |
5571 | 0 | { |
5572 | 0 | if (GetBlobIsOpen(image)) |
5573 | 0 | CloseBlob(image); |
5574 | 0 | return(MagickFail); |
5575 | 0 | } |
5576 | 27.4k | scene=0; |
5577 | 27.4k | do |
5578 | 34.3k | { |
5579 | 34.3k | ImageCharacteristics |
5580 | 34.3k | characteristics; |
5581 | | |
5582 | 34.3k | if ((image->columns == 0) || (image->rows == 0) || !GetPixelCachePresent(image)) |
5583 | 0 | { |
5584 | 0 | if (logging) |
5585 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5586 | 0 | "Image columns=%lu, rows=%lu, pixel-cache=%s", |
5587 | 0 | image->columns, image->rows, |
5588 | 0 | GetPixelCachePresent(image) ? "Present" : "Missing!"); |
5589 | 0 | ThrowException(&(image->exception),CoderError,ImageColumnOrRowSizeIsNotSupported,image->filename); |
5590 | 0 | break; |
5591 | 0 | } |
5592 | | |
5593 | | /* |
5594 | | Initialize TIFF fields. |
5595 | | */ |
5596 | 34.3k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLESPERPIXEL, |
5597 | 34.3k | &samples_per_pixel); |
5598 | 34.3k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_BITSPERSAMPLE, |
5599 | 34.3k | &bits_per_sample); |
5600 | 34.3k | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_SAMPLEFORMAT,&sample_format); |
5601 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_IMAGELENGTH,(uint32) image->rows); |
5602 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_IMAGEWIDTH,(uint32) image->columns); |
5603 | | |
5604 | 34.3k | ExportPixelAreaOptionsInit(&export_options); |
5605 | 34.3k | depth=image->depth; |
5606 | 34.3k | bits_per_sample=8; |
5607 | 34.3k | predictor=0U; |
5608 | 34.3k | method=ScanLineMethod; |
5609 | 34.3k | if ((AccessDefinition(image_info,"tiff","tile")) || |
5610 | 34.3k | (AccessDefinition(image_info,"tiff","tile-geometry")) || |
5611 | 34.3k | (AccessDefinition(image_info,"tiff","tile-width")) || |
5612 | 34.3k | (AccessDefinition(image_info,"tiff","tile-height"))) |
5613 | 0 | method=TiledMethod; |
5614 | | |
5615 | | /* |
5616 | | Decide how to compress the image. |
5617 | | */ |
5618 | 34.3k | compression=image->compression; |
5619 | 34.3k | if (image_info->compression != UndefinedCompression) |
5620 | 1.67k | compression=image_info->compression; |
5621 | 34.3k | if (UndefinedCompression == compression) |
5622 | 2.78k | compression=NoCompression; |
5623 | | |
5624 | | /* |
5625 | | Ensure that only supported compression types are requested. |
5626 | | */ |
5627 | 34.3k | { |
5628 | 34.3k | char |
5629 | 34.3k | compression_name[MaxTextExtent]; |
5630 | | |
5631 | 34.3k | if (CompressionSupported(compression,compression_name,sizeof(compression_name)) != MagickTrue) |
5632 | 0 | { |
5633 | 0 | compression=NoCompression; |
5634 | 0 | if (logging) |
5635 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5636 | 0 | "%s compression not supported. " |
5637 | 0 | "Compression request removed", |
5638 | 0 | compression_name); |
5639 | 0 | } |
5640 | 34.3k | } |
5641 | | |
5642 | | /* |
5643 | | Determine libtiff compression settings. |
5644 | | */ |
5645 | 34.3k | compress_tag=COMPRESSION_NONE; |
5646 | 34.3k | fill_order=FILLORDER_MSB2LSB; |
5647 | 34.3k | switch (compression) |
5648 | 34.3k | { |
5649 | | /* |
5650 | | Note that RFC 2301 recommends using LSB2MSB fill order for FAX |
5651 | | since it is the transmission order used on the wire for FAX. |
5652 | | However, it also states that all conforming readers should be |
5653 | | able to read data in both bit orders. |
5654 | | */ |
5655 | 1.58k | case FaxCompression: |
5656 | 1.58k | { |
5657 | 1.58k | compress_tag=COMPRESSION_CCITTFAX3; |
5658 | 1.58k | fill_order=FILLORDER_LSB2MSB; |
5659 | 1.58k | break; |
5660 | 0 | } |
5661 | 4.95k | case Group4Compression: |
5662 | 4.95k | { |
5663 | 4.95k | compress_tag=COMPRESSION_CCITTFAX4; |
5664 | 4.95k | fill_order=FILLORDER_LSB2MSB; |
5665 | 4.95k | break; |
5666 | 0 | } |
5667 | 0 | #if defined(COMPRESSION_JBIG) |
5668 | 0 | case JBIG1Compression: |
5669 | 0 | { |
5670 | 0 | compress_tag=COMPRESSION_JBIG; |
5671 | 0 | fill_order=FILLORDER_LSB2MSB; |
5672 | 0 | break; |
5673 | 0 | } |
5674 | 0 | #endif /* defined(COMPRESSION_JBIG) */ |
5675 | 8.15k | case JPEGCompression: |
5676 | 8.15k | { |
5677 | 8.15k | compress_tag=COMPRESSION_JPEG; |
5678 | 8.15k | break; |
5679 | 0 | } |
5680 | 750 | case LZWCompression: |
5681 | 750 | { |
5682 | 750 | compress_tag=COMPRESSION_LZW; |
5683 | 750 | break; |
5684 | 0 | } |
5685 | 0 | #if defined(COMPRESSION_LZMA) |
5686 | 0 | case LZMACompression: |
5687 | 0 | { |
5688 | 0 | compress_tag=COMPRESSION_LZMA; |
5689 | 0 | break; |
5690 | 0 | } |
5691 | 0 | #endif /* defined(COMPRESSION_LZMA) */ |
5692 | 0 | case RLECompression: |
5693 | 0 | { |
5694 | 0 | compress_tag=COMPRESSION_PACKBITS; |
5695 | 0 | break; |
5696 | 0 | } |
5697 | 3.52k | case ZipCompression: |
5698 | 3.52k | { |
5699 | 3.52k | compress_tag=COMPRESSION_ADOBE_DEFLATE; |
5700 | 3.52k | break; |
5701 | 0 | } |
5702 | 0 | #if defined(COMPRESSION_ZSTD) |
5703 | 2.42k | case ZSTDCompression: |
5704 | 2.42k | { |
5705 | 2.42k | compress_tag=COMPRESSION_ZSTD; |
5706 | 2.42k | break; |
5707 | 0 | } |
5708 | 0 | #endif /* defined(COMPRESSION_ZSTD) */ |
5709 | 0 | #if defined(COMPRESSION_WEBP) |
5710 | 3.12k | case WebPCompression: |
5711 | 3.12k | { |
5712 | 3.12k | compress_tag=COMPRESSION_WEBP; |
5713 | 3.12k | break; |
5714 | 0 | } |
5715 | 0 | #endif /* defined(COMPRESSION_WEBP) */ |
5716 | 0 | #if defined(COMPRESSION_LERC) |
5717 | 0 | case LERCCompression: |
5718 | 0 | { |
5719 | 0 | compress_tag=COMPRESSION_LERC; |
5720 | 0 | break; |
5721 | 0 | } |
5722 | 0 | #endif /* defined(COMPRESSION_LERC) */ |
5723 | 9.85k | default: |
5724 | 9.85k | { |
5725 | 9.85k | compress_tag=COMPRESSION_NONE; |
5726 | 9.85k | break; |
5727 | 0 | } |
5728 | 34.3k | } |
5729 | | |
5730 | | /* |
5731 | | Ensure that image is in desired output space |
5732 | | */ |
5733 | 34.3k | if ((image_info->type != UndefinedType) && |
5734 | 1.67k | (image_info->type != OptimizeType)) |
5735 | 1.67k | { |
5736 | 1.67k | if (SetImageType(image,image_info->type) == MagickFail) |
5737 | 0 | { |
5738 | 0 | ThrowException(&image->exception,CoderError,UnableToSetImageType, |
5739 | 0 | image->filename); |
5740 | 0 | status=MagickFail; |
5741 | 0 | break; |
5742 | 0 | } |
5743 | 1.67k | } |
5744 | 32.7k | else if (!IsCMYKColorspace(image->colorspace) && |
5745 | 29.9k | (!IsRGBColorspace(image->colorspace))) |
5746 | 3.01k | { |
5747 | 3.01k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
5748 | 0 | { |
5749 | 0 | ThrowException(&image->exception,CoderError,UnableToTransformColorspace, |
5750 | 0 | image->filename); |
5751 | 0 | status=MagickFail; |
5752 | 0 | break; |
5753 | 0 | } |
5754 | 3.01k | } |
5755 | | |
5756 | | /* |
5757 | | Analyze image to be written. |
5758 | | */ |
5759 | 34.3k | if (!GetImageCharacteristics(image,&characteristics, |
5760 | 34.3k | (OptimizeType == image_info->type), |
5761 | 34.3k | &image->exception)) |
5762 | 0 | { |
5763 | 0 | ThrowException(&image->exception,CoderError,UnableToGetImageCharacteristics, |
5764 | 0 | image->filename); |
5765 | 0 | status=MagickFail; |
5766 | 0 | break; |
5767 | 0 | } |
5768 | | |
5769 | 34.3k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5770 | 34.3k | "Image characteristics: cmyk=%c, gray=%c, mono=%c," |
5771 | 34.3k | " opaque=%c, palette=%c", |
5772 | 34.3k | (characteristics.cmyk ? 'y' : 'n'), |
5773 | 34.3k | (characteristics.grayscale ? 'y' : 'n'), |
5774 | 34.3k | (characteristics.monochrome ? 'y' : 'n'), |
5775 | 34.3k | (characteristics.opaque ? 'y' : 'n'), |
5776 | 34.3k | (characteristics.palette ? 'y' : 'n')); |
5777 | | |
5778 | | |
5779 | | /* |
5780 | | Some compression types do not work with a matte channel. |
5781 | | Disable matte channel for these types. |
5782 | | */ |
5783 | 34.3k | if (image->matte) |
5784 | 5.23k | { |
5785 | 5.23k | switch (compress_tag) |
5786 | 5.23k | { |
5787 | 4 | case COMPRESSION_CCITTFAX3: |
5788 | 16 | case COMPRESSION_CCITTFAX4: |
5789 | 16 | case COMPRESSION_JBIG: |
5790 | 74 | case COMPRESSION_JPEG: |
5791 | 74 | { |
5792 | 74 | if (logging) |
5793 | 0 | { |
5794 | 0 | const char * |
5795 | 0 | compress_type; |
5796 | |
|
5797 | 0 | compress_type=CompressionTagToString(compress_tag); |
5798 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5799 | 0 | "Disabled image matte channel since " |
5800 | 0 | "%s compression not supported with " |
5801 | 0 | "alpha channel.", |
5802 | 0 | compress_type); |
5803 | 0 | } |
5804 | 74 | image->matte=MagickFalse; |
5805 | 74 | break; |
5806 | 16 | } |
5807 | 5.16k | default: |
5808 | 5.16k | { |
5809 | 5.16k | } |
5810 | 5.23k | } |
5811 | 5.23k | } |
5812 | | |
5813 | | /* |
5814 | | Choose best photometric based on image properties. |
5815 | | */ |
5816 | 34.3k | if (characteristics.cmyk) |
5817 | 2.74k | { |
5818 | 2.74k | photometric=PHOTOMETRIC_SEPARATED; |
5819 | 2.74k | } |
5820 | 31.6k | else if (characteristics.monochrome) |
5821 | 6.53k | { |
5822 | 6.53k | photometric=PHOTOMETRIC_MINISWHITE; |
5823 | 6.53k | depth=1; |
5824 | 6.53k | } |
5825 | 25.1k | else if (characteristics.palette) |
5826 | 1.27k | { |
5827 | 1.27k | photometric=PHOTOMETRIC_PALETTE; |
5828 | 1.27k | } |
5829 | 23.8k | else if (characteristics.grayscale) |
5830 | 12.3k | { |
5831 | 12.3k | photometric=PHOTOMETRIC_MINISBLACK; |
5832 | 12.3k | } |
5833 | 11.4k | else |
5834 | 11.4k | { |
5835 | 11.4k | photometric=PHOTOMETRIC_RGB; |
5836 | 11.4k | } |
5837 | | |
5838 | | /* |
5839 | | If optimization is requested, disable matte channel if image |
5840 | | is opaque. |
5841 | | */ |
5842 | 34.3k | if ((OptimizeType == image_info->type) && |
5843 | 0 | (characteristics.opaque && image->matte)) |
5844 | 0 | { |
5845 | 0 | image->matte=MagickFalse; |
5846 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5847 | 0 | "Disabled image matte channel since image is " |
5848 | 0 | "opaque."); |
5849 | 0 | } |
5850 | | |
5851 | | /* |
5852 | | Currently we only support JPEG compression with MINISWHITE, |
5853 | | MINISBLACK, and RGB. FAX compression types require |
5854 | | MINISWHITE. CMYK takes precedence over JPEG compression. |
5855 | | */ |
5856 | 34.3k | if ((compress_tag == COMPRESSION_JPEG) && |
5857 | 8.15k | (photometric == PHOTOMETRIC_PALETTE)) |
5858 | 3 | { |
5859 | 3 | photometric=PHOTOMETRIC_RGB; |
5860 | 3 | if (logging) |
5861 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5862 | 0 | "Using RGB photometric due to request for" |
5863 | 0 | " JPEG compression."); |
5864 | 3 | } |
5865 | 34.3k | else if (compress_tag == COMPRESSION_CCITTFAX3) |
5866 | 1.58k | { |
5867 | 1.58k | photometric=PHOTOMETRIC_MINISWHITE; |
5868 | 1.58k | depth=1; |
5869 | 1.58k | if (logging) |
5870 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5871 | 0 | "Using MINISWHITE photometric due to request" |
5872 | 0 | " for Group3 FAX compression."); |
5873 | 1.58k | } |
5874 | 32.7k | else if (compress_tag == COMPRESSION_CCITTFAX4) |
5875 | 4.95k | { |
5876 | 4.95k | photometric=PHOTOMETRIC_MINISWHITE; |
5877 | 4.95k | depth=1; |
5878 | 4.95k | if (logging) |
5879 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5880 | 0 | "Using MINISWHITE photometric due to request" |
5881 | 0 | " for Group4 FAX compression."); |
5882 | 4.95k | } |
5883 | 27.8k | #if defined(COMPRESSION_JBIG) |
5884 | 27.8k | else if (compress_tag == COMPRESSION_JBIG) |
5885 | 0 | { |
5886 | 0 | photometric=PHOTOMETRIC_MINISWHITE; |
5887 | 0 | depth=1; |
5888 | 0 | if (logging) |
5889 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5890 | 0 | "Using MINISWHITE photometric due to request" |
5891 | 0 | " for JBIG compression."); |
5892 | 0 | } |
5893 | 27.8k | #endif /* defined(COMPRESSION_JBIG) */ |
5894 | 27.8k | #if defined(COMPRESSION_WEBP) |
5895 | 27.8k | else if (compress_tag == COMPRESSION_WEBP) |
5896 | 3.12k | { |
5897 | 3.12k | photometric=PHOTOMETRIC_RGB; |
5898 | 3.12k | if (logging) |
5899 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
5900 | 0 | "Using RGB photometric due to request for" |
5901 | 0 | " WebP compression."); |
5902 | 3.12k | } |
5903 | 34.3k | #endif /* defined(COMPRESSION_WEBP) */ |
5904 | | |
5905 | | /* |
5906 | | Allow user to override the photometric. |
5907 | | */ |
5908 | 34.3k | switch (image_info->type) |
5909 | 34.3k | { |
5910 | 1.67k | case BilevelType: |
5911 | 1.67k | { |
5912 | 1.67k | photometric=PHOTOMETRIC_MINISWHITE; |
5913 | 1.67k | depth=1; |
5914 | 1.67k | break; |
5915 | 0 | } |
5916 | 0 | case GrayscaleType: |
5917 | 0 | { |
5918 | 0 | photometric=PHOTOMETRIC_MINISBLACK; |
5919 | 0 | break; |
5920 | 0 | } |
5921 | 0 | case GrayscaleMatteType: |
5922 | 0 | { |
5923 | 0 | photometric=PHOTOMETRIC_MINISBLACK; |
5924 | 0 | if (!image->matte) |
5925 | 0 | SetImageOpacity(image,OpaqueOpacity); |
5926 | 0 | break; |
5927 | 0 | } |
5928 | 0 | case PaletteType: |
5929 | 0 | { |
5930 | 0 | photometric=PHOTOMETRIC_PALETTE; |
5931 | 0 | break; |
5932 | 0 | } |
5933 | 0 | case PaletteMatteType: |
5934 | 0 | { |
5935 | 0 | photometric=PHOTOMETRIC_PALETTE; |
5936 | 0 | if (!image->matte) |
5937 | 0 | SetImageOpacity(image,OpaqueOpacity); |
5938 | 0 | break; |
5939 | 0 | } |
5940 | 0 | case TrueColorType: |
5941 | 0 | { |
5942 | 0 | photometric=PHOTOMETRIC_RGB; |
5943 | 0 | break; |
5944 | 0 | } |
5945 | 0 | case TrueColorMatteType: |
5946 | 0 | { |
5947 | 0 | photometric=PHOTOMETRIC_RGB; |
5948 | 0 | if (!image->matte) |
5949 | 0 | SetImageOpacity(image,OpaqueOpacity); |
5950 | 0 | break; |
5951 | 0 | } |
5952 | 0 | case ColorSeparationType: |
5953 | 0 | { |
5954 | 0 | photometric=PHOTOMETRIC_SEPARATED; |
5955 | 0 | break; |
5956 | 0 | } |
5957 | 0 | case ColorSeparationMatteType: |
5958 | 0 | { |
5959 | 0 | photometric=PHOTOMETRIC_SEPARATED; |
5960 | 0 | if (!image->matte) |
5961 | 0 | SetImageOpacity(image,OpaqueOpacity); |
5962 | 0 | break; |
5963 | 0 | } |
5964 | 32.7k | case UndefinedType: |
5965 | 32.7k | case OptimizeType: |
5966 | 32.7k | { |
5967 | | /* No special handling */ |
5968 | 32.7k | } |
5969 | 34.3k | } |
5970 | | |
5971 | | /* |
5972 | | Allow the user to over-ride the photometric for bilevel and |
5973 | | gray images since 'type' is insufficient for this. |
5974 | | */ |
5975 | 34.3k | if ((PHOTOMETRIC_MINISWHITE == photometric) || |
5976 | 25.5k | (PHOTOMETRIC_MINISBLACK == photometric)) |
5977 | 18.9k | { |
5978 | 18.9k | const char * |
5979 | 18.9k | value; |
5980 | | |
5981 | | /* |
5982 | | Photometric |
5983 | | */ |
5984 | 18.9k | value=AccessDefinition(image_info,"tiff","photometric"); |
5985 | 18.9k | if (value) |
5986 | 0 | { |
5987 | 0 | if (LocaleCompare(value,"miniswhite") == 0) |
5988 | 0 | photometric=PHOTOMETRIC_MINISWHITE; |
5989 | 0 | else if (LocaleCompare(value,"minisblack") == 0) |
5990 | 0 | photometric=PHOTOMETRIC_MINISBLACK; |
5991 | 0 | } |
5992 | 18.9k | } |
5993 | | |
5994 | | /* |
5995 | | If the user has selected something other than MINISWHITE, |
5996 | | MINISBLACK, or RGB, then remove JPEG compression. Also remove |
5997 | | fax compression if photometric is not compatible. |
5998 | | */ |
5999 | 34.3k | if ((compress_tag == COMPRESSION_JPEG) && |
6000 | 8.15k | ((photometric != PHOTOMETRIC_MINISWHITE) && |
6001 | 8.15k | (photometric != PHOTOMETRIC_MINISBLACK) && |
6002 | 1.62k | (photometric != PHOTOMETRIC_RGB) && |
6003 | 25 | (photometric != PHOTOMETRIC_YCBCR))) |
6004 | 25 | { |
6005 | 25 | compress_tag=COMPRESSION_NONE; |
6006 | 25 | if (logging) |
6007 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6008 | 0 | "Ignoring request for JPEG compression due " |
6009 | 0 | "to incompatible photometric."); |
6010 | 25 | } |
6011 | 34.3k | else if (((compress_tag == COMPRESSION_CCITTFAX3) || |
6012 | 32.7k | (compress_tag == COMPRESSION_CCITTFAX4)) && |
6013 | 6.54k | ((photometric != PHOTOMETRIC_MINISWHITE) && |
6014 | 0 | (photometric != PHOTOMETRIC_MINISBLACK))) |
6015 | 0 | { |
6016 | 0 | compress_tag=COMPRESSION_NONE; |
6017 | 0 | fill_order=FILLORDER_MSB2LSB; |
6018 | 0 | if (logging) |
6019 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6020 | 0 | "Ignoring request for FAX compression" |
6021 | 0 | " due to incompatible photometric."); |
6022 | 0 | } |
6023 | 34.3k | #if defined(COMPRESSION_JBIG) |
6024 | 34.3k | else if ((compress_tag == COMPRESSION_JBIG) && |
6025 | 0 | (photometric != PHOTOMETRIC_MINISWHITE)) |
6026 | 0 | { |
6027 | 0 | compress_tag=COMPRESSION_NONE; |
6028 | 0 | fill_order=FILLORDER_MSB2LSB; |
6029 | 0 | if (logging) |
6030 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6031 | 0 | "Ignoring request for JBIG compression" |
6032 | 0 | " due to incompatible photometric."); |
6033 | 0 | } |
6034 | 34.3k | #endif /* defined(COMPRESSION_JBIG) */ |
6035 | | |
6036 | | /* |
6037 | | Support writing bits per sample of 8, 16, & 32 by default. |
6038 | | */ |
6039 | 46.7k | for (bits_per_sample=8; bits_per_sample < depth; ) |
6040 | 12.3k | bits_per_sample*=2; |
6041 | | |
6042 | | /* |
6043 | | Now choose appropriate settings for the photometric. |
6044 | | */ |
6045 | 34.3k | switch (photometric) |
6046 | 34.3k | { |
6047 | 8.84k | case PHOTOMETRIC_MINISWHITE: |
6048 | 18.9k | case PHOTOMETRIC_MINISBLACK: |
6049 | 18.9k | { |
6050 | 18.9k | samples_per_pixel=1; |
6051 | 18.9k | if (depth == 1) |
6052 | 9.29k | bits_per_sample=1; |
6053 | 18.9k | break; |
6054 | 8.84k | } |
6055 | 11.4k | case PHOTOMETRIC_RGB: |
6056 | 11.4k | { |
6057 | 11.4k | samples_per_pixel=3; |
6058 | 11.4k | break; |
6059 | 8.84k | } |
6060 | 1.26k | case PHOTOMETRIC_PALETTE: |
6061 | 1.26k | { |
6062 | 1.26k | samples_per_pixel=1; |
6063 | 1.26k | bits_per_sample=1; |
6064 | | /* |
6065 | | Support colormap indexes of 1, 2, 4, 8, and 16 by default. |
6066 | | */ |
6067 | 3.39k | while ((1UL << bits_per_sample) < image->colors) |
6068 | 2.12k | bits_per_sample*=2; |
6069 | 1.26k | break; |
6070 | 8.84k | } |
6071 | 2.74k | case PHOTOMETRIC_SEPARATED: |
6072 | 2.74k | { |
6073 | 2.74k | samples_per_pixel=4; /* CMYK */ |
6074 | | |
6075 | 2.74k | (void) TIFFSetField(tiff,TIFFTAG_INKSET,INKSET_CMYK); |
6076 | 2.74k | if (logging) |
6077 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6078 | 0 | "Using INKSET_CMYK"); |
6079 | 2.74k | break; |
6080 | 8.84k | } |
6081 | 0 | case PHOTOMETRIC_YCBCR: |
6082 | 0 | { |
6083 | | /* This is here to support JPEGCOLORMODE_RGB*/ |
6084 | 0 | samples_per_pixel=3; |
6085 | 0 | break; |
6086 | 8.84k | } |
6087 | 34.3k | } |
6088 | | |
6089 | 34.3k | if (COMPRESSION_JPEG == compress_tag) |
6090 | 8.13k | { |
6091 | | /* |
6092 | | JPEG compression can only use size specified by |
6093 | | BITS_IN_JSAMPLE (except for MK1 JPEG) which supports two |
6094 | | sizes. |
6095 | | |
6096 | | Maybe there is a tricky way to obtain this value from |
6097 | | libtiff or libtiff can be extended? |
6098 | | |
6099 | | FIXME |
6100 | | */ |
6101 | 8.13k | #if defined(BITS_IN_JSAMPLE) |
6102 | 8.13k | depth=BITS_IN_JSAMPLE; |
6103 | 8.13k | bits_per_sample=BITS_IN_JSAMPLE; |
6104 | | #else |
6105 | | depth=8; |
6106 | | bits_per_sample=8; |
6107 | | #endif |
6108 | 8.13k | } |
6109 | | |
6110 | 34.3k | #if defined(COMPRESSION_WEBP) |
6111 | 34.3k | if (COMPRESSION_WEBP == compress_tag) |
6112 | 3.12k | { |
6113 | | /* |
6114 | | WebP compression only supports a depth of 8. |
6115 | | */ |
6116 | 3.12k | depth=8; |
6117 | 3.12k | bits_per_sample=8; |
6118 | 3.12k | } |
6119 | 34.3k | #endif /* if defined(COMPRESSION_WEBP) */ |
6120 | | |
6121 | 34.3k | alpha_type=UnspecifiedAlpha; |
6122 | 34.3k | if (image->matte) |
6123 | 5.16k | { |
6124 | | /* |
6125 | | Image has a matte channel. Mark it correctly. |
6126 | | */ |
6127 | 5.16k | uint16 |
6128 | 5.16k | extra_samples, |
6129 | 5.16k | sample_info[1]; |
6130 | | |
6131 | 5.16k | const char * |
6132 | 5.16k | value; |
6133 | | |
6134 | 5.16k | alpha_type=UnassociatedAlpha; |
6135 | | |
6136 | 5.16k | if ((value=AccessDefinition(image_info,"tiff","alpha"))) |
6137 | 0 | { |
6138 | 0 | if (LocaleCompare(value,"unspecified") == 0) |
6139 | 0 | alpha_type=UnspecifiedAlpha; |
6140 | 0 | else if (LocaleCompare(value,"associated") == 0) |
6141 | 0 | alpha_type=AssociatedAlpha; |
6142 | 0 | else if (LocaleCompare(value,"unassociated") == 0) |
6143 | 0 | alpha_type=UnassociatedAlpha; |
6144 | 0 | } |
6145 | 5.16k | else if ((attribute=GetImageAttribute(image,"alpha"))) |
6146 | 5.08k | { |
6147 | 5.08k | if (LocaleCompare(attribute->value,"unspecified") == 0) |
6148 | 0 | alpha_type=UnspecifiedAlpha; |
6149 | 5.08k | else if (LocaleCompare(attribute->value,"associated") == 0) |
6150 | 1.96k | alpha_type=AssociatedAlpha; |
6151 | 3.11k | else if (LocaleCompare(attribute->value,"unassociated") == 0) |
6152 | 3.11k | alpha_type=UnassociatedAlpha; |
6153 | 5.08k | } |
6154 | | |
6155 | 5.16k | samples_per_pixel += 1; |
6156 | 5.16k | extra_samples=1; |
6157 | 5.16k | sample_info[0]=EXTRASAMPLE_ASSOCALPHA; |
6158 | 5.16k | switch (alpha_type) |
6159 | 5.16k | { |
6160 | 0 | case UnspecifiedAlpha: |
6161 | 0 | { |
6162 | 0 | sample_info[0]=EXTRASAMPLE_UNSPECIFIED; |
6163 | 0 | break; |
6164 | 0 | } |
6165 | 1.96k | case AssociatedAlpha: |
6166 | 1.96k | { |
6167 | 1.96k | sample_info[0]=EXTRASAMPLE_ASSOCALPHA; |
6168 | 1.96k | break; |
6169 | 0 | } |
6170 | 3.19k | case UnassociatedAlpha: |
6171 | 3.19k | { |
6172 | 3.19k | sample_info[0]=EXTRASAMPLE_UNASSALPHA; |
6173 | 3.19k | break; |
6174 | 0 | } |
6175 | 5.16k | } |
6176 | | |
6177 | 5.16k | (void) TIFFSetField(tiff,TIFFTAG_EXTRASAMPLES,extra_samples, |
6178 | 5.16k | &sample_info); |
6179 | 5.16k | } |
6180 | | |
6181 | | /* |
6182 | | Allow the advanced user to over-ride some TIFF write options |
6183 | | */ |
6184 | 34.3k | { |
6185 | 34.3k | const char * |
6186 | 34.3k | value; |
6187 | | |
6188 | 34.3k | unsigned int |
6189 | 34.3k | old_value; |
6190 | | |
6191 | | /* |
6192 | | Fill order |
6193 | | */ |
6194 | 34.3k | value=AccessDefinition(image_info,"tiff","fill-order"); |
6195 | 34.3k | if (value) |
6196 | 1.67k | { |
6197 | 1.67k | if (LocaleNCompare(value,"msb2lsb",3) == 0) |
6198 | 1.67k | fill_order=FILLORDER_MSB2LSB; |
6199 | 0 | else if (LocaleNCompare(value,"lsb2msb",3) == 0) |
6200 | 0 | fill_order=FILLORDER_LSB2MSB; |
6201 | 1.67k | } |
6202 | | |
6203 | | /* |
6204 | | Sample format |
6205 | | */ |
6206 | 34.3k | value=AccessDefinition(image_info,"tiff","sample-format"); |
6207 | 34.3k | if (value) |
6208 | 0 | { |
6209 | 0 | if (LocaleCompare(value,"unsigned") == 0) |
6210 | 0 | sample_format=SAMPLEFORMAT_UINT; |
6211 | 0 | else if (LocaleCompare(value,"ieeefp") == 0) |
6212 | 0 | sample_format=SAMPLEFORMAT_IEEEFP; |
6213 | 0 | } |
6214 | | |
6215 | | /* |
6216 | | Bits per sample (needs to be after sample format) |
6217 | | */ |
6218 | 34.3k | value=AccessDefinition(image_info,"tiff","bits-per-sample"); |
6219 | 34.3k | if (value) |
6220 | 0 | { |
6221 | 0 | old_value=bits_per_sample; |
6222 | 0 | bits_per_sample=MagickAtoI(value); |
6223 | 0 | if (sample_format == SAMPLEFORMAT_IEEEFP) |
6224 | 0 | { |
6225 | | /* |
6226 | | If floating point is selected, ensure that valid |
6227 | | bits-per-sample values are specified. |
6228 | | */ |
6229 | 0 | if ((bits_per_sample != 16) && |
6230 | 0 | (bits_per_sample != 24) && |
6231 | 0 | (bits_per_sample != 32) && |
6232 | 0 | (bits_per_sample != 64)) |
6233 | 0 | bits_per_sample=32; |
6234 | 0 | } |
6235 | 0 | else |
6236 | 0 | { |
6237 | | /* Clamp maximum unsigned bits per sample to 32 bits */ |
6238 | 0 | if ((bits_per_sample < 1) || |
6239 | 0 | ((bits_per_sample > 32) && (bits_per_sample != 64))) |
6240 | 0 | bits_per_sample=old_value; |
6241 | 0 | } |
6242 | 0 | if ((logging) && (old_value != bits_per_sample)) |
6243 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6244 | 0 | "User override (bits-per-sample): %u bits " |
6245 | 0 | "per sample (was %u)", |
6246 | 0 | (unsigned int) bits_per_sample, old_value); |
6247 | 0 | } |
6248 | | |
6249 | | /* |
6250 | | Samples per pixel |
6251 | | */ |
6252 | 34.3k | value=AccessDefinition(image_info,"tiff","samples-per-pixel"); |
6253 | 34.3k | if (value) |
6254 | 0 | { |
6255 | 0 | old_value=samples_per_pixel; |
6256 | 0 | samples_per_pixel=MagickAtoI(value); |
6257 | 0 | if (logging) |
6258 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6259 | 0 | "User override (samples-per-pixel): %u " |
6260 | 0 | "samples per pixel (was %u)", |
6261 | 0 | (unsigned int) samples_per_pixel, old_value); |
6262 | 0 | } |
6263 | 34.3k | } |
6264 | | |
6265 | | /* |
6266 | | Determine planar configuration. |
6267 | | */ |
6268 | 34.3k | planar_config=PLANARCONFIG_CONTIG; |
6269 | 34.3k | if (samples_per_pixel > 1) |
6270 | 15.6k | { |
6271 | 15.6k | switch (image->interlace) |
6272 | 15.6k | { |
6273 | 13.5k | case UndefinedInterlace: |
6274 | 13.5k | break; |
6275 | 5 | case NoInterlace: |
6276 | 14 | case LineInterlace: |
6277 | 14 | case PartitionInterlace: |
6278 | 14 | planar_config=PLANARCONFIG_CONTIG; |
6279 | 14 | break; |
6280 | 2.06k | case PlaneInterlace: |
6281 | 2.06k | planar_config=PLANARCONFIG_SEPARATE; |
6282 | 2.06k | break; |
6283 | 15.6k | } |
6284 | | |
6285 | 15.6k | switch (image_info->interlace) |
6286 | 15.6k | { |
6287 | 15.6k | case UndefinedInterlace: |
6288 | 15.6k | break; |
6289 | 0 | case NoInterlace: |
6290 | 0 | case LineInterlace: |
6291 | 0 | case PartitionInterlace: |
6292 | 0 | planar_config=PLANARCONFIG_CONTIG; |
6293 | 0 | break; |
6294 | 0 | case PlaneInterlace: |
6295 | 0 | planar_config=PLANARCONFIG_SEPARATE; |
6296 | 0 | break; |
6297 | 15.6k | } |
6298 | 15.6k | } |
6299 | | |
6300 | | /* |
6301 | | YCbCr encoding compresses much better than RGB. Use YCbCr |
6302 | | encoding for JPEG (normal for JPEG files). |
6303 | | |
6304 | | FIXME: Need to find a way to select between RGB or YCbCr |
6305 | | encoding with JPEG compression. Testing shows that YCbCr is |
6306 | | over 2.5X smaller than RGB but the PSNR is a bit lower, |
6307 | | particularly in red and blue channels. Someone might want to |
6308 | | use RGB to minimize color loss. |
6309 | | */ |
6310 | 34.3k | if ((compress_tag == COMPRESSION_JPEG) && |
6311 | 8.13k | (planar_config == PLANARCONFIG_CONTIG) && |
6312 | 8.05k | (photometric == PHOTOMETRIC_RGB)) |
6313 | 1.51k | photometric=PHOTOMETRIC_YCBCR; |
6314 | | |
6315 | 34.3k | if (logging) |
6316 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6317 | 0 | "Using %s photometric, %u samples per pixel, " |
6318 | 0 | "%u bits per sample, format %s", |
6319 | 0 | PhotometricTagToString(photometric), |
6320 | 0 | (unsigned int) samples_per_pixel, |
6321 | 0 | (unsigned int) bits_per_sample, |
6322 | 0 | sample_format == SAMPLEFORMAT_UINT ? "Unsigned" : |
6323 | 0 | sample_format == SAMPLEFORMAT_IEEEFP ? "IEEEFP" : |
6324 | 0 | "unknown"); |
6325 | | |
6326 | | /* |
6327 | | Only set fill order if the setting is not the default. |
6328 | | */ |
6329 | 34.3k | if (FILLORDER_MSB2LSB != fill_order) |
6330 | 4.86k | (void) TIFFSetField(tiff,TIFFTAG_FILLORDER,fill_order); |
6331 | 34.3k | if (logging) |
6332 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6333 | 0 | "Using %s bit fill order", |
6334 | 0 | (fill_order == FILLORDER_MSB2LSB ? "MSB2LSB" : |
6335 | 0 | (fill_order == FILLORDER_LSB2MSB ? "LSB2MSB" : |
6336 | 0 | "undefined"))); |
6337 | 34.3k | if (image->orientation != UndefinedOrientation) |
6338 | 3.11k | (void) TIFFSetField(tiff,TIFFTAG_ORIENTATION,(uint16) image->orientation); |
6339 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_PHOTOMETRIC,photometric); |
6340 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_BITSPERSAMPLE,bits_per_sample); |
6341 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_SAMPLESPERPIXEL,samples_per_pixel); |
6342 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_SAMPLEFORMAT,sample_format); |
6343 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_PLANARCONFIG,planar_config); |
6344 | 34.3k | if (logging) |
6345 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6346 | 0 | "%s image planes", |
6347 | 0 | (planar_config == PLANARCONFIG_SEPARATE ? |
6348 | 0 | "Separate" : "Contiguous")); |
6349 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_COMPRESSION,compress_tag); |
6350 | | |
6351 | | /* |
6352 | | Determine rows per strip given current image width, bits per |
6353 | | sample, samples per pixel, tags, and compression specific |
6354 | | requirements. Tries to create strips with |
6355 | | TARGET_BYTES_PER_STRIP of uncompressed data. |
6356 | | */ |
6357 | 34.3k | switch (compress_tag) |
6358 | 34.3k | { |
6359 | 8.13k | case COMPRESSION_JPEG: |
6360 | 8.13k | { |
6361 | | /* |
6362 | | Set JPEG RGB mode since we don't have a suitable YCbCR encoder. |
6363 | | */ |
6364 | 8.13k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6365 | 8.13k | "JPEG Quality: %u", (unsigned) image_info->quality); |
6366 | 8.13k | (void) TIFFSetField(tiff,TIFFTAG_JPEGQUALITY,image_info->quality); |
6367 | 8.13k | if (IsRGBColorspace(image->colorspace)) |
6368 | 8.13k | { |
6369 | 8.13k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6370 | 8.13k | "TIFFTAG_JPEGCOLORMODE: JPEGCOLORMODE_RGB"); |
6371 | 8.13k | (void) TIFFSetField(tiff,TIFFTAG_JPEGCOLORMODE,JPEGCOLORMODE_RGB); |
6372 | 8.13k | } |
6373 | 8.13k | if (bits_per_sample == 12) |
6374 | 0 | { |
6375 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6376 | 0 | "TIFFTAG_JPEGTABLESMODE: JPEGTABLESMODE_QUANT"); |
6377 | 0 | (void) TIFFSetField(tiff,TIFFTAG_JPEGTABLESMODE,JPEGTABLESMODE_QUANT); |
6378 | 0 | } |
6379 | 8.13k | break; |
6380 | 0 | } |
6381 | 3.52k | case COMPRESSION_ADOBE_DEFLATE: |
6382 | 3.52k | { |
6383 | | /* |
6384 | | Use horizontal differencing (type 2) for images which are |
6385 | | likely to be continuous tone. The TIFF spec says that this |
6386 | | usually leads to better compression. |
6387 | | */ |
6388 | 3.52k | if (((photometric == PHOTOMETRIC_RGB) || |
6389 | 2.74k | (photometric == PHOTOMETRIC_MINISBLACK)) && |
6390 | 1.93k | ((bits_per_sample == 8) || (bits_per_sample == 16))) |
6391 | 1.68k | predictor=PREDICTOR_HORIZONTAL; |
6392 | 3.52k | { |
6393 | | /* |
6394 | | Zip quality has a useful range of 1-9. |
6395 | | */ |
6396 | 3.52k | unsigned int zip_quality=image_info->quality / 10; |
6397 | 3.52k | if (zip_quality < 1) |
6398 | 0 | zip_quality=1; |
6399 | 3.52k | if (zip_quality > 9) |
6400 | 0 | zip_quality=9; |
6401 | | |
6402 | 3.52k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6403 | 3.52k | "TIFFTAG_ZIPQUALITY: %u", zip_quality); |
6404 | 3.52k | (void) TIFFSetField(tiff,TIFFTAG_ZIPQUALITY,zip_quality); |
6405 | 3.52k | } |
6406 | 3.52k | break; |
6407 | 0 | } |
6408 | 1.58k | case COMPRESSION_CCITTFAX3: |
6409 | 1.58k | { |
6410 | | /* |
6411 | | Set Group 3 Options. Group 3 options are arranged as 32 flag bits. |
6412 | | Specify byte-aligned EOL padding option. |
6413 | | |
6414 | | Group3Options = 4,5. LONG. Data may be one- or |
6415 | | two-dimensional, but EOLs must be |
6416 | | byte-aligned. Uncompressed data is not allowed. |
6417 | | |
6418 | | bit 0 = 0 for 1-Dimensional, 1 for 2-Dimensional |
6419 | | |
6420 | | bit 1 = must be 0 (uncompressed data not allowed) |
6421 | | |
6422 | | bit 2 = 1 for byte-aligned EOLs |
6423 | | |
6424 | | */ |
6425 | 1.58k | uint32 |
6426 | 1.58k | group_three_options = 4; |
6427 | | |
6428 | 1.58k | const char * |
6429 | 1.58k | value; |
6430 | | |
6431 | 1.58k | if ((value=AccessDefinition(image_info,"tiff","group-three-options"))) |
6432 | 0 | { |
6433 | 0 | group_three_options=(uint32) strtol(value,(char **)NULL, 10); |
6434 | 0 | } |
6435 | 1.58k | (void) TIFFSetField(tiff,TIFFTAG_GROUP3OPTIONS,group_three_options); |
6436 | 1.58k | break; |
6437 | 0 | } |
6438 | 4.95k | case COMPRESSION_CCITTFAX4: |
6439 | 4.95k | { |
6440 | 4.95k | break; |
6441 | 0 | } |
6442 | 0 | #if defined(COMPRESSION_LZMA) |
6443 | 0 | case COMPRESSION_LZMA: |
6444 | 0 | { |
6445 | 0 | unsigned int |
6446 | 0 | lzma_preset; |
6447 | |
|
6448 | 0 | const char * |
6449 | 0 | lzma_preset_str; |
6450 | | |
6451 | | /* |
6452 | | Lzma preset has a useful range of 1-9. |
6453 | | |
6454 | | We default to 1 since testing does not show much benefit |
6455 | | from use of larger values. However, we allow the |
6456 | | power-user who wants to experiment to change the preset |
6457 | | value via syntax like '-define tiff:lzmapreset=7'. This |
6458 | | ability is intentionally not documented other than here. |
6459 | | */ |
6460 | |
|
6461 | 0 | lzma_preset=1; |
6462 | 0 | if ((lzma_preset_str=AccessDefinition(image_info,"tiff","lzmapreset"))) |
6463 | 0 | lzma_preset=(unsigned short) MagickAtoI(lzma_preset_str); |
6464 | |
|
6465 | 0 | if (lzma_preset < 1) |
6466 | 0 | lzma_preset=1; |
6467 | 0 | if (lzma_preset > 9) |
6468 | 0 | lzma_preset=9; |
6469 | 0 | (void) TIFFSetField(tiff,TIFFTAG_LZMAPRESET,lzma_preset); |
6470 | 0 | if (logging) |
6471 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6472 | 0 | "LZMA PRESET set to %u", lzma_preset); |
6473 | | |
6474 | | /* |
6475 | | Use horizontal differencing (type 2) for images which are |
6476 | | likely to be continuous tone. The TIFF spec says that this |
6477 | | usually leads to better compression. |
6478 | | */ |
6479 | 0 | if (((photometric == PHOTOMETRIC_RGB) || |
6480 | 0 | (photometric == PHOTOMETRIC_MINISBLACK)) && |
6481 | 0 | ((bits_per_sample == 8) || (bits_per_sample == 16))) |
6482 | 0 | predictor=PREDICTOR_HORIZONTAL; |
6483 | 0 | break; |
6484 | 0 | } |
6485 | 0 | #endif /* COMPRESSION_LZMA */ |
6486 | 0 | #if defined(COMPRESSION_JBIG) |
6487 | 0 | case COMPRESSION_JBIG: |
6488 | 0 | { |
6489 | 0 | break; |
6490 | 0 | } |
6491 | 0 | #endif /* COMPRESSION_JBIG */ |
6492 | 750 | case COMPRESSION_LZW: |
6493 | 750 | { |
6494 | | /* |
6495 | | Use horizontal differencing (type 2) for images which are |
6496 | | likely to be continuous tone. The TIFF spec says that this |
6497 | | usually leads to better compression. |
6498 | | */ |
6499 | 750 | if (((photometric == PHOTOMETRIC_RGB) || |
6500 | 438 | (photometric == PHOTOMETRIC_MINISBLACK)) && |
6501 | 664 | ((bits_per_sample == 8) || (bits_per_sample == 16))) |
6502 | 659 | predictor=PREDICTOR_HORIZONTAL; |
6503 | 750 | break; |
6504 | 0 | } |
6505 | 0 | #if defined(COMPRESSION_ZSTD) |
6506 | 2.42k | case COMPRESSION_ZSTD: |
6507 | 2.42k | { |
6508 | | /* |
6509 | | Use horizontal differencing (type 2) for images which are |
6510 | | likely to be continuous tone. The TIFF spec says that this |
6511 | | usually leads to better compression. |
6512 | | */ |
6513 | 2.42k | if (((photometric == PHOTOMETRIC_RGB) || |
6514 | 925 | (photometric == PHOTOMETRIC_MINISBLACK)) && |
6515 | 1.69k | ((bits_per_sample == 8) || (bits_per_sample == 16))) |
6516 | 1.67k | predictor=PREDICTOR_HORIZONTAL; |
6517 | 2.42k | { |
6518 | | /* |
6519 | | Zstd level has a useful range of 1-19 (or even 22). |
6520 | | |
6521 | | Libtiff uses a default level of 9. |
6522 | | |
6523 | | Default for ImageInfo 'quality' is 75, which is translated to 9. |
6524 | | |
6525 | | Use -define tiff:zstd-compress-level=<value> to specify a value. |
6526 | | */ |
6527 | 2.42k | const char *value; |
6528 | 2.42k | int compress_level = (image_info->quality*9)/75; |
6529 | 2.42k | #if defined(HasZSTD) |
6530 | 2.42k | int max_compression = ZSTD_maxCLevel(); |
6531 | | #else |
6532 | | int max_compression = 19; |
6533 | | #endif |
6534 | 2.42k | if ((value=AccessDefinition(image_info,"tiff","zstd-compress-level"))) |
6535 | 0 | compress_level=MagickAtoI(value); |
6536 | 2.42k | if (compress_level < 1) |
6537 | 0 | compress_level=1; |
6538 | 2.42k | if (compress_level > max_compression) |
6539 | 0 | compress_level=max_compression; |
6540 | 2.42k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6541 | 2.42k | "TIFFTAG_ZSTD_LEVEL: %u", compress_level); |
6542 | 2.42k | (void) TIFFSetField(tiff,TIFFTAG_ZSTD_LEVEL,compress_level); |
6543 | 2.42k | } |
6544 | 2.42k | break; |
6545 | 0 | } |
6546 | 0 | #endif /* defined(COMPRESSION_ZSTD) */ |
6547 | 0 | #if defined(COMPRESSION_WEBP) |
6548 | 3.12k | case COMPRESSION_WEBP: |
6549 | 3.12k | { |
6550 | | /* TIFFTAG_WEBP_LEVEL */ |
6551 | 3.12k | if (image_info->quality != DefaultCompressionQuality) |
6552 | 0 | { |
6553 | 0 | int quality = (int) image_info->quality; |
6554 | 0 | if (quality < 1) |
6555 | 0 | quality=1; |
6556 | 0 | else if (quality > 100) |
6557 | 0 | quality=100; |
6558 | |
|
6559 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6560 | 0 | "TIFFTAG_WEBP_LEVEL: %d", quality); |
6561 | 0 | (void) TIFFSetField(tiff,TIFFTAG_WEBP_LEVEL,quality); |
6562 | 0 | } |
6563 | | |
6564 | | /* TIFFTAG_WEBP_LOSSLESS */ |
6565 | 3.12k | { |
6566 | 3.12k | const char *value; |
6567 | 3.12k | if (((value=AccessDefinition(image_info,"tiff","webp-lossless")) != NULL) || |
6568 | 3.12k | ((value=AccessDefinition(image_info,"webp","lossless")) != NULL)) |
6569 | 0 | { |
6570 | 0 | int lossless=(LocaleCompare(value,"TRUE") == 0 ? 1 : 0); |
6571 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6572 | 0 | "TIFFTAG_WEBP_LOSSLESS: %d", lossless); |
6573 | 0 | (void) TIFFSetField(tiff,TIFFTAG_WEBP_LOSSLESS,lossless); |
6574 | 0 | } |
6575 | 3.12k | } |
6576 | 3.12k | break; |
6577 | 0 | } |
6578 | 0 | #endif /* defined(COMPRESSION_WEBP) */ |
6579 | 0 | #if defined(COMPRESSION_LERC) |
6580 | 0 | case COMPRESSION_LERC: |
6581 | 0 | { |
6582 | | /* |
6583 | | TIFFTAG_LERC_MAXZERROR is used to define the maximum |
6584 | | allowed error (precision loss) when compressing data |
6585 | | using LERC. It specifies the maximum absolute difference |
6586 | | allowed between the original and compressed pixel |
6587 | | values. |
6588 | | */ |
6589 | 0 | { |
6590 | 0 | const char *str_value; |
6591 | 0 | if ((str_value=AccessDefinition(image_info,"tiff","lerc-maxzerror")) != NULL) |
6592 | 0 | { |
6593 | 0 | double max_z_error; |
6594 | 0 | if (MagickAtoFChk(str_value,&max_z_error) != MagickFail) |
6595 | 0 | { |
6596 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6597 | 0 | "TIFFTAG_LERC_MAXZERROR: %f", max_z_error); |
6598 | 0 | (void) TIFFSetField(tiff,TIFFTAG_LERC_MAXZERROR,max_z_error); |
6599 | 0 | } |
6600 | 0 | } |
6601 | 0 | } |
6602 | | |
6603 | | /* |
6604 | | TIFFTAG_LERC_ADD_COMPRESSION adds a secondary (followup) |
6605 | | compression algorithm for LERC. |
6606 | | */ |
6607 | 0 | { |
6608 | 0 | const char *str_value; |
6609 | 0 | if ((str_value=AccessDefinition(image_info,"tiff","lerc-compression")) != NULL) |
6610 | 0 | { |
6611 | 0 | if ((LocaleCompare(str_value,"NONE") == 0) || |
6612 | 0 | (LocaleCompare(str_value,"0") == 0)) |
6613 | 0 | { |
6614 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6615 | 0 | "TIFFTAG_LERC_ADD_COMPRESSION: none"); |
6616 | 0 | (void) TIFFSetField(tiff,TIFFTAG_LERC_ADD_COMPRESSION,LERC_ADD_COMPRESSION_NONE); |
6617 | 0 | } |
6618 | 0 | else if ((LocaleCompare(str_value,"DEFLATE") == 0) || |
6619 | 0 | (LocaleCompare(str_value,"ZIP") == 0) || |
6620 | 0 | (LocaleCompare(str_value,"1") == 0)) |
6621 | 0 | { |
6622 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6623 | 0 | "TIFFTAG_LERC_ADD_COMPRESSION: deflate"); |
6624 | 0 | (void) TIFFSetField(tiff,TIFFTAG_LERC_ADD_COMPRESSION,LERC_ADD_COMPRESSION_DEFLATE); |
6625 | | /* TIFFTAG_ZIPQUALITY (-1 or 1-12) */ |
6626 | 0 | if ((str_value=AccessDefinition(image_info,"tiff","lerc-zip-quality")) != NULL) |
6627 | 0 | { |
6628 | 0 | int int_value = -1; |
6629 | 0 | if ((MagickAtoIChk(str_value,&int_value) != MagickFail) && |
6630 | 0 | ((-1 == int_value) || (int_value <= 12 && int_value >= 0))) |
6631 | 0 | { |
6632 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6633 | 0 | "TIFFTAG_ZIPQUALITY: %d", int_value); |
6634 | 0 | (void) TIFFSetField(tiff,TIFFTAG_ZIPQUALITY,int_value); |
6635 | 0 | } |
6636 | 0 | } |
6637 | 0 | } |
6638 | 0 | else if ((LocaleCompare(str_value,"ZSTD") == 0) || |
6639 | 0 | (LocaleCompare(str_value,"2") == 0)) |
6640 | 0 | { |
6641 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6642 | 0 | "TIFFTAG_LERC_ADD_COMPRESSION: zstd"); |
6643 | 0 | (void) TIFFSetField(tiff,TIFFTAG_LERC_ADD_COMPRESSION,LERC_ADD_COMPRESSION_ZSTD); |
6644 | | /* TIFFTAG_ZSTD_LEVEL (1-22) */ |
6645 | 0 | if ((str_value=AccessDefinition(image_info,"tiff","lerc-zstd-level")) != NULL) |
6646 | 0 | { |
6647 | 0 | int int_value = -1; |
6648 | 0 | if ((MagickAtoIChk(str_value,&int_value) != MagickFail) && |
6649 | 0 | (int_value <= 22 && int_value >= 1)) |
6650 | 0 | { |
6651 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6652 | 0 | "TIFFTAG_ZSTD_LEVEL: %d", int_value); |
6653 | 0 | (void) TIFFSetField(tiff,TIFFTAG_ZSTD_LEVEL,int_value); |
6654 | 0 | } |
6655 | 0 | } |
6656 | 0 | } |
6657 | 0 | } |
6658 | 0 | } |
6659 | 0 | break; |
6660 | 0 | } |
6661 | 0 | #endif /* defined(COMPRESSION_WEBP) */ |
6662 | 9.87k | default: |
6663 | 9.87k | { |
6664 | 9.87k | break; |
6665 | 0 | } |
6666 | 34.3k | } |
6667 | | |
6668 | 34.3k | scanline_size=TIFFScanlineSize(tiff); |
6669 | | /* (void) LogMagickEvent(CoderEvent,GetMagickModule(),"TIFFScanlineSize(): %lu", (unsigned long) scanline_size); */ |
6670 | 34.3k | rows_per_strip=TIFFDefaultStripSize(tiff,0); |
6671 | | /* (void) LogMagickEvent(CoderEvent,GetMagickModule(),"TIFFDefaultStripSize(): %lu", (unsigned long) rows_per_strip); */ |
6672 | | |
6673 | 34.3k | switch (compress_tag) |
6674 | 34.3k | { |
6675 | 1.58k | case COMPRESSION_CCITTFAX3: |
6676 | 6.54k | case COMPRESSION_CCITTFAX4: |
6677 | 6.54k | #if defined(COMPRESSION_JBIG) |
6678 | 6.54k | case COMPRESSION_JBIG: |
6679 | 6.54k | #endif /* COMPRESSION_JBIG */ |
6680 | 6.54k | { |
6681 | | /* |
6682 | | It is recommended (but not required) to output FAX/JBIG |
6683 | | as one strip. We will limit strip size to 32 megapixels |
6684 | | (4MiB) by default. |
6685 | | */ |
6686 | 6.54k | bytes_per_strip_target = 4*TIFF_BYTES_PER_STRIP; |
6687 | 6.54k | break; |
6688 | 6.54k | } |
6689 | 0 | #if defined(COMPRESSION_LZMA) |
6690 | 0 | case COMPRESSION_LZMA: |
6691 | 0 | { |
6692 | 0 | uint32 |
6693 | 0 | lzma_preset = 0; |
6694 | | |
6695 | | /* |
6696 | | Strip memory target for various compression preset levels. |
6697 | | Values are arbitrary. LZMA is a memory and CPU pig. |
6698 | | */ |
6699 | 0 | static const unsigned int |
6700 | 0 | lzma_memory_mb[] = |
6701 | 0 | { /* Level Compress Decompress */ |
6702 | 0 | 1U, /* 1 2 MB 1 MB */ |
6703 | 0 | 4U, /* 2 12 MB 2 MB */ |
6704 | 0 | 4U, /* 3 12 MB 1 MB */ |
6705 | 0 | 4U, /* 4 16 MB 2 MB */ |
6706 | 0 | 6U, /* 5 26 MB 3 MB */ |
6707 | 0 | 10U, /* 6 45 MB 5 MB */ |
6708 | 0 | 18U, /* 7 83 MB 9 MB */ |
6709 | 0 | 34U, /* 8 159 MB 17 MB */ |
6710 | 0 | 66U /* 9 311 MB 33 MB */ |
6711 | 0 | }; |
6712 | |
|
6713 | 0 | (void) TIFFGetFieldDefaulted(tiff,TIFFTAG_LZMAPRESET,&lzma_preset); |
6714 | 0 | bytes_per_strip_target = (size_t) lzma_memory_mb[lzma_preset-1]*1024*1024; |
6715 | 0 | break; |
6716 | 6.54k | } |
6717 | 0 | #endif /* COMPRESSION_LZMA */ |
6718 | 27.8k | default: |
6719 | 27.8k | { |
6720 | 27.8k | } |
6721 | 34.3k | } |
6722 | | |
6723 | 34.3k | if (rows_per_strip==0 || scanline_size==0) |
6724 | 0 | rows_per_strip=1; |
6725 | 34.3k | else |
6726 | 34.3k | { |
6727 | 34.3k | if (scanline_size < (tsize_t) bytes_per_strip_target) |
6728 | 34.3k | rows_per_strip *= (uint32) ((size_t) bytes_per_strip_target / ((size_t) rows_per_strip*scanline_size)); |
6729 | 34.3k | if (rows_per_strip > image->rows) |
6730 | 33.8k | rows_per_strip=image->rows; |
6731 | 34.3k | if (rows_per_strip < 1) |
6732 | 0 | rows_per_strip=1; |
6733 | 34.3k | } |
6734 | | |
6735 | | /* |
6736 | | It seems that some programs fail to handle more than 32K or |
6737 | | 64K strips in an image due to using a 16-bit strip counter. |
6738 | | The solution is to use a larger strip size. This approach |
6739 | | might cause excessively large strips for mega-sized images and |
6740 | | someday we may remove the solution since the problematic |
6741 | | readers will have expired. |
6742 | | */ |
6743 | 34.3k | if ((image->rows/rows_per_strip) > 32767) |
6744 | 0 | rows_per_strip=image->rows/32768; |
6745 | 34.3k | if (rows_per_strip < 1) |
6746 | 0 | rows_per_strip=1; |
6747 | | |
6748 | | /* |
6749 | | Allow the user to specify the predictor (at their own peril) |
6750 | | */ |
6751 | 34.3k | { |
6752 | 34.3k | const char * |
6753 | 34.3k | value; |
6754 | | |
6755 | 34.3k | if ((value=AccessDefinition(image_info,"tiff","predictor"))) |
6756 | 0 | predictor=(unsigned short) MagickAtoI(value); |
6757 | 34.3k | } |
6758 | | |
6759 | 34.3k | if (predictor != 0) |
6760 | 4.01k | { |
6761 | 4.01k | if (logging) |
6762 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6763 | 0 | "Requesting predictor %u", |
6764 | 0 | (unsigned int) predictor); |
6765 | 4.01k | (void) TIFFSetField(tiff,TIFFTAG_PREDICTOR,predictor); |
6766 | 4.01k | } |
6767 | | |
6768 | | /* |
6769 | | Allow the user to override rows-per-strip settings. |
6770 | | */ |
6771 | 34.3k | if (method != TiledMethod) |
6772 | 34.3k | { |
6773 | 34.3k | const char * |
6774 | 34.3k | value; |
6775 | | |
6776 | 34.3k | if ((value=AccessDefinition(image_info,"tiff","rows-per-strip"))) |
6777 | 0 | { |
6778 | 0 | unsigned int |
6779 | 0 | old_value; |
6780 | |
|
6781 | 0 | old_value=rows_per_strip; |
6782 | 0 | rows_per_strip=MagickAtoI(value); |
6783 | 0 | if (logging) |
6784 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6785 | 0 | "User override (rows_per_strip): %u rows" |
6786 | 0 | " per strip (was %u)", |
6787 | 0 | (unsigned int) rows_per_strip, old_value); |
6788 | 0 | } |
6789 | 34.3k | if ((value=AccessDefinition(image_info,"tiff","strip-per-page"))) |
6790 | 1.67k | { |
6791 | 1.67k | if (LocaleCompare("TRUE",value) == 0) |
6792 | 1.67k | { |
6793 | 1.67k | rows_per_strip=(uint32) image->rows; |
6794 | | |
6795 | 1.67k | if (logging) |
6796 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6797 | 0 | "User requested a single strip per " |
6798 | 0 | "page (strip-per-page)"); |
6799 | 1.67k | } |
6800 | 1.67k | } |
6801 | 34.3k | } |
6802 | | |
6803 | 34.3k | if (COMPRESSION_JPEG == compress_tag) |
6804 | 8.13k | { |
6805 | | /* |
6806 | | RowsPerStrip must be multiple of 16 for JPEG. |
6807 | | */ |
6808 | 8.13k | rows_per_strip=(((rows_per_strip < 16 ? 16 : rows_per_strip)+1)/16)*16; |
6809 | 8.13k | } |
6810 | | |
6811 | 34.3k | if (logging) |
6812 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6813 | 0 | "Using %s compression", |
6814 | 0 | CompressionTagToString(compress_tag)); |
6815 | 34.3k | if (logging) |
6816 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6817 | 0 | "Image depth %lu bits",depth); |
6818 | 34.3k | if (method != TiledMethod) |
6819 | 34.3k | { |
6820 | 34.3k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6821 | 34.3k | "Rows per strip: %u (%lu bytes/strip)", |
6822 | 34.3k | (unsigned int) rows_per_strip, |
6823 | 34.3k | (unsigned long) scanline_size*rows_per_strip); |
6824 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_ROWSPERSTRIP,rows_per_strip); |
6825 | 34.3k | } |
6826 | | |
6827 | | |
6828 | 34.3k | if ((image->x_resolution != 0) && (image->y_resolution != 0)) |
6829 | 2.43k | { |
6830 | 2.43k | unsigned short |
6831 | 2.43k | units; |
6832 | | |
6833 | | /* |
6834 | | Set image resolution. |
6835 | | */ |
6836 | 2.43k | units=RESUNIT_NONE; |
6837 | 2.43k | if (image->units == PixelsPerInchResolution) |
6838 | 2.20k | units=RESUNIT_INCH; |
6839 | 2.43k | if (image->units == PixelsPerCentimeterResolution) |
6840 | 84 | units=RESUNIT_CENTIMETER; |
6841 | 2.43k | if (logging) |
6842 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6843 | 0 | "Resolution %gx%g pixels%s", |
6844 | 0 | image->x_resolution, image->y_resolution, |
6845 | 0 | (units == RESUNIT_NONE ? " (undefined units)" : |
6846 | 0 | (units == RESUNIT_INCH ? " per inch" : |
6847 | 0 | (units == RESUNIT_CENTIMETER ? " per centimeter" |
6848 | 0 | : "BAD VALUE")))); |
6849 | 2.43k | (void) TIFFSetField(tiff,TIFFTAG_RESOLUTIONUNIT,(uint16) units); |
6850 | 2.43k | (void) TIFFSetField(tiff,TIFFTAG_XRESOLUTION,image->x_resolution); |
6851 | 2.43k | (void) TIFFSetField(tiff,TIFFTAG_YRESOLUTION,image->y_resolution); |
6852 | 2.43k | } |
6853 | | |
6854 | 34.3k | #if EXPERIMENTAL_EXIF_TAGS |
6855 | 34.3k | #if TIFFLIB_VERSION >= 20120922 |
6856 | 34.3k | if (status!=MagickFail) |
6857 | 34.3k | { |
6858 | 34.3k | const unsigned char *profile_data; |
6859 | 34.3k | size_t profile_length; |
6860 | 34.3k | if ((profile_data=GetImageProfile(image,"Exif",&profile_length)) != 0) |
6861 | 77 | { |
6862 | 77 | AddExifFields(tiff,profile_data,profile_length,logging, 0/* FLAG_BASE */); |
6863 | 77 | } |
6864 | 34.3k | } |
6865 | 34.3k | #endif /* TIFFLIB_VERSION >= 20120922 */ |
6866 | 34.3k | #endif /* defined(EXPERIMENTAL_EXIF_TAGS) */ |
6867 | | |
6868 | 34.3k | if (image->chromaticity.white_point.x != 0.0) |
6869 | 336 | { |
6870 | 336 | float |
6871 | 336 | chromaticity[6]; |
6872 | | |
6873 | | /* |
6874 | | Set image primary chromaticities (x,y coordinates of RGB |
6875 | | colorants and white point). |
6876 | | */ |
6877 | 336 | chromaticity[0]=(float) image->chromaticity.red_primary.x; |
6878 | 336 | chromaticity[1]=(float) image->chromaticity.red_primary.y; |
6879 | 336 | chromaticity[2]=(float) image->chromaticity.green_primary.x; |
6880 | 336 | chromaticity[3]=(float) image->chromaticity.green_primary.y; |
6881 | 336 | chromaticity[4]=(float) image->chromaticity.blue_primary.x; |
6882 | 336 | chromaticity[5]=(float) image->chromaticity.blue_primary.y; |
6883 | 336 | if (logging) |
6884 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6885 | 0 | "Primary Chromaticities: " |
6886 | 0 | "r=%gx%g g=%gx%g b=%gx%g", |
6887 | 0 | chromaticity[0] /* red_primary.x */, |
6888 | 0 | chromaticity[1] /* red_primary.y */, |
6889 | 0 | chromaticity[2] /* green_primary.x */, |
6890 | 0 | chromaticity[3] /* green_primary.y */, |
6891 | 0 | chromaticity[4] /* blue_primary.x */, |
6892 | 0 | chromaticity[5] /* blue_primary.y */); |
6893 | 336 | (void) TIFFSetField(tiff,TIFFTAG_PRIMARYCHROMATICITIES,chromaticity); |
6894 | 336 | chromaticity[0]=(float) image->chromaticity.white_point.x; |
6895 | 336 | chromaticity[1]=(float) image->chromaticity.white_point.y; |
6896 | 336 | if (logging) |
6897 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6898 | 0 | "White Point: %gx%g", |
6899 | 0 | chromaticity[0] /* white_point.x */, |
6900 | 0 | chromaticity[1] /* white_point.y */); |
6901 | 336 | (void) TIFFSetField(tiff,TIFFTAG_WHITEPOINT,chromaticity); |
6902 | 336 | } |
6903 | 34.3k | { |
6904 | | /* |
6905 | | Copy any embedded profiles to TIFF. |
6906 | | */ |
6907 | 34.3k | size_t |
6908 | 34.3k | profile_length=0; |
6909 | | |
6910 | 34.3k | const unsigned char |
6911 | 34.3k | *profile_data=0; |
6912 | | |
6913 | 34.3k | #if defined(TIFFTAG_XMLPACKET) |
6914 | | /* |
6915 | | XML XMP profile. |
6916 | | */ |
6917 | 34.3k | if ((profile_data=GetImageProfile(image,"XMP",&profile_length)) != 0) |
6918 | 18 | { |
6919 | 18 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6920 | 18 | "XMP embedded profile with length %lu bytes", |
6921 | 18 | (unsigned long) profile_length); |
6922 | 18 | (void) TIFFSetField(tiff,TIFFTAG_XMLPACKET,(uint32) profile_length, |
6923 | 18 | profile_data); |
6924 | 18 | } |
6925 | 34.3k | #endif /* defined(TIFFTAG_XMLPACKET) */ |
6926 | | |
6927 | 34.3k | #if defined(TIFFTAG_ICCPROFILE) |
6928 | | /* |
6929 | | ICC color profile. |
6930 | | */ |
6931 | 34.3k | if ((profile_data=GetImageProfile(image,"ICM",&profile_length)) != 0) |
6932 | 158 | { |
6933 | 158 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6934 | 158 | "ICC ICM embedded profile with length %lu bytes", |
6935 | 158 | (unsigned long) profile_length); |
6936 | 158 | (void) TIFFSetField(tiff,TIFFTAG_ICCPROFILE,(uint32)profile_length, |
6937 | 158 | profile_data); |
6938 | 158 | } |
6939 | 34.3k | #endif /* defined(ICC_SUPPORT) */ |
6940 | | |
6941 | | /* |
6942 | | IPTC NewsPhoto profile. |
6943 | | */ |
6944 | 34.3k | if ((profile_data=GetImageProfile(image,"IPTC",&profile_length)) != 0) |
6945 | 370 | { |
6946 | 370 | int |
6947 | 370 | profile_tag=0; |
6948 | 370 | #if defined(TIFFTAG_PHOTOSHOP) |
6949 | | /* Photoshop profile (with embedded IPTC profile). */ |
6950 | 370 | profile_tag=TIFFTAG_PHOTOSHOP; |
6951 | 370 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6952 | 370 | "Photoshop embedded profile with length %lu" |
6953 | 370 | " bytes", |
6954 | 370 | (unsigned long) profile_length); |
6955 | | #else |
6956 | | /* IPTC TAG from RichTIFF specifications */ |
6957 | | profile_tag=TIFFTAG_RICHTIFFIPTC; |
6958 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
6959 | | "IPTC Newsphoto embedded profile with length" |
6960 | | " %lu bytes", |
6961 | | (unsigned long) profile_length); |
6962 | | |
6963 | | #endif /* defined(PHOTOSHOP_SUPPORT) */ |
6964 | 370 | WriteNewsProfile(tiff,profile_tag,profile_data,profile_length); |
6965 | 370 | } |
6966 | 34.3k | } |
6967 | 34.3k | attribute=GetImageAttribute(image,"subfiletype"); |
6968 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
6969 | 14.1k | { |
6970 | | /* |
6971 | | Support "REDUCEDIMAGE", "PAGE", and "MASK". Any other |
6972 | | value results in no subfile type tag applied. |
6973 | | */ |
6974 | 14.1k | if (LocaleCompare(attribute->value,"REDUCEDIMAGE") == 0) |
6975 | 5.15k | { |
6976 | 5.15k | (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_REDUCEDIMAGE); |
6977 | 5.15k | } |
6978 | 8.95k | else if (LocaleCompare(attribute->value,"PAGE") == 0) |
6979 | 0 | { |
6980 | 0 | (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_PAGE); |
6981 | 0 | } |
6982 | 8.95k | else if (LocaleCompare(attribute->value,"MASK") == 0) |
6983 | 0 | { |
6984 | 0 | (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_MASK); |
6985 | 0 | } |
6986 | 14.1k | } |
6987 | 20.2k | else |
6988 | 20.2k | { |
6989 | | /* |
6990 | | Page and Page number tags. Page is the current page number |
6991 | | (0 based) and pages is the total number of pages. |
6992 | | */ |
6993 | | |
6994 | 20.2k | uint16 |
6995 | 20.2k | page, |
6996 | 20.2k | pages; |
6997 | | |
6998 | 20.2k | page=(uint16) scene; |
6999 | 20.2k | pages=(uint16) image_list_length; |
7000 | | |
7001 | 20.2k | if (image_info->adjoin && pages > 1) |
7002 | 1.87k | { |
7003 | | /* |
7004 | | SubFileType = 2. LONG. The value 2 identifies a single |
7005 | | page of a multi-page image. |
7006 | | */ |
7007 | 1.87k | (void) TIFFSetField(tiff,TIFFTAG_SUBFILETYPE,FILETYPE_PAGE); |
7008 | 1.87k | } |
7009 | | |
7010 | 20.2k | (void) TIFFSetField(tiff,TIFFTAG_PAGENUMBER,page,pages); |
7011 | 20.2k | } |
7012 | 34.3k | attribute=GetImageAttribute(image,"artist"); |
7013 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7014 | 291 | (void) TIFFSetField(tiff,TIFFTAG_ARTIST,attribute->value); |
7015 | | |
7016 | 34.3k | attribute=GetImageAttribute(image,"copyright"); |
7017 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7018 | 385 | (void) TIFFSetField(tiff,33432,attribute->value); /* TIFFTAG_COPYRIGHT */ |
7019 | | |
7020 | 34.3k | attribute=GetImageAttribute(image,"timestamp"); |
7021 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7022 | 680 | (void) TIFFSetField(tiff,TIFFTAG_DATETIME,attribute->value); |
7023 | | |
7024 | 34.3k | (void) TIFFSetField(tiff,TIFFTAG_DOCUMENTNAME,image->filename); |
7025 | | |
7026 | 34.3k | attribute=GetImageAttribute(image,"hostcomputer"); |
7027 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7028 | 3.08k | (void) TIFFSetField(tiff,TIFFTAG_HOSTCOMPUTER,attribute->value); |
7029 | | |
7030 | 34.3k | attribute=GetImageAttribute(image,"comment"); |
7031 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7032 | 183 | (void) TIFFSetField(tiff,TIFFTAG_IMAGEDESCRIPTION,attribute->value); |
7033 | | |
7034 | 34.3k | attribute=GetImageAttribute(image,"make"); |
7035 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7036 | 434 | (void) TIFFSetField(tiff,TIFFTAG_MAKE,attribute->value); |
7037 | | |
7038 | 34.3k | attribute=GetImageAttribute(image,"model"); |
7039 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7040 | 479 | (void) TIFFSetField(tiff,TIFFTAG_MODEL,attribute->value); |
7041 | | |
7042 | 34.3k | attribute=GetImageAttribute(image,"label"); |
7043 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7044 | 187 | (void) TIFFSetField(tiff,TIFFTAG_PAGENAME,attribute->value); |
7045 | | |
7046 | 34.3k | attribute=GetImageAttribute(image,"software"); |
7047 | 34.3k | if (attribute != (const ImageAttribute *) NULL) |
7048 | 625 | { |
7049 | | /* |
7050 | | Allow skipping software tag if attribute exists but is empty. |
7051 | | */ |
7052 | 625 | if (strlen(attribute->value)) |
7053 | 360 | (void) TIFFSetField(tiff,TIFFTAG_SOFTWARE,attribute->value); |
7054 | 625 | } |
7055 | 33.7k | else |
7056 | 33.7k | { |
7057 | 33.7k | (void) TIFFSetField(tiff,TIFFTAG_SOFTWARE, |
7058 | 33.7k | GetMagickVersion((unsigned long *) NULL)); |
7059 | 33.7k | } |
7060 | | |
7061 | | #if 0 |
7062 | | /* |
7063 | | This tag is not supported by libtiff so the tag extension |
7064 | | mechanism would need to be used to add support for it. |
7065 | | */ |
7066 | | attribute=GetImageAttribute(image,"imageid"); |
7067 | | if (attribute != (const ImageAttribute *) NULL) |
7068 | | (void) TIFFSetField(tiff,TIFFTAG_OPIIMAGEID,attribute->value); |
7069 | | #endif |
7070 | | |
7071 | 34.3k | if (photometric == PHOTOMETRIC_PALETTE) |
7072 | 1.26k | { |
7073 | 1.26k | uint16 |
7074 | 1.26k | *blue, |
7075 | 1.26k | *green, |
7076 | 1.26k | *red; |
7077 | | |
7078 | | /* |
7079 | | Initialize TIFF colormap. |
7080 | | */ |
7081 | 1.26k | blue=MagickAllocateResourceLimitedClearedArray(unsigned short *, |
7082 | 1.26k | 65536L,sizeof(unsigned short)); |
7083 | 1.26k | green=MagickAllocateResourceLimitedClearedArray(unsigned short *, |
7084 | 1.26k | 65536L,sizeof(unsigned short)); |
7085 | 1.26k | red=MagickAllocateResourceLimitedClearedArray(unsigned short *, |
7086 | 1.26k | 65536L,sizeof(unsigned short)); |
7087 | 1.26k | if ((blue == (unsigned short *) NULL) || |
7088 | 1.26k | (green == (unsigned short *) NULL) || |
7089 | 1.26k | (red == (unsigned short *) NULL)) |
7090 | 0 | { |
7091 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,blue); |
7092 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,green); |
7093 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,red); |
7094 | 0 | ThrowTIFFWriterException(ResourceLimitError,MemoryAllocationFailed, |
7095 | 0 | image); |
7096 | 0 | } |
7097 | 169k | for (i=0; i < image->colors; i++) |
7098 | 168k | { |
7099 | 168k | red[i]=ScaleQuantumToShort(image->colormap[i].red); |
7100 | 168k | green[i]=ScaleQuantumToShort(image->colormap[i].green); |
7101 | 168k | blue[i]=ScaleQuantumToShort(image->colormap[i].blue); |
7102 | 168k | } |
7103 | 1.26k | (void) TIFFSetField(tiff,TIFFTAG_COLORMAP,red,green,blue); |
7104 | 1.26k | MagickFreeResourceLimitedMemory(unsigned short *,red); |
7105 | 1.26k | MagickFreeResourceLimitedMemory(unsigned short *,green); |
7106 | 1.26k | MagickFreeResourceLimitedMemory(unsigned short *,blue); |
7107 | 1.26k | } |
7108 | | /* |
7109 | | Set extra export options for grayscale. |
7110 | | */ |
7111 | 34.3k | if (photometric == PHOTOMETRIC_MINISWHITE) |
7112 | 8.84k | export_options.grayscale_miniswhite=MagickTrue; |
7113 | 25.5k | else if (photometric == PHOTOMETRIC_MINISBLACK) |
7114 | 10.0k | export_options.grayscale_miniswhite=MagickFalse; |
7115 | | /* |
7116 | | Set extra export options for floating point. |
7117 | | */ |
7118 | 34.3k | if (sample_format == SAMPLEFORMAT_IEEEFP) |
7119 | 0 | { |
7120 | 0 | const char * |
7121 | 0 | definition_value; |
7122 | |
|
7123 | 0 | export_options.sample_type=FloatQuantumSampleType; |
7124 | 0 | if ((definition_value=AccessDefinition(image_info,"tiff","min-sample-value"))) |
7125 | 0 | export_options.double_minvalue=strtod(definition_value,(char **)NULL); |
7126 | 0 | if ((definition_value=AccessDefinition(image_info,"tiff","max-sample-value"))) |
7127 | 0 | export_options.double_maxvalue=strtod(definition_value,(char **)NULL); |
7128 | 0 | (void) TIFFSetField(tiff,TIFFTAG_SMINSAMPLEVALUE, |
7129 | 0 | export_options.double_minvalue); |
7130 | 0 | (void) TIFFSetField(tiff,TIFFTAG_SMAXSAMPLEVALUE, |
7131 | 0 | export_options.double_maxvalue); |
7132 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7133 | 0 | "Using min sample value %g, max sample value %g", |
7134 | 0 | export_options.double_minvalue, |
7135 | 0 | export_options.double_maxvalue); |
7136 | 0 | } |
7137 | | |
7138 | | /* |
7139 | | For sample sizes matching a CPU native word, use native endian |
7140 | | order for import. |
7141 | | */ |
7142 | 34.3k | if ((16 == bits_per_sample) || (32 == bits_per_sample) || (64 == bits_per_sample)) |
7143 | 6.34k | export_options.endian=NativeEndian; |
7144 | | |
7145 | | /* |
7146 | | Export pixels to TIFF. |
7147 | | |
7148 | | FIXME: JBIG needs a Strip writer. |
7149 | | */ |
7150 | 34.3k | switch (method) |
7151 | 34.3k | { |
7152 | 0 | default: |
7153 | 34.3k | case ScanLineMethod: |
7154 | 34.3k | { |
7155 | | /* |
7156 | | Write TIFF image as scanlines |
7157 | | */ |
7158 | 34.3k | unsigned char |
7159 | 34.3k | *scanline; |
7160 | | |
7161 | 34.3k | const PixelPacket |
7162 | 34.3k | *p; |
7163 | | |
7164 | 34.3k | int |
7165 | 34.3k | max_sample, |
7166 | 34.3k | quantum_samples, |
7167 | 34.3k | sample; |
7168 | | |
7169 | 34.3k | QuantumType |
7170 | 34.3k | quantum_type; |
7171 | | |
7172 | | /* |
7173 | | Allocate memory for one scanline. |
7174 | | */ |
7175 | 34.3k | scanline_size=TIFFScanlineSize(tiff); |
7176 | | |
7177 | 34.3k | if (logging) |
7178 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7179 | 0 | "Using scanline %s write method with %u " |
7180 | 0 | "bits per sample (%lu bytes/scanline)", |
7181 | 0 | PhotometricTagToString(photometric), |
7182 | 0 | bits_per_sample, (unsigned long) scanline_size); |
7183 | | /* |
7184 | | Prepare for separate/contiguous retrieval. |
7185 | | */ |
7186 | 34.3k | max_sample=1; |
7187 | 34.3k | if (planar_config == PLANARCONFIG_SEPARATE) |
7188 | 2.06k | { |
7189 | 2.06k | if (QuantumTransferMode(image,photometric,compress_tag, |
7190 | 2.06k | sample_format,samples_per_pixel, |
7191 | 2.06k | PLANARCONFIG_CONTIG,0,&quantum_type, |
7192 | 2.06k | &quantum_samples,&image->exception) |
7193 | 2.06k | == MagickPass) |
7194 | 2.06k | max_sample=quantum_samples; |
7195 | 2.06k | } |
7196 | | |
7197 | 34.3k | scanline=MagickAllocateResourceLimitedMemory(unsigned char *,(size_t) scanline_size); |
7198 | 34.3k | if (scanline == (unsigned char *) NULL) |
7199 | 34.3k | ThrowTIFFWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
7200 | | /* |
7201 | | For each plane |
7202 | | */ |
7203 | 73.3k | for (sample=0; (status != MagickFail) && (sample < max_sample); sample++) |
7204 | 38.9k | { |
7205 | | /* |
7206 | | Determine quantum parse method. |
7207 | | */ |
7208 | 38.9k | if (QuantumTransferMode(image,photometric,compress_tag, |
7209 | 38.9k | sample_format,samples_per_pixel, |
7210 | 38.9k | planar_config,sample,&quantum_type, |
7211 | 38.9k | &quantum_samples,&image->exception) |
7212 | 38.9k | == MagickFail) |
7213 | 0 | { |
7214 | 0 | status=MagickFail; |
7215 | 0 | break; |
7216 | 0 | } |
7217 | 13.9M | for (y=0; (status != MagickFail) && (y < image->rows); y++) |
7218 | 13.8M | { |
7219 | 13.8M | if ((image->matte) && (alpha_type == AssociatedAlpha)) |
7220 | 619k | p=GetImagePixels(image,0,y,image->columns,1); |
7221 | 13.2M | else |
7222 | 13.2M | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
7223 | 13.8M | if (p == (const PixelPacket *) NULL) |
7224 | 0 | { |
7225 | 0 | status=MagickFail; |
7226 | 0 | break; |
7227 | 0 | } |
7228 | | /* |
7229 | | Convert to associated alpha if necessary. |
7230 | | */ |
7231 | 13.8M | if ((sample == 0) && (image->matte) && |
7232 | 684k | (alpha_type == AssociatedAlpha)) |
7233 | 611k | AssociateAlphaRegion(image); |
7234 | | /* |
7235 | | Export pixels to scanline. |
7236 | | */ |
7237 | 13.8M | if (ExportImagePixelArea(image,quantum_type,bits_per_sample, |
7238 | 13.8M | scanline,&export_options,&export_info) |
7239 | 13.8M | == MagickFail) |
7240 | 0 | { |
7241 | 0 | status=MagickFail; |
7242 | 0 | break; |
7243 | 0 | } |
7244 | | /* |
7245 | | Write scanline. |
7246 | | */ |
7247 | 13.8M | #if !defined(WORDS_BIGENDIAN) |
7248 | 13.8M | if (24 == bits_per_sample) |
7249 | 0 | SwabDataToNativeEndian(bits_per_sample,scanline,scanline_size); |
7250 | 13.8M | #endif |
7251 | 13.8M | if (TIFFWriteScanline(tiff, scanline,y,sample) == -1) |
7252 | 31 | { |
7253 | 31 | status=MagickFail; |
7254 | 31 | break; |
7255 | 31 | } |
7256 | | |
7257 | 13.8M | if (image->previous == (Image *) NULL) |
7258 | 12.7M | if (QuantumTick(y+(magick_int64_t)sample*image->rows, (magick_int64_t)image->rows*max_sample)) |
7259 | 1.74M | if (!MagickMonitorFormatted(y+ (magick_int64_t)sample*image->rows, |
7260 | 1.74M | (magick_int64_t)image->rows*max_sample,&image->exception, |
7261 | 1.74M | SaveImageText,image->filename, |
7262 | 1.74M | image->columns,image->rows)) |
7263 | 0 | { |
7264 | 0 | status=MagickFail; |
7265 | 0 | break; |
7266 | 0 | } |
7267 | | |
7268 | 13.8M | } |
7269 | 38.9k | if (status == MagickFail) |
7270 | 31 | break; |
7271 | 38.9k | } |
7272 | 34.3k | MagickFreeResourceLimitedMemory(unsigned char *,scanline); |
7273 | 34.3k | break; |
7274 | 34.3k | } |
7275 | 0 | case TiledMethod: |
7276 | 0 | { |
7277 | | /* |
7278 | | Write TIFF image as tiles |
7279 | | */ |
7280 | 0 | unsigned char |
7281 | 0 | *tile; |
7282 | |
|
7283 | 0 | uint32 |
7284 | 0 | tile_columns, |
7285 | 0 | tile_rows; |
7286 | |
|
7287 | 0 | tsize_t |
7288 | 0 | stride, |
7289 | 0 | tile_size, |
7290 | 0 | tile_size_max; |
7291 | |
|
7292 | 0 | int |
7293 | 0 | max_sample, |
7294 | 0 | quantum_samples, |
7295 | 0 | sample; |
7296 | |
|
7297 | 0 | QuantumType |
7298 | 0 | quantum_type; |
7299 | |
|
7300 | 0 | unsigned long |
7301 | 0 | tile_total_pixels; |
7302 | |
|
7303 | 0 | if (logging) |
7304 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7305 | 0 | "Using tiled %s write method with %u bits " |
7306 | 0 | "per sample", |
7307 | 0 | PhotometricTagToString(photometric), |
7308 | 0 | bits_per_sample); |
7309 | | |
7310 | | /* |
7311 | | Determine tile size |
7312 | | */ |
7313 | 0 | { |
7314 | 0 | const char * |
7315 | 0 | value; |
7316 | |
|
7317 | 0 | tile_columns=0; |
7318 | 0 | tile_rows=0; |
7319 | | |
7320 | | /* |
7321 | | Enable tiled output (with default size, or specified separately) |
7322 | | -define tiff:tile |
7323 | | |
7324 | | Use an exact tile size in rows & columns |
7325 | | -define tiff:tile-geometry=128x128 |
7326 | | |
7327 | | Use a specific tile width (pixels) |
7328 | | -define tiff:tile-width=128 |
7329 | | |
7330 | | Use a specific tile height (pixels) |
7331 | | -define tiff:tile-height=128 |
7332 | | */ |
7333 | 0 | if ((value=AccessDefinition(image_info,"tiff","tile-geometry"))) |
7334 | 0 | { |
7335 | 0 | double |
7336 | 0 | width, |
7337 | 0 | height; |
7338 | |
|
7339 | 0 | if (GetMagickDimension(value,&width,&height,NULL,NULL) == 2) |
7340 | 0 | { |
7341 | 0 | tile_rows=(uint32) height; |
7342 | 0 | tile_columns=(uint32) width; |
7343 | 0 | } |
7344 | 0 | } |
7345 | 0 | if ((value=AccessDefinition(image_info,"tiff","tile-width"))) |
7346 | 0 | { |
7347 | 0 | tile_columns=MagickAtoL(value); |
7348 | 0 | } |
7349 | 0 | if ((value=AccessDefinition(image_info,"tiff","tile-height"))) |
7350 | 0 | { |
7351 | 0 | tile_rows=MagickAtoL(value); |
7352 | 0 | } |
7353 | |
|
7354 | 0 | TIFFDefaultTileSize(tiff,&tile_columns,&tile_rows); |
7355 | 0 | } |
7356 | |
|
7357 | 0 | if (!TIFFSetField(tiff,TIFFTAG_TILEWIDTH,tile_columns)) |
7358 | 0 | status=MagickFail; |
7359 | 0 | if (!TIFFSetField(tiff,TIFFTAG_TILELENGTH,tile_rows)) |
7360 | 0 | status=MagickFail; |
7361 | | /* |
7362 | | Obtain the maximum number of bytes required to contain a tile. |
7363 | | */ |
7364 | 0 | tile_size_max=TIFFTileSize(tiff); |
7365 | | /* |
7366 | | Compute the total number of pixels in one tile |
7367 | | */ |
7368 | 0 | tile_total_pixels=tile_columns*tile_rows; |
7369 | 0 | if (logging) |
7370 | 0 | { |
7371 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7372 | 0 | "TIFF tile geometry %ux%u, %lu pixels", |
7373 | 0 | (unsigned int)tile_columns, |
7374 | 0 | (unsigned int)tile_rows, |
7375 | 0 | tile_total_pixels); |
7376 | 0 | } |
7377 | | /* |
7378 | | Allocate tile buffer |
7379 | | */ |
7380 | 0 | tile=MagickAllocateResourceLimitedMemory(unsigned char *, (size_t) tile_size_max); |
7381 | 0 | if (tile == (unsigned char *) NULL) |
7382 | 0 | ThrowTIFFWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
7383 | | /* |
7384 | | Prepare for separate/contiguous retrieval. |
7385 | | */ |
7386 | 0 | max_sample=1; |
7387 | 0 | if (planar_config == PLANARCONFIG_SEPARATE) |
7388 | 0 | { |
7389 | 0 | if (QuantumTransferMode(image,photometric,compress_tag, |
7390 | 0 | sample_format,samples_per_pixel, |
7391 | 0 | PLANARCONFIG_CONTIG,0,&quantum_type, |
7392 | 0 | &quantum_samples,&image->exception) |
7393 | 0 | == MagickPass) |
7394 | 0 | max_sample=quantum_samples; |
7395 | 0 | } |
7396 | | /* |
7397 | | Obtain per-row stride. |
7398 | | */ |
7399 | 0 | stride=TIFFTileRowSize(tiff); |
7400 | | |
7401 | | /* |
7402 | | Process each plane. |
7403 | | */ |
7404 | 0 | for (sample=0; (status != MagickFail) && (sample < max_sample); sample++) |
7405 | 0 | { |
7406 | | /* |
7407 | | Determine quantum parse method. |
7408 | | */ |
7409 | 0 | if (QuantumTransferMode(image,photometric,compress_tag, |
7410 | 0 | sample_format,samples_per_pixel, |
7411 | 0 | planar_config,sample,&quantum_type, |
7412 | 0 | &quantum_samples,&image->exception) |
7413 | 0 | == MagickFail) |
7414 | 0 | { |
7415 | 0 | status=MagickFail; |
7416 | 0 | break; |
7417 | 0 | } |
7418 | 0 | for (y=0; (status != MagickFail) && (y < image->rows); y+=tile_rows) |
7419 | 0 | { |
7420 | 0 | for (x=0; (status != MagickFail) && (x < image->columns); x+=tile_columns) |
7421 | 0 | { |
7422 | 0 | const PixelPacket |
7423 | 0 | *p; |
7424 | |
|
7425 | 0 | long |
7426 | 0 | tile_set_columns, |
7427 | 0 | tile_set_rows; |
7428 | |
|
7429 | 0 | unsigned char |
7430 | 0 | *q; |
7431 | |
|
7432 | 0 | register long |
7433 | 0 | yy; |
7434 | | |
7435 | | /* |
7436 | | Compute image region corresponding to tile. |
7437 | | */ |
7438 | 0 | if (x+tile_columns > image->columns) |
7439 | 0 | tile_set_columns=(tile_columns-(x+tile_columns-image->columns)); |
7440 | 0 | else |
7441 | 0 | tile_set_columns=tile_columns; |
7442 | 0 | if (y+tile_rows > image->rows) |
7443 | 0 | tile_set_rows=(tile_rows-(y+tile_rows-image->rows)); |
7444 | 0 | else |
7445 | 0 | tile_set_rows=tile_rows; |
7446 | |
|
7447 | 0 | q=tile; |
7448 | 0 | for (yy=y; (status != MagickFail) && (yy < (long) y+tile_set_rows); yy++) |
7449 | 0 | { |
7450 | | /* |
7451 | | Obtain pixel region corresponding to tile row. |
7452 | | */ |
7453 | 0 | if ((image->matte) && (alpha_type == AssociatedAlpha)) |
7454 | 0 | p=GetImagePixels(image,x,yy,tile_set_columns,1); |
7455 | 0 | else |
7456 | 0 | p=AcquireImagePixels(image,x,yy,tile_set_columns, |
7457 | 0 | 1,&image->exception); |
7458 | 0 | if (p == (const PixelPacket *) NULL) |
7459 | 0 | { |
7460 | 0 | status=MagickFail; |
7461 | 0 | break; |
7462 | 0 | } |
7463 | | /* |
7464 | | Convert to associated alpha if necessary. |
7465 | | */ |
7466 | 0 | if ((sample == 0) && (image->matte) && |
7467 | 0 | (alpha_type == AssociatedAlpha)) |
7468 | 0 | AssociateAlphaRegion(image); |
7469 | | /* |
7470 | | Export tile row |
7471 | | */ |
7472 | 0 | if (ExportImagePixelArea(image,quantum_type, |
7473 | 0 | bits_per_sample,q, |
7474 | 0 | &export_options,&export_info) |
7475 | 0 | == MagickFail) |
7476 | 0 | { |
7477 | 0 | status=MagickFail; |
7478 | 0 | break; |
7479 | 0 | } |
7480 | | /* |
7481 | | Enforce that we did not overrun our buffer. |
7482 | | */ |
7483 | 0 | assert(export_info.bytes_exported <= (size_t) scanline_size); |
7484 | 0 | q += stride; |
7485 | 0 | } /* for yy */ |
7486 | 0 | if (status == MagickFail) |
7487 | 0 | break; |
7488 | | /* |
7489 | | Write tile. |
7490 | | */ |
7491 | 0 | #if !defined(WORDS_BIGENDIAN) |
7492 | 0 | if (24 == bits_per_sample) |
7493 | 0 | SwabDataToNativeEndian(bits_per_sample,tile,tile_size_max); |
7494 | 0 | #endif |
7495 | 0 | if ((tile_size=TIFFWriteTile(tiff,tile,x,y,0,sample)) == -1) |
7496 | 0 | { |
7497 | 0 | status=MagickFail; |
7498 | 0 | } |
7499 | 0 | if (status == MagickFail) |
7500 | 0 | break; |
7501 | 0 | } /* for x */ |
7502 | | /* |
7503 | | Progress indicator. |
7504 | | */ |
7505 | 0 | if (image->previous == (Image *) NULL) |
7506 | 0 | if (QuantumTick((y+sample*image->rows)/tile_rows, |
7507 | 0 | (image->rows*max_sample)/tile_rows)) |
7508 | 0 | if (!MagickMonitorFormatted((y+sample*image->rows)/tile_rows, |
7509 | 0 | (image->rows*max_sample)/tile_rows, |
7510 | 0 | &image->exception, |
7511 | 0 | SaveImageText,image->filename, |
7512 | 0 | image->columns,image->rows)) |
7513 | 0 | status=MagickFail; |
7514 | |
|
7515 | 0 | if (status == MagickFail) |
7516 | 0 | break; |
7517 | 0 | } /* for y */ |
7518 | 0 | } /* for sample */ |
7519 | 0 | MagickFreeResourceLimitedMemory(unsigned char *,tile); |
7520 | 0 | break; |
7521 | 0 | } |
7522 | 34.3k | } |
7523 | 34.3k | if (image_info->verbose > 1) |
7524 | 0 | TIFFPrintDirectory(tiff,stdout,MagickFalse); |
7525 | 34.3k | if (!TIFFWriteDirectory(tiff)) |
7526 | 93 | { |
7527 | 93 | status=MagickFail; |
7528 | 93 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7529 | 93 | "TIFFWriteDirectory returns failed status!"); |
7530 | 93 | } |
7531 | | |
7532 | 34.3k | #if EXPERIMENTAL_EXIF_TAGS |
7533 | 34.3k | #if TIFFLIB_VERSION >= 20120922 |
7534 | | |
7535 | 34.3k | if (status!=MagickFail) |
7536 | 34.2k | { |
7537 | 34.2k | const unsigned char *profile_data; |
7538 | 34.2k | size_t profile_length; |
7539 | 34.2k | if ((profile_data=GetImageProfile(image,"Exif",&profile_length)) != 0) |
7540 | 77 | { |
7541 | 77 | magick_uint64_t dir_EXIF_offset = 0; |
7542 | 77 | magick_uint64_t dir_GPS_offset = 0; |
7543 | | /* |
7544 | | * Unfortunately it depends on the prehistory, what number TIFFCurrentDirectory() will get back. |
7545 | | * Therefore, the current main IFD number has to be adapted. However, this is an inconsistency in LibTIFF which should be |
7546 | | * corrected. This means that the provided code to determine/handle current directory number here is just a current work around. |
7547 | | */ |
7548 | 77 | tdir_t current_mainifd = TIFFCurrentDirectory(tiff); |
7549 | 77 | if (TIFFCurrentDirOffset(tiff) > 0 && current_mainifd > 0) current_mainifd--; |
7550 | | |
7551 | 77 | TIFFFreeDirectory(tiff); |
7552 | 77 | if (TIFFCreateEXIFDirectory(tiff) == 0) |
7553 | 77 | { |
7554 | 77 | if (AddExifFields(tiff,profile_data,profile_length,logging, FLAG_EXIF) > 0) |
7555 | 0 | { /* Now write the directory of Exif data */ |
7556 | |
|
7557 | 0 | if (!TIFFWriteCustomDirectory(tiff, &dir_EXIF_offset)) |
7558 | 0 | { |
7559 | 0 | LogMagickEvent(CoderEvent,GetMagickModule(),"Failed TIFFWriteCustomDirectory() of the Exif data"); |
7560 | 0 | } |
7561 | 0 | } |
7562 | 77 | } |
7563 | | |
7564 | 77 | #if TIFFLIB_VERSION >= 20230609 |
7565 | 77 | if (TIFFCreateGPSDirectory(tiff) == 0) |
7566 | 77 | { |
7567 | 77 | if (AddExifFields(tiff,profile_data,profile_length,logging, FLAG_GPS) > 0) |
7568 | 0 | { /* Now write the directory of Exif data */ |
7569 | 0 | if (!TIFFWriteCustomDirectory(tiff, &dir_GPS_offset)) |
7570 | 0 | { |
7571 | 0 | LogMagickEvent(CoderEvent,GetMagickModule(),"Failed TIFFWriteCustomDirectory() of the ExifGPS data"); |
7572 | 0 | } |
7573 | 0 | } |
7574 | 77 | else |
7575 | 77 | TIFFSetDirectory(tiff, 0); |
7576 | 77 | } |
7577 | 77 | #endif |
7578 | | |
7579 | 77 | if (dir_EXIF_offset>0 || dir_GPS_offset>0) |
7580 | 0 | { /* Go back to the first directory, and add the EXIFIFD pointer. */ |
7581 | 0 | TIFFSetDirectory(tiff, 0); |
7582 | 0 | if (dir_EXIF_offset>0) |
7583 | 0 | TIFFSetField(tiff, TIFFTAG_EXIFIFD, dir_EXIF_offset); |
7584 | 0 | if (dir_GPS_offset>0) |
7585 | 0 | TIFFSetField(tiff, TIFFTAG_GPSIFD, dir_GPS_offset); |
7586 | 0 | } |
7587 | | |
7588 | | /* Save changed tiff-directory to file */ |
7589 | 77 | if (image->next != (Image *)NULL) |
7590 | 0 | { |
7591 | 0 | if (!TIFFWriteDirectory(tiff)) |
7592 | 0 | { |
7593 | 0 | (void)LogMagickEvent(CoderEvent, GetMagickModule(), "TIFFWriteDirectory returns failed status!"); |
7594 | 0 | } |
7595 | | /* Re configure directory status for next image. Reset current IFD number. */ |
7596 | 0 | if (!TIFFSetDirectory(tiff, current_mainifd)) |
7597 | 0 | { |
7598 | 0 | if (logging) |
7599 | 0 | LogMagickEvent(CoderEvent,GetMagickModule(),"TIFFSetDirectory() failed.\n"); |
7600 | 0 | } |
7601 | 0 | TIFFFreeDirectory(tiff); |
7602 | 0 | if (!TIFFCreateDirectory(tiff)) |
7603 | 0 | { |
7604 | 0 | if (logging) |
7605 | 0 | LogMagickEvent(CoderEvent,GetMagickModule(),"TIFFCreateDirectory() failed.\n"); |
7606 | 0 | } |
7607 | 0 | } |
7608 | 77 | } |
7609 | 34.2k | } |
7610 | 34.3k | #endif /* TIFFLIB_VERSION >= 20120922 */ |
7611 | 34.3k | #endif /* if EXPERIMENTAL_EXIF_TAGS */ |
7612 | | |
7613 | 34.3k | if (image->next == (Image *) NULL) |
7614 | 27.4k | break; |
7615 | 6.93k | image=SyncNextImageInList(image); |
7616 | 6.93k | if ((status &= MagickMonitorFormatted(scene++, |
7617 | 6.93k | image_list_length, |
7618 | 6.93k | &image->exception, |
7619 | 6.93k | SaveImagesText, |
7620 | 6.93k | image->filename)) == MagickFail) |
7621 | 44 | break; |
7622 | 6.93k | } while (image_info->adjoin); |
7623 | 34.4k | while (image->previous != (Image *) NULL) |
7624 | 6.93k | image=image->previous; |
7625 | 27.4k | if (TIFFFlush(tiff) != 1) /* Flush pending writes, check status */ |
7626 | 93 | status=MagickFail; |
7627 | 27.4k | TIFFClose(tiff); /* Should implicity invoke CloseBlob(image) */ |
7628 | | |
7629 | 27.4k | if (status == MagickFail) |
7630 | 93 | { |
7631 | | /* |
7632 | | Handle write failure. |
7633 | | */ |
7634 | | |
7635 | 93 | if (unlink(filename) != -1) |
7636 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
7637 | 0 | "Removed broken output file \"%s\"",filename); |
7638 | 93 | } |
7639 | | |
7640 | 27.4k | return(status); |
7641 | 27.4k | } |
7642 | | #endif |