/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 | 498k | { |
132 | 498k | unsigned int |
133 | 498k | value; |
134 | | |
135 | 498k | int |
136 | 498k | c; |
137 | | |
138 | | /* |
139 | | Skip any leading whitespace. |
140 | | */ |
141 | 498k | do |
142 | 9.28M | { |
143 | 9.28M | c=ReadBlobByte(image); |
144 | 9.28M | if (c == EOF) |
145 | 61.5k | { |
146 | 61.5k | *status=MagickFail; |
147 | 61.5k | return(0); |
148 | 61.5k | } |
149 | 9.28M | } while (!isdigit(c)); |
150 | 436k | c &= 0xff; |
151 | 436k | if (base == 2) |
152 | 11.3k | { |
153 | 11.3k | if (c > '1') |
154 | 2.02k | { |
155 | 2.02k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
156 | 2.02k | "ERROR: Unexpected character '%c' @%" |
157 | 2.02k | MAGICK_OFF_F "d", c, TellBlob(image)); |
158 | 2.02k | *status=MagickFail; |
159 | 2.02k | return(0); |
160 | 2.02k | } |
161 | 9.32k | return(c-'0'); |
162 | 11.3k | } |
163 | | /* |
164 | | Evaluate number. |
165 | | */ |
166 | 425k | value=0; |
167 | 812k | while(1) |
168 | 812k | { |
169 | 812k | unsigned int |
170 | 812k | old_value = value; |
171 | | |
172 | 812k | old_value=value; |
173 | 812k | value*=10; |
174 | 812k | value+=c-'0'; |
175 | 812k | if (value < old_value) |
176 | 7.13k | { |
177 | 7.13k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
178 | 7.13k | "ERROR: integer overflow (%u > %u) @%" |
179 | 7.13k | MAGICK_OFF_F "d", old_value, value, |
180 | 7.13k | TellBlob(image)); |
181 | 7.13k | *status=MagickFail; |
182 | 7.13k | return(0); |
183 | 7.13k | } |
184 | 804k | c=ReadBlobByte(image); |
185 | 804k | if ((c == EOF) || !(isdigit(c))) |
186 | 418k | break; |
187 | 386k | c &= 0xff; |
188 | 386k | } |
189 | | |
190 | 418k | return(value); |
191 | 425k | } |
192 | | |
193 | | static unsigned int PNMIntegerOrComment(Image *image,const unsigned int base,MagickPassFail *status) |
194 | 220k | { |
195 | 220k | unsigned int |
196 | 220k | value; |
197 | | |
198 | 220k | unsigned int |
199 | 220k | count = 0; |
200 | | |
201 | 220k | int |
202 | 220k | c; |
203 | | |
204 | 220k | static const char P7Comment[] = "END_OF_COMMENTS\n"; |
205 | | |
206 | | /* |
207 | | Skip any leading whitespace. |
208 | | */ |
209 | 220k | do |
210 | 3.18M | { |
211 | 3.18M | c=ReadBlobByte(image); |
212 | 3.18M | if (c == EOF) |
213 | 16.7k | return(0); |
214 | 3.16M | c &= 0xff; |
215 | 3.16M | if (c == '#') |
216 | 85.0k | { |
217 | 85.0k | char |
218 | 85.0k | *comment; |
219 | | |
220 | 85.0k | const ImageAttribute |
221 | 85.0k | *comment_attr; |
222 | | |
223 | 85.0k | ExtendedSignedIntegralType |
224 | 85.0k | offset; |
225 | | |
226 | 85.0k | register char |
227 | 85.0k | *p, |
228 | 85.0k | *q; |
229 | | |
230 | 85.0k | size_t |
231 | 85.0k | length; |
232 | | |
233 | | /* |
234 | | Read comment. |
235 | | */ |
236 | 85.0k | if ((comment_attr=GetImageAttribute(image,"comment")) != (const ImageAttribute *) NULL) |
237 | 79.8k | { |
238 | | /* |
239 | | If existing comment text length exceeds arbitrary limit, |
240 | | then do no further comment processing for this file. |
241 | | */ |
242 | 79.8k | if (comment_attr->length > MaxTextExtent*2) |
243 | 2.98k | { |
244 | 4.39M | for ( ; (c != EOF) && (c != '\n'); ) |
245 | 4.39M | c=ReadBlobByte(image); |
246 | 2.98k | return 0; |
247 | 2.98k | } |
248 | 79.8k | } |
249 | 82.0k | length=MaxTextExtent; |
250 | 82.0k | comment=MagickAllocateResourceLimitedMemory(char *,length+sizeof(P7Comment)); |
251 | 82.0k | p=comment; |
252 | 82.0k | offset=p-comment; |
253 | 82.0k | if (comment != (char *) NULL) |
254 | 167M | for ( ; (c != EOF) && (c != '\n'); p++) |
255 | 167M | { |
256 | 167M | if ((size_t) (p-comment) >= length) |
257 | 80.7k | { |
258 | 80.7k | size_t |
259 | 80.7k | text_length; |
260 | | |
261 | 80.7k | char |
262 | 80.7k | *new_comment; |
263 | | |
264 | 80.7k | text_length=(size_t) (p-comment); |
265 | 80.7k | length+=MaxTextExtent; |
266 | 80.7k | new_comment=MagickReallocateResourceLimitedMemory(char *,comment,length+sizeof(P7Comment)); |
267 | 80.7k | if (new_comment == (char *) NULL) |
268 | 0 | { |
269 | 0 | MagickFreeResourceLimitedMemory(char *,comment); |
270 | 0 | break; |
271 | 0 | } |
272 | 80.7k | comment=new_comment; |
273 | 80.7k | p=comment+text_length; |
274 | 80.7k | } |
275 | 167M | c=ReadBlobByte(image); |
276 | 167M | *p=c; |
277 | 167M | *(p+1)='\0'; |
278 | 167M | } |
279 | 82.0k | if (comment == (char *) NULL) |
280 | 0 | return(0); |
281 | 82.0k | q=comment+offset; |
282 | 82.0k | if (LocaleCompare(q,P7Comment) == 0) |
283 | 194 | *q='\0'; |
284 | | /* |
285 | | FIXME: |
286 | | Implicitly extend existing comment attribute since comments |
287 | | can span multiple lines. |
288 | | */ |
289 | 82.0k | (void) SetImageAttribute(image,"comment",comment); |
290 | 82.0k | MagickFreeResourceLimitedMemory(char *,comment); |
291 | 82.0k | continue; |
292 | 82.0k | } |
293 | 3.16M | } 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 | 328k | while(1) |
312 | 328k | { |
313 | 328k | unsigned int |
314 | 328k | old_value = value; |
315 | | |
316 | 328k | old_value=value; |
317 | 328k | value*=10; |
318 | 328k | value+=c-'0'; |
319 | 328k | if (value < old_value) |
320 | 3.70k | { |
321 | 3.70k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
322 | 3.70k | "ERROR: integer overflow (%u > %u) @%" |
323 | 3.70k | MAGICK_OFF_F "d", old_value, value, |
324 | 3.70k | TellBlob(image)); |
325 | 3.70k | *status=MagickFail; |
326 | 3.70k | return(0); |
327 | 3.70k | } |
328 | 324k | c=ReadBlobByte(image); |
329 | 324k | if ((c == EOF) || !(isdigit(c))) |
330 | 197k | break; |
331 | 127k | count++; |
332 | 127k | c &= 0xff; |
333 | 127k | } |
334 | 197k | 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 | 197k | return(value); |
352 | 200k | } |
353 | | |
354 | | #define ValidateScalingIndex(image, index, max) \ |
355 | 77.6k | do \ |
356 | 77.6k | { \ |
357 | 77.6k | if (index > max) \ |
358 | 77.6k | ThrowReaderException(CorruptImageError,CorruptImage, image); \ |
359 | 69.2k | } while (0) |
360 | | |
361 | | #define ValidateScalingPixel(image, pixel, max) \ |
362 | 20.0k | do \ |
363 | 20.0k | { \ |
364 | 20.0k | ValidateScalingIndex(image, pixel.red, max); \ |
365 | 20.0k | ValidateScalingIndex(image, pixel.green, max); \ |
366 | 18.0k | ValidateScalingIndex(image, pixel.blue, max); \ |
367 | 15.8k | } 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 | 59.0k | { |
384 | 59.0k | const char *s = "unknown"; |
385 | | |
386 | 59.0k | switch (f) |
387 | 59.0k | { |
388 | 0 | case Undefined_PNM_Format: |
389 | 0 | s = "Undefined"; |
390 | 0 | break; |
391 | 5.20k | case PBM_ASCII_Format: /* P1 */ |
392 | 5.20k | s = "PBM ASCII"; |
393 | 5.20k | break; |
394 | 10.6k | case PGM_ASCII_Format: /* P2 */ |
395 | 10.6k | s = "PGM ASCII"; |
396 | 10.6k | break; |
397 | 15.5k | case PPM_ASCII_Format: /* P3 */ |
398 | 15.5k | s = "PPM ASCII"; |
399 | 15.5k | break; |
400 | 3.62k | case PBM_RAW_Format: /* P4 */ |
401 | 3.62k | s = "PBM RAW"; |
402 | 3.62k | break; |
403 | 3.57k | case PGM_RAW_Format: /* P5 */ |
404 | 3.57k | s = "PGM RAW"; |
405 | 3.57k | break; |
406 | 16.3k | case PPM_RAW_Format: /* P6 */ |
407 | 16.3k | s = "PPM RAW"; |
408 | 16.3k | break; |
409 | 3.92k | case PAM_Format: /* P7 */ |
410 | 3.92k | s = "PAM"; |
411 | 3.92k | break; |
412 | 121 | case XV_332_Format: /* P7 332 */ |
413 | 121 | s = "XV 332 icon"; |
414 | 121 | break; |
415 | 59.0k | } |
416 | 59.0k | return s; |
417 | 59.0k | } |
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 | 208k | { |
436 | 208k | char |
437 | 208k | c; |
438 | | |
439 | 208k | PNMSubformat |
440 | 208k | format; |
441 | | |
442 | 208k | Image |
443 | 208k | *image; |
444 | | |
445 | 208k | long |
446 | 208k | y; |
447 | | |
448 | 208k | LongPixelPacket |
449 | 208k | pixel; |
450 | | |
451 | 208k | register IndexPacket |
452 | 208k | *indexes; |
453 | | |
454 | 208k | register unsigned long |
455 | 208k | i; |
456 | | |
457 | 208k | size_t |
458 | 208k | count, |
459 | 208k | number_pixels; |
460 | | |
461 | 208k | unsigned int |
462 | 208k | index, |
463 | 208k | bits_per_sample; |
464 | | |
465 | 208k | MagickPassFail |
466 | 208k | status; |
467 | | |
468 | 208k | unsigned int |
469 | 208k | max_value, |
470 | 208k | samples_per_pixel; |
471 | | |
472 | | /* |
473 | | Open image file. |
474 | | */ |
475 | 208k | assert(image_info != (const ImageInfo *) NULL); |
476 | 208k | assert(image_info->signature == MagickSignature); |
477 | 208k | assert(exception != (ExceptionInfo *) NULL); |
478 | 208k | assert(exception->signature == MagickSignature); |
479 | 208k | image=AllocateImage(image_info); |
480 | 208k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
481 | 208k | if (status == MagickFail) |
482 | 208k | ThrowReaderException(FileOpenError,UnableToOpenFile,image); |
483 | | /* |
484 | | Read PNM image. |
485 | | */ |
486 | 208k | count=ReadBlob(image,1,(char *) &c); |
487 | 208k | do |
488 | 208k | { |
489 | | /* |
490 | | Initialize image structure. |
491 | | */ |
492 | 208k | max_value=0; |
493 | 208k | bits_per_sample=0; |
494 | 208k | samples_per_pixel=0; |
495 | | |
496 | 208k | if (count == 0) |
497 | 208k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
498 | | |
499 | 208k | if (c != 'P') |
500 | 83 | { |
501 | 83 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Read %c rather than expected 'P'!",c); |
502 | 83 | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
503 | 0 | } |
504 | | |
505 | 208k | c=ReadBlobByte(image); |
506 | 208k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"PNM Format Id: P%c", |
507 | 208k | c); |
508 | | |
509 | 208k | switch (c) |
510 | 208k | { |
511 | 17.8k | case '1': format=PBM_ASCII_Format; break; |
512 | 40.4k | case '2': format=PGM_ASCII_Format; break; |
513 | 27.7k | case '3': format=PPM_ASCII_Format; break; |
514 | 19.3k | case '4': format=PBM_RAW_Format; break; |
515 | 25.6k | case '5': format=PGM_RAW_Format; break; |
516 | 48.1k | case '6': format=PPM_RAW_Format; break; |
517 | 28.4k | case '7': |
518 | 28.4k | { |
519 | 28.4k | if ((ReadBlobByte(image) == ' ') && |
520 | 9.69k | (PNMIntegerOrComment(image,10,&status) == 332)) |
521 | 6.72k | format=XV_332_Format; |
522 | 21.6k | else |
523 | 21.6k | format=PAM_Format; |
524 | 28.4k | break; |
525 | 0 | } |
526 | 1.19k | default: |
527 | 1.19k | { |
528 | 1.19k | format=Undefined_PNM_Format; |
529 | 1.19k | } |
530 | 208k | } |
531 | | |
532 | 208k | if (status == MagickFail) |
533 | 208k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
534 | | |
535 | 208k | if (PAM_Format == format) |
536 | 21.5k | { |
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.5k | char |
550 | 21.5k | keyword[MaxTextExtent]; |
551 | | |
552 | 21.5k | register char |
553 | 21.5k | *p; |
554 | | |
555 | 21.5k | int |
556 | 21.5k | c; |
557 | | |
558 | 347k | while (1) |
559 | 347k | { |
560 | 347k | p=keyword; |
561 | 347k | c=ReadBlobByte(image); |
562 | 347k | do |
563 | 1.20M | { |
564 | 1.20M | if (isalnum(c) || ('#' == c)) |
565 | 1.18M | if ((p-keyword) < (MaxTextExtent-1)) |
566 | 1.15M | { |
567 | 1.15M | *p++=c; |
568 | 1.15M | if ('#' == c) |
569 | 217k | break; |
570 | 1.15M | } |
571 | 988k | c=ReadBlobByte(image); |
572 | 988k | } while (isalnum(c) || ('#' == c)); |
573 | 347k | *p='\0'; |
574 | | |
575 | 347k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
576 | 347k | "Keyword \"%s\"",keyword); |
577 | 347k | if ((EOF == c) || (LocaleCompare(keyword,"ENDHDR") == 0)) |
578 | 4.75k | { |
579 | 4.75k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
580 | 4.75k | "Exiting header!"); |
581 | 4.75k | break; |
582 | 4.75k | } |
583 | 343k | else if (LocaleCompare(keyword,"HEIGHT") == 0) |
584 | 23.8k | { |
585 | 23.8k | image->rows=PNMIntegerOrComment(image,10,&status); |
586 | 23.8k | if (status == MagickFail) |
587 | 23.7k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
588 | 23.7k | } |
589 | 319k | else if (LocaleCompare(keyword,"WIDTH") == 0) |
590 | 19.8k | { |
591 | 19.8k | image->columns=PNMInteger(image,10,&status); |
592 | 19.8k | if (status == MagickFail) |
593 | 19.8k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
594 | 19.8k | } |
595 | 299k | else if (LocaleCompare(keyword,"DEPTH") == 0) |
596 | 20.3k | { |
597 | 20.3k | samples_per_pixel=PNMInteger(image,10,&status); |
598 | 20.3k | if (status == MagickFail) |
599 | 20.3k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
600 | 20.3k | } |
601 | 279k | else if (LocaleCompare(keyword,"MAXVAL") == 0) |
602 | 16.0k | { |
603 | 16.0k | max_value=PNMInteger(image,10,&status); |
604 | 16.0k | if (status == MagickFail) |
605 | 16.0k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
606 | 16.0k | } |
607 | 262k | else if (LocaleCompare(keyword,"TUPLTYPE") == 0) |
608 | 30.7k | { |
609 | | /* Skip white space */ |
610 | 30.7k | do |
611 | 37.0k | { |
612 | 37.0k | c=ReadBlobByte(image); |
613 | 37.0k | } while (isspace(c) && (EOF != c)); |
614 | 30.7k | if (EOF == c) |
615 | 96 | break; |
616 | | /* Tupletype argument */ |
617 | 30.6k | p=keyword; |
618 | 30.6k | do |
619 | 59.0M | { |
620 | 59.0M | if ((p-keyword) < (MaxTextExtent-1)) |
621 | 5.81M | *p++=c; |
622 | 59.0M | c=ReadBlobByte(image); |
623 | 59.0M | } while (('\n' != c) && (EOF != c)); |
624 | 30.6k | *p='\0'; |
625 | 30.6k | if (EOF == c) |
626 | 432 | break; |
627 | 30.1k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
628 | 30.1k | "TUPLTYPE \"%s\"",keyword); |
629 | 30.1k | if (LocaleNCompare(keyword,"BLACKANDWHITE",13) == 0) |
630 | 1.67k | { |
631 | 1.67k | image->colorspace=GRAYColorspace; |
632 | 1.67k | image->is_monochrome=MagickTrue; |
633 | 1.67k | } |
634 | 28.5k | else if (LocaleNCompare(keyword,"CMYK",4) == 0) |
635 | 8.19k | { |
636 | 8.19k | image->colorspace=CMYKColorspace; |
637 | 8.19k | } |
638 | 20.3k | else if (LocaleNCompare(keyword,"GRAYSCALE",9) == 0) |
639 | 2.67k | { |
640 | 2.67k | image->colorspace=GRAYColorspace; |
641 | 2.67k | } |
642 | 17.6k | else if (LocaleNCompare(keyword,"RGB",3) == 0) |
643 | 15 | { |
644 | 15 | } |
645 | | |
646 | | /* |
647 | | Check for alpha flag. |
648 | | */ |
649 | 30.1k | count=strlen(keyword); |
650 | 30.1k | if ((count > 6) && (LocaleNCompare(keyword+count-6,"_ALPHA",6) == 0)) |
651 | 6.19k | { |
652 | 6.19k | image->matte=MagickTrue; |
653 | 6.19k | } |
654 | 30.1k | } |
655 | 232k | else if (LocaleNCompare(keyword,"#",1) == 0) |
656 | 217k | { |
657 | | /* Skip leading white space */ |
658 | 217k | do |
659 | 306k | { |
660 | 306k | c=ReadBlobByte(image); |
661 | 306k | } while (isspace(c) && (EOF != c)); |
662 | 217k | if (EOF == c) |
663 | 1.32k | break; |
664 | | |
665 | | /* Comment */ |
666 | 216k | p=keyword; |
667 | 110M | while ((p-keyword) < (MaxTextExtent-2)) |
668 | 109M | { |
669 | 109M | *p++=c; |
670 | 109M | c=ReadBlobByte(image); |
671 | 109M | if (c == '\n') |
672 | 163k | { |
673 | 163k | *p++=c; |
674 | 163k | break; |
675 | 163k | } |
676 | 109M | if (c == EOF) |
677 | 2.39k | break; |
678 | 109M | } |
679 | 216k | *p='\0'; |
680 | | /* |
681 | | FIXME: |
682 | | Implicitly extend existing comment attribute since comments |
683 | | can span multiple lines. |
684 | | */ |
685 | 216k | (void) SetImageAttribute(image,"comment",keyword); |
686 | 216k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
687 | 216k | "Comment: \"%s\"",keyword); |
688 | 216k | } |
689 | 14.8k | else |
690 | 14.8k | { |
691 | | /* Unknown! */ |
692 | 14.8k | do |
693 | 33.2M | { |
694 | 33.2M | c=ReadBlobByte(image); |
695 | 33.2M | } while (('\n' != c) && (EOF != c)); |
696 | 14.8k | break; |
697 | 14.8k | } |
698 | 347k | } |
699 | 21.5k | } |
700 | 187k | else |
701 | 187k | { |
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 | 187k | image->columns=PNMIntegerOrComment(image,10,&status); |
715 | 187k | image->rows=PNMInteger(image,10,&status); |
716 | | |
717 | 187k | if ((format == PBM_ASCII_Format) || (format == PBM_RAW_Format)) |
718 | 37.1k | max_value=1; /* bitmap */ |
719 | 149k | else |
720 | 149k | max_value=PNMInteger(image,10,&status); |
721 | | |
722 | 187k | if (status == MagickFail) |
723 | 142k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
724 | | |
725 | 142k | switch (format) |
726 | 142k | { |
727 | 13.8k | case PBM_ASCII_Format: |
728 | 30.5k | case PBM_RAW_Format: |
729 | 63.6k | 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.7k | case PPM_ASCII_Format: |
737 | 58.0k | case PPM_RAW_Format: |
738 | 58.0k | { |
739 | 58.0k | samples_per_pixel=3; |
740 | 58.0k | break; |
741 | 22.7k | } |
742 | 93 | default: |
743 | 93 | { |
744 | 93 | } |
745 | 142k | } |
746 | 142k | } |
747 | | |
748 | 163k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Dimensions: %lux%lu", |
749 | 163k | image->columns,image->rows); |
750 | 163k | if ((image->columns == 0) || (image->rows == 0)) |
751 | 141k | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
752 | | |
753 | 141k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Max Value: %u", |
754 | 141k | max_value); |
755 | 141k | 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 | 49.9k | bits_per_sample=1; |
764 | 87.3k | else if (max_value <= 255U) |
765 | 59.8k | bits_per_sample=8; |
766 | 27.5k | else if (max_value <= 65535U) |
767 | 14.9k | bits_per_sample=16; |
768 | 12.5k | else if (max_value <= 4294967295U) |
769 | 12.5k | 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 | 13.6k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
782 | 137k | image->filename); |
783 | | |
784 | 137k | if ((1 == samples_per_pixel) && (max_value < MaxColormapSize) && |
785 | 70.5k | ((size_t) image->columns*image->rows >= max_value)) |
786 | 53.2k | { |
787 | 53.2k | image->storage_class=PseudoClass; |
788 | 53.2k | image->colors= |
789 | 53.2k | max_value >= MaxColormapSize ? MaxColormapSize : max_value+1; |
790 | 53.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colors: %u", |
791 | 53.2k | image->colors); |
792 | 53.2k | } |
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 | 53.2k | { |
798 | | /* |
799 | | Create colormap. |
800 | | */ |
801 | 53.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
802 | 53.2k | "Allocating colormap with %u colors", |
803 | 53.2k | image->colors); |
804 | 53.2k | if (!AllocateImageColormap(image,image->colors)) |
805 | 0 | ThrowReaderException(ResourceLimitError,MemoryAllocationFailed, |
806 | 53.2k | image); |
807 | 53.2k | if ((format == XV_332_Format) && (image->colors == 256)) |
808 | 572 | { |
809 | | /* |
810 | | Initialize 332 colormap. |
811 | | */ |
812 | 572 | i=0; |
813 | 5.14k | for (pixel.red=0; pixel.red < 8; pixel.red++) |
814 | 41.1k | for (pixel.green=0; pixel.green < 8; pixel.green++) |
815 | 183k | for (pixel.blue=0; pixel.blue < 4; pixel.blue++) |
816 | 146k | { |
817 | 146k | image->colormap[i].red=(Quantum) |
818 | 146k | (((double) MaxRGB*pixel.red)/0x07+0.5); |
819 | 146k | image->colormap[i].green=(Quantum) |
820 | 146k | (((double) MaxRGB*pixel.green)/0x07+0.5); |
821 | 146k | image->colormap[i].blue=(Quantum) |
822 | 146k | (((double) MaxRGB*pixel.blue)/0x03+0.5); |
823 | 146k | i++; |
824 | 146k | } |
825 | 572 | } |
826 | 53.2k | } |
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 | 78.1k | ThrowReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
833 | | |
834 | | /* |
835 | | Decode PNM pixels to Image pixel packets. |
836 | | */ |
837 | 59.1k | switch (format) |
838 | 59.1k | { |
839 | 5.20k | case PBM_ASCII_Format: |
840 | 5.20k | { |
841 | | /* |
842 | | Convert PBM image to pixel packets. |
843 | | */ |
844 | 5.20k | register unsigned long |
845 | 5.20k | x=0; |
846 | | |
847 | 5.20k | register PixelPacket |
848 | 5.20k | *q; |
849 | | |
850 | 5.20k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
851 | 5.20k | "Reading %s pixel data",PNMSubformatToString(format)); |
852 | 11.9k | for (y=0; y < (long) image->rows; y++) |
853 | 11.5k | { |
854 | 11.5k | q=SetImagePixels(image,0,y,image->columns,1); |
855 | 11.5k | if (q == (PixelPacket *) NULL) |
856 | 1.64k | break; |
857 | 9.92k | indexes=AccessMutableIndexes(image); |
858 | 19.2k | for (x=0; x < image->columns; x++) |
859 | 12.4k | { |
860 | 12.4k | index=!PNMInteger(image,2,&status); |
861 | 12.4k | if (status == MagickFail) |
862 | 3.15k | break; |
863 | 9.32k | VerifyColormapIndex(status,image,index); |
864 | 9.32k | indexes[x]=index; |
865 | 9.32k | *q++=image->colormap[index]; |
866 | 9.32k | } |
867 | 9.92k | if (!SyncImagePixels(image)) |
868 | 0 | break; |
869 | 9.92k | if (image->previous == (Image *) NULL) |
870 | 9.92k | if (QuantumTick(y,image->rows)) |
871 | 7.22k | if (!MagickMonitorFormatted(y,image->rows,exception, |
872 | 7.22k | LoadImageText,image->filename, |
873 | 7.22k | image->columns,image->rows)) |
874 | 0 | break; |
875 | 9.92k | if (status == MagickFail) |
876 | 3.15k | break; |
877 | 9.92k | } |
878 | 5.20k | image->is_grayscale=MagickTrue; |
879 | 5.20k | image->is_monochrome=MagickTrue; |
880 | | |
881 | | |
882 | 5.20k | if (status == MagickFail) |
883 | 3.15k | { |
884 | 3.15k | if (EOFBlob(image)) |
885 | 1.12k | { |
886 | 1.12k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
887 | 1.12k | "PBM raw (P1) reader EOF at x=%lu, y=%lu", x, y); |
888 | 1.12k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
889 | 1.12k | image->filename); |
890 | 1.12k | } |
891 | 2.02k | else |
892 | 2.02k | { |
893 | 2.02k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
894 | 2.02k | "PBM raw (P1) corrupt data at x=%lu, y=%lu", x, y); |
895 | 2.02k | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
896 | 2.02k | } |
897 | 3.15k | } |
898 | 5.20k | break; |
899 | 0 | } |
900 | 10.6k | case PGM_ASCII_Format: |
901 | 10.6k | { |
902 | | /* |
903 | | Convert PGM image to pixel packets. |
904 | | */ |
905 | 10.6k | register unsigned long |
906 | 10.6k | x=0; |
907 | | |
908 | 10.6k | register PixelPacket |
909 | 10.6k | *q; |
910 | | |
911 | 10.6k | unsigned long |
912 | 10.6k | intensity; |
913 | | |
914 | 10.6k | MagickBool |
915 | 10.6k | is_grayscale, |
916 | 10.6k | is_monochrome; |
917 | | |
918 | 10.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
919 | 10.6k | "Reading %s pixel data",PNMSubformatToString(format)); |
920 | 10.6k | is_grayscale=MagickTrue; |
921 | 10.6k | is_monochrome=MagickTrue; |
922 | 22.6k | for (y=0; y < (long) image->rows; y++) |
923 | 20.3k | { |
924 | 20.3k | q=SetImagePixels(image,0,y,image->columns,1); |
925 | 20.3k | if (q == (PixelPacket *) NULL) |
926 | 3.70k | break; |
927 | 16.6k | if (image->storage_class == PseudoClass) |
928 | 11.0k | { |
929 | | /* |
930 | | PseudoClass |
931 | | */ |
932 | 11.0k | indexes=AccessMutableIndexes(image); |
933 | 25.0k | for (x=0; x < image->columns; x++) |
934 | 16.6k | { |
935 | 16.6k | intensity=PNMInteger(image,10,&status); |
936 | 16.6k | if (status == MagickFail) |
937 | 1.01k | break; |
938 | 15.6k | ValidateScalingIndex(image, intensity, max_value); |
939 | 13.9k | index=intensity; |
940 | 13.9k | VerifyColormapIndex(status,image,index); |
941 | 13.9k | if (status == MagickFail) |
942 | 0 | break; |
943 | 13.9k | indexes[x]=index; |
944 | 13.9k | *q=image->colormap[index]; |
945 | 13.9k | is_monochrome &= IsMonochrome(*q); |
946 | 13.9k | q++; |
947 | 13.9k | } |
948 | 11.0k | } |
949 | 5.57k | else |
950 | 5.57k | { |
951 | | /* |
952 | | DirectClass |
953 | | */ |
954 | 13.2k | for (x=0; x < image->columns; x++) |
955 | 9.72k | { |
956 | 9.72k | intensity=PNMInteger(image,10,&status); |
957 | 9.72k | if (status == MagickFail) |
958 | 1.60k | break; |
959 | 8.12k | ValidateScalingIndex(image, intensity, max_value); |
960 | 7.71k | intensity=ScaleAnyToQuantum(intensity, max_value); |
961 | 7.71k | q->red=q->green=q->blue=intensity; |
962 | 7.71k | is_monochrome &= IsMonochrome(*q); |
963 | 7.71k | q++; |
964 | 7.71k | } |
965 | 5.57k | } |
966 | 14.5k | if (status == MagickFail) |
967 | 2.62k | break; |
968 | 11.9k | if (!SyncImagePixels(image)) |
969 | 0 | break; |
970 | 11.9k | if (image->previous == (Image *) NULL) |
971 | 11.9k | if (QuantumTick(y,image->rows)) |
972 | 9.05k | if (!MagickMonitorFormatted(y,image->rows,exception, |
973 | 9.05k | LoadImageText,image->filename, |
974 | 9.05k | image->columns,image->rows)) |
975 | 0 | break; |
976 | 11.9k | } |
977 | 8.57k | image->is_monochrome=is_monochrome; |
978 | 8.57k | image->is_grayscale=is_grayscale; |
979 | 8.57k | if (status == MagickFail) |
980 | 2.62k | { |
981 | 2.62k | if (EOFBlob(image)) |
982 | 2.54k | { |
983 | 2.54k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
984 | 2.54k | "PGM raw (P2) reader EOF at x=%lu, y=%lu", x, y); |
985 | 2.54k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
986 | 2.54k | image->filename); |
987 | 2.54k | } |
988 | 79 | else |
989 | 79 | { |
990 | 79 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
991 | 79 | "PGM raw (P2) corrupt data at x=%lu, y=%lu", x, y); |
992 | 79 | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
993 | 79 | } |
994 | 2.62k | } |
995 | 8.57k | break; |
996 | 10.6k | } |
997 | 15.5k | case PPM_ASCII_Format: |
998 | 15.5k | { |
999 | | /* |
1000 | | Convert PPM image to pixel packets. |
1001 | | */ |
1002 | 15.5k | register unsigned long |
1003 | 15.5k | x=0; |
1004 | | |
1005 | 15.5k | register PixelPacket |
1006 | 15.5k | *q; |
1007 | | |
1008 | 15.5k | MagickBool |
1009 | 15.5k | is_grayscale, |
1010 | 15.5k | is_monochrome; |
1011 | | |
1012 | 15.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1013 | 15.5k | "Reading %s pixel data",PNMSubformatToString(format)); |
1014 | 15.5k | is_grayscale=MagickTrue; |
1015 | 15.5k | is_monochrome=MagickTrue; |
1016 | 23.5k | for (y=0; y < (long) image->rows; y++) |
1017 | 22.6k | { |
1018 | 22.6k | q=SetImagePixels(image,0,y,image->columns,1); |
1019 | 22.6k | if (q == (PixelPacket *) NULL) |
1020 | 4.61k | break; |
1021 | 31.7k | for (x=0; x < image->columns; x++) |
1022 | 23.7k | { |
1023 | 23.7k | pixel.red=PNMInteger(image,10,&status); |
1024 | 23.7k | if (status == MagickFail) |
1025 | 2.17k | break; |
1026 | 21.5k | pixel.green=PNMInteger(image,10,&status); |
1027 | 21.5k | if (status == MagickFail) |
1028 | 598 | break; |
1029 | 20.9k | pixel.blue=PNMInteger(image,10,&status); |
1030 | 20.9k | if (status == MagickFail) |
1031 | 974 | break; |
1032 | 20.0k | ValidateScalingPixel(image, pixel, max_value); |
1033 | 13.7k | pixel.red=ScaleAnyToQuantum(pixel.red, max_value); |
1034 | 13.7k | pixel.green=ScaleAnyToQuantum(pixel.green, max_value); |
1035 | 13.7k | pixel.blue=ScaleAnyToQuantum(pixel.blue, max_value); |
1036 | 13.7k | q->red=(Quantum) pixel.red; |
1037 | 13.7k | q->green=(Quantum) pixel.green; |
1038 | 13.7k | q->blue=(Quantum) pixel.blue; |
1039 | 13.7k | is_monochrome &= IsMonochrome(*q); |
1040 | 13.7k | is_grayscale &= IsGray(*q); |
1041 | 13.7k | q++; |
1042 | 13.7k | } |
1043 | 11.7k | if (status == MagickFail) |
1044 | 3.74k | break; |
1045 | 8.00k | if (!SyncImagePixels(image)) |
1046 | 0 | break; |
1047 | 8.00k | if (image->previous == (Image *) NULL) |
1048 | 8.00k | if (QuantumTick(y,image->rows)) |
1049 | 5.12k | if (!MagickMonitorFormatted(y,image->rows,exception, |
1050 | 5.12k | LoadImageText,image->filename, |
1051 | 5.12k | image->columns,image->rows)) |
1052 | 0 | break; |
1053 | 8.00k | } |
1054 | 9.29k | image->is_monochrome=is_monochrome; |
1055 | 9.29k | image->is_grayscale=is_grayscale; |
1056 | 9.29k | if (status == MagickFail) |
1057 | 3.74k | { |
1058 | 3.74k | if (EOFBlob(image)) |
1059 | 3.63k | { |
1060 | 3.63k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1061 | 3.63k | "PNM raw (P3) reader EOF at x=%lu, y=%lu", x, y); |
1062 | 3.63k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
1063 | 3.63k | image->filename); |
1064 | 3.63k | } |
1065 | 111 | else |
1066 | 111 | { |
1067 | 111 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1068 | 111 | "PNM raw (P3) corrupt data at x=%lu, y=%lu", x, y); |
1069 | 111 | ThrowException(exception,CorruptImageError,CorruptImage,image->filename); |
1070 | 111 | } |
1071 | 3.74k | } |
1072 | 9.29k | break; |
1073 | 15.5k | } |
1074 | 3.62k | case PBM_RAW_Format: |
1075 | 7.19k | case PGM_RAW_Format: |
1076 | 23.5k | case PPM_RAW_Format: |
1077 | 27.4k | case PAM_Format: |
1078 | 27.5k | case XV_332_Format: |
1079 | 27.5k | { |
1080 | | /* |
1081 | | Convert PBM/PGM/PPM/PAM/XV raw raster image to pixel packets. |
1082 | | */ |
1083 | 27.5k | ImportPixelAreaOptions |
1084 | 27.5k | import_options; |
1085 | | |
1086 | 27.5k | QuantumType |
1087 | 27.5k | quantum_type; |
1088 | | |
1089 | 27.5k | size_t |
1090 | 27.5k | bytes_per_row; |
1091 | | |
1092 | 27.5k | MagickBool |
1093 | 27.5k | check_pixels, |
1094 | 27.5k | is_grayscale, |
1095 | 27.5k | is_monochrome, |
1096 | 27.5k | use_scaling; |
1097 | | |
1098 | 27.5k | unsigned long |
1099 | 27.5k | max_value_given_bits, |
1100 | 27.5k | row_count=0; |
1101 | | |
1102 | 27.5k | ThreadViewDataSet |
1103 | 27.5k | *scanline_set; |
1104 | | |
1105 | 27.5k | double |
1106 | 27.5k | sample_scale; |
1107 | | |
1108 | 27.5k | unsigned int |
1109 | 27.5k | sample_max; |
1110 | | |
1111 | | #if defined(HAVE_OPENMP) |
1112 | | int |
1113 | | pnm_read_threads; |
1114 | | #endif |
1115 | | |
1116 | 27.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1117 | 27.5k | "Reading %s pixel data",PNMSubformatToString(format)); |
1118 | | |
1119 | 27.5k | ImportPixelAreaOptionsInit(&import_options); |
1120 | | |
1121 | 27.5k | check_pixels=MagickTrue; |
1122 | 27.5k | is_grayscale=MagickTrue; |
1123 | 27.5k | is_monochrome=MagickTrue; |
1124 | | |
1125 | | /* |
1126 | | Deduce correct import parameters. |
1127 | | */ |
1128 | 27.5k | quantum_type=UndefinedQuantum; |
1129 | 27.5k | import_options.grayscale_miniswhite=MagickFalse; |
1130 | 27.5k | max_value_given_bits=MaxValueGivenBits(bits_per_sample); |
1131 | 27.5k | 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.5k | sample_max=RoundDoubleToQuantum((MaxRGBDouble*max_value)/max_value_given_bits); |
1138 | 27.5k | if (sample_max == 0U) |
1139 | 0 | { |
1140 | 0 | ThrowException(exception,CorruptImageError,ImproperImageHeader, |
1141 | 0 | image->filename); |
1142 | 0 | break; |
1143 | 0 | } |
1144 | 27.5k | sample_scale=MaxRGBDouble/sample_max; |
1145 | 27.5k | use_scaling=(MaxRGB != sample_max); |
1146 | 27.5k | bytes_per_row=0; |
1147 | | |
1148 | 27.5k | if (1 == samples_per_pixel) |
1149 | 7.94k | { |
1150 | 7.94k | if (1 == bits_per_sample) |
1151 | 4.04k | { |
1152 | | /* PBM */ |
1153 | 4.04k | import_options.grayscale_miniswhite=MagickTrue; |
1154 | 4.04k | quantum_type=GrayQuantum; |
1155 | 4.04k | } |
1156 | 3.90k | else |
1157 | 3.90k | { |
1158 | | /* PGM & XV_332 */ |
1159 | 3.90k | if ((XV_332_Format == format) && (image->storage_class == PseudoClass)) |
1160 | 110 | { |
1161 | 110 | quantum_type=IndexQuantum; |
1162 | 110 | } |
1163 | 3.79k | else |
1164 | 3.79k | { |
1165 | 3.79k | quantum_type=GrayQuantum; |
1166 | 3.79k | } |
1167 | 3.90k | } |
1168 | 7.94k | } |
1169 | 19.6k | else if (2 == samples_per_pixel && image->matte) |
1170 | 658 | { |
1171 | 658 | quantum_type=GrayAlphaQuantum; |
1172 | 658 | } |
1173 | 18.9k | else if (3 == samples_per_pixel) |
1174 | 16.4k | { |
1175 | | /* PPM */ |
1176 | 16.4k | quantum_type=RGBQuantum; |
1177 | 16.4k | } |
1178 | 2.53k | else if (4 == samples_per_pixel) |
1179 | 1.37k | { |
1180 | 1.37k | if (CMYKColorspace == image->colorspace) |
1181 | 653 | quantum_type=CMYKQuantum; |
1182 | 723 | else |
1183 | 723 | quantum_type=RGBAQuantum; |
1184 | 1.37k | } |
1185 | 1.16k | else if (5 == samples_per_pixel) |
1186 | 659 | { |
1187 | 659 | if (CMYKColorspace == image->colorspace) |
1188 | 641 | quantum_type=CMYKAQuantum; |
1189 | 659 | } |
1190 | | |
1191 | 27.5k | if (image->logging) |
1192 | 27.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1193 | 27.5k | "Import Quantum Type: %s", |
1194 | 27.5k | QuantumTypeToString(quantum_type)); |
1195 | | |
1196 | 27.5k | samples_per_pixel=MagickGetQuantumSamplesPerPixel(quantum_type); |
1197 | 27.5k | if (image->logging) |
1198 | 27.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1199 | 27.5k | "Samples/Pixel: %u", samples_per_pixel); |
1200 | | |
1201 | 27.5k | if (1 == bits_per_sample) |
1202 | 6.13k | { |
1203 | | /* bytes_per_row=(((size_t) image->columns*samples_per_pixel+7) >> 3); */ |
1204 | 6.13k | bytes_per_row=MagickArraySize(image->columns,samples_per_pixel); |
1205 | 6.13k | if (bytes_per_row) |
1206 | 5.90k | bytes_per_row += 7; |
1207 | 6.13k | if (bytes_per_row) |
1208 | 5.90k | bytes_per_row >>= 3; |
1209 | 6.13k | } |
1210 | 21.4k | else |
1211 | 21.4k | { |
1212 | 21.4k | bytes_per_row=MagickArraySize((((size_t) bits_per_sample+7)/8)* |
1213 | 21.4k | samples_per_pixel,image->columns); |
1214 | 21.4k | } |
1215 | | |
1216 | 27.5k | if (image->logging) |
1217 | 27.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1218 | 27.5k | "Bytes/Row: %" MAGICK_SIZE_T_F "u", |
1219 | 27.5k | (MAGICK_SIZE_T) bytes_per_row); |
1220 | | |
1221 | 27.5k | if (1 == samples_per_pixel) |
1222 | 7.94k | { |
1223 | 7.94k | check_pixels=MagickFalse; |
1224 | 7.94k | } |
1225 | 27.5k | if (GrayQuantum == quantum_type) |
1226 | 7.83k | { |
1227 | 7.83k | if (1 == bits_per_sample) |
1228 | 4.04k | { |
1229 | 4.04k | is_grayscale=MagickTrue; |
1230 | 4.04k | is_monochrome=MagickTrue; |
1231 | 4.04k | } |
1232 | 3.79k | else |
1233 | 3.79k | { |
1234 | 3.79k | is_grayscale=MagickTrue; |
1235 | 3.79k | is_monochrome=MagickFalse; |
1236 | 3.79k | } |
1237 | 7.83k | } |
1238 | | |
1239 | | /* Validate file size before allocating memory */ |
1240 | 27.5k | if (BlobIsSeekable(image)) |
1241 | 27.5k | { |
1242 | 27.5k | const magick_off_t file_size = GetBlobSize(image); |
1243 | 27.5k | const magick_off_t current_offset = TellBlob(image); |
1244 | 27.5k | if ((file_size > 0) && |
1245 | 27.5k | (current_offset > 0) && |
1246 | 27.5k | (file_size > current_offset)) |
1247 | 26.0k | { |
1248 | 26.0k | const magick_off_t remaining = file_size-current_offset; |
1249 | 26.0k | const magick_off_t needed = (magick_off_t) image->rows * |
1250 | 26.0k | (magick_off_t) bytes_per_row; |
1251 | 26.0k | if ((remaining < (magick_off_t) bytes_per_row) || |
1252 | 24.7k | (remaining < needed)) |
1253 | 1.42k | { |
1254 | 1.42k | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
1255 | 1.42k | image->filename); |
1256 | 1.42k | break; |
1257 | 1.42k | } |
1258 | 26.0k | } |
1259 | 27.5k | } |
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.1k | scanline_set=AllocateThreadViewDataArray(image,exception,bytes_per_row,1); |
1269 | 26.1k | 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 | 742k | for (y=0; y < (long) image->rows; y++) |
1279 | 716k | { |
1280 | 716k | register unsigned long |
1281 | 716k | x; |
1282 | | |
1283 | 716k | register PixelPacket |
1284 | 716k | *q = (PixelPacket *) NULL; |
1285 | | |
1286 | 716k | void |
1287 | 716k | *pixels; |
1288 | | |
1289 | 716k | MagickBool |
1290 | 716k | thread_status; |
1291 | | |
1292 | 716k | MagickBool |
1293 | 716k | thread_is_grayscale, |
1294 | 716k | thread_is_monochrome; |
1295 | | |
1296 | 716k | unsigned long |
1297 | 716k | thread_row_count; |
1298 | | |
1299 | 716k | ImportPixelAreaInfo |
1300 | 716k | import_info; |
1301 | | |
1302 | | #if PNMReadUseOpenMP |
1303 | | # pragma omp critical (GM_ReadPNMImage) |
1304 | | #endif |
1305 | 716k | thread_status=status; |
1306 | 716k | if (thread_status == MagickFail) |
1307 | 135k | continue; |
1308 | | |
1309 | 581k | pixels=AccessThreadViewData(scanline_set); |
1310 | | |
1311 | | #if PNMReadUseOpenMP |
1312 | | # pragma omp critical (GM_ReadPNMImage) |
1313 | | #endif |
1314 | 581k | { |
1315 | 581k | thread_is_grayscale=is_grayscale; |
1316 | 581k | thread_is_monochrome=is_monochrome; |
1317 | | |
1318 | 581k | if (ReadBlobZC(image,bytes_per_row,&pixels) != bytes_per_row) |
1319 | 1.15k | thread_status=MagickFail; |
1320 | | |
1321 | 581k | thread_row_count=row_count; |
1322 | 581k | row_count++; |
1323 | | |
1324 | 581k | if (image->previous == (Image *) NULL) |
1325 | 581k | if (QuantumTick(thread_row_count,image->rows)) |
1326 | 167k | if (!MagickMonitorFormatted(thread_row_count,image->rows, |
1327 | 167k | exception,LoadImageText, |
1328 | 167k | image->filename, |
1329 | 167k | image->columns,image->rows)) |
1330 | 0 | thread_status=MagickFail; |
1331 | 581k | } |
1332 | | |
1333 | 581k | if (thread_status != MagickFail) |
1334 | 579k | if ((q=SetImagePixels(image,0,thread_row_count,image->columns,1)) == |
1335 | 579k | (PixelPacket *) NULL) |
1336 | 7.03k | thread_status=MagickFail; |
1337 | | |
1338 | 581k | if (thread_status != MagickFail) |
1339 | 572k | if (!ImportImagePixelArea(image,quantum_type,bits_per_sample, |
1340 | 572k | (const unsigned char*) pixels, |
1341 | 572k | &import_options,&import_info)) |
1342 | 600 | thread_status=MagickFail; |
1343 | | /* |
1344 | | Scale sub-ranged pixels up to full range if necessary |
1345 | | */ |
1346 | 581k | if ((thread_status != MagickFail) && (use_scaling)) |
1347 | 3.92M | for (x=0; x < image->columns; x++) |
1348 | 3.84M | { |
1349 | 3.84M | SetRedSample(&q[x], |
1350 | 3.84M | RoundDoubleToQuantum(sample_scale* |
1351 | 3.84M | GetRedSample(&q[x]))); |
1352 | 3.84M | SetGreenSample(&q[x], |
1353 | 3.84M | RoundDoubleToQuantum(sample_scale* |
1354 | 3.84M | GetGreenSample(&q[x]))); |
1355 | 3.84M | SetBlueSample(&q[x], |
1356 | 3.84M | RoundDoubleToQuantum(sample_scale* |
1357 | 3.84M | GetBlueSample(&q[x]))); |
1358 | 3.84M | if (image->matte) |
1359 | 367k | SetOpacitySample(&q[x], |
1360 | 3.84M | MaxRGB- |
1361 | 3.84M | RoundDoubleToQuantum(sample_scale* |
1362 | 3.84M | (MaxRGB- |
1363 | 3.84M | GetOpacitySample(&q[x])))); |
1364 | 3.84M | } |
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 | 581k | if (thread_status != MagickFail) |
1374 | 572k | if (check_pixels) |
1375 | 60.0k | if (thread_is_grayscale || thread_is_monochrome) |
1376 | 228k | for (x=0; x < image->columns; x++) |
1377 | 210k | { |
1378 | 210k | thread_is_grayscale = thread_is_grayscale && IsGray(q[x]); |
1379 | 210k | thread_is_monochrome = thread_is_monochrome && IsMonochrome(q[x]); |
1380 | 210k | if (!thread_is_grayscale && !thread_is_monochrome) |
1381 | 8.92k | break; |
1382 | 210k | } |
1383 | | |
1384 | 581k | if (thread_status != MagickFail) |
1385 | 572k | if (!SyncImagePixels(image)) |
1386 | 0 | thread_status=MagickFail; |
1387 | | |
1388 | | #if PNMReadUseOpenMP |
1389 | | # pragma omp critical (GM_ReadPNMImage) |
1390 | | #endif |
1391 | 581k | { |
1392 | 581k | if (thread_status == MagickFail) |
1393 | 8.78k | status=MagickFail; |
1394 | | |
1395 | 581k | if (!thread_is_grayscale) |
1396 | 42.2k | is_grayscale=thread_is_grayscale; |
1397 | | |
1398 | 581k | if (!thread_is_monochrome) |
1399 | 79.3k | is_monochrome=thread_is_monochrome; |
1400 | 581k | } |
1401 | 581k | } |
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 | 7.73k | CopyException(exception,&image->exception); |
1407 | 25.6k | if (EOFBlob(image)) |
1408 | 24.4k | ThrowReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
1409 | 24.4k | break; |
1410 | 25.6k | } |
1411 | 90 | default: |
1412 | 90 | ThrowReaderException(CorruptImageError,ImproperImageHeader,image); |
1413 | 59.1k | } |
1414 | 48.8k | StopTimer(&image->timer); |
1415 | 48.8k | if (status ==MagickFail) |
1416 | 17.1k | break; |
1417 | | /* |
1418 | | Proceed to next image. |
1419 | | */ |
1420 | 31.7k | if (image_info->subrange != 0) |
1421 | 31.3k | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
1422 | 31.3k | break; |
1423 | 450 | if ((format == PBM_ASCII_Format) || (format == PGM_ASCII_Format) || (format == PPM_ASCII_Format)) |
1424 | 14 | do |
1425 | 207 | { |
1426 | | /* |
1427 | | Skip to end of line. |
1428 | | */ |
1429 | 207 | count=ReadBlob(image,1,&c); |
1430 | 207 | if (count == 0) |
1431 | 11 | break; |
1432 | 207 | } while (c != '\n'); |
1433 | 450 | count=ReadBlob(image,1,&c); |
1434 | 450 | if ((count != 0) && (c == 'P')) |
1435 | 35 | { |
1436 | | /* |
1437 | | Allocate next image structure. |
1438 | | */ |
1439 | 35 | AllocateNextImage(image_info,image); |
1440 | 35 | if (image->next == (Image *) NULL) |
1441 | 0 | { |
1442 | 0 | DestroyImageList(image); |
1443 | 0 | return((Image *) NULL); |
1444 | 0 | } |
1445 | 35 | image=SyncNextImageInList(image); |
1446 | 35 | if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image), |
1447 | 35 | exception,LoadImagesText, |
1448 | 35 | image->filename)) |
1449 | 0 | break; |
1450 | 35 | } |
1451 | 450 | } while ((count != 0) && (c == 'P')); |
1452 | 48.8k | while (image->previous != (Image *) NULL) |
1453 | 17 | image=image->previous; |
1454 | 48.8k | CloseBlob(image); |
1455 | 48.8k | return(image); |
1456 | 208k | } |
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.60k | { |
1634 | 7.60k | char |
1635 | 7.60k | buffer[MaxTextExtent]; |
1636 | | |
1637 | 7.60k | const ImageAttribute |
1638 | 7.60k | *attribute; |
1639 | | |
1640 | 7.60k | IndexPacket |
1641 | 7.60k | index; |
1642 | | |
1643 | 7.60k | PNMSubformat |
1644 | 7.60k | format; |
1645 | | |
1646 | 7.60k | unsigned int |
1647 | 7.60k | depth; |
1648 | | |
1649 | 7.60k | unsigned long |
1650 | 7.60k | y; |
1651 | | |
1652 | 7.60k | register const PixelPacket |
1653 | 7.60k | *p; |
1654 | | |
1655 | 7.60k | register const IndexPacket |
1656 | 7.60k | *indexes; |
1657 | | |
1658 | 7.60k | register unsigned long |
1659 | 7.60k | i, |
1660 | 7.60k | x; |
1661 | | |
1662 | 7.60k | unsigned int |
1663 | 7.60k | scene, |
1664 | 7.60k | status; |
1665 | | |
1666 | 7.60k | size_t |
1667 | 7.60k | image_list_length; |
1668 | | |
1669 | | /* |
1670 | | Open output image file. |
1671 | | */ |
1672 | 7.60k | assert(image_info != (const ImageInfo *) NULL); |
1673 | 7.60k | assert(image_info->signature == MagickSignature); |
1674 | 7.60k | assert(image != (Image *) NULL); |
1675 | 7.60k | assert(image->signature == MagickSignature); |
1676 | 7.60k | image_list_length=GetImageListLength(image); |
1677 | 7.60k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
1678 | 7.60k | if (status == MagickFail) |
1679 | 7.60k | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
1680 | 7.60k | scene=0; |
1681 | 7.60k | do |
1682 | 7.60k | { |
1683 | 7.60k | depth=(image->depth <= 8 ? 8 : image->depth <= 16 ? 16 : 32); |
1684 | | |
1685 | | /* |
1686 | | Write PNM file header. |
1687 | | */ |
1688 | 7.60k | format=Undefined_PNM_Format; |
1689 | 7.60k | if (LocaleCompare(image_info->magick,"P7") == 0) |
1690 | 1.05k | { |
1691 | 1.05k | format=XV_332_Format; |
1692 | 1.05k | } |
1693 | 6.55k | else if (LocaleCompare(image_info->magick,"PPM") == 0) |
1694 | 1.21k | { |
1695 | 1.21k | format=PPM_RAW_Format; |
1696 | 1.21k | } |
1697 | 5.33k | else if (LocaleCompare(image_info->magick,"PGM") == 0) |
1698 | 1.18k | { |
1699 | 1.18k | format=PGM_RAW_Format; |
1700 | 1.18k | } |
1701 | 4.15k | else if (LocaleCompare(image_info->magick,"PBM") == 0) |
1702 | 1.49k | { |
1703 | 1.49k | format=PBM_RAW_Format; |
1704 | 1.49k | } |
1705 | 2.65k | else if (LocaleCompare(image_info->magick,"PAM") == 0) |
1706 | 1.38k | { |
1707 | 1.38k | format=PAM_Format; |
1708 | 1.38k | } |
1709 | 1.26k | else /* PNM auto format */ |
1710 | 1.26k | { |
1711 | 1.26k | ImageCharacteristics |
1712 | 1.26k | characteristics; |
1713 | | |
1714 | | /* |
1715 | | Make sure that image is in an RGB type space. |
1716 | | */ |
1717 | 1.26k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1718 | 1.26k | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1719 | | |
1720 | | /* |
1721 | | Analyze image to be written. |
1722 | | */ |
1723 | 1.26k | if (!GetImageCharacteristics(image,&characteristics, |
1724 | 1.26k | (OptimizeType == image_info->type), |
1725 | 1.26k | &image->exception)) |
1726 | 1.26k | ThrowWriterException(CoderError,UnableToGetImageCharacteristics,image); |
1727 | | |
1728 | 1.26k | if ((characteristics.monochrome) && |
1729 | 428 | (image_info->type != GrayscaleType) && |
1730 | 428 | (image_info->type != GrayscaleMatteType) && |
1731 | 428 | (image_info->type != TrueColorType) && |
1732 | 428 | (image_info->type != TrueColorMatteType)) |
1733 | 428 | { |
1734 | | /* PBM */ |
1735 | 428 | format=PBM_RAW_Format; |
1736 | 428 | } |
1737 | 838 | else if ((characteristics.grayscale) && |
1738 | 157 | (image_info->type != TrueColorType) && |
1739 | 157 | (image_info->type != TrueColorMatteType)) |
1740 | 157 | { |
1741 | | /* PGM */ |
1742 | 157 | format=PGM_RAW_Format; |
1743 | 157 | } |
1744 | 681 | else |
1745 | 681 | { |
1746 | | /* PPM */ |
1747 | 681 | format=PPM_RAW_Format; |
1748 | 681 | } |
1749 | 1.26k | } |
1750 | | |
1751 | | /* |
1752 | | Check if ASCII subformat is requested. |
1753 | | */ |
1754 | 7.60k | if ((PBM_RAW_Format == format) || (PGM_RAW_Format == format) | (PPM_RAW_Format == format)) |
1755 | 5.16k | { |
1756 | 5.16k | MagickBool |
1757 | 5.16k | ascii = MagickFalse; |
1758 | | |
1759 | | /* |
1760 | | If quality is set to zero or "pnm:ascii" is defined, then |
1761 | | select an ASCII subformat. |
1762 | | */ |
1763 | 5.16k | if (image_info->quality == 0) |
1764 | 0 | ascii=MagickTrue; |
1765 | 5.16k | else if ((AccessDefinition(image_info,"pnm","ascii"))) |
1766 | 0 | ascii=MagickTrue; |
1767 | | |
1768 | 5.16k | if (ascii) |
1769 | 0 | { |
1770 | 0 | if (PBM_RAW_Format == format) |
1771 | 0 | format=PBM_ASCII_Format; |
1772 | 0 | else if (PGM_RAW_Format == format) |
1773 | 0 | format=PGM_ASCII_Format; |
1774 | 0 | else if (PPM_RAW_Format == format) |
1775 | 0 | format=PPM_ASCII_Format; |
1776 | 0 | } |
1777 | 5.16k | } |
1778 | | |
1779 | 7.60k | { |
1780 | 7.60k | const char *header = ""; |
1781 | 7.60k | switch (format) |
1782 | 7.60k | { |
1783 | 0 | case Undefined_PNM_Format: break; |
1784 | 0 | case PBM_ASCII_Format: header="P1"; break; |
1785 | 0 | case PGM_ASCII_Format: header="P2"; break; |
1786 | 0 | case PPM_ASCII_Format: header="P3"; break; |
1787 | 1.92k | case PBM_RAW_Format: header="P4"; break; |
1788 | 1.34k | case PGM_RAW_Format: header="P5"; break; |
1789 | 1.90k | case PPM_RAW_Format: header="P6"; break; |
1790 | 1.38k | case PAM_Format: header="P7"; break; |
1791 | 1.05k | case XV_332_Format: header="P7 332"; break; |
1792 | 7.60k | } |
1793 | 7.60k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Format Id: %s", |
1794 | 7.60k | header); |
1795 | 7.60k | (void) WriteBlobString(image,header); |
1796 | 7.60k | (void) WriteBlobByte(image,'\n'); |
1797 | 7.60k | } |
1798 | | |
1799 | 0 | attribute=GetImageAttribute(image,"comment"); |
1800 | 7.60k | if (attribute != (const ImageAttribute *) NULL) |
1801 | 1.77k | { |
1802 | 1.77k | register char |
1803 | 1.77k | *av; |
1804 | | |
1805 | | /* |
1806 | | Write comments to file. |
1807 | | */ |
1808 | 1.77k | (void) WriteBlobByte(image,'#'); |
1809 | 93.7M | for (av=attribute->value; *av != '\0'; av++) |
1810 | 93.7M | { |
1811 | 93.7M | (void) WriteBlobByte(image,*av); |
1812 | 93.7M | if ((*av == '\n') && (*(av+1) != '\0')) |
1813 | 34.0k | (void) WriteBlobByte(image,'#'); |
1814 | 93.7M | } |
1815 | 1.77k | (void) WriteBlobByte(image,'\n'); |
1816 | 1.77k | } |
1817 | 7.60k | if ((PAM_Format != format) && (XV_332_Format != format)) |
1818 | 5.16k | { |
1819 | 5.16k | MagickFormatString(buffer,sizeof(buffer),"%lu %lu\n",image->columns,image->rows); |
1820 | 5.16k | (void) WriteBlobString(image,buffer); |
1821 | 5.16k | } |
1822 | | /* |
1823 | | Write PNM raster pixels. |
1824 | | */ |
1825 | 7.60k | switch (format) |
1826 | 7.60k | { |
1827 | 0 | case PBM_ASCII_Format: |
1828 | 0 | { |
1829 | 0 | unsigned int |
1830 | 0 | polarity; |
1831 | |
|
1832 | 0 | size_t |
1833 | 0 | j; |
1834 | | |
1835 | | /* |
1836 | | Convert image to a PBM ASCII image. |
1837 | | */ |
1838 | 0 | (void) SetImageType(image,BilevelType); |
1839 | 0 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < (MaxRGB/2); |
1840 | 0 | if (image->colors == 2) |
1841 | 0 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < |
1842 | 0 | PixelIntensityToQuantum(&image->colormap[1]); |
1843 | 0 | i=0; |
1844 | 0 | j=0; |
1845 | 0 | for (y=0; y < image->rows; y++) |
1846 | 0 | { |
1847 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1848 | 0 | if (p == (const PixelPacket *) NULL) |
1849 | 0 | { |
1850 | 0 | status=MagickFail; |
1851 | 0 | break; |
1852 | 0 | } |
1853 | 0 | indexes=AccessImmutableIndexes(image); |
1854 | 0 | for (x=0; x < image->columns; x++) |
1855 | 0 | { |
1856 | 0 | buffer[j++] = (indexes[x] == polarity ? '0' : '1'); |
1857 | 0 | buffer[j++] = ' '; |
1858 | 0 | i++; |
1859 | 0 | if (i == 36) |
1860 | 0 | { |
1861 | 0 | buffer[j++] = '\n'; |
1862 | 0 | i=0; |
1863 | 0 | } |
1864 | 0 | if (j+4 > sizeof(buffer)) |
1865 | 0 | { |
1866 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1867 | 0 | j=0; |
1868 | 0 | if (MagickFail == status) |
1869 | 0 | break; |
1870 | 0 | } |
1871 | 0 | } |
1872 | 0 | if (image->previous == (Image *) NULL) |
1873 | 0 | if (QuantumTick(y,image->rows)) |
1874 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1875 | 0 | SaveImageText,image->filename, |
1876 | 0 | image->columns,image->rows)) |
1877 | 0 | { |
1878 | 0 | status=MagickFail; |
1879 | 0 | break; |
1880 | 0 | } |
1881 | 0 | if (MagickFail == status) |
1882 | 0 | break; |
1883 | 0 | } |
1884 | 0 | if (MagickFail != status) |
1885 | 0 | { |
1886 | 0 | if (i != 0) |
1887 | 0 | buffer[j++] = '\n'; |
1888 | 0 | if (j > 0) |
1889 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1890 | 0 | } |
1891 | 0 | break; |
1892 | 0 | } |
1893 | 0 | case PGM_ASCII_Format: |
1894 | 0 | { |
1895 | | /* |
1896 | | Convert image to a PGM ASCII image. |
1897 | | */ |
1898 | 0 | size_t |
1899 | 0 | j; |
1900 | |
|
1901 | 0 | unsigned int |
1902 | 0 | value; |
1903 | | |
1904 | | /* |
1905 | | Make sure that image is in an RGB type space. |
1906 | | */ |
1907 | 0 | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1908 | 0 | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1909 | |
|
1910 | 0 | i=0; |
1911 | 0 | j=0; |
1912 | |
|
1913 | 0 | value=(depth <=8 ? 255U : depth <= 16 ? 65535U : 4294967295U); |
1914 | |
|
1915 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u\n",value); |
1916 | 0 | for (y=0; y < image->rows; y++) |
1917 | 0 | { |
1918 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1919 | 0 | if (p == (const PixelPacket *) NULL) |
1920 | 0 | { |
1921 | 0 | status=MagickFail; |
1922 | 0 | break; |
1923 | 0 | } |
1924 | 0 | for (x=0; x < image->columns; x++) |
1925 | 0 | { |
1926 | 0 | if (image->is_grayscale) |
1927 | 0 | index=p->red; |
1928 | 0 | else |
1929 | 0 | index=PixelIntensityToQuantum(p); |
1930 | 0 | if (depth <= 8) |
1931 | 0 | { |
1932 | | /* Use LUT for speed */ |
1933 | 0 | value=ScaleQuantumToChar(index); |
1934 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
1935 | 0 | buffer[j++] = ' '; |
1936 | 0 | } |
1937 | 0 | else if (depth <= 16) |
1938 | 0 | { |
1939 | 0 | value=ScaleQuantumToShort(index); |
1940 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j)," %u",value); |
1941 | 0 | } |
1942 | 0 | else |
1943 | 0 | { |
1944 | 0 | value=ScaleQuantumToLong(index); |
1945 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j)," %u",value); |
1946 | 0 | } |
1947 | |
|
1948 | 0 | i++; |
1949 | 0 | if (i == 12) |
1950 | 0 | { |
1951 | 0 | buffer[j++] = '\n'; |
1952 | 0 | i=0; |
1953 | 0 | } |
1954 | 0 | if (j+8 > sizeof(buffer)) |
1955 | 0 | { |
1956 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1957 | 0 | j=0; |
1958 | 0 | if (MagickFail == status) |
1959 | 0 | break; |
1960 | 0 | } |
1961 | 0 | p++; |
1962 | 0 | } |
1963 | 0 | if (image->previous == (Image *) NULL) |
1964 | 0 | if (QuantumTick(y,image->rows)) |
1965 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1966 | 0 | SaveImageText,image->filename, |
1967 | 0 | image->columns,image->rows)) |
1968 | 0 | { |
1969 | 0 | status=MagickFail; |
1970 | 0 | break; |
1971 | 0 | } |
1972 | 0 | if (MagickFail == status) |
1973 | 0 | break; |
1974 | 0 | } |
1975 | 0 | if (MagickFail != status) |
1976 | 0 | { |
1977 | 0 | if (i != 0) |
1978 | 0 | buffer[j++] = '\n'; |
1979 | 0 | if (j > 0) |
1980 | 0 | status=(WriteBlob(image,j,buffer) == j); |
1981 | 0 | } |
1982 | 0 | break; |
1983 | 0 | } |
1984 | 0 | case PPM_ASCII_Format: |
1985 | 0 | { |
1986 | | /* |
1987 | | Convert image to a PPM ASCII image. |
1988 | | */ |
1989 | 0 | size_t |
1990 | 0 | j; |
1991 | |
|
1992 | 0 | unsigned int |
1993 | 0 | value; |
1994 | | |
1995 | | /* |
1996 | | Make sure that image is in an RGB type space. |
1997 | | */ |
1998 | 0 | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1999 | 0 | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
2000 | |
|
2001 | 0 | i=0; |
2002 | 0 | j=0; |
2003 | |
|
2004 | 0 | value=(depth <=8 ? 255U : (depth <= 16 ? 65535U : 4294967295U)); |
2005 | |
|
2006 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u\n",value); |
2007 | 0 | for (y=0; y < image->rows; y++) |
2008 | 0 | { |
2009 | 0 | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2010 | 0 | if (p == (const PixelPacket *) NULL) |
2011 | 0 | { |
2012 | 0 | status=MagickFail; |
2013 | 0 | break; |
2014 | 0 | } |
2015 | 0 | for (x=0; x < image->columns; x++) |
2016 | 0 | { |
2017 | 0 | if (depth <= 8) |
2018 | 0 | { |
2019 | | /* Use LUT for speed */ |
2020 | 0 | value=ScaleQuantumToChar(p->red); |
2021 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2022 | 0 | buffer[j++] = ' '; |
2023 | 0 | value=ScaleQuantumToChar(p->green); |
2024 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2025 | 0 | buffer[j++] = ' '; |
2026 | 0 | value=ScaleQuantumToChar(p->blue); |
2027 | 0 | AppendUnsignedCharValueToString(j,buffer,value); |
2028 | 0 | buffer[j++] = ' '; |
2029 | 0 | } |
2030 | 0 | else if (depth <= 16) |
2031 | 0 | { |
2032 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u %u %u ", |
2033 | 0 | ScaleQuantumToShort(p->red), |
2034 | 0 | ScaleQuantumToShort(p->green), |
2035 | 0 | ScaleQuantumToShort(p->blue)); |
2036 | 0 | } |
2037 | 0 | else |
2038 | 0 | { |
2039 | 0 | j += snprintf(&buffer[j],(sizeof(buffer)-j),"%u %u %u ", |
2040 | 0 | (unsigned int) ScaleQuantumToLong(p->red), |
2041 | 0 | (unsigned int) ScaleQuantumToLong(p->green), |
2042 | 0 | (unsigned int) ScaleQuantumToLong(p->blue)); |
2043 | 0 | } |
2044 | 0 | i++; |
2045 | 0 | if (i == 4) |
2046 | 0 | { |
2047 | 0 | buffer[j++] = '\n'; |
2048 | 0 | i=0; |
2049 | 0 | } |
2050 | 0 | if (j+(8*3) > sizeof(buffer)) |
2051 | 0 | { |
2052 | 0 | status=(WriteBlob(image,j,buffer) == j); |
2053 | 0 | j=0; |
2054 | 0 | if (MagickFail == status) |
2055 | 0 | break; |
2056 | 0 | } |
2057 | 0 | p++; |
2058 | 0 | } |
2059 | 0 | if (image->previous == (Image *) NULL) |
2060 | 0 | if (QuantumTick(y,image->rows)) |
2061 | 0 | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2062 | 0 | SaveImageText,image->filename, |
2063 | 0 | image->columns,image->rows)) |
2064 | 0 | { |
2065 | 0 | status=MagickFail; |
2066 | 0 | break; |
2067 | 0 | } |
2068 | 0 | if (MagickFail == status) |
2069 | 0 | break; |
2070 | 0 | } |
2071 | 0 | if (MagickFail != status) |
2072 | 0 | { |
2073 | 0 | if (i != 0) |
2074 | 0 | buffer[j++] = '\n'; |
2075 | 0 | if (j > 0) |
2076 | 0 | status=(WriteBlob(image,j,buffer) == j); |
2077 | 0 | } |
2078 | 0 | break; |
2079 | 0 | } |
2080 | 1.92k | case PBM_RAW_Format: |
2081 | 3.26k | case PGM_RAW_Format: |
2082 | 5.16k | case PPM_RAW_Format: |
2083 | 6.55k | case PAM_Format: |
2084 | 6.55k | { |
2085 | 6.55k | ExportPixelAreaOptions |
2086 | 6.55k | export_options; |
2087 | | |
2088 | 6.55k | size_t |
2089 | 6.55k | bytes_per_row; |
2090 | | |
2091 | 6.55k | QuantumType |
2092 | 6.55k | quantum_type; |
2093 | | |
2094 | 6.55k | unsigned int |
2095 | 6.55k | bits_per_sample, |
2096 | 6.55k | samples_per_pixel; |
2097 | | |
2098 | 6.55k | MagickBool |
2099 | 6.55k | grayscale_miniswhite=MagickFalse; |
2100 | | |
2101 | 6.55k | unsigned char |
2102 | 6.55k | *pixels; |
2103 | | |
2104 | | /* |
2105 | | Deduce correct export parameters. |
2106 | | */ |
2107 | 6.55k | bits_per_sample=(depth <=8 ? 8 : (depth <= 16 ? 16 : 32)); |
2108 | 6.55k | quantum_type=RGBQuantum; |
2109 | 6.55k | if (PBM_RAW_Format == format) |
2110 | 1.92k | { |
2111 | 1.92k | bits_per_sample=1; |
2112 | 1.92k | grayscale_miniswhite=MagickTrue; |
2113 | 1.92k | quantum_type=GrayQuantum; |
2114 | 1.92k | } |
2115 | 4.62k | else if (PGM_RAW_Format == format) |
2116 | 1.34k | { |
2117 | 1.34k | quantum_type=GrayQuantum; |
2118 | 1.34k | } |
2119 | 3.28k | else if (PPM_RAW_Format == format) |
2120 | 1.90k | { |
2121 | 1.90k | quantum_type=RGBQuantum; |
2122 | 1.90k | } |
2123 | 1.38k | else if (PAM_Format == format) |
2124 | 1.38k | { |
2125 | 1.38k | ImageCharacteristics |
2126 | 1.38k | characteristics; |
2127 | | |
2128 | | /* |
2129 | | Make sure image is of desired type. |
2130 | | */ |
2131 | 1.38k | if (UndefinedType != image_info->type) |
2132 | 0 | SetImageType(image,image_info->type); |
2133 | | |
2134 | | /* |
2135 | | Analyze the image to get its characteristics. |
2136 | | */ |
2137 | 1.38k | if (!GetImageCharacteristics(image,&characteristics, |
2138 | 1.38k | (OptimizeType == image_info->type), |
2139 | 1.38k | &image->exception)) |
2140 | 0 | { |
2141 | 0 | CloseBlob(image); |
2142 | 0 | return MagickFail; |
2143 | 0 | } |
2144 | | |
2145 | | /* |
2146 | | Choose best encoding based on image characteristics. |
2147 | | */ |
2148 | 1.38k | if (characteristics.cmyk) |
2149 | 392 | { |
2150 | 392 | if (image->matte) |
2151 | 167 | quantum_type=CMYKAQuantum; |
2152 | 225 | else |
2153 | 225 | quantum_type=CMYKQuantum; |
2154 | 392 | } |
2155 | 997 | else if (characteristics.monochrome) |
2156 | 428 | { |
2157 | 428 | bits_per_sample=1; |
2158 | 428 | grayscale_miniswhite=MagickTrue; |
2159 | | |
2160 | 428 | if (image->matte) |
2161 | 84 | quantum_type=GrayAlphaQuantum; |
2162 | 344 | else |
2163 | 344 | quantum_type=GrayQuantum; |
2164 | 428 | } |
2165 | 569 | else if (characteristics.grayscale) |
2166 | 214 | { |
2167 | 214 | if (image->matte) |
2168 | 81 | quantum_type=GrayAlphaQuantum; |
2169 | 133 | else |
2170 | 133 | quantum_type=GrayQuantum; |
2171 | 214 | } |
2172 | 355 | else |
2173 | 355 | { |
2174 | 355 | if (image->matte) |
2175 | 49 | quantum_type=RGBAQuantum; |
2176 | 306 | else |
2177 | 306 | quantum_type=RGBQuantum; |
2178 | 355 | } |
2179 | 1.38k | } |
2180 | 6.55k | if (image->logging) |
2181 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2182 | 0 | "Export Quantum Type: %s", |
2183 | 0 | QuantumTypeToString(quantum_type)); |
2184 | | |
2185 | 6.55k | samples_per_pixel=MagickGetQuantumSamplesPerPixel(quantum_type); |
2186 | 6.55k | if (image->logging) |
2187 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2188 | 0 | "Samples/Pixel: %u", samples_per_pixel); |
2189 | | |
2190 | 6.55k | if (1 == bits_per_sample) |
2191 | 2.35k | { |
2192 | | /* bytes_per_row=(((size_t) image->columns*samples_per_pixel+7) >> 3); */ |
2193 | 2.35k | bytes_per_row=MagickArraySize(image->columns,samples_per_pixel); |
2194 | 2.35k | if (bytes_per_row) |
2195 | 2.35k | bytes_per_row += 7; |
2196 | 2.35k | if (bytes_per_row) |
2197 | 2.35k | bytes_per_row >>= 3; |
2198 | 2.35k | } |
2199 | 4.20k | else |
2200 | 4.20k | { |
2201 | 4.20k | bytes_per_row=MagickArraySize((((size_t) bits_per_sample+7)/8)* |
2202 | 4.20k | samples_per_pixel,image->columns); |
2203 | 4.20k | } |
2204 | | |
2205 | 6.55k | if (image->logging) |
2206 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2207 | 0 | "Bytes/Row: %" MAGICK_SIZE_T_F "u", |
2208 | 0 | (MAGICK_SIZE_T) bytes_per_row); |
2209 | | |
2210 | 6.55k | ExportPixelAreaOptionsInit(&export_options); |
2211 | 6.55k | export_options.grayscale_miniswhite=grayscale_miniswhite; |
2212 | | |
2213 | | /* |
2214 | | Allocate memory for pixels. |
2215 | | */ |
2216 | 6.55k | pixels=MagickAllocateResourceLimitedMemory(unsigned char *,bytes_per_row); |
2217 | 6.55k | if (pixels == (unsigned char *) NULL) |
2218 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
2219 | 6.55k | image); |
2220 | | |
2221 | | /* |
2222 | | Output header details |
2223 | | */ |
2224 | 6.55k | if (PAM_Format == format) |
2225 | 1.38k | { |
2226 | | /* |
2227 | | PAM header |
2228 | | */ |
2229 | 1.38k | const char *tuple_type=NULL; |
2230 | | |
2231 | 1.38k | if (GrayQuantum == quantum_type) |
2232 | 477 | { |
2233 | 477 | if (1 == bits_per_sample) |
2234 | 344 | tuple_type="BLACKANDWHITE"; |
2235 | 133 | else |
2236 | 133 | tuple_type="GRAYSCALE"; |
2237 | 477 | } |
2238 | 912 | else if (GrayAlphaQuantum == quantum_type) |
2239 | 165 | { |
2240 | 165 | if (1 == bits_per_sample) |
2241 | 84 | tuple_type="BLACKANDWHITE_ALPHA"; |
2242 | 81 | else |
2243 | 81 | tuple_type="GRAYSCALE_ALPHA"; |
2244 | 165 | } |
2245 | 747 | else if (RGBQuantum == quantum_type) |
2246 | 306 | tuple_type="RGB"; |
2247 | 441 | else if (RGBAQuantum == quantum_type) |
2248 | 49 | tuple_type="RGB_ALPHA"; |
2249 | 392 | else if (CMYKQuantum == quantum_type) |
2250 | 225 | tuple_type="CMYK"; |
2251 | 167 | else if (CMYKAQuantum == quantum_type) |
2252 | 167 | tuple_type="CMYK_ALPHA"; |
2253 | | |
2254 | 1.38k | MagickFormatString(buffer,sizeof(buffer),"WIDTH %lu\nHEIGHT %lu\nDEPTH %u" |
2255 | 1.38k | "\nMAXVAL %lu\nTUPLTYPE %s\n", |
2256 | 1.38k | image->columns,image->rows,samples_per_pixel, |
2257 | 1.38k | MaxValueGivenBits(bits_per_sample),tuple_type); |
2258 | 1.38k | if (image->logging) |
2259 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2260 | 0 | "PAM Header: WIDTH %lu, HEIGHT %lu, " |
2261 | 0 | "DEPTH %u, MAXVAL %lu, TUPLTYPE %s", |
2262 | 0 | image->columns, |
2263 | 0 | image->rows,samples_per_pixel, |
2264 | 0 | MaxValueGivenBits(bits_per_sample), |
2265 | 0 | tuple_type); |
2266 | 1.38k | WriteBlobString(image,buffer); |
2267 | | |
2268 | 1.38k | (void) WriteBlobString(image,"ENDHDR\n"); |
2269 | 1.38k | } |
2270 | 5.16k | else if ((PGM_RAW_Format == format) || (PPM_RAW_Format == format)) |
2271 | 3.24k | { |
2272 | | /* |
2273 | | PGM, PPM header |
2274 | | */ |
2275 | 3.24k | MagickFormatString(buffer,sizeof(buffer),"%lu\n",MaxValueGivenBits(bits_per_sample)); |
2276 | 3.24k | WriteBlobString(image,buffer); |
2277 | 3.24k | } |
2278 | | |
2279 | | /* |
2280 | | Output pixels |
2281 | | */ |
2282 | 428k | for (y=0; y < image->rows; y++) |
2283 | 421k | { |
2284 | 421k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2285 | 421k | if (p == (const PixelPacket *) NULL) |
2286 | 0 | break; |
2287 | 421k | if (ExportImagePixelArea(image,quantum_type,bits_per_sample,pixels,&export_options,0) == MagickFail) |
2288 | 0 | break; |
2289 | 421k | if (WriteBlob(image,bytes_per_row,(char *) pixels) != bytes_per_row) |
2290 | 182 | break; |
2291 | 421k | if (image->previous == (Image *) NULL) |
2292 | 421k | if (QuantumTick(y,image->rows)) |
2293 | 117k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2294 | 117k | SaveImageText,image->filename, |
2295 | 117k | image->columns,image->rows)) |
2296 | 0 | break; |
2297 | 421k | } |
2298 | 6.55k | MagickFreeResourceLimitedMemory(unsigned char *,pixels); |
2299 | | |
2300 | 6.55k | break; |
2301 | 6.55k | } |
2302 | 1.05k | case XV_332_Format: |
2303 | 1.05k | { |
2304 | 1.05k | static const short int |
2305 | 1.05k | dither_red[2][16]= |
2306 | 1.05k | { |
2307 | 1.05k | {-16, 4, -1, 11,-14, 6, -3, 9,-15, 5, -2, 10,-13, 7, -4, 8}, |
2308 | 1.05k | { 15, -5, 0,-12, 13, -7, 2,-10, 14, -6, 1,-11, 12, -8, 3, -9} |
2309 | 1.05k | }, |
2310 | 1.05k | dither_green[2][16]= |
2311 | 1.05k | { |
2312 | 1.05k | { 11,-15, 7, -3, 8,-14, 4, -2, 10,-16, 6, -4, 9,-13, 5, -1}, |
2313 | 1.05k | {-12, 14, -8, 2, -9, 13, -5, 1,-11, 15, -7, 3,-10, 12, -6, 0} |
2314 | 1.05k | }, |
2315 | 1.05k | dither_blue[2][16]= |
2316 | 1.05k | { |
2317 | 1.05k | { -3, 9,-13, 7, -1, 11,-15, 5, -4, 8,-14, 6, -2, 10,-16, 4}, |
2318 | 1.05k | { 2,-10, 12, -8, 0,-12, 14, -6, 3, -9, 13, -7, 1,-11, 15, -5} |
2319 | 1.05k | }; |
2320 | | |
2321 | 1.05k | long |
2322 | 1.05k | value; |
2323 | | |
2324 | 1.05k | Quantum |
2325 | 1.05k | pixel; |
2326 | | |
2327 | 1.05k | unsigned short |
2328 | 1.05k | *blue_map[2][16], |
2329 | 1.05k | *green_map[2][16], |
2330 | 1.05k | *red_map[2][16]; |
2331 | | |
2332 | 1.05k | unsigned int |
2333 | 1.05k | j; |
2334 | | |
2335 | | /* |
2336 | | Allocate and initialize dither maps. |
2337 | | */ |
2338 | 1.05k | memset(blue_map,0,sizeof(blue_map)); |
2339 | 1.05k | memset(green_map,0,sizeof(green_map)); |
2340 | 1.05k | memset(red_map,0,sizeof(red_map)); |
2341 | 3.16k | for (i=0; i < 2; i++) |
2342 | 35.8k | for (j=0; j < 16; j++) |
2343 | 33.7k | { |
2344 | 33.7k | red_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2345 | 33.7k | 256*sizeof(unsigned short)); |
2346 | 33.7k | green_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2347 | 33.7k | 256*sizeof(unsigned short)); |
2348 | 33.7k | blue_map[i][j]=MagickAllocateResourceLimitedMemory(unsigned short *, |
2349 | 33.7k | 256*sizeof(unsigned short)); |
2350 | 33.7k | if ((red_map[i][j] == (unsigned short *) NULL) || |
2351 | 33.7k | (green_map[i][j] == (unsigned short *) NULL) || |
2352 | 33.7k | (blue_map[i][j] == (unsigned short *) NULL)) |
2353 | 0 | { |
2354 | 0 | for (i=0; i < 2; i++) |
2355 | 0 | for (j=0; j < 16; j++) |
2356 | 0 | { |
2357 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,green_map[i][j]); |
2358 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,blue_map[i][j]); |
2359 | 0 | MagickFreeResourceLimitedMemory(unsigned short *,red_map[i][j]); |
2360 | 0 | } |
2361 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed, |
2362 | 0 | image); |
2363 | 0 | } |
2364 | 33.7k | } |
2365 | | /* |
2366 | | Initialize dither tables. |
2367 | | */ |
2368 | 3.16k | for (i=0; i < 2; i++) |
2369 | 35.8k | for (j=0; j < 16; j++) |
2370 | 8.67M | for (x=0; x < 256; x++) |
2371 | 8.64M | { |
2372 | 8.64M | value=x-16; |
2373 | 8.64M | if (x < 48) |
2374 | 1.62M | value=x/2+8; |
2375 | 8.64M | value+=dither_red[i][j]; |
2376 | 8.64M | red_map[i][j][x]=(unsigned short) |
2377 | 8.64M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2378 | 8.64M | value=x-16; |
2379 | 8.64M | if (x < 48) |
2380 | 1.62M | value=x/2+8; |
2381 | 8.64M | value+=dither_green[i][j]; |
2382 | 8.64M | green_map[i][j][x]=(unsigned short) |
2383 | 8.64M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2384 | 8.64M | value=x-32; |
2385 | 8.64M | if (x < 112) |
2386 | 3.78M | value=x/2+24; |
2387 | 8.64M | value+=2*dither_blue[i][j]; |
2388 | 8.64M | blue_map[i][j][x]=(unsigned short) |
2389 | 8.64M | ((value < 0) ? 0 : (value > 255) ? 255 : value); |
2390 | 8.64M | } |
2391 | | /* |
2392 | | Write pixels. |
2393 | | */ |
2394 | 1.05k | (void) WriteBlobString(image,"#END_OF_COMMENTS\n"); |
2395 | 1.05k | MagickFormatString(buffer,sizeof(buffer),"%lu %lu 255\n",image->columns,image->rows); |
2396 | 1.05k | (void) WriteBlobString(image,buffer); |
2397 | 1.05k | i=0; |
2398 | 1.05k | j=0; |
2399 | 102k | for (y=0; y < image->rows; y++) |
2400 | 101k | { |
2401 | 101k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
2402 | 101k | if (p == (const PixelPacket *) NULL) |
2403 | 0 | break; |
2404 | 60.2M | for (x=0; x < image->columns; x++) |
2405 | 60.0M | { |
2406 | 60.0M | if (!image_info->dither) |
2407 | 0 | pixel=(Quantum) ((ScaleQuantumToChar(p->red) & 0xe0) | |
2408 | 0 | ((ScaleQuantumToChar(p->green) & 0xe0) >> 3) | |
2409 | 0 | ((ScaleQuantumToChar(p->blue) & 0xc0) >> 6)); |
2410 | 60.0M | else |
2411 | 60.0M | pixel=(Quantum) |
2412 | 60.0M | ((red_map[i][j][ScaleQuantumToChar(p->red)] & 0xe0) | |
2413 | 60.0M | ((green_map[i][j][ScaleQuantumToChar(p->green)] & 0xe0) >> 3) | |
2414 | 60.0M | ((blue_map[i][j][ScaleQuantumToChar(p->blue)] & 0xc0) >> 6)); |
2415 | 60.0M | (void) WriteBlobByte(image,pixel); |
2416 | 60.0M | p++; |
2417 | 60.0M | j++; |
2418 | 60.0M | if (j == 16) |
2419 | 3.75M | j=0; |
2420 | 60.0M | } |
2421 | 101k | i++; |
2422 | 101k | if (i == 2) |
2423 | 50.5k | i=0; |
2424 | 101k | if (QuantumTick(y,image->rows)) |
2425 | 21.8k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
2426 | 21.8k | SaveImageText,image->filename, |
2427 | 21.8k | image->columns,image->rows)) |
2428 | 0 | break; |
2429 | 101k | } |
2430 | | /* |
2431 | | Free allocated memory. |
2432 | | */ |
2433 | 3.16k | for (i=0; i < 2; i++) |
2434 | 35.8k | for (j=0; j < 16; j++) |
2435 | 33.7k | { |
2436 | 33.7k | MagickFreeResourceLimitedMemory(unsigned short *,green_map[i][j]); |
2437 | 33.7k | MagickFreeResourceLimitedMemory(unsigned short *,blue_map[i][j]); |
2438 | 33.7k | MagickFreeResourceLimitedMemory(unsigned short *,red_map[i][j]); |
2439 | 33.7k | } |
2440 | 1.05k | break; |
2441 | 1.05k | } |
2442 | 0 | case Undefined_PNM_Format: |
2443 | 0 | break; |
2444 | 7.60k | } |
2445 | 7.60k | if (image->next == (Image *) NULL) |
2446 | 7.60k | break; |
2447 | 0 | image=SyncNextImageInList(image); |
2448 | 0 | if (status != MagickFail) |
2449 | 0 | status=MagickMonitorFormatted(scene++,image_list_length, |
2450 | 0 | &image->exception,SaveImagesText, |
2451 | 0 | image->filename); |
2452 | 0 | if (status == MagickFail) |
2453 | 0 | break; |
2454 | 0 | } while (image_info->adjoin); |
2455 | 7.60k | if (image_info->adjoin) |
2456 | 7.60k | while (image->previous != (Image *) NULL) |
2457 | 0 | image=image->previous; |
2458 | 7.60k | status &= CloseBlob(image); |
2459 | 7.60k | return(status); |
2460 | 7.60k | } |