/src/graphicsmagick/coders/pnm.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 | | % PPPP N N M M % |
15 | | % P P NN N MM MM % |
16 | | % PPPP N N N M M M % |
17 | | % P N NN M M % |
18 | | % P N N M M % |
19 | | % % |
20 | | % % |
21 | | % Read/Write PBMPlus Portable Anymap Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % |
33 | | */ |
34 | | |
35 | | /* |
36 | | Include declarations. |
37 | | */ |
38 | | #include "magick/studio.h" |
39 | | #include "magick/analyze.h" |
40 | | #include "magick/attribute.h" |
41 | | #include "magick/blob.h" |
42 | | #include "magick/color.h" |
43 | | #include "magick/colormap.h" |
44 | | #include "magick/constitute.h" |
45 | | #include "magick/log.h" |
46 | | #include "magick/magick.h" |
47 | | #include "magick/monitor.h" |
48 | | #include "magick/omp_data_view.h" |
49 | | #include "magick/pixel_cache.h" |
50 | | #include "magick/utility.h" |
51 | | #include "magick/static.h" |
52 | | |
53 | | /* |
54 | | Forward declarations. |
55 | | */ |
56 | | static unsigned int |
57 | | WritePNMImage(const ImageInfo *,Image *); |
58 | | |
59 | | |
60 | | /* |
61 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
62 | | % % |
63 | | % % |
64 | | % % |
65 | | % I s P N M % |
66 | | % % |
67 | | % % |
68 | | % % |
69 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
70 | | % |
71 | | % Method IsPNM returns True if the image format type, identified by the |
72 | | % magick string, is PNM. |
73 | | % |
74 | | % The format of the IsPNM method is: |
75 | | % |
76 | | % unsigned int IsPNM(const unsigned char *magick,const size_t length) |
77 | | % |
78 | | % A description of each parameter follows: |
79 | | % |
80 | | % o status: Method IsPNM returns True if the image format type is PNM. |
81 | | % |
82 | | % o magick: This string is generally the first few bytes of an image file |
83 | | % or blob. |
84 | | % |
85 | | % o length: Specifies the length of the magick string. |
86 | | % |
87 | | % |
88 | | */ |
89 | | static unsigned int IsPNM(const unsigned char *magick,const size_t length) |
90 | 0 | { |
91 | 0 | if (length < 2) |
92 | 0 | return(False); |
93 | 0 | if ((*magick == 'P') && isdigit((int) magick[1])) |
94 | 0 | return(True); |
95 | 0 | return(False); |
96 | 0 | } |
97 | | |
98 | | /* |
99 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
100 | | % % |
101 | | % % |
102 | | % % |
103 | | % R e a d P N M I m a g e % |
104 | | % % |
105 | | % % |
106 | | % % |
107 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
108 | | % |
109 | | % Method ReadPNMImage reads a Portable Anymap image file and returns it. |
110 | | % It allocates the memory necessary for the new Image structure and returns |
111 | | % a pointer to the new image. |
112 | | % |
113 | | % The format of the ReadPNMImage method is: |
114 | | % |
115 | | % Image *ReadPNMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
116 | | % |
117 | | % A description of each parameter follows: |
118 | | % |
119 | | % o image: Method ReadPNMImage returns a pointer to the image after |
120 | | % reading. A null image is returned if there is a memory shortage or |
121 | | % if the image cannot be read. |
122 | | % |
123 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
124 | | % |
125 | | % o exception: return any errors or warnings in this structure. |
126 | | % |
127 | | % |
128 | | */ |
129 | | |
130 | | static unsigned int PNMInteger(Image *image,const unsigned int base,MagickPassFail *status) |
131 | 494k | { |
132 | 494k | unsigned int |
133 | 494k | value; |
134 | | |
135 | 494k | int |
136 | 494k | c; |
137 | | |
138 | | /* |
139 | | Skip any leading whitespace. |
140 | | */ |
141 | 494k | do |
142 | 7.54M | { |
143 | 7.54M | c=ReadBlobByte(image); |
144 | 7.54M | if (c == EOF) |
145 | 59.3k | { |
146 | 59.3k | *status=MagickFail; |
147 | 59.3k | return(0); |
148 | 59.3k | } |
149 | 7.54M | } while (!isdigit(c)); |
150 | 435k | c &= 0xff; |
151 | 435k | if (base == 2) |
152 | 12.2k | { |
153 | 12.2k | if (c > '1') |
154 | 3.06k | { |
155 | 3.06k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
156 | 3.06k | "ERROR: Unexpected character '%c' @%" |
157 | 3.06k | MAGICK_OFF_F "d", c, TellBlob(image)); |
158 | 3.06k | *status=MagickFail; |
159 | 3.06k | return(0); |
160 | 3.06k | } |
161 | 9.16k | return(c-'0'); |
162 | 12.2k | } |
163 | | /* |
164 | | Evaluate number. |
165 | | */ |
166 | 423k | value=0; |
167 | 819k | while(1) |
168 | 819k | { |
169 | 819k | unsigned int |
170 | 819k | old_value = value; |
171 | | |
172 | 819k | old_value=value; |
173 | 819k | value*=10; |
174 | 819k | value+=c-'0'; |
175 | 819k | if (value < old_value) |
176 | 6.82k | { |
177 | 6.82k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
178 | 6.82k | "ERROR: integer overflow (%u > %u) @%" |
179 | 6.82k | MAGICK_OFF_F "d", old_value, value, |
180 | 6.82k | TellBlob(image)); |
181 | 6.82k | *status=MagickFail; |
182 | 6.82k | return(0); |
183 | 6.82k | } |
184 | 812k | c=ReadBlobByte(image); |
185 | 812k | if ((c == EOF) || !(isdigit(c))) |
186 | 416k | break; |
187 | 396k | c &= 0xff; |
188 | 396k | } |
189 | | |
190 | 416k | return(value); |
191 | 423k | } |
192 | | |
193 | | static unsigned int PNMIntegerOrComment(Image *image,const unsigned int base,MagickPassFail *status) |
194 | 219k | { |
195 | 219k | unsigned int |
196 | 219k | value; |
197 | | |
198 | 219k | unsigned int |
199 | 219k | count = 0; |
200 | | |
201 | 219k | int |
202 | 219k | c; |
203 | | |
204 | 219k | static const char P7Comment[] = "END_OF_COMMENTS\n"; |
205 | | |
206 | | /* |
207 | | Skip any leading whitespace. |
208 | | */ |
209 | 219k | do |
210 | 3.22M | { |
211 | 3.22M | c=ReadBlobByte(image); |
212 | 3.22M | if (c == EOF) |
213 | 15.9k | return(0); |
214 | 3.21M | c &= 0xff; |
215 | 3.21M | if (c == '#') |
216 | 83.8k | { |
217 | 83.8k | char |
218 | 83.8k | *comment; |
219 | | |
220 | 83.8k | const ImageAttribute |
221 | 83.8k | *comment_attr; |
222 | | |
223 | 83.8k | ExtendedSignedIntegralType |
224 | 83.8k | offset; |
225 | | |
226 | 83.8k | register char |
227 | 83.8k | *p, |
228 | 83.8k | *q; |
229 | | |
230 | 83.8k | size_t |
231 | 83.8k | length; |
232 | | |
233 | | /* |
234 | | Read comment. |
235 | | */ |
236 | 83.8k | if ((comment_attr=GetImageAttribute(image,"comment")) != (const ImageAttribute *) NULL) |
237 | 79.0k | { |
238 | | /* |
239 | | If existing comment text length exceeds arbitrary limit, |
240 | | then do no further comment processing for this file. |
241 | | */ |
242 | 79.0k | if (comment_attr->length > MaxTextExtent*2) |
243 | 3.21k | { |
244 | 7.22M | for ( ; (c != EOF) && (c != '\n'); ) |
245 | 7.22M | c=ReadBlobByte(image); |
246 | 3.21k | return 0; |
247 | 3.21k | } |
248 | 79.0k | } |
249 | 80.6k | length=MaxTextExtent; |
250 | 80.6k | comment=MagickAllocateResourceLimitedMemory(char *,length+sizeof(P7Comment)); |
251 | 80.6k | p=comment; |
252 | 80.6k | offset=p-comment; |
253 | 80.6k | if (comment != (char *) NULL) |
254 | 171M | for ( ; (c != EOF) && (c != '\n'); p++) |
255 | 171M | { |
256 | 171M | if ((size_t) (p-comment) >= length) |
257 | 82.4k | { |
258 | 82.4k | size_t |
259 | 82.4k | text_length; |
260 | | |
261 | 82.4k | char |
262 | 82.4k | *new_comment; |
263 | | |
264 | 82.4k | text_length=(size_t) (p-comment); |
265 | 82.4k | length+=MaxTextExtent; |
266 | 82.4k | new_comment=MagickReallocateResourceLimitedMemory(char *,comment,length+sizeof(P7Comment)); |
267 | 82.4k | if (new_comment == (char *) NULL) |
268 | 0 | { |
269 | 0 | MagickFreeResourceLimitedMemory(char *,comment); |
270 | 0 | break; |
271 | 0 | } |
272 | 82.4k | comment=new_comment; |
273 | 82.4k | p=comment+text_length; |
274 | 82.4k | } |
275 | 171M | c=ReadBlobByte(image); |
276 | 171M | *p=c; |
277 | 171M | *(p+1)='\0'; |
278 | 171M | } |
279 | 80.6k | if (comment == (char *) NULL) |
280 | 0 | return(0); |
281 | 80.6k | q=comment+offset; |
282 | 80.6k | if (LocaleCompare(q,P7Comment) == 0) |
283 | 208 | *q='\0'; |
284 | | /* |
285 | | FIXME: |
286 | | Implicitly extend existing comment attribute since comments |
287 | | can span multiple lines. |
288 | | */ |
289 | 80.6k | (void) SetImageAttribute(image,"comment",comment); |
290 | 80.6k | MagickFreeResourceLimitedMemory(char *,comment); |
291 | 80.6k | continue; |
292 | 80.6k | } |
293 | 3.21M | } while (!isdigit(c)); |
294 | 200k | count++; |
295 | 200k | if (base == 2) |
296 | 0 | { |
297 | 0 | if (c > '1') |
298 | 0 | { |
299 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
300 | 0 | "ERROR: Unexpected character '%c' @%" |
301 | 0 | MAGICK_OFF_F "d", c, TellBlob(image)); |
302 | 0 | *status=MagickFail; |
303 | 0 | return(0); |
304 | 0 | } |
305 | 0 | return(c-'0'); |
306 | 0 | } |
307 | | /* |
308 | | Evaluate number. |
309 | | */ |
310 | 200k | value=0; |
311 | 684k | while(1) |
312 | 684k | { |
313 | 684k | unsigned int |
314 | 684k | old_value = value; |
315 | | |
316 | 684k | old_value=value; |
317 | 684k | value*=10; |
318 | 684k | value+=c-'0'; |
319 | 684k | if (value < old_value) |
320 | 3.43k | { |
321 | 3.43k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
322 | 3.43k | "ERROR: integer overflow (%u > %u) @%" |
323 | 3.43k | MAGICK_OFF_F "d", old_value, value, |
324 | 3.43k | TellBlob(image)); |
325 | 3.43k | *status=MagickFail; |
326 | 3.43k | return(0); |
327 | 3.43k | } |
328 | 681k | c=ReadBlobByte(image); |
329 | 681k | if ((c == EOF) || !(isdigit(c))) |
330 | 196k | break; |
331 | 484k | count++; |
332 | 484k | c &= 0xff; |
333 | 484k | } |
334 | 196k | if (!count) |
335 | 0 | { |
336 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
337 | 0 | "ERROR: did not read any digits! @%" |
338 | 0 | MAGICK_OFF_F "d", TellBlob(image)); |
339 | 0 | *status=MagickFail; |
340 | 0 | } |
341 | | #if 0 |
342 | | do |
343 | | { |
344 | | value*=10; |
345 | | value+=c-'0'; |
346 | | c=ReadBlobByte(image); |
347 | | if (c == EOF) |
348 | | return(value); |
349 | | } while (isdigit(c)); |
350 | | #endif |
351 | 196k | return(value); |
352 | 200k | } |
353 | | |
354 | | #define ValidateScalingIndex(image, index, max) \ |
355 | 81.6k | do \ |
356 | 81.6k | { \ |
357 | 81.6k | if (index > max) \ |
358 | 81.6k | ThrowReaderException(CorruptImageError,CorruptImage, image); \ |
359 | 72.8k | } while (0) |
360 | | |
361 | | #define ValidateScalingPixel(image, pixel, max) \ |
362 | 21.1k | do \ |
363 | 21.1k | { \ |
364 | 21.1k | ValidateScalingIndex(image, pixel.red, max); \ |
365 | 21.1k | ValidateScalingIndex(image, pixel.green, max); \ |
366 | 19.0k | ValidateScalingIndex(image, pixel.blue, max); \ |
367 | 16.5k | } while (0) |
368 | | |
369 | | typedef enum |
370 | | { |
371 | | Undefined_PNM_Format, |
372 | | PBM_ASCII_Format, /* P1 */ |
373 | | PGM_ASCII_Format, /* P2 */ |
374 | | PPM_ASCII_Format, /* P3 */ |
375 | | PBM_RAW_Format, /* P4 */ |
376 | | PGM_RAW_Format, /* P5 */ |
377 | | PPM_RAW_Format, /* P6 */ |
378 | | PAM_Format, /* P7 */ |
379 | | XV_332_Format /* P7 332 */ |
380 | | } PNMSubformat; |
381 | | |
382 | | static const char *PNMSubformatToString(const PNMSubformat f) |
383 | 60.2k | { |
384 | 60.2k | const char *s = "unknown"; |
385 | | |
386 | 60.2k | switch (f) |
387 | 60.2k | { |
388 | 0 | case Undefined_PNM_Format: |
389 | 0 | s = "Undefined"; |
390 | 0 | break; |
391 | 6.73k | case PBM_ASCII_Format: /* P1 */ |
392 | 6.73k | s = "PBM ASCII"; |
393 | 6.73k | break; |
394 | 9.60k | case PGM_ASCII_Format: /* P2 */ |
395 | 9.60k | s = "PGM ASCII"; |
396 | 9.60k | break; |
397 | 16.2k | case PPM_ASCII_Format: /* P3 */ |
398 | 16.2k | s = "PPM ASCII"; |
399 | 16.2k | break; |
400 | 3.27k | case PBM_RAW_Format: /* P4 */ |
401 | 3.27k | s = "PBM RAW"; |
402 | 3.27k | break; |
403 | 3.96k | case PGM_RAW_Format: /* P5 */ |
404 | 3.96k | s = "PGM RAW"; |
405 | 3.96k | break; |
406 | 16.3k | case PPM_RAW_Format: /* P6 */ |
407 | 16.3k | s = "PPM RAW"; |
408 | 16.3k | break; |
409 | 3.91k | case PAM_Format: /* P7 */ |
410 | 3.91k | s = "PAM"; |
411 | 3.91k | break; |
412 | 111 | case XV_332_Format: /* P7 332 */ |
413 | 111 | s = "XV 332 icon"; |
414 | 111 | break; |
415 | 60.2k | } |
416 | 60.2k | return s; |
417 | 60.2k | } |
418 | | |
419 | | #if defined(HAVE_OPENMP) |
420 | | # define PNMReadUseOpenMP 1 |
421 | | |
422 | | static int PNMReadThreads(const Image* image, const size_t bytes_per_row) |
423 | | { |
424 | | const int omp_max_threads = omp_get_max_threads(); |
425 | | int threads; |
426 | | ARG_NOT_USED(image); |
427 | | threads=(int)(Min(bytes_per_row/4096U,(size_t) Max(0,omp_max_threads))); |
428 | | if (0 == threads) |
429 | | threads=1; |
430 | | return threads; |
431 | | } |
432 | | #endif /* defined(HAVE_OPENMP) */ |
433 | | |
434 | | static Image *ReadPNMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
435 | 207k | { |
436 | 207k | char |
437 | 207k | c; |
438 | | |
439 | 207k | PNMSubformat |
440 | 207k | format; |
441 | | |
442 | 207k | Image |
443 | 207k | *image; |
444 | | |
445 | 207k | long |
446 | 207k | y; |
447 | | |
448 | 207k | LongPixelPacket |
449 | 207k | pixel; |
450 | | |
451 | 207k | register IndexPacket |
452 | 207k | *indexes; |
453 | | |
454 | 207k | register unsigned long |
455 | 207k | i; |
456 | | |
457 | 207k | size_t |
458 | 207k | count, |
459 | 207k | number_pixels; |
460 | | |
461 | 207k | unsigned int |
462 | 207k | index, |
463 | 207k | bits_per_sample; |
464 | | |
465 | 207k | MagickPassFail |
466 | 207k | status; |
467 | | |
468 | 207k | unsigned int |
469 | 207k | max_value, |
470 | 207k | samples_per_pixel; |
471 | | |
472 | | /* |
473 | | Open image file. |
474 | | */ |
475 | 207k | assert(image_info != (const ImageInfo *) NULL); |
476 | 207k | assert(image_info->signature == MagickSignature); |
477 | 207k | assert(exception != (ExceptionInfo *) NULL); |
478 | 207k | assert(exception->signature == MagickSignature); |
479 | 207k | image=AllocateImage(image_info); |
480 | 207k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
481 | 207k | if (status == MagickFail) |
482 | 207k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
483 | | /* |
484 | | Read PNM image. |
485 | | */ |
486 | 207k | count=ReadBlob(image,1,(char *) &c); |
487 | 207k | do |
488 | 207k | { |
489 | | /* |
490 | | Initialize image structure. |
491 | | */ |
492 | 207k | max_value=0; |
493 | 207k | bits_per_sample=0; |
494 | 207k | samples_per_pixel=0; |
495 | | |
496 | 207k | if (count == 0) |
497 | 207k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
498 | | |
499 | 207k | if (c != 'P') |
500 | 66 | { |
501 | 66 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Read %c rather than expected 'P'!",c); |
502 | 66 | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
503 | 0 | } |
504 | | |
505 | 207k | c=ReadBlobByte(image); |
506 | 207k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"PNM Format Id: P%c", |
507 | 207k | c); |
508 | | |
509 | 207k | switch (c) |
510 | 207k | { |
511 | 19.4k | case '1': format=PBM_ASCII_Format; break; |
512 | 38.5k | case '2': format=PGM_ASCII_Format; break; |
513 | 27.3k | case '3': format=PPM_ASCII_Format; break; |
514 | 19.6k | case '4': format=PBM_RAW_Format; break; |
515 | 24.1k | case '5': format=PGM_RAW_Format; break; |
516 | 48.6k | case '6': format=PPM_RAW_Format; break; |
517 | 27.9k | case '7': |
518 | 27.9k | { |
519 | 27.9k | if ((ReadBlobByte(image) == ' ') && |
520 | 9.50k | (PNMIntegerOrComment(image,10,&status) == 332)) |
521 | 6.47k | format=XV_332_Format; |
522 | 21.4k | else |
523 | 21.4k | format=PAM_Format; |
524 | 27.9k | break; |
525 | 0 | } |
526 | 1.38k | default: |
527 | 1.38k | { |
528 | 1.38k | format=Undefined_PNM_Format; |
529 | 1.38k | } |
530 | 207k | } |
531 | | |
532 | 207k | if (status == MagickFail) |
533 | 206k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
534 | | |
535 | 206k | if (PAM_Format == format) |
536 | 21.3k | { |
537 | | /* |
538 | | PAM header format |
539 | | |
540 | | P7 |
541 | | WIDTH 227 |
542 | | HEIGHT 149 |
543 | | DEPTH 3 |
544 | | MAXVAL 255 |
545 | | TUPLTYPE RGB |
546 | | ENDHDR |
547 | | */ |
548 | | |
549 | 21.3k | char |
550 | 21.3k | keyword[MaxTextExtent]; |
551 | | |
552 | 21.3k | register char |
553 | 21.3k | *p; |
554 | | |
555 | 21.3k | int |
556 | 21.3k | c; |
557 | | |
558 | 334k | while (1) |
559 | 334k | { |
560 | 334k | p=keyword; |
561 | 334k | c=ReadBlobByte(image); |
562 | 334k | do |
563 | 1.18M | { |
564 | 1.18M | if (isalnum(c) || ('#' == c)) |
565 | 1.15M | if ((p-keyword) < (MaxTextExtent-1)) |
566 | 1.14M | { |
567 | 1.14M | *p++=c; |
568 | 1.14M | if ('#' == c) |
569 | 208k | break; |
570 | 1.14M | } |
571 | 974k | c=ReadBlobByte(image); |
572 | 974k | } while (isalnum(c) || ('#' == c)); |
573 | 334k | *p='\0'; |
574 | | |
575 | 334k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
576 | 334k | "Keyword \"%s\"",keyword); |
577 | 334k | if ((EOF == c) || (LocaleCompare(keyword,"ENDHDR") == 0)) |
578 | 4.62k | { |
579 | 4.62k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
580 | 4.62k | "Exiting header!"); |
581 | 4.62k | break; |
582 | 4.62k | } |
583 | 329k | else if (LocaleCompare(keyword,"HEIGHT") == 0) |
584 | 24.2k | { |
585 | 24.2k | image->rows=PNMIntegerOrComment(image,10,&status); |
586 | 24.2k | if (status == MagickFail) |
587 | 24.2k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
588 | 24.2k | } |
589 | 305k | else if (LocaleCompare(keyword,"WIDTH") == 0) |
590 | 19.0k | { |
591 | 19.0k | image->columns=PNMInteger(image,10,&status); |
592 | 19.0k | if (status == MagickFail) |
593 | 19.0k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
594 | 19.0k | } |
595 | 286k | else if (LocaleCompare(keyword,"DEPTH") == 0) |
596 | 18.2k | { |
597 | 18.2k | samples_per_pixel=PNMInteger(image,10,&status); |
598 | 18.2k | if (status == MagickFail) |
599 | 18.2k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
600 | 18.2k | } |
601 | 268k | else if (LocaleCompare(keyword,"MAXVAL") == 0) |
602 | 14.0k | { |
603 | 14.0k | max_value=PNMInteger(image,10,&status); |
604 | 14.0k | if (status == MagickFail) |
605 | 13.9k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
606 | 13.9k | } |
607 | 254k | else if (LocaleCompare(keyword,"TUPLTYPE") == 0) |
608 | 31.4k | { |
609 | | /* Skip white space */ |
610 | 31.4k | do |
611 | 38.7k | { |
612 | 38.7k | c=ReadBlobByte(image); |
613 | 38.7k | } while (isspace(c) && (EOF != c)); |
614 | 31.4k | if (EOF == c) |
615 | 99 | break; |
616 | | /* Tupletype argument */ |
617 | 31.3k | p=keyword; |
618 | 31.3k | do |
619 | 59.2M | { |
620 | 59.2M | if ((p-keyword) < (MaxTextExtent-1)) |
621 | 6.11M | *p++=c; |
622 | 59.2M | c=ReadBlobByte(image); |
623 | 59.2M | } while (('\n' != c) && (EOF != c)); |
624 | 31.3k | *p='\0'; |
625 | 31.3k | if (EOF == c) |
626 | 554 | break; |
627 | 30.7k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
628 | 30.7k | "TUPLTYPE \"%s\"",keyword); |
629 | 30.7k | if (LocaleNCompare(keyword,"BLACKANDWHITE",13) == 0) |
630 | 1.60k | { |
631 | 1.60k | image->colorspace=GRAYColorspace; |
632 | 1.60k | image->is_monochrome=MagickTrue; |
633 | 1.60k | } |
634 | 29.1k | else if (LocaleNCompare(keyword,"CMYK",4) == 0) |
635 | 7.52k | { |
636 | 7.52k | image->colorspace=CMYKColorspace; |
637 | 7.52k | } |
638 | 21.6k | else if (LocaleNCompare(keyword,"GRAYSCALE",9) == 0) |
639 | 4.05k | { |
640 | 4.05k | image->colorspace=GRAYColorspace; |
641 | 4.05k | } |
642 | 17.5k | else if (LocaleNCompare(keyword,"RGB",3) == 0) |
643 | 33 | { |
644 | 33 | } |
645 | | |
646 | | /* |
647 | | Check for alpha flag. |
648 | | */ |
649 | 30.7k | count=strlen(keyword); |
650 | 30.7k | if ((count > 6) && (LocaleNCompare(keyword+count-6,"_ALPHA",6) == 0)) |
651 | 6.17k | { |
652 | 6.17k | image->matte=MagickTrue; |
653 | 6.17k | } |
654 | 30.7k | } |
655 | 222k | else if (LocaleNCompare(keyword,"#",1) == 0) |
656 | 208k | { |
657 | | /* Skip leading white space */ |
658 | 208k | do |
659 | 305k | { |
660 | 305k | c=ReadBlobByte(image); |
661 | 305k | } while (isspace(c) && (EOF != c)); |
662 | 208k | if (EOF == c) |
663 | 1.35k | break; |
664 | | |
665 | | /* Comment */ |
666 | 206k | p=keyword; |
667 | 92.0M | while ((p-keyword) < (MaxTextExtent-2)) |
668 | 92.0M | { |
669 | 92.0M | *p++=c; |
670 | 92.0M | c=ReadBlobByte(image); |
671 | 92.0M | if (c == '\n') |
672 | 162k | { |
673 | 162k | *p++=c; |
674 | 162k | break; |
675 | 162k | } |
676 | 91.8M | if (c == EOF) |
677 | 2.25k | break; |
678 | 91.8M | } |
679 | 206k | *p='\0'; |
680 | | /* |
681 | | FIXME: |
682 | | Implicitly extend existing comment attribute since comments |
683 | | can span multiple lines. |
684 | | */ |
685 | 206k | (void) SetImageAttribute(image,"comment",keyword); |
686 | 206k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
687 | 206k | "Comment: \"%s\"",keyword); |
688 | 206k | } |
689 | 14.6k | else |
690 | 14.6k | { |
691 | | /* Unknown! */ |
692 | 14.6k | do |
693 | 28.0M | { |
694 | 28.0M | c=ReadBlobByte(image); |
695 | 28.0M | } while (('\n' != c) && (EOF != c)); |
696 | 14.6k | break; |
697 | 14.6k | } |
698 | 334k | } |
699 | 21.3k | } |
700 | 185k | else |
701 | 185k | { |
702 | | /* |
703 | | PNM header type format |
704 | | |
705 | | P1 |
706 | | # feep.pbm |
707 | | 24 7 |
708 | | |
709 | | P3 |
710 | | # feep.ppm |
711 | | 4 4 |
712 | | 15 |
713 | | */ |
714 | 185k | image->columns=PNMIntegerOrComment(image,10,&status); |
715 | 185k | image->rows=PNMInteger(image,10,&status); |
716 | | |
717 | 185k | if ((format == PBM_ASCII_Format) || (format == PBM_RAW_Format)) |
718 | 39.1k | max_value=1; /* bitmap */ |
719 | 146k | else |
720 | 146k | max_value=PNMInteger(image,10,&status); |
721 | | |
722 | 185k | if (status == MagickFail) |
723 | 142k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
724 | | |
725 | 142k | switch (format) |
726 | 142k | { |
727 | 15.6k | case PBM_ASCII_Format: |
728 | 32.1k | case PBM_RAW_Format: |
729 | 63.7k | case PGM_ASCII_Format: |
730 | 80.0k | case PGM_RAW_Format: |
731 | 84.2k | case XV_332_Format: |
732 | 84.2k | { |
733 | 84.2k | samples_per_pixel=1; |
734 | 84.2k | break; |
735 | 80.0k | } |
736 | 22.5k | case PPM_ASCII_Format: |
737 | 58.4k | case PPM_RAW_Format: |
738 | 58.4k | { |
739 | 58.4k | samples_per_pixel=3; |
740 | 58.4k | break; |
741 | 22.5k | } |
742 | 99 | default: |
743 | 99 | { |
744 | 99 | } |
745 | 142k | } |
746 | 142k | } |
747 | | |
748 | 164k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Dimensions: %lux%lu", |
749 | 164k | image->columns,image->rows); |
750 | 164k | if ((image->columns == 0) || (image->rows == 0)) |
751 | 142k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
752 | | |
753 | 142k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Max Value: %u", |
754 | 142k | max_value); |
755 | 142k | if ((max_value == 0) || (max_value > 4294967295U)) |
756 | 140k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
757 | | |
758 | 140k | if ((format == XV_332_Format) && (max_value != 255)) |
759 | 137k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
760 | | |
761 | 137k | bits_per_sample=0; |
762 | 137k | if (max_value <= 1) |
763 | 51.3k | bits_per_sample=1; |
764 | 86.2k | else if (max_value <= 255U) |
765 | 59.7k | bits_per_sample=8; |
766 | 26.4k | else if (max_value <= 65535U) |
767 | 14.6k | bits_per_sample=16; |
768 | 11.8k | else if (max_value <= 4294967295U) |
769 | 11.8k | bits_per_sample=32; |
770 | | |
771 | 137k | image->depth=Min(bits_per_sample,QuantumDepth); |
772 | | |
773 | 137k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Image Depth: %u", |
774 | 137k | image->depth); |
775 | 137k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Samples Per Pixel: %u", |
776 | 137k | samples_per_pixel); |
777 | 137k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Bits Per Sample: %u", |
778 | 137k | bits_per_sample); |
779 | | |
780 | 137k | if (EOFBlob(image)) |
781 | 14.5k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
782 | 137k | image->filename); |
783 | | |
784 | 137k | if ((1 == samples_per_pixel) && (max_value < MaxColormapSize) && |
785 | 70.2k | ((size_t) image->columns*image->rows >= max_value)) |
786 | 52.9k | { |
787 | 52.9k | image->storage_class=PseudoClass; |
788 | 52.9k | image->colors= |
789 | 52.9k | max_value >= MaxColormapSize ? MaxColormapSize : max_value+1; |
790 | 52.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colors: %u", |
791 | 52.9k | image->colors); |
792 | 52.9k | } |
793 | 137k | number_pixels=MagickArraySize(image->columns,image->rows); |
794 | 137k | if (number_pixels == 0) |
795 | 137k | ThrowReaderException(CorruptImageError,NegativeOrZeroImageSize,image); |
796 | 137k | if (image->storage_class == PseudoClass) |
797 | 52.9k | { |
798 | | /* |
799 | | Create colormap. |
800 | | */ |
801 | 52.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
802 | 52.9k | "Allocating colormap with %u colors", |
803 | 52.9k | image->colors); |
804 | 52.9k | if (!AllocateImageColormap(image,image->colors)) |
805 | 0 | ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, |
806 | 52.9k | image); |
807 | 52.9k | if ((format == XV_332_Format) && (image->colors == 256)) |
808 | 556 | { |
809 | | /* |
810 | | Initialize 332 colormap. |
811 | | */ |
812 | 556 | i=0; |
813 | 5.00k | for (pixel.red=0; pixel.red < 8; pixel.red++) |
814 | 40.0k | for (pixel.green=0; pixel.green < 8; pixel.green++) |
815 | 177k | for (pixel.blue=0; pixel.blue < 4; pixel.blue++) |
816 | 142k | { |
817 | 142k | image->colormap[i].red=(Quantum) |
818 | 142k | (((double) MaxRGB*pixel.red)/0x07+0.5); |
819 | 142k | image->colormap[i].green=(Quantum) |
820 | 142k | (((double) MaxRGB*pixel.green)/0x07+0.5); |
821 | 142k | image->colormap[i].blue=(Quantum) |
822 | 142k | (((double) MaxRGB*pixel.blue)/0x03+0.5); |
823 | 142k | i++; |
824 | 142k | } |
825 | 556 | } |
826 | 52.9k | } |
827 | 137k | if (image_info->ping && (image_info->subrange != 0)) |
828 | 0 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
829 | 0 | break; |
830 | | |
831 | 137k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
832 | 77.2k | ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
833 | | |
834 | | /* |
835 | | Decode PNM pixels to Image pixel packets. |
836 | | */ |
837 | 60.3k | switch (format) |
838 | 60.3k | { |
839 | 6.73k | case PBM_ASCII_Format: |
840 | 6.73k | { |
841 | | /* |
842 | | Convert PBM image to pixel packets. |
843 | | */ |
844 | 6.73k | register unsigned long |
845 | 6.73k | x=0; |
846 | | |
847 | 6.73k | register PixelPacket |
848 | 6.73k | *q; |
849 | | |
850 | 6.73k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
851 | 6.73k | "Reading %s pixel data",PNMSubformatToString(format)); |
852 | 14.5k | for (y=0; y < (long) image->rows; y++) |
853 | 14.0k | { |
854 | 14.0k | q=SetImagePixels(image,0,y,image->columns,1); |
855 | 14.0k | if (q == (PixelPacket *) NULL) |
856 | 2.03k | break; |
857 | 12.0k | indexes=AccessMutableIndexes(image); |
858 | 21.2k | for (x=0; x < image->columns; x++) |
859 | 13.3k | { |
860 | 13.3k | index=!PNMInteger(image,2,&status); |
861 | 13.3k | if (status == MagickFail) |
862 | 4.21k | break; |
863 | 9.16k | VerifyColormapIndex(status,image,index); |
864 | 9.16k | indexes[x]=index; |
865 | 9.16k | *q++=image->colormap[index]; |
866 | 9.16k | } |
867 | 12.0k | if (!SyncImagePixels(image)) |
868 | 0 | break; |
869 | 12.0k | if (image->previous == (Image *) NULL) |
870 | 12.0k | if (QuantumTick(y,image->rows)) |
871 | 8.65k | if (!MagickMonitorFormatted(y,image->rows,exception, |
872 | 8.65k | LoadImageText,image->filename, |
873 | 8.65k | image->columns,image->rows)) |
874 | 0 | break; |
875 | 12.0k | if (status == MagickFail) |
876 | 4.21k | break; |
877 | 12.0k | } |
878 | 6.73k | image->is_grayscale=MagickTrue; |
879 | 6.73k | image->is_monochrome=MagickTrue; |
880 | | |
881 | | |
882 | 6.73k | if (status == MagickFail) |
883 | 4.21k | { |
884 | 4.21k | if (EOFBlob(image)) |
885 | 1.15k | { |
886 | 1.15k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
887 | 1.15k | "PBM raw (P1) reader EOF at x=%lu, y=%lu", x, y); |
888 | 1.15k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
889 | 1.15k | image->filename); |
890 | 1.15k | } |
891 | 3.06k | else |
892 | 3.06k | { |
893 | 3.06k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
894 | 3.06k | "PBM raw (P1) corrupt data at x=%lu, y=%lu", x, y); |
895 | 3.06k | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
896 | 3.06k | } |
897 | 4.21k | } |
898 | 6.73k | break; |
899 | 0 | } |
900 | 9.60k | case PGM_ASCII_Format: |
901 | 9.60k | { |
902 | | /* |
903 | | Convert PGM image to pixel packets. |
904 | | */ |
905 | 9.60k | register unsigned long |
906 | 9.60k | x=0; |
907 | | |
908 | 9.60k | register PixelPacket |
909 | 9.60k | *q; |
910 | | |
911 | 9.60k | unsigned long |
912 | 9.60k | intensity; |
913 | | |
914 | 9.60k | MagickBool |
915 | 9.60k | is_grayscale, |
916 | 9.60k | is_monochrome; |
917 | | |
918 | 9.60k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
919 | 9.60k | "Reading %s pixel data",PNMSubformatToString(format)); |
920 | 9.60k | is_grayscale=MagickTrue; |
921 | 9.60k | is_monochrome=MagickTrue; |
922 | 21.4k | for (y=0; y < (long) image->rows; y++) |
923 | 19.5k | { |
924 | 19.5k | q=SetImagePixels(image,0,y,image->columns,1); |
925 | 19.5k | if (q == (PixelPacket *) NULL) |
926 | 3.06k | break; |
927 | 16.4k | if (image->storage_class == PseudoClass) |
928 | 10.9k | { |
929 | | /* |
930 | | PseudoClass |
931 | | */ |
932 | 10.9k | indexes=AccessMutableIndexes(image); |
933 | 26.0k | for (x=0; x < image->columns; x++) |
934 | 17.7k | { |
935 | 17.7k | intensity=PNMInteger(image,10,&status); |
936 | 17.7k | if (status == MagickFail) |
937 | 1.00k | break; |
938 | 16.7k | ValidateScalingIndex(image, intensity, max_value); |
939 | 15.0k | index=intensity; |
940 | 15.0k | VerifyColormapIndex(status,image,index); |
941 | 15.0k | if (status == MagickFail) |
942 | 0 | break; |
943 | 15.0k | indexes[x]=index; |
944 | 15.0k | *q=image->colormap[index]; |
945 | 15.0k | is_monochrome &= IsMonochrome(*q); |
946 | 15.0k | q++; |
947 | 15.0k | } |
948 | 10.9k | } |
949 | 5.51k | else |
950 | 5.51k | { |
951 | | /* |
952 | | DirectClass |
953 | | */ |
954 | 13.2k | for (x=0; x < image->columns; x++) |
955 | 9.73k | { |
956 | 9.73k | intensity=PNMInteger(image,10,&status); |
957 | 9.73k | if (status == MagickFail) |
958 | 1.56k | break; |
959 | 8.17k | ValidateScalingIndex(image, intensity, max_value); |
960 | 7.75k | intensity=ScaleAnyToQuantum(intensity, max_value); |
961 | 7.75k | q->red=q->green=q->blue=intensity; |
962 | 7.75k | is_monochrome &= IsMonochrome(*q); |
963 | 7.75k | q++; |
964 | 7.75k | } |
965 | 5.51k | } |
966 | 14.4k | if (status == MagickFail) |
967 | 2.57k | break; |
968 | 11.8k | if (!SyncImagePixels(image)) |
969 | 0 | break; |
970 | 11.8k | if (image->previous == (Image *) NULL) |
971 | 11.8k | if (QuantumTick(y,image->rows)) |
972 | 8.90k | if (!MagickMonitorFormatted(y,image->rows,exception, |
973 | 8.90k | LoadImageText,image->filename, |
974 | 8.90k | image->columns,image->rows)) |
975 | 0 | break; |
976 | 11.8k | } |
977 | 7.53k | image->is_monochrome=is_monochrome; |
978 | 7.53k | image->is_grayscale=is_grayscale; |
979 | 7.53k | if (status == MagickFail) |
980 | 2.57k | { |
981 | 2.57k | if (EOFBlob(image)) |
982 | 2.49k | { |
983 | 2.49k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
984 | 2.49k | "PGM raw (P2) reader EOF at x=%lu, y=%lu", x, y); |
985 | 2.49k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
986 | 2.49k | image->filename); |
987 | 2.49k | } |
988 | 81 | else |
989 | 81 | { |
990 | 81 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
991 | 81 | "PGM raw (P2) corrupt data at x=%lu, y=%lu", x, y); |
992 | 81 | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
993 | 81 | } |
994 | 2.57k | } |
995 | 7.53k | break; |
996 | 9.60k | } |
997 | 16.2k | case PPM_ASCII_Format: |
998 | 16.2k | { |
999 | | /* |
1000 | | Convert PPM image to pixel packets. |
1001 | | */ |
1002 | 16.2k | register unsigned long |
1003 | 16.2k | x=0; |
1004 | | |
1005 | 16.2k | register PixelPacket |
1006 | 16.2k | *q; |
1007 | | |
1008 | 16.2k | MagickBool |
1009 | 16.2k | is_grayscale, |
1010 | 16.2k | is_monochrome; |
1011 | | |
1012 | 16.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1013 | 16.2k | "Reading %s pixel data",PNMSubformatToString(format)); |
1014 | 16.2k | is_grayscale=MagickTrue; |
1015 | 16.2k | is_monochrome=MagickTrue; |
1016 | 24.6k | for (y=0; y < (long) image->rows; y++) |
1017 | 23.5k | { |
1018 | 23.5k | q=SetImagePixels(image,0,y,image->columns,1); |
1019 | 23.5k | if (q == (PixelPacket *) NULL) |
1020 | 4.52k | break; |
1021 | 33.4k | for (x=0; x < image->columns; x++) |
1022 | 25.1k | { |
1023 | 25.1k | pixel.red=PNMInteger(image,10,&status); |
1024 | 25.1k | if (status == MagickFail) |
1025 | 2.19k | break; |
1026 | 22.9k | pixel.green=PNMInteger(image,10,&status); |
1027 | 22.9k | if (status == MagickFail) |
1028 | 617 | break; |
1029 | 22.2k | pixel.blue=PNMInteger(image,10,&status); |
1030 | 22.2k | if (status == MagickFail) |
1031 | 1.12k | break; |
1032 | 21.1k | ValidateScalingPixel(image, pixel, max_value); |
1033 | 14.3k | pixel.red=ScaleAnyToQuantum(pixel.red, max_value); |
1034 | 14.3k | pixel.green=ScaleAnyToQuantum(pixel.green, max_value); |
1035 | 14.3k | pixel.blue=ScaleAnyToQuantum(pixel.blue, max_value); |
1036 | 14.3k | q->red=(Quantum) pixel.red; |
1037 | 14.3k | q->green=(Quantum) pixel.green; |
1038 | 14.3k | q->blue=(Quantum) pixel.blue; |
1039 | 14.3k | is_monochrome &= IsMonochrome(*q); |
1040 | 14.3k | is_grayscale &= IsGray(*q); |
1041 | 14.3k | q++; |
1042 | 14.3k | } |
1043 | 12.2k | if (status == MagickFail) |
1044 | 3.93k | break; |
1045 | 8.32k | if (!SyncImagePixels(image)) |
1046 | 0 | break; |
1047 | 8.32k | if (image->previous == (Image *) NULL) |
1048 | 8.32k | if (QuantumTick(y,image->rows)) |
1049 | 5.37k | if (!MagickMonitorFormatted(y,image->rows,exception, |
1050 | 5.37k | LoadImageText,image->filename, |
1051 | 5.37k | image->columns,image->rows)) |
1052 | 0 | break; |
1053 | 8.32k | } |
1054 | 9.47k | image->is_monochrome=is_monochrome; |
1055 | 9.47k | image->is_grayscale=is_grayscale; |
1056 | 9.47k | if (status == MagickFail) |
1057 | 3.93k | { |
1058 | 3.93k | if (EOFBlob(image)) |
1059 | 3.78k | { |
1060 | 3.78k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1061 | 3.78k | "PNM raw (P3) reader EOF at x=%lu, y=%lu", x, y); |
1062 | 3.78k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
1063 | 3.78k | image->filename); |
1064 | 3.78k | } |
1065 | 153 | else |
1066 | 153 | { |
1067 | 153 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1068 | 153 | "PNM raw (P3) corrupt data at x=%lu, y=%lu", x, y); |
1069 | 153 | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
1070 | 153 | } |
1071 | 3.93k | } |
1072 | 9.47k | break; |
1073 | 16.2k | } |
1074 | 3.27k | case PBM_RAW_Format: |
1075 | 7.23k | case PGM_RAW_Format: |
1076 | 23.5k | case PPM_RAW_Format: |
1077 | 27.4k | case PAM_Format: |
1078 | 27.6k | case XV_332_Format: |
1079 | 27.6k | { |
1080 | | /* |
1081 | | Convert PBM/PGM/PPM/PAM/XV raw raster image to pixel packets. |
1082 | | */ |
1083 | 27.6k | ImportPixelAreaOptions |
1084 | 27.6k | import_options; |
1085 | | |
1086 | 27.6k | QuantumType |
1087 | 27.6k | quantum_type; |
1088 | | |
1089 | 27.6k | size_t |
1090 | 27.6k | bytes_per_row; |
1091 | | |
1092 | 27.6k | MagickBool |
1093 | 27.6k | check_pixels, |
1094 | 27.6k | is_grayscale, |
1095 | 27.6k | is_monochrome, |
1096 | 27.6k | use_scaling; |
1097 | | |
1098 | 27.6k | unsigned long |
1099 | 27.6k | max_value_given_bits, |
1100 | 27.6k | row_count=0; |
1101 | | |
1102 | 27.6k | ThreadViewDataSet |
1103 | 27.6k | *scanline_set; |
1104 | | |
1105 | 27.6k | double |
1106 | 27.6k | sample_scale; |
1107 | | |
1108 | 27.6k | unsigned int |
1109 | 27.6k | sample_max; |
1110 | | |
1111 | | #if defined(HAVE_OPENMP) |
1112 | | int |
1113 | | pnm_read_threads; |
1114 | | #endif |
1115 | | |
1116 | 27.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1117 | 27.6k | "Reading %s pixel data",PNMSubformatToString(format)); |
1118 | | |
1119 | 27.6k | ImportPixelAreaOptionsInit(&import_options); |
1120 | | |
1121 | 27.6k | check_pixels=MagickTrue; |
1122 | 27.6k | is_grayscale=MagickTrue; |
1123 | 27.6k | is_monochrome=MagickTrue; |
1124 | | |
1125 | | /* |
1126 | | Deduce correct import parameters. |
1127 | | */ |
1128 | 27.6k | quantum_type=UndefinedQuantum; |
1129 | 27.6k | import_options.grayscale_miniswhite=MagickFalse; |
1130 | 27.6k | max_value_given_bits=MaxValueGivenBits(bits_per_sample); |
1131 | 27.6k | if (max_value_given_bits == 0UL) |
1132 | 0 | { |
1133 | 0 | ThrowException(exception,CorruptImageError,ImproperImageHeader, |
1134 | 0 | image->filename); |
1135 | 0 | break; |
1136 | 0 | } |
1137 | 27.6k | sample_max=RoundDoubleToQuantum((MaxRGBDouble*max_value)/max_value_given_bits); |
1138 | 27.6k | if (sample_max == 0U) |
1139 | 0 | { |
1140 | 0 | ThrowException(exception,CorruptImageError,ImproperImageHeader, |
1141 | 0 | image->filename); |
1142 | 0 | break; |
1143 | 0 | } |
1144 | 27.6k | sample_scale=MaxRGBDouble/sample_max; |
1145 | 27.6k | use_scaling=(MaxRGB != sample_max); |
1146 | 27.6k | bytes_per_row=0; |
1147 | | |
1148 | 27.6k | if (1 == samples_per_pixel) |
1149 | 7.93k | { |
1150 | 7.93k | if (1 == bits_per_sample) |
1151 | 3.65k | { |
1152 | | /* PBM */ |
1153 | 3.65k | import_options.grayscale_miniswhite=MagickTrue; |
1154 | 3.65k | quantum_type=GrayQuantum; |
1155 | 3.65k | } |
1156 | 4.28k | else |
1157 | 4.28k | { |
1158 | | /* PGM & XV_332 */ |
1159 | 4.28k | if ((XV_332_Format == format) && (image->storage_class == PseudoClass)) |
1160 | 99 | { |
1161 | 99 | quantum_type=IndexQuantum; |
1162 | 99 | } |
1163 | 4.18k | else |
1164 | 4.18k | { |
1165 | 4.18k | quantum_type=GrayQuantum; |
1166 | 4.18k | } |
1167 | 4.28k | } |
1168 | 7.93k | } |
1169 | 19.6k | else if (2 == samples_per_pixel && image->matte) |
1170 | 663 | { |
1171 | 663 | quantum_type=GrayAlphaQuantum; |
1172 | 663 | } |
1173 | 19.0k | else if (3 == samples_per_pixel) |
1174 | 16.4k | { |
1175 | | /* PPM */ |
1176 | 16.4k | quantum_type=RGBQuantum; |
1177 | 16.4k | } |
1178 | 2.54k | else if (4 == samples_per_pixel) |
1179 | 1.37k | { |
1180 | 1.37k | if (CMYKColorspace == image->colorspace) |
1181 | 652 | quantum_type=CMYKQuantum; |
1182 | 727 | else |
1183 | 727 | quantum_type=RGBAQuantum; |
1184 | 1.37k | } |
1185 | 1.16k | else if (5 == samples_per_pixel) |
1186 | 647 | { |
1187 | 647 | if (CMYKColorspace == image->colorspace) |
1188 | 627 | quantum_type=CMYKAQuantum; |
1189 | 647 | } |
1190 | | |
1191 | 27.6k | if (image->logging) |
1192 | 27.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1193 | 27.6k | "Import Quantum Type: %s", |
1194 | 27.6k | QuantumTypeToString(quantum_type)); |
1195 | | |
1196 | 27.6k | samples_per_pixel=MagickGetQuantumSamplesPerPixel(quantum_type); |
1197 | 27.6k | if (image->logging) |
1198 | 27.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1199 | 27.6k | "Samples/Pixel: %u", samples_per_pixel); |
1200 | | |
1201 | 27.6k | if (1 == bits_per_sample) |
1202 | 5.83k | { |
1203 | | /* bytes_per_row=(((size_t) image->columns*samples_per_pixel+7) >> 3); */ |
1204 | 5.83k | bytes_per_row=MagickArraySize(image->columns,samples_per_pixel); |
1205 | 5.83k | if (bytes_per_row) |
1206 | 5.60k | bytes_per_row += 7; |
1207 | 5.83k | if (bytes_per_row) |
1208 | 5.60k | bytes_per_row >>= 3; |
1209 | 5.83k | } |
1210 | 21.7k | else |
1211 | 21.7k | { |
1212 | 21.7k | bytes_per_row=MagickArraySize((((size_t) bits_per_sample+7)/8)* |
1213 | 21.7k | samples_per_pixel,image->columns); |
1214 | 21.7k | } |
1215 | | |
1216 | 27.6k | if (image->logging) |
1217 | 27.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1218 | 27.6k | "Bytes/Row: %" MAGICK_SIZE_T_F "u", |
1219 | 27.6k | (MAGICK_SIZE_T) bytes_per_row); |
1220 | | |
1221 | 27.6k | if (1 == samples_per_pixel) |
1222 | 7.93k | { |
1223 | 7.93k | check_pixels=MagickFalse; |
1224 | 7.93k | } |
1225 | 27.6k | if (GrayQuantum == quantum_type) |
1226 | 7.83k | { |
1227 | 7.83k | if (1 == bits_per_sample) |
1228 | 3.65k | { |
1229 | 3.65k | is_grayscale=MagickTrue; |
1230 | 3.65k | is_monochrome=MagickTrue; |
1231 | 3.65k | } |
1232 | 4.18k | else |
1233 | 4.18k | { |
1234 | 4.18k | is_grayscale=MagickTrue; |
1235 | 4.18k | is_monochrome=MagickFalse; |
1236 | 4.18k | } |
1237 | 7.83k | } |
1238 | | |
1239 | | /* Validate file size before allocating memory */ |
1240 | 27.6k | if (BlobIsSeekable(image)) |
1241 | 27.6k | { |
1242 | 27.6k | const magick_off_t file_size = GetBlobSize(image); |
1243 | 27.6k | const magick_off_t current_offset = TellBlob(image); |
1244 | 27.6k | if ((file_size > 0) && |
1245 | 27.6k | (current_offset > 0) && |
1246 | 27.6k | (file_size > current_offset)) |
1247 | 25.8k | { |
1248 | 25.8k | const magick_off_t remaining = file_size-current_offset; |
1249 | 25.8k | const magick_off_t needed = (magick_off_t) image->rows * |
1250 | 25.8k | (magick_off_t) bytes_per_row; |
1251 | 25.8k | if ((remaining < (magick_off_t) bytes_per_row) || |
1252 | 24.6k | (remaining < needed)) |
1253 | 1.37k | { |
1254 | 1.37k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
1255 | 1.37k | image->filename); |
1256 | 1.37k | break; |
1257 | 1.37k | } |
1258 | 25.8k | } |
1259 | 27.6k | } |
1260 | | |
1261 | | #if defined(HAVE_OPENMP) |
1262 | | pnm_read_threads = PNMReadThreads(image,bytes_per_row); |
1263 | | if (image->logging) |
1264 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1265 | | "Using %d thread%s...", pnm_read_threads, |
1266 | | pnm_read_threads > 1 ? "s" : ""); |
1267 | | #endif |
1268 | 26.2k | scanline_set=AllocateThreadViewDataArray(image,exception,bytes_per_row,1); |
1269 | 26.2k | if (scanline_set == (ThreadViewDataSet *) NULL) |
1270 | 25.6k | ThrowReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1271 | | #if PNMReadUseOpenMP |
1272 | | # if defined(TUNE_OPENMP) |
1273 | | # pragma omp parallel for schedule(runtime) shared(is_grayscale,is_monochrome,row_count,status) |
1274 | | # else |
1275 | | # pragma omp parallel for num_threads(pnm_read_threads) schedule(static,1) shared(is_grayscale,is_monochrome,row_count,status) |
1276 | | # endif |
1277 | | #endif |
1278 | 708k | for (y=0; y < (long) image->rows; y++) |
1279 | 682k | { |
1280 | 682k | register unsigned long |
1281 | 682k | x; |
1282 | | |
1283 | 682k | register PixelPacket |
1284 | 682k | *q = (PixelPacket *) NULL; |
1285 | | |
1286 | 682k | void |
1287 | 682k | *pixels; |
1288 | | |
1289 | 682k | MagickBool |
1290 | 682k | thread_status; |
1291 | | |
1292 | 682k | MagickBool |
1293 | 682k | thread_is_grayscale, |
1294 | 682k | thread_is_monochrome; |
1295 | | |
1296 | 682k | unsigned long |
1297 | 682k | thread_row_count; |
1298 | | |
1299 | 682k | ImportPixelAreaInfo |
1300 | 682k | import_info; |
1301 | | |
1302 | | #if PNMReadUseOpenMP |
1303 | | # pragma omp critical (GM_ReadPNMImage) |
1304 | | #endif |
1305 | 682k | thread_status=status; |
1306 | 682k | if (thread_status == MagickFail) |
1307 | 135k | continue; |
1308 | | |
1309 | 547k | pixels=AccessThreadViewData(scanline_set); |
1310 | | |
1311 | | #if PNMReadUseOpenMP |
1312 | | # pragma omp critical (GM_ReadPNMImage) |
1313 | | #endif |
1314 | 547k | { |
1315 | 547k | thread_is_grayscale=is_grayscale; |
1316 | 547k | thread_is_monochrome=is_monochrome; |
1317 | | |
1318 | 547k | if (ReadBlobZC(image,bytes_per_row,&pixels) != bytes_per_row) |
1319 | 1.31k | thread_status=MagickFail; |
1320 | | |
1321 | 547k | thread_row_count=row_count; |
1322 | 547k | row_count++; |
1323 | | |
1324 | 547k | if (image->previous == (Image *) NULL) |
1325 | 547k | if (QuantumTick(thread_row_count,image->rows)) |
1326 | 161k | if (!MagickMonitorFormatted(thread_row_count,image->rows, |
1327 | 161k | exception,LoadImageText, |
1328 | 161k | image->filename, |
1329 | 161k | image->columns,image->rows)) |
1330 | 0 | thread_status=MagickFail; |
1331 | 547k | } |
1332 | | |
1333 | 547k | if (thread_status != MagickFail) |
1334 | 545k | if ((q=SetImagePixels(image,0,thread_row_count,image->columns,1)) == |
1335 | 545k | (PixelPacket *) NULL) |
1336 | 5.45k | thread_status=MagickFail; |
1337 | | |
1338 | 547k | if (thread_status != MagickFail) |
1339 | 540k | if (!ImportImagePixelArea(image,quantum_type,bits_per_sample, |
1340 | 540k | (const unsigned char*) pixels, |
1341 | 540k | &import_options,&import_info)) |
1342 | 614 | thread_status=MagickFail; |
1343 | | /* |
1344 | | Scale sub-ranged pixels up to full range if necessary |
1345 | | */ |
1346 | 547k | if ((thread_status != MagickFail) && (use_scaling)) |
1347 | 4.37M | for (x=0; x < image->columns; x++) |
1348 | 4.29M | { |
1349 | 4.29M | SetRedSample(&q[x], |
1350 | 4.29M | RoundDoubleToQuantum(sample_scale* |
1351 | 4.29M | GetRedSample(&q[x]))); |
1352 | 4.29M | SetGreenSample(&q[x], |
1353 | 4.29M | RoundDoubleToQuantum(sample_scale* |
1354 | 4.29M | GetGreenSample(&q[x]))); |
1355 | 4.29M | SetBlueSample(&q[x], |
1356 | 4.29M | RoundDoubleToQuantum(sample_scale* |
1357 | 4.29M | GetBlueSample(&q[x]))); |
1358 | 4.29M | if (image->matte) |
1359 | 379k | SetOpacitySample(&q[x], |
1360 | 4.29M | MaxRGB- |
1361 | 4.29M | RoundDoubleToQuantum(sample_scale* |
1362 | 4.29M | (MaxRGB- |
1363 | 4.29M | GetOpacitySample(&q[x])))); |
1364 | 4.29M | } |
1365 | | /* |
1366 | | For a DirectClass image, check all pixels for |
1367 | | gray/monochrome status since this format is often |
1368 | | used for input from Ghostscript, which may output |
1369 | | bilevel or gray in an RGB format. It is easier to |
1370 | | check now while the pixels are still "hot" in |
1371 | | memory. |
1372 | | */ |
1373 | 547k | if (thread_status != MagickFail) |
1374 | 539k | if (check_pixels) |
1375 | 61.8k | if (thread_is_grayscale || thread_is_monochrome) |
1376 | 429k | for (x=0; x < image->columns; x++) |
1377 | 413k | { |
1378 | 413k | thread_is_grayscale = thread_is_grayscale && IsGray(q[x]); |
1379 | 413k | thread_is_monochrome = thread_is_monochrome && IsMonochrome(q[x]); |
1380 | 413k | if (!thread_is_grayscale && !thread_is_monochrome) |
1381 | 10.5k | break; |
1382 | 413k | } |
1383 | | |
1384 | 547k | if (thread_status != MagickFail) |
1385 | 539k | if (!SyncImagePixels(image)) |
1386 | 0 | thread_status=MagickFail; |
1387 | | |
1388 | | #if PNMReadUseOpenMP |
1389 | | # pragma omp critical (GM_ReadPNMImage) |
1390 | | #endif |
1391 | 547k | { |
1392 | 547k | if (thread_status == MagickFail) |
1393 | 7.38k | status=MagickFail; |
1394 | | |
1395 | 547k | if (!thread_is_grayscale) |
1396 | 45.4k | is_grayscale=thread_is_grayscale; |
1397 | | |
1398 | 547k | if (!thread_is_monochrome) |
1399 | 85.4k | is_monochrome=thread_is_monochrome; |
1400 | 547k | } |
1401 | 547k | } |
1402 | 25.6k | DestroyThreadViewDataSet(scanline_set); |
1403 | 25.6k | image->is_monochrome=is_monochrome; |
1404 | 25.6k | image->is_grayscale=is_grayscale; |
1405 | 25.6k | if ((status == MagickFail) && (image->exception.severity)) |
1406 | 6.14k | CopyException(exception,&image->exception); |
1407 | 25.6k | if (EOFBlob(image)) |
1408 | 24.2k | ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
1409 | 24.2k | break; |
1410 | 25.6k | } |
1411 | 89 | default: |
1412 | 89 | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
1413 | 60.3k | } |
1414 | 49.3k | StopTimer(&image->timer); |
1415 | 49.3k | if (status ==MagickFail) |
1416 | 16.7k | break; |
1417 | | /* |
1418 | | Proceed to next image. |
1419 | | */ |
1420 | 32.6k | if (image_info->subrange != 0) |
1421 | 32.3k | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
1422 | 32.3k | break; |
1423 | 264 | if ((format == PBM_ASCII_Format) || (format == PGM_ASCII_Format) || (format == PPM_ASCII_Format)) |
1424 | 17 | do |
1425 | 294 | { |
1426 | | /* |
1427 | | Skip to end of line. |
1428 | | */ |
1429 | 294 | count=ReadBlob(image,1,&c); |
1430 | 294 | if (count == 0) |
1431 | 15 | break; |
1432 | 294 | } while (c != '\n'); |
1433 | 264 | count=ReadBlob(image,1,&c); |
1434 | 264 | if ((count != 0) && (c == 'P')) |
1435 | 1 | { |
1436 | | /* |
1437 | | Allocate next image structure. |
1438 | | */ |
1439 | 1 | AllocateNextImage(image_info,image); |
1440 | 1 | if (image->next == (Image *) NULL) |
1441 | 0 | { |
1442 | 0 | DestroyImageList(image); |
1443 | 0 | return((Image *) NULL); |
1444 | 0 | } |
1445 | 1 | image=SyncNextImageInList(image); |
1446 | 1 | if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image), |
1447 | 1 | exception,LoadImagesText, |
1448 | 1 | image->filename)) |
1449 | 0 | break; |
1450 | 1 | } |
1451 | 264 | } while ((count != 0) && (c == 'P')); |
1452 | 49.3k | while (image->previous != (Image *) NULL) |
1453 | 0 | image=image->previous; |
1454 | 49.3k | CloseBlob(image); |
1455 | 49.3k | return(image); |
1456 | 207k | } |
1457 | | |
1458 | | /* |
1459 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1460 | | % % |
1461 | | % % |
1462 | | % % |
1463 | | % R e g i s t e r P N M I m a g e % |
1464 | | % % |
1465 | | % % |
1466 | | % % |
1467 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1468 | | % |
1469 | | % Method RegisterPNMImage adds attributes for the PNM image format to |
1470 | | % the list of supported formats. The attributes include the image format |
1471 | | % tag, a method to read and/or write the format, whether the format |
1472 | | % supports the saving of more than one frame to the same file or blob, |
1473 | | % whether the format supports native in-memory I/O, and a brief |
1474 | | % description of the format. |
1475 | | % |
1476 | | % The format of the RegisterPNMImage method is: |
1477 | | % |
1478 | | % RegisterPNMImage(void) |
1479 | | % |
1480 | | */ |
1481 | | ModuleExport void RegisterPNMImage(void) |
1482 | 11 | { |
1483 | 11 | MagickInfo |
1484 | 11 | *entry; |
1485 | | |
1486 | 11 | entry=SetMagickInfo("P7"); |
1487 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1488 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1489 | 11 | entry->description="Xv thumbnail format"; |
1490 | 11 | entry->extension_treatment=IgnoreExtensionTreatment; |
1491 | 11 | entry->module="PNM"; |
1492 | 11 | (void) RegisterMagickInfo(entry); |
1493 | | |
1494 | 11 | entry=SetMagickInfo("PAM"); |
1495 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1496 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1497 | 11 | entry->description="Portable Arbitrary Map format"; |
1498 | 11 | entry->module="PNM"; |
1499 | 11 | entry->coder_class=PrimaryCoderClass; |
1500 | 11 | (void) RegisterMagickInfo(entry); |
1501 | | |
1502 | 11 | entry=SetMagickInfo("PBM"); |
1503 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1504 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1505 | 11 | entry->description="Portable bitmap format (black/white)"; |
1506 | 11 | entry->module="PNM"; |
1507 | 11 | entry->coder_class=PrimaryCoderClass; |
1508 | 11 | (void) RegisterMagickInfo(entry); |
1509 | | |
1510 | 11 | entry=SetMagickInfo("PGM"); |
1511 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1512 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1513 | 11 | entry->description="Portable graymap format (gray scale)"; |
1514 | 11 | entry->module="PNM"; |
1515 | 11 | entry->coder_class=PrimaryCoderClass; |
1516 | 11 | (void) RegisterMagickInfo(entry); |
1517 | | |
1518 | 11 | entry=SetMagickInfo("PNM"); |
1519 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1520 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1521 | 11 | entry->magick=(MagickHandler) IsPNM; |
1522 | 11 | entry->description="Portable anymap"; |
1523 | 11 | entry->module="PNM"; |
1524 | 11 | entry->coder_class=PrimaryCoderClass; |
1525 | 11 | (void) RegisterMagickInfo(entry); |
1526 | | |
1527 | 11 | entry=SetMagickInfo("PPM"); |
1528 | 11 | entry->decoder=(DecoderHandler) ReadPNMImage; |
1529 | 11 | entry->encoder=(EncoderHandler) WritePNMImage; |
1530 | 11 | entry->description="Portable pixmap format (color)"; |
1531 | 11 | entry->module="PNM"; |
1532 | 11 | entry->coder_class=PrimaryCoderClass; |
1533 | 11 | (void) RegisterMagickInfo(entry); |
1534 | | |
1535 | 11 | } |
1536 | | |
1537 | | /* |
1538 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1539 | | % % |
1540 | | % % |
1541 | | % % |
1542 | | % U n r e g i s t e r P N M I m a g e % |
1543 | | % % |
1544 | | % % |
1545 | | % % |
1546 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1547 | | % |
1548 | | % Method UnregisterPNMImage removes format registrations made by the |
1549 | | % PNM module from the list of supported formats. |
1550 | | % |
1551 | | % The format of the UnregisterPNMImage method is: |
1552 | | % |
1553 | | % UnregisterPNMImage(void) |
1554 | | % |
1555 | | */ |
1556 | | ModuleExport void UnregisterPNMImage(void) |
1557 | 0 | { |
1558 | 0 | (void) UnregisterMagickInfo("P7"); |
1559 | 0 | (void) UnregisterMagickInfo("PAM"); |
1560 | 0 | (void) UnregisterMagickInfo("PBM"); |
1561 | 0 | (void) UnregisterMagickInfo("PGM"); |
1562 | 0 | (void) UnregisterMagickInfo("PNM"); |
1563 | 0 | (void) UnregisterMagickInfo("PPM"); |
1564 | 0 | } |
1565 | | |
1566 | | /* |
1567 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1568 | | % % |
1569 | | % % |
1570 | | % % |
1571 | | % W r i t e P N M I m a g e % |
1572 | | % % |
1573 | | % % |
1574 | | % % |
1575 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1576 | | % |
1577 | | % Procedure WritePNMImage writes an image to a file in the PNM rasterfile |
1578 | | % format. |
1579 | | % |
1580 | | % The format of the WritePNMImage method is: |
1581 | | % |
1582 | | % unsigned int WritePNMImage(const ImageInfo *image_info,Image *image) |
1583 | | % |
1584 | | % A description of each parameter follows. |
1585 | | % |
1586 | | % o status: Method WritePNMImage return True if the image is written. |
1587 | | % False is returned is there is a memory shortage or if the image file |
1588 | | % fails to write. |
1589 | | % |
1590 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
1591 | | % |
1592 | | % o image: A pointer to an Image structure. |
1593 | | % |
1594 | | % |
1595 | | */ |
1596 | | static const char lut_255[][4] = |
1597 | | { |
1598 | | "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", |
1599 | | "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", |
1600 | | "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", |
1601 | | "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", |
1602 | | "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64", "65", "66", |
1603 | | "67", "68", "69", "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", |
1604 | | "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", |
1605 | | "93", "94", "95", "96", "97", "98", "99", "100", "101", "102", "103", "104", |
1606 | | "105", "106", "107", "108", "109", "110", "111", "112", "113", "114", "115", |
1607 | | "116", "117", "118", "119", "120", "121", "122", "123", "124", "125", "126", |
1608 | | "127", "128", "129", "130", "131", "132", "133", "134", "135", "136", "137", |
1609 | | "138", "139", "140", "141", "142", "143", "144", "145", "146", "147", "148", |
1610 | | "149", "150", "151", "152", "153", "154", "155", "156", "157", "158", "159", |
1611 | | "160", "161", "162", "163", "164", "165", "166", "167", "168", "169", "170", |
1612 | | "171", "172", "173", "174", "175", "176", "177", "178", "179", "180", "181", |
1613 | | "182", "183", "184", "185", "186", "187", "188", "189", "190", "191", "192", |
1614 | | "193", "194", "195", "196", "197", "198", "199", "200", "201", "202", "203", |
1615 | | "204", "205", "206", "207", "208", "209", "210", "211", "212", "213", "214", |
1616 | | "215", "216", "217", "218", "219", "220", "221", "222", "223", "224", "225", |
1617 | | "226", "227", "228", "229", "230", "231", "232", "233", "234", "235", "236", |
1618 | | "237", "238", "239", "240", "241", "242", "243", "244", "245", "246", "247", |
1619 | | "248", "249", "250", "251", "252", "253", "254", "255" |
1620 | | }; |
1621 | | |
1622 | 0 | #define AppendUnsignedCharValueToString(j,buffer,value) \ |
1623 | 0 | { \ |
1624 | 0 | const char *lut_entry=lut_255[value]; \ |
1625 | 0 | while(*lut_entry != '\0') \ |
1626 | 0 | { \ |
1627 | 0 | buffer[j++]=*lut_entry; \ |
1628 | 0 | lut_entry++; \ |
1629 | 0 | } \ |
1630 | 0 | } |
1631 | | |
1632 | | static unsigned int WritePNMImage(const ImageInfo *image_info,Image *image) |
1633 | 7.41k | { |
1634 | 7.41k | char |
1635 | 7.41k | buffer[MaxTextExtent]; |
1636 | | |
1637 | 7.41k | const ImageAttribute |
1638 | 7.41k | *attribute; |
1639 | | |
1640 | 7.41k | IndexPacket |
1641 | 7.41k | index; |
1642 | | |
1643 | 7.41k | PNMSubformat |
1644 | 7.41k | format; |
1645 | | |
1646 | 7.41k | unsigned int |
1647 | 7.41k | depth; |
1648 | | |
1649 | 7.41k | unsigned long |
1650 | 7.41k | y; |
1651 | | |
1652 | 7.41k | register const PixelPacket |
1653 | 7.41k | *p; |
1654 | | |
1655 | 7.41k | register const IndexPacket |
1656 | 7.41k | *indexes; |
1657 | | |
1658 | 7.41k | register unsigned long |
1659 | 7.41k | i, |
1660 | 7.41k | x; |
1661 | | |
1662 | 7.41k | unsigned int |
1663 | 7.41k | scene; |
1664 | | |
1665 | 7.41k | MagickPassFail |
1666 | 7.41k | status; |
1667 | | |
1668 | 7.41k | size_t |
1669 | 7.41k | image_list_length; |
1670 | | |
1671 | | /* |
1672 | | Open output image file. |
1673 | | */ |
1674 | 7.41k | assert(image_info != (const ImageInfo *) NULL); |
1675 | 7.41k | assert(image_info->signature == MagickSignature); |
1676 | 7.41k | assert(image != (Image *) NULL); |
1677 | 7.41k | assert(image->signature == MagickSignature); |
1678 | 7.41k | image_list_length=GetImageListLength(image); |
1679 | 7.41k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
1680 | 7.41k | if (status == MagickFail) |
1681 | 7.41k | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
1682 | 7.41k | scene=0; |
1683 | 7.41k | do |
1684 | 7.41k | { |
1685 | 7.41k | depth=(image->depth <= 8 ? 8 : image->depth <= 16 ? 16 : 32); |
1686 | | |
1687 | | /* |
1688 | | Write PNM file header. |
1689 | | */ |
1690 | 7.41k | format=Undefined_PNM_Format; |
1691 | 7.41k | if (LocaleCompare(image_info->magick,"P7") == 0) |
1692 | 1.07k | { |
1693 | 1.07k | format=XV_332_Format; |
1694 | 1.07k | } |
1695 | 6.33k | else if (LocaleCompare(image_info->magick,"PPM") == 0) |
1696 | 1.26k | { |
1697 | 1.26k | format=PPM_RAW_Format; |
1698 | 1.26k | } |
1699 | 5.06k | else if (LocaleCompare(image_info->magick,"PGM") == 0) |
1700 | 1.22k | { |
1701 | 1.22k | format=PGM_RAW_Format; |
1702 | 1.22k | } |
1703 | 3.84k | else if (LocaleCompare(image_info->magick,"PBM") == 0) |
1704 | 1.23k | { |
1705 | 1.23k | format=PBM_RAW_Format; |
1706 | 1.23k | } |
1707 | 2.61k | else if (LocaleCompare(image_info->magick,"PAM") == 0) |
1708 | 1.35k | { |
1709 | 1.35k | format=PAM_Format; |
1710 | 1.35k | } |
1711 | 1.25k | else /* PNM auto format */ |
1712 | 1.25k | { |
1713 | 1.25k | ImageCharacteristics |
1714 | 1.25k | characteristics; |
1715 | | |
1716 | | /* |
1717 | | Make sure that image is in an RGB type space. |
1718 | | */ |
1719 | 1.25k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1720 | 1.25k | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1721 | | |
1722 | | /* |
1723 | | Analyze image to be written. |
1724 | | */ |
1725 | 1.25k | if (!GetImageCharacteristics(image,&characteristics, |
1726 | 1.25k | (OptimizeType == image_info->type), |
1727 | 1.25k | &image->exception)) |
1728 | 1.25k | ThrowWriterException(CoderError,UnableToGetImageCharacteristics,image); |
1729 | | |
1730 | 1.25k | if ((characteristics.monochrome) && |
1731 | 419 | (image_info->type != GrayscaleType) && |
1732 | 419 | (image_info->type != GrayscaleMatteType) && |
1733 | 419 | (image_info->type != TrueColorType) && |
1734 | 419 | (image_info->type != TrueColorMatteType)) |
1735 | 419 | { |
1736 | | /* PBM */ |
1737 | 419 | format=PBM_RAW_Format; |
1738 | 419 | } |
1739 | 836 | else if ((characteristics.grayscale) && |
1740 | 171 | (image_info->type != TrueColorType) && |
1741 | 171 | (image_info->type != TrueColorMatteType)) |
1742 | 171 | { |
1743 | | /* PGM */ |
1744 | 171 | format=PGM_RAW_Format; |
1745 | 171 | } |
1746 | 665 | else |
1747 | 665 | { |
1748 | | /* PPM */ |
1749 | 665 | format=PPM_RAW_Format; |
1750 | 665 | } |
1751 | 1.25k | } |
1752 | | |
1753 | | /* |
1754 | | Check if ASCII subformat is requested. |
1755 | | */ |
1756 | 7.41k | if ((PBM_RAW_Format == format) || (PGM_RAW_Format == format) | (PPM_RAW_Format == format)) |
1757 | 4.97k | { |
1758 | 4.97k | MagickBool |
1759 | 4.97k | ascii = MagickFalse; |
1760 | | |
1761 | | /* |
1762 | | If quality is set to zero or "pnm:ascii" is defined, then |
1763 | | select an ASCII subformat. |
1764 | | */ |
1765 | 4.97k | if (image_info->quality == 0) |
1766 | 0 | ascii=MagickTrue; |
1767 | 4.97k | else if ((AccessDefinition(image_info,"pnm","ascii"))) |
1768 | 0 | ascii=MagickTrue; |
1769 | | |
1770 | 4.97k | if (ascii) |
1771 | 0 | { |
1772 | 0 | if (PBM_RAW_Format == format) |
1773 | 0 | format=PBM_ASCII_Format; |
1774 | 0 | else if (PGM_RAW_Format == format) |
1775 | 0 | format=PGM_ASCII_Format; |
1776 | 0 | else if (PPM_RAW_Format == format) |
1777 | 0 | format=PPM_ASCII_Format; |
1778 | 0 | } |
1779 | 4.97k | } |
1780 | | |
1781 | 7.41k | { |
1782 | 7.41k | const char *header = ""; |
1783 | 7.41k | switch (format) |
1784 | 7.41k | { |
1785 | 0 | case Undefined_PNM_Format: break; |
1786 | 0 | case PBM_ASCII_Format: header="P1"; break; |
1787 | 0 | case PGM_ASCII_Format: header="P2"; break; |
1788 | 0 | case PPM_ASCII_Format: header="P3"; break; |
1789 | 1.65k | case PBM_RAW_Format: header="P4"; break; |
1790 | 1.39k | case PGM_RAW_Format: header="P5"; break; |
1791 | 1.93k | case PPM_RAW_Format: header="P6"; break; |
1792 | 1.35k | case PAM_Format: header="P7"; break; |
1793 | 1.07k | case XV_332_Format: header="P7 332"; break; |
1794 | 7.41k | } |
1795 | 7.41k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Format Id: %s", |
1796 | 7.41k | header); |
1797 | 7.41k | (void) WriteBlobString(image,header); |
1798 | 7.41k | (void) WriteBlobByte(image,'\n'); |
1799 | 7.41k | } |
1800 | | |
1801 | 0 | attribute=GetImageAttribute(image,"comment"); |
1802 | 7.41k | if (attribute != (const ImageAttribute *) NULL) |
1803 | 1.55k | { |
1804 | 1.55k | register char |
1805 | 1.55k | *av; |
1806 | | |
1807 | | /* |
1808 | | Write comments to file. |
1809 | | */ |
1810 | 1.55k | (void) WriteBlobByte(image,'#'); |
1811 | 89.2M | for (av=attribute->value; *av != '\0'; av++) |
1812 | 89.2M | { |
1813 | 89.2M | (void) WriteBlobByte(image,*av); |
1814 | 89.2M | if ((*av == '\n') && (*(av+1) != '\0')) |
1815 | 26.9k | (void) WriteBlobByte(image,'#'); |
1816 | 89.2M | } |
1817 | 1.55k | (void) WriteBlobByte(image,'\n'); |
1818 | 1.55k | } |
1819 | 7.41k | if ((PAM_Format != format) && (XV_332_Format != format)) |
1820 | 4.97k | { |
1821 | 4.97k | MagickFormatString(buffer,sizeof(buffer),"%lu %lu\n",image->columns,image->rows); |
1822 | 4.97k | (void) WriteBlobString(image,buffer); |
1823 | 4.97k | } |
1824 | | /* |
1825 | | Write PNM raster pixels. |
1826 | | */ |
1827 | 7.41k | switch (format) |
1828 | 7.41k | { |
1829 | 0 | case PBM_ASCII_Format: |
1830 | 0 | { |
1831 | 0 | unsigned int |
1832 | 0 | polarity; |
1833 | |
|
1834 | 0 | size_t |
1835 | 0 | j; |
1836 | | |
1837 | | /* |
1838 | | Convert image to a PBM ASCII image. |
1839 | | */ |
1840 | 0 | status &= SetImageType(image,BilevelType); |
1841 | 0 | if ((status &= SetImageType(image,BilevelType)) == MagickFail) |
1842 | 0 | ThrowWriterException(CoderError,UnableToSetImageType,image); |
1843 | 0 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < (MaxRGB/2); |
1844 | 0 | if (image->colors == 2) |
1845 | 0 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < |
1846 | 0 | PixelIntensityToQuantum(&image->colormap[1]); |
1847 | 0 | i=0; |
1848 | 0 | j=0; |
1849 | 0 | for (y=0; y < image->rows; y++) |
1850 | 0 | { |
1851 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1852 | 0 | if (p == (const PixelPacket *) NULL) |
1853 | 0 | { |
1854 | 0 | status=MagickFail; |
1855 | 0 | break; |
1856 | 0 | } |
1857 | 0 | indexes=AccessImmutableIndexes(image); |
1858 | 0 | for (x=0; x < image->columns; x++) |
1859 | 0 | { |
1860 | 0 | buffer[j++] = (indexes[x] == polarity ? '0' : '1'); |
1861 | 0 | buffer[j++] = ' '; |
1862 | 0 | i++; |
1863 | 0 | if (i == 36) |
1864 | 0 | { |
1865 | 0 | buffer[j++] = '\n'; |
1866 | 0 | i=0; |
1867 | 0 | } |
1868 | 0 | if (j+4 > sizeof(buffer)) |
1869 | 0 | { |
1870 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1871 | 0 | j=0; |
1872 | 0 | if (MagickFail == status) |
1873 | 0 | break; |
1874 | 0 | } |
1875 | 0 | } |
1876 | 0 | if (image->previous == (Image *) NULL) |
1877 | 0 | if (QuantumTick(y,image->rows)) |
1878 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1879 | 0 | SaveImageText,image->filename, |
1880 | 0 | image->columns,image->rows)) |
1881 | 0 | { |
1882 | 0 | status=MagickFail; |
1883 | 0 | break; |
1884 | 0 | } |
1885 | 0 | if (MagickFail == status) |
1886 | 0 | break; |
1887 | 0 | } |
1888 | 0 | if (MagickFail != status) |
1889 | 0 | { |
1890 | 0 | if (i != 0) |
1891 | 0 | buffer[j++] = '\n'; |
1892 | 0 | if (j > 0) |
1893 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1894 | 0 | } |
1895 | 0 | break; |
1896 | 0 | } |
1897 | 0 | case PGM_ASCII_Format: |
1898 | 0 | { |
1899 | | /* |
1900 | | Convert image to a PGM ASCII image. |
1901 | | */ |
1902 | 0 | size_t |
1903 | 0 | j; |
1904 | |
|
1905 | 0 | unsigned int |
1906 | 0 | value; |
1907 | | |
1908 | | /* |
1909 | | Make sure that image is in an RGB type space. |
1910 | | */ |
1911 | 0 | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1912 | 0 | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1913 | |
|
1914 | 0 | i=0; |
1915 | 0 | j=0; |
1916 | |
|
1917 | 0 | value=(depth <=8 ? 255U : depth <= 16 ? 65535U : 4294967295U); |
1918 | |
|
1919 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u\n",value); |
1920 | 0 | for (y=0; y < image->rows; y++) |
1921 | 0 | { |
1922 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1923 | 0 | if (p == (const PixelPacket *) NULL) |
1924 | 0 | { |
1925 | 0 | status=MagickFail; |
1926 | 0 | break; |
1927 | 0 | } |
1928 | 0 | for (x=0; x < image->columns; x++) |
1929 | 0 | { |
1930 | 0 | if (image->is_grayscale) |
1931 | 0 | index=p->red; |
1932 | 0 | else |
1933 | 0 | index=PixelIntensityToQuantum(p); |
1934 | 0 | if (depth <= 8) |
1935 | 0 | { |
1936 | | /* Use LUT for speed */ |
1937 | 0 | value=ScaleQuantumToChar(index); |
1938 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
1939 | 0 | buffer[j++] = ' '; |
1940 | 0 | } |
1941 | 0 | else if (depth <= 16) |
1942 | 0 | { |
1943 | 0 | value=ScaleQuantumToShort(index); |
1944 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j)," %u",value); |
1945 | 0 | } |
1946 | 0 | else |
1947 | 0 | { |
1948 | 0 | value=ScaleQuantumToLong(index); |
1949 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j)," %u",value); |
1950 | 0 | } |
1951 | |
|
1952 | 0 | i++; |
1953 | 0 | if (i == 12) |
1954 | 0 | { |
1955 | 0 | buffer[j++] = '\n'; |
1956 | 0 | i=0; |
1957 | 0 | } |
1958 | 0 | if (j+8 > sizeof(buffer)) |
1959 | 0 | { |
1960 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1961 | 0 | j=0; |
1962 | 0 | if (MagickFail == status) |
1963 | 0 | break; |
1964 | 0 | } |
1965 | 0 | p++; |
1966 | 0 | } |
1967 | 0 | if (image->previous == (Image *) NULL) |
1968 | 0 | if (QuantumTick(y,image->rows)) |
1969 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1970 | 0 | SaveImageText,image->filename, |
1971 | 0 | image->columns,image->rows)) |
1972 | 0 | { |
1973 | 0 | status=MagickFail; |
1974 | 0 | break; |
1975 | 0 | } |
1976 | 0 | if (MagickFail == status) |
1977 | 0 | break; |
1978 | 0 | } |
1979 | 0 | if (MagickFail != status) |
1980 | 0 | { |
1981 | 0 | if (i != 0) |
1982 | 0 | buffer[j++] = '\n'; |
1983 | 0 | if (j > 0) |
1984 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1985 | 0 | } |
1986 | 0 | break; |
1987 | 0 | } |
1988 | 0 | case PPM_ASCII_Format: |
1989 | 0 | { |
1990 | | /* |
1991 | | Convert image to a PPM ASCII image. |
1992 | | */ |
1993 | 0 | size_t |
1994 | 0 | j; |
1995 | |
|
1996 | 0 | unsigned int |
1997 | 0 | value; |
1998 | | |
1999 | | /* |
2000 | | Make sure that image is in an RGB type space. |
2001 | | */ |
2002 | 0 | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
2003 | 0 | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
2004 | |
|
2005 | 0 | i=0; |
2006 | 0 | j=0; |
2007 | |
|
2008 | 0 | value=(depth <=8 ? 255U : (depth <= 16 ? 65535U : 4294967295U)); |
2009 | |
|
2010 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u\n",value); |
2011 | 0 | for (y=0; y < image->rows; y++) |
2012 | 0 | { |
2013 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2014 | 0 | if (p == (const PixelPacket *) NULL) |
2015 | 0 | { |
2016 | 0 | status=MagickFail; |
2017 | 0 | break; |
2018 | 0 | } |
2019 | 0 | for (x=0; x < image->columns; x++) |
2020 | 0 | { |
2021 | 0 | if (depth <= 8) |
2022 | 0 | { |
2023 | | /* Use LUT for speed */ |
2024 | 0 | value=ScaleQuantumToChar(p->red); |
2025 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2026 | 0 | buffer[j++] = ' '; |
2027 | 0 | value=ScaleQuantumToChar(p->green); |
2028 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2029 | 0 | buffer[j++] = ' '; |
2030 | 0 | value=ScaleQuantumToChar(p->blue); |
2031 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2032 | 0 | buffer[j++] = ' '; |
2033 | 0 | } |
2034 | 0 | else if (depth <= 16) |
2035 | 0 | { |
2036 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u %u %u ", |
2037 | 0 | ScaleQuantumToShort(p->red), |
2038 | 0 | ScaleQuantumToShort(p->green), |
2039 | 0 | ScaleQuantumToShort(p->blue)); |
2040 | 0 | } |
2041 | 0 | else |
2042 | 0 | { |
2043 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u %u %u ", |
2044 | 0 | (unsigned int) ScaleQuantumToLong(p->red), |
2045 | 0 | (unsigned int) ScaleQuantumToLong(p->green), |
2046 | 0 | (unsigned int) ScaleQuantumToLong(p->blue)); |
2047 | 0 | } |
2048 | 0 | i++; |
2049 | 0 | if (i == 4) |
2050 | 0 | { |
2051 | 0 | buffer[j++] = '\n'; |
2052 | 0 | i=0; |
2053 | 0 | } |
2054 | 0 | if (j+(8*3) > sizeof(buffer)) |
2055 | 0 | { |
2056 | 0 | status=(WriteBlob(image,j,buffer) == j); |
2057 | 0 | j=0; |
2058 | 0 | if (MagickFail == status) |
2059 | 0 | break; |
2060 | 0 | } |
2061 | 0 | p++; |
2062 | 0 | } |
2063 | 0 | if (image->previous == (Image *) NULL) |
2064 | 0 | if (QuantumTick(y,image->rows)) |
2065 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2066 | 0 | SaveImageText,image->filename, |
2067 | 0 | image->columns,image->rows)) |
2068 | 0 | { |
2069 | 0 | status=MagickFail; |
2070 | 0 | break; |
2071 | 0 | } |
2072 | 0 | if (MagickFail == status) |
2073 | 0 | break; |
2074 | 0 | } |
2075 | 0 | if (MagickFail != status) |
2076 | 0 | { |
2077 | 0 | if (i != 0) |
2078 | 0 | buffer[j++] = '\n'; |
2079 | 0 | if (j > 0) |
2080 | 0 | status=(WriteBlob(image,j,buffer) == j); |
2081 | 0 | } |
2082 | 0 | break; |
2083 | 0 | } |
2084 | 1.65k | case PBM_RAW_Format: |
2085 | 3.04k | case PGM_RAW_Format: |
2086 | 4.97k | case PPM_RAW_Format: |
2087 | 6.33k | case PAM_Format: |
2088 | 6.33k | { |
2089 | 6.33k | ExportPixelAreaOptions |
2090 | 6.33k | export_options; |
2091 | | |
2092 | 6.33k | size_t |
2093 | 6.33k | bytes_per_row; |
2094 | | |
2095 | 6.33k | QuantumType |
2096 | 6.33k | quantum_type; |
2097 | | |
2098 | 6.33k | unsigned int |
2099 | 6.33k | bits_per_sample, |
2100 | 6.33k | samples_per_pixel; |
2101 | | |
2102 | 6.33k | MagickBool |
2103 | 6.33k | grayscale_miniswhite=MagickFalse; |
2104 | | |
2105 | 6.33k | unsigned char |
2106 | 6.33k | *pixels; |
2107 | | |
2108 | | /* |
2109 | | Deduce correct export parameters. |
2110 | | */ |
2111 | 6.33k | bits_per_sample=(depth <=8 ? 8 : (depth <= 16 ? 16 : 32)); |
2112 | 6.33k | quantum_type=RGBQuantum; |
2113 | 6.33k | if (PBM_RAW_Format == format) |
2114 | 1.65k | { |
2115 | 1.65k | bits_per_sample=1; |
2116 | 1.65k | grayscale_miniswhite=MagickTrue; |
2117 | 1.65k | quantum_type=GrayQuantum; |
2118 | 1.65k | } |
2119 | 4.68k | else if (PGM_RAW_Format == format) |
2120 | 1.39k | { |
2121 | 1.39k | quantum_type=GrayQuantum; |
2122 | 1.39k | } |
2123 | 3.29k | else if (PPM_RAW_Format == format) |
2124 | 1.93k | { |
2125 | 1.93k | quantum_type=RGBQuantum; |
2126 | 1.93k | } |
2127 | 1.35k | else if (PAM_Format == format) |
2128 | 1.35k | { |
2129 | 1.35k | ImageCharacteristics |
2130 | 1.35k | characteristics; |
2131 | | |
2132 | | /* |
2133 | | Make sure image is of desired type. |
2134 | | */ |
2135 | 1.35k | if (UndefinedType != image_info->type) |
2136 | 0 | if ((status &= SetImageType(image,image_info->type)) == MagickFail) |
2137 | 1.35k | ThrowWriterException(CoderError,UnableToSetImageType,image); |
2138 | | |
2139 | | /* |
2140 | | Analyze the image to get its characteristics. |
2141 | | */ |
2142 | 1.35k | if ((status &= GetImageCharacteristics(image,&characteristics, |
2143 | 1.35k | (OptimizeType == image_info->type), |
2144 | 1.35k | &image->exception)) == MagickFail) |
2145 | 0 | { |
2146 | 0 | CloseBlob(image); |
2147 | 0 | return MagickFail; |
2148 | 0 | } |
2149 | | |
2150 | | /* |
2151 | | Choose best encoding based on image characteristics. |
2152 | | */ |
2153 | 1.35k | if (characteristics.cmyk) |
2154 | 373 | { |
2155 | 373 | if (image->matte) |
2156 | 153 | quantum_type=CMYKAQuantum; |
2157 | 220 | else |
2158 | 220 | quantum_type=CMYKQuantum; |
2159 | 373 | } |
2160 | 985 | else if (characteristics.monochrome) |
2161 | 426 | { |
2162 | 426 | bits_per_sample=1; |
2163 | 426 | grayscale_miniswhite=MagickTrue; |
2164 | | |
2165 | 426 | if (image->matte) |
2166 | 84 | quantum_type=GrayAlphaQuantum; |
2167 | 342 | else |
2168 | 342 | quantum_type=GrayQuantum; |
2169 | 426 | } |
2170 | 559 | else if (characteristics.grayscale) |
2171 | 217 | { |
2172 | 217 | if (image->matte) |
2173 | 74 | quantum_type=GrayAlphaQuantum; |
2174 | 143 | else |
2175 | 143 | quantum_type=GrayQuantum; |
2176 | 217 | } |
2177 | 342 | else |
2178 | 342 | { |
2179 | 342 | if (image->matte) |
2180 | 54 | quantum_type=RGBAQuantum; |
2181 | 288 | else |
2182 | 288 | quantum_type=RGBQuantum; |
2183 | 342 | } |
2184 | 1.35k | } |
2185 | 6.33k | if (image->logging) |
2186 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2187 | 0 | "Export Quantum Type: %s", |
2188 | 0 | QuantumTypeToString(quantum_type)); |
2189 | | |
2190 | 6.33k | samples_per_pixel=MagickGetQuantumSamplesPerPixel(quantum_type); |
2191 | 6.33k | if (image->logging) |
2192 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2193 | 0 | "Samples/Pixel: %u", samples_per_pixel); |
2194 | | |
2195 | 6.33k | if (1 == bits_per_sample) |
2196 | 2.07k | { |
2197 | | /* bytes_per_row=(((size_t) image->columns*samples_per_pixel+7) >> 3); */ |
2198 | 2.07k | bytes_per_row=MagickArraySize(image->columns,samples_per_pixel); |
2199 | 2.07k | if (bytes_per_row) |
2200 | 2.07k | bytes_per_row += 7; |
2201 | 2.07k | if (bytes_per_row) |
2202 | 2.07k | bytes_per_row >>= 3; |
2203 | 2.07k | } |
2204 | 4.25k | else |
2205 | 4.25k | { |
2206 | 4.25k | bytes_per_row=MagickArraySize((((size_t) bits_per_sample+7)/8)* |
2207 | 4.25k | samples_per_pixel,image->columns); |
2208 | 4.25k | } |
2209 | | |
2210 | 6.33k | if (image->logging) |
2211 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2212 | 0 | "Bytes/Row: %" MAGICK_SIZE_T_F "u", |
2213 | 0 | (MAGICK_SIZE_T) bytes_per_row); |
2214 | | |
2215 | 6.33k | ExportPixelAreaOptionsInit(&export_options); |
2216 | 6.33k | export_options.grayscale_miniswhite=grayscale_miniswhite; |
2217 | | |
2218 | | /* |
2219 | | Allocate memory for pixels. |
2220 | | */ |
2221 | 6.33k | pixels=MagickAllocateResourceLimitedMemory(unsigned char *,bytes_per_row); |
2222 | 6.33k | if (pixels == (unsigned char *) NULL) |
2223 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
2224 | 6.33k | image); |
2225 | | |
2226 | | /* |
2227 | | Output header details |
2228 | | */ |
2229 | 6.33k | if (PAM_Format == format) |
2230 | 1.35k | { |
2231 | | /* |
2232 | | PAM header |
2233 | | */ |
2234 | 1.35k | const char *tuple_type=NULL; |
2235 | | |
2236 | 1.35k | if (GrayQuantum == quantum_type) |
2237 | 485 | { |
2238 | 485 | if (1 == bits_per_sample) |
2239 | 342 | tuple_type="BLACKANDWHITE"; |
2240 | 143 | else |
2241 | 143 | tuple_type="GRAYSCALE"; |
2242 | 485 | } |
2243 | 873 | else if (GrayAlphaQuantum == quantum_type) |
2244 | 158 | { |
2245 | 158 | if (1 == bits_per_sample) |
2246 | 84 | tuple_type="BLACKANDWHITE_ALPHA"; |
2247 | 74 | else |
2248 | 74 | tuple_type="GRAYSCALE_ALPHA"; |
2249 | 158 | } |
2250 | 715 | else if (RGBQuantum == quantum_type) |
2251 | 288 | tuple_type="RGB"; |
2252 | 427 | else if (RGBAQuantum == quantum_type) |
2253 | 54 | tuple_type="RGB_ALPHA"; |
2254 | 373 | else if (CMYKQuantum == quantum_type) |
2255 | 220 | tuple_type="CMYK"; |
2256 | 153 | else if (CMYKAQuantum == quantum_type) |
2257 | 153 | tuple_type="CMYK_ALPHA"; |
2258 | | |
2259 | 1.35k | MagickFormatString(buffer,sizeof(buffer),"WIDTH %lu\nHEIGHT %lu\nDEPTH %u" |
2260 | 1.35k | "\nMAXVAL %lu\nTUPLTYPE %s\n", |
2261 | 1.35k | image->columns,image->rows,samples_per_pixel, |
2262 | 1.35k | MaxValueGivenBits(bits_per_sample),tuple_type); |
2263 | 1.35k | if (image->logging) |
2264 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2265 | 0 | "PAM Header: WIDTH %lu, HEIGHT %lu, " |
2266 | 0 | "DEPTH %u, MAXVAL %lu, TUPLTYPE %s", |
2267 | 0 | image->columns, |
2268 | 0 | image->rows,samples_per_pixel, |
2269 | 0 | MaxValueGivenBits(bits_per_sample), |
2270 | 0 | tuple_type); |
2271 | 1.35k | WriteBlobString(image,buffer); |
2272 | | |
2273 | 1.35k | (void) WriteBlobString(image,"ENDHDR\n"); |
2274 | 1.35k | } |
2275 | 4.97k | else if ((PGM_RAW_Format == format) || (PPM_RAW_Format == format)) |
2276 | 3.32k | { |
2277 | | /* |
2278 | | PGM, PPM header |
2279 | | */ |
2280 | 3.32k | MagickFormatString(buffer,sizeof(buffer),"%lu\n",MaxValueGivenBits(bits_per_sample)); |
2281 | 3.32k | WriteBlobString(image,buffer); |
2282 | 3.32k | } |
2283 | | |
2284 | | /* |
2285 | | Output pixels |
2286 | | */ |
2287 | 398k | for (y=0; y < image->rows; y++) |
2288 | 391k | { |
2289 | 391k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2290 | 391k | if (p == (const PixelPacket *) NULL) |
2291 | 0 | break; |
2292 | 391k | if (ExportImagePixelArea(image,quantum_type,bits_per_sample,pixels,&export_options,0) == MagickFail) |
2293 | 0 | break; |
2294 | 391k | if (WriteBlob(image,bytes_per_row,(char *) pixels) != bytes_per_row) |
2295 | 170 | break; |
2296 | 391k | if (image->previous == (Image *) NULL) |
2297 | 391k | if (QuantumTick(y,image->rows)) |
2298 | 112k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2299 | 112k | SaveImageText,image->filename, |
2300 | 112k | image->columns,image->rows)) |
2301 | 0 | break; |
2302 | 391k | } |
2303 | 6.33k | MagickFreeResourceLimitedMemory(unsigned char *,pixels); |
2304 | | |
2305 | 6.33k | break; |
2306 | 6.33k | } |
2307 | 1.07k | case XV_332_Format: |
2308 | 1.07k | { |
2309 | 1.07k | static const short int |
2310 | 1.07k | dither_red[2][16]= |
2311 | 1.07k | { |
2312 | 1.07k | {-16, 4, -1, 11,-14, 6, -3, 9,-15, 5, -2, 10,-13, 7, -4, 8}, |
2313 | 1.07k | { 15, -5, 0,-12, 13, -7, 2,-10, 14, -6, 1,-11, 12, -8, 3, -9} |
2314 | 1.07k | }, |
2315 | 1.07k | dither_green[2][16]= |
2316 | 1.07k | { |
2317 | 1.07k | { 11,-15, 7, -3, 8,-14, 4, -2, 10,-16, 6, -4, 9,-13, 5, -1}, |
2318 | 1.07k | {-12, 14, -8, 2, -9, 13, -5, 1,-11, 15, -7, 3,-10, 12, -6, 0} |
2319 | 1.07k | }, |
2320 | 1.07k | dither_blue[2][16]= |
2321 | 1.07k | { |
2322 | 1.07k | { -3, 9,-13, 7, -1, 11,-15, 5, -4, 8,-14, 6, -2, 10,-16, 4}, |
2323 | 1.07k | { 2,-10, 12, -8, 0,-12, 14, -6, 3, -9, 13, -7, 1,-11, 15, -5} |
2324 | 1.07k | }; |
2325 | | |
2326 | 1.07k | long |
2327 | 1.07k | value; |
2328 | | |
2329 | 1.07k | Quantum |
2330 | 1.07k | pixel; |
2331 | | |
2332 | 1.07k | unsigned short |
2333 | 1.07k | *blue_map[2][16], |
2334 | 1.07k | *green_map[2][16], |
2335 | 1.07k | *red_map[2][16]; |
2336 | | |
2337 | 1.07k | unsigned int |
2338 | 1.07k | j; |
2339 | | |
2340 | | /* |
2341 | | Allocate and initialize dither maps. |
2342 | | */ |
2343 | 1.07k | memset(blue_map,0,sizeof(blue_map)); |
2344 | 1.07k | memset(green_map,0,sizeof(green_map)); |
2345 | 1.07k | memset(red_map,0,sizeof(red_map)); |
2346 | 3.23k | for (i=0; i < 2; i++) |
2347 | 36.6k | for (j=0; j < 16; j++) |
2348 | 34.5k | { |
2349 | 34.5k | red_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2350 | 34.5k | 256*sizeof(unsigned short)); |
2351 | 34.5k | green_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2352 | 34.5k | 256*sizeof(unsigned short)); |
2353 | 34.5k | blue_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2354 | 34.5k | 256*sizeof(unsigned short)); |
2355 | 34.5k | if ((red_map[i][j] == (unsigned short *) NULL) || |
2356 | 34.5k | (green_map[i][j] == (unsigned short *) NULL) || |
2357 | 34.5k | (blue_map[i][j] == (unsigned short *) NULL)) |
2358 | 0 | { |
2359 | 0 | for (i=0; i < 2; i++) |
2360 | 0 | for (j=0; j < 16; j++) |
2361 | 0 | { |
2362 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,green_map[i][j]); |
2363 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,blue_map[i][j]); |
2364 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,red_map[i][j]); |
2365 | 0 | } |
2366 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
2367 | 0 | image); |
2368 | 0 | } |
2369 | 34.5k | } |
2370 | | /* |
2371 | | Initialize dither tables. |
2372 | | */ |
2373 | 3.23k | for (i=0; i < 2; i++) |
2374 | 36.6k | for (j=0; j < 16; j++) |
2375 | 8.87M | for (x=0; x < 256; x++) |
2376 | 8.83M | { |
2377 | 8.83M | value=x-16; |
2378 | 8.83M | if (x < 48) |
2379 | 1.65M | value=x/2+8; |
2380 | 8.83M | value+=dither_red[i][j]; |
2381 | 8.83M | red_map[i][j][x]=(unsigned short) |
2382 | 8.83M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2383 | 8.83M | value=x-16; |
2384 | 8.83M | if (x < 48) |
2385 | 1.65M | value=x/2+8; |
2386 | 8.83M | value+=dither_green[i][j]; |
2387 | 8.83M | green_map[i][j][x]=(unsigned short) |
2388 | 8.83M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2389 | 8.83M | value=x-32; |
2390 | 8.83M | if (x < 112) |
2391 | 3.86M | value=x/2+24; |
2392 | 8.83M | value+=2*dither_blue[i][j]; |
2393 | 8.83M | blue_map[i][j][x]=(unsigned short) |
2394 | 8.83M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2395 | 8.83M | } |
2396 | | /* |
2397 | | Write pixels. |
2398 | | */ |
2399 | 1.07k | (void) WriteBlobString(image,"#END_OF_COMMENTS\n"); |
2400 | 1.07k | MagickFormatString(buffer,sizeof(buffer),"%lu %lu 255\n",image->columns,image->rows); |
2401 | 1.07k | (void) WriteBlobString(image,buffer); |
2402 | 1.07k | i=0; |
2403 | 1.07k | j=0; |
2404 | 101k | for (y=0; y < image->rows; y++) |
2405 | 100k | { |
2406 | 100k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2407 | 100k | if (p == (const PixelPacket *) NULL) |
2408 | 0 | break; |
2409 | 58.5M | for (x=0; x < image->columns; x++) |
2410 | 58.4M | { |
2411 | 58.4M | if (!image_info->dither) |
2412 | 0 | pixel=(Quantum) ((ScaleQuantumToChar(p->red) & 0xe0) | |
2413 | 0 | ((ScaleQuantumToChar(p->green) & 0xe0) >> 3) | |
2414 | 0 | ((ScaleQuantumToChar(p->blue) & 0xc0) >> 6)); |
2415 | 58.4M | else |
2416 | 58.4M | pixel=(Quantum) |
2417 | 58.4M | ((red_map[i][j][ScaleQuantumToChar(p->red)] & 0xe0) | |
2418 | 58.4M | ((green_map[i][j][ScaleQuantumToChar(p->green)] & 0xe0) >> 3) | |
2419 | 58.4M | ((blue_map[i][j][ScaleQuantumToChar(p->blue)] & 0xc0) >> 6)); |
2420 | 58.4M | (void) WriteBlobByte(image,pixel); |
2421 | 58.4M | p++; |
2422 | 58.4M | j++; |
2423 | 58.4M | if (j == 16) |
2424 | 3.65M | j=0; |
2425 | 58.4M | } |
2426 | 100k | i++; |
2427 | 100k | if (i == 2) |
2428 | 49.8k | i=0; |
2429 | 100k | if (QuantumTick(y,image->rows)) |
2430 | 22.1k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2431 | 22.1k | SaveImageText,image->filename, |
2432 | 22.1k | image->columns,image->rows)) |
2433 | 0 | break; |
2434 | 100k | } |
2435 | | /* |
2436 | | Free allocated memory. |
2437 | | */ |
2438 | 3.23k | for (i=0; i < 2; i++) |
2439 | 36.6k | for (j=0; j < 16; j++) |
2440 | 34.5k | { |
2441 | 34.5k | MagickFreeResourceLimitedMemory(unsigned short *,green_map[i][j]); |
2442 | 34.5k | MagickFreeResourceLimitedMemory(unsigned short *,blue_map[i][j]); |
2443 | 34.5k | MagickFreeResourceLimitedMemory(unsigned short *,red_map[i][j]); |
2444 | 34.5k | } |
2445 | 1.07k | break; |
2446 | 1.07k | } |
2447 | 0 | case Undefined_PNM_Format: |
2448 | 0 | break; |
2449 | 7.41k | } |
2450 | 7.41k | if (image->next == (Image *) NULL) |
2451 | 7.41k | break; |
2452 | 0 | image=SyncNextImageInList(image); |
2453 | 0 | if (status != MagickFail) |
2454 | 0 | status=MagickMonitorFormatted(scene++,image_list_length, |
2455 | 0 | &image->exception,SaveImagesText, |
2456 | 0 | image->filename); |
2457 | 0 | if (status == MagickFail) |
2458 | 0 | break; |
2459 | 0 | } while (image_info->adjoin); |
2460 | 7.41k | if (image_info->adjoin) |
2461 | 7.41k | while (image->previous != (Image *) NULL) |
2462 | 0 | image=image->previous; |
2463 | 7.41k | status &= CloseBlob(image); |
2464 | 7.41k | return(status); |
2465 | 7.41k | } |