/src/graphicsmagick/coders/pcx.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | % Copyright (C) 2003 - 2020 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 CCCC X X % |
15 | | % P P C X X % |
16 | | % PPPP C X % |
17 | | % P C X X % |
18 | | % P CCCC X X % |
19 | | % % |
20 | | % % |
21 | | % Read/Write ZSoft IBM PC Paintbrush 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/blob.h" |
41 | | #include "magick/colormap.h" |
42 | | #include "magick/log.h" |
43 | | #include "magick/magick.h" |
44 | | #include "magick/monitor.h" |
45 | | #include "magick/pixel_cache.h" |
46 | | #include "magick/utility.h" |
47 | | |
48 | | /* |
49 | | Typedef declarations. |
50 | | */ |
51 | | typedef struct _PCXInfo |
52 | | { |
53 | | unsigned char |
54 | | identifier, |
55 | | version, |
56 | | encoding, |
57 | | bits_per_pixel; |
58 | | |
59 | | unsigned short |
60 | | left, |
61 | | top, |
62 | | right, |
63 | | bottom, |
64 | | horizontal_resolution, |
65 | | vertical_resolution; |
66 | | |
67 | | unsigned char |
68 | | reserved, |
69 | | planes; |
70 | | |
71 | | unsigned short |
72 | | bytes_per_line, |
73 | | palette_info, |
74 | | horizontal_screen_size, |
75 | | vertical_screen_size; |
76 | | |
77 | | unsigned char |
78 | | colormap_signature; |
79 | | } PCXInfo; |
80 | | |
81 | | /* |
82 | | Forward declarations. |
83 | | */ |
84 | | static unsigned int |
85 | | WritePCXImage(const ImageInfo *,Image *); |
86 | | |
87 | | /* |
88 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
89 | | % % |
90 | | % % |
91 | | % % |
92 | | % I s D C X % |
93 | | % % |
94 | | % % |
95 | | % % |
96 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
97 | | % |
98 | | % Method IsDCX returns True if the image format type, identified by the |
99 | | % magick string, is DCX. |
100 | | % |
101 | | % The format of the IsDCX method is: |
102 | | % |
103 | | % unsigned int IsDCX(const unsigned char *magick,const size_t length) |
104 | | % |
105 | | % A description of each parameter follows: |
106 | | % |
107 | | % o status: Method IsDCX returns True if the image format type is DCX. |
108 | | % |
109 | | % o magick: This string is generally the first few bytes of an image file |
110 | | % or blob. |
111 | | % |
112 | | % o length: Specifies the length of the magick string. |
113 | | % |
114 | | % |
115 | | */ |
116 | | static unsigned int IsDCX(const unsigned char *magick,const size_t length) |
117 | 0 | { |
118 | 0 | if (length < 4) |
119 | 0 | return(False); |
120 | 0 | if (memcmp(magick,"\261\150\336\72",4) == 0) |
121 | 0 | return(True); |
122 | 0 | return(False); |
123 | 0 | } |
124 | | |
125 | | /* |
126 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
127 | | % % |
128 | | % % |
129 | | % % |
130 | | % I s P C X % |
131 | | % % |
132 | | % % |
133 | | % % |
134 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
135 | | % |
136 | | % Method IsPCX returns True if the image format type, identified by the |
137 | | % magick string, is PCX. |
138 | | % |
139 | | % The format of the IsPCX method is: |
140 | | % |
141 | | % unsigned int IsPCX(const unsigned char *magick,const size_t length) |
142 | | % |
143 | | % A description of each parameter follows: |
144 | | % |
145 | | % o status: Method IsPCX returns True if the image format type is PCX. |
146 | | % |
147 | | % o magick: This string is generally the first few bytes of an image file |
148 | | % or blob. |
149 | | % |
150 | | % o length: Specifies the length of the magick string. |
151 | | % |
152 | | % |
153 | | */ |
154 | | static unsigned int IsPCX(const unsigned char *magick,const size_t length) |
155 | 0 | { |
156 | 0 | if (length < 2) |
157 | 0 | return(False); |
158 | 0 | if (memcmp(magick,"\012\002",2) == 0) |
159 | 0 | return(True); |
160 | 0 | if (memcmp(magick,"\012\005",2) == 0) |
161 | 0 | return(True); |
162 | 0 | return(False); |
163 | 0 | } |
164 | | |
165 | | /* |
166 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
167 | | % % |
168 | | % % |
169 | | % % |
170 | | % R e a d P C X I m a g e % |
171 | | % % |
172 | | % % |
173 | | % % |
174 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
175 | | % |
176 | | % Method ReadPCXImage reads a ZSoft IBM PC Paintbrush file and returns it. |
177 | | % It allocates the memory necessary for the new Image structure and returns |
178 | | % a pointer to the new image. |
179 | | % |
180 | | % The format of the ReadPCXImage method is: |
181 | | % |
182 | | % Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception) |
183 | | % |
184 | | % A description of each parameter follows: |
185 | | % |
186 | | % o image: Method ReadPCXImage returns a pointer to the image after |
187 | | % reading. A null image is returned if there is a memory shortage or |
188 | | % if the image cannot be read. |
189 | | % |
190 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
191 | | % |
192 | | % o exception: return any errors or warnings in this structure. |
193 | | % |
194 | | % |
195 | | */ |
196 | 2.47k | #define ThrowPCXReaderException(code_,reason_,image_) \ |
197 | 2.47k | { \ |
198 | 2.47k | MagickFreeResourceLimitedMemory(page_table) \ |
199 | 2.47k | MagickFreeResourceLimitedMemory(pcx_pixels); \ |
200 | 2.47k | MagickFreeResourceLimitedMemory(scanline); \ |
201 | 2.47k | ThrowReaderException(code_,reason_,image_); \ |
202 | 0 | } |
203 | | static Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception) |
204 | 3.88k | { |
205 | 3.88k | Image |
206 | 3.88k | *image; |
207 | | |
208 | 3.88k | ExtendedSignedIntegralType |
209 | 3.88k | *page_table = (ExtendedSignedIntegralType *) NULL; |
210 | | |
211 | 3.88k | int |
212 | 3.88k | c, |
213 | 3.88k | bits, |
214 | 3.88k | id, |
215 | 3.88k | mask; |
216 | | |
217 | 3.88k | long |
218 | 3.88k | y; |
219 | | |
220 | 3.88k | PCXInfo |
221 | 3.88k | pcx_info; |
222 | | |
223 | 3.88k | register IndexPacket |
224 | 3.88k | index, |
225 | 3.88k | *indexes; |
226 | | |
227 | 3.88k | register long |
228 | 3.88k | x; |
229 | | |
230 | 3.88k | register PixelPacket |
231 | 3.88k | *q; |
232 | | |
233 | 3.88k | register unsigned int |
234 | 3.88k | i; |
235 | | |
236 | 3.88k | register unsigned char |
237 | 3.88k | *p, |
238 | 3.88k | *r; |
239 | | |
240 | 3.88k | size_t |
241 | 3.88k | count; |
242 | | |
243 | 3.88k | unsigned char |
244 | 3.88k | packet, |
245 | 3.88k | pcx_colormap[256*3], |
246 | 3.88k | *pcx_pixels = (unsigned char *) NULL, |
247 | 3.88k | *scanline = (unsigned char *) NULL; |
248 | | |
249 | 3.88k | unsigned int |
250 | 3.88k | status; |
251 | | |
252 | 3.88k | size_t |
253 | 3.88k | scanline_size, |
254 | 3.88k | pcx_packets; |
255 | | |
256 | 3.88k | magick_off_t |
257 | 3.88k | file_size; |
258 | | |
259 | | /* |
260 | | Open image file. |
261 | | */ |
262 | 3.88k | assert(image_info != (const ImageInfo *) NULL); |
263 | 3.88k | assert(image_info->signature == MagickSignature); |
264 | 3.88k | assert(exception != (ExceptionInfo *) NULL); |
265 | 3.88k | assert(exception->signature == MagickSignature); |
266 | 3.88k | image=AllocateImage(image_info); |
267 | 3.88k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
268 | 3.88k | if (status == False) |
269 | 3.88k | ThrowPCXReaderException(FileOpenError,UnableToOpenFile,image); |
270 | | /* |
271 | | Determine if this is a PCX file. |
272 | | */ |
273 | 3.88k | page_table=(ExtendedSignedIntegralType *) NULL; |
274 | 3.88k | if (LocaleCompare(image_info->magick,"DCX") == 0) |
275 | 1.02k | { |
276 | 1.02k | unsigned long |
277 | 1.02k | magic; |
278 | | |
279 | | /* |
280 | | Read the DCX page table. |
281 | | */ |
282 | 1.02k | magic=ReadBlobLSBLong(image); |
283 | 1.02k | if (magic != 987654321) |
284 | 963 | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
285 | 963 | page_table=MagickAllocateResourceLimitedArray(ExtendedSignedIntegralType *, |
286 | 963 | 1024,sizeof(ExtendedSignedIntegralType)); |
287 | 963 | if (page_table == (ExtendedSignedIntegralType *) NULL) |
288 | 963 | ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
289 | 141k | for (id=0; id < 1024; id++) |
290 | 141k | { |
291 | 141k | page_table[id]=(ExtendedSignedIntegralType) ReadBlobLSBLong(image); |
292 | 141k | if (page_table[id] == 0) |
293 | 861 | break; |
294 | 141k | } |
295 | 963 | } |
296 | 3.81k | if (page_table != (ExtendedSignedIntegralType *) NULL) |
297 | 963 | if (SeekBlob(image,(ExtendedSignedIntegralType) page_table[0],SEEK_SET) |
298 | 963 | == -1) |
299 | 3.81k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
300 | 3.81k | file_size=GetBlobSize(image); |
301 | 3.81k | count=ReadBlob(image,1,(char *) &pcx_info.identifier); |
302 | 3.81k | for (id=1; id < 1024; id++) |
303 | 3.81k | { |
304 | 3.81k | MagickBool |
305 | 3.81k | read_header_ok = MagickFalse; |
306 | | |
307 | | /* |
308 | | Verify PCX identifier. |
309 | | */ |
310 | 3.81k | do |
311 | 3.81k | { |
312 | 3.81k | if ((c = ReadBlobByte(image)) == EOF) |
313 | 72 | break; |
314 | 3.74k | pcx_info.version=c; |
315 | 3.74k | if ((count != 1) || (pcx_info.identifier != 0x0aU)) |
316 | 3.71k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
317 | 3.71k | if ((c = ReadBlobByte(image)) == EOF) |
318 | 4 | break; |
319 | 3.71k | pcx_info.encoding=c; |
320 | 3.71k | if ((c = ReadBlobByte(image)) == EOF) |
321 | 664 | break; |
322 | 3.04k | pcx_info.bits_per_pixel=c; |
323 | 3.04k | pcx_info.left=ReadBlobLSBShort(image); |
324 | 3.04k | pcx_info.top=ReadBlobLSBShort(image); |
325 | 3.04k | pcx_info.right=ReadBlobLSBShort(image); |
326 | 3.04k | pcx_info.bottom=ReadBlobLSBShort(image); |
327 | 3.04k | pcx_info.horizontal_resolution=ReadBlobLSBShort(image); |
328 | 3.04k | pcx_info.vertical_resolution=ReadBlobLSBShort(image); |
329 | 3.04k | (void) memset(pcx_colormap,0,sizeof(pcx_colormap)); |
330 | 3.04k | if (ReadBlob(image,3*16,(char *) pcx_colormap) != 3*16) |
331 | 396 | break; |
332 | 2.65k | if ((c = ReadBlobByte(image)) == EOF) |
333 | 2 | break; |
334 | 2.64k | pcx_info.reserved=c; |
335 | 2.64k | if ((c = ReadBlobByte(image)) == EOF) |
336 | 18 | break; |
337 | 2.63k | pcx_info.planes=c; |
338 | 2.63k | pcx_info.bytes_per_line=ReadBlobLSBShort(image); |
339 | 2.63k | pcx_info.palette_info=ReadBlobLSBShort(image); |
340 | 2.63k | pcx_info.horizontal_screen_size=ReadBlobLSBShort(image); |
341 | 2.63k | pcx_info.vertical_screen_size=ReadBlobLSBShort(image); |
342 | 2.63k | if (EOFBlob(image)) |
343 | 23 | break; |
344 | 2.60k | read_header_ok=MagickTrue; |
345 | 2.60k | } while (0); |
346 | | |
347 | 3.78k | if (!read_header_ok) |
348 | 2.60k | ThrowPCXReaderException(CorruptImageError,UnexpectedEndOfFile,image); |
349 | | |
350 | 2.60k | if (image->logging) |
351 | 2.60k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
352 | 2.60k | "PCX Header (%d):\n" |
353 | 2.60k | " version=%u\n" |
354 | 2.60k | " encoding=%u\n" |
355 | 2.60k | " bits_per_pixel=%u\n" |
356 | 2.60k | " left=%u\n" |
357 | 2.60k | " top=%u\n" |
358 | 2.60k | " right=%u\n" |
359 | 2.60k | " bottom=%u\n" |
360 | 2.60k | " horizontal_resolution=%u\n" |
361 | 2.60k | " vertical_resolution=%u\n" |
362 | 2.60k | " reserved=%u\n" |
363 | 2.60k | " planes=%u\n" |
364 | 2.60k | " bytes_per_line=%u\n" |
365 | 2.60k | " palette_info=%u\n" |
366 | 2.60k | " horizontal_screen_size=%u\n" |
367 | 2.60k | " vertical_screen_size=%u", |
368 | 2.60k | id, |
369 | 2.60k | (unsigned int) pcx_info.version, |
370 | 2.60k | (unsigned int) pcx_info.encoding, |
371 | 2.60k | (unsigned int) pcx_info.bits_per_pixel, |
372 | 2.60k | (unsigned int) pcx_info.left, |
373 | 2.60k | (unsigned int) pcx_info.top, |
374 | 2.60k | (unsigned int) pcx_info.right, |
375 | 2.60k | (unsigned int) pcx_info.bottom, |
376 | 2.60k | (unsigned int) pcx_info.horizontal_resolution, |
377 | 2.60k | (unsigned int) pcx_info.vertical_resolution, |
378 | 2.60k | (unsigned int) pcx_info.reserved, |
379 | 2.60k | (unsigned int) pcx_info.planes, |
380 | 2.60k | (unsigned int) pcx_info.bytes_per_line, |
381 | 2.60k | (unsigned int) pcx_info.palette_info, |
382 | 2.60k | (unsigned int) pcx_info.horizontal_screen_size, |
383 | 2.60k | (unsigned int) pcx_info.vertical_screen_size |
384 | 2.60k | ); |
385 | | |
386 | | /* |
387 | | Read PCX raster colormap. |
388 | | */ |
389 | 2.60k | image->columns=(pcx_info.right-pcx_info.left)+1; |
390 | 2.60k | image->rows=(pcx_info.bottom-pcx_info.top)+1; |
391 | 2.60k | image->depth=8; /* or pcx_info.bits_per_pixel */ |
392 | 2.60k | image->units=PixelsPerInchResolution; |
393 | 2.60k | image->x_resolution=pcx_info.horizontal_resolution; |
394 | 2.60k | image->y_resolution=pcx_info.vertical_resolution; |
395 | 2.60k | image->colors=16; |
396 | | |
397 | | /* |
398 | | Validate rows, columns, bits |
399 | | */ |
400 | 2.60k | if ((pcx_info.right < pcx_info.left) || |
401 | 2.60k | (pcx_info.bottom < pcx_info.top) || |
402 | 2.60k | (image->columns == 0) || |
403 | 2.60k | (image->rows == 0) || |
404 | 2.60k | ((pcx_info.bits_per_pixel != 1) && |
405 | 2.58k | (pcx_info.bits_per_pixel != 2) && |
406 | 2.58k | (pcx_info.bits_per_pixel != 4) && |
407 | 2.58k | (pcx_info.bits_per_pixel != 8))) |
408 | 2.56k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
409 | | |
410 | | /* |
411 | | Validate bytes per line. It is ok to have more bytes per line |
412 | | than the total bits suggest, but not less. |
413 | | */ |
414 | 2.56k | { |
415 | 2.56k | size_t bytes_per_line; |
416 | | |
417 | 2.56k | bytes_per_line = MagickArraySize(image->columns,pcx_info.bits_per_pixel); |
418 | 2.56k | if (bytes_per_line) |
419 | 2.56k | bytes_per_line += 7U; |
420 | 2.56k | if (bytes_per_line) |
421 | 2.56k | bytes_per_line /= 8U; |
422 | 2.56k | if (image->logging) |
423 | 2.56k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
424 | 2.56k | "Bytes per line: reqire >= %" MAGICK_SIZE_T_F "u, have %u", |
425 | 2.56k | (MAGICK_SIZE_T) bytes_per_line, pcx_info.bytes_per_line); |
426 | 2.56k | if ((bytes_per_line == 0) || (pcx_info.bytes_per_line < bytes_per_line)) |
427 | 2.20k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
428 | 2.20k | } |
429 | | /* |
430 | | Validate number of planes. We only support 1, 2, 3, 4 but some |
431 | | files might have extra planes (which we ignore). |
432 | | */ |
433 | 2.20k | if (pcx_info.planes == 0) |
434 | 1.90k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
435 | 1.90k | if (pcx_info.planes > 6) |
436 | 1.86k | ThrowPCXReaderException(CorruptImageError,UnsupportedNumberOfPlanes,image); |
437 | | |
438 | 1.86k | if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1)) |
439 | 313 | { |
440 | 313 | image->storage_class=DirectClass; |
441 | 313 | if (image->logging) |
442 | 313 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
443 | 313 | "DirectClass image"); |
444 | 313 | } |
445 | 1.55k | else |
446 | 1.55k | { |
447 | 1.55k | if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1)) |
448 | 1.55k | if ((pcx_info.version == 3) || (pcx_info.version == 5) || |
449 | 1.55k | ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)) |
450 | 762 | { |
451 | 762 | image->colors=1 << (pcx_info.bits_per_pixel*pcx_info.planes); |
452 | 762 | if (image->colors > 256) |
453 | 14 | image->colors = 256; |
454 | 762 | } |
455 | | |
456 | 1.55k | if (!AllocateImageColormap(image,image->colors)) |
457 | 1.55k | ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
458 | | |
459 | | /* |
460 | | 256 color images have their color map at the end of the file. |
461 | | Colormap for 1 bit/pixel images is explicitly initialized. |
462 | | */ |
463 | 1.55k | if (image->colors <= 16) |
464 | 1.28k | { |
465 | 1.28k | p=pcx_colormap; |
466 | 15.6k | for (i=0; i < image->colors; i++) |
467 | 14.3k | { |
468 | 14.3k | image->colormap[i].red=ScaleCharToQuantum(*p++); |
469 | 14.3k | image->colormap[i].green=ScaleCharToQuantum(*p++); |
470 | 14.3k | image->colormap[i].blue=ScaleCharToQuantum(*p++); |
471 | 14.3k | image->colormap[i].opacity=OpaqueOpacity; |
472 | 14.3k | } |
473 | 1.28k | } |
474 | 1.55k | if (image->logging) |
475 | 1.55k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
476 | 1.55k | "PseudoClass image with %u colors", image->colors); |
477 | 1.55k | } |
478 | | |
479 | 102k | for (i=0; i < 54; i++) |
480 | 100k | (void) ReadBlobByte(image); |
481 | 1.86k | if (image_info->ping && (image_info->subrange != 0)) |
482 | 0 | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
483 | 0 | break; |
484 | | |
485 | 1.86k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
486 | 1.82k | ThrowPCXReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
487 | | |
488 | | |
489 | | /* |
490 | | Check that filesize is reasonable given header |
491 | | */ |
492 | 1.82k | { |
493 | 1.82k | double |
494 | 1.82k | uncompressed_size; |
495 | | |
496 | 1.82k | uncompressed_size=((double) image->rows*pcx_info.bytes_per_line*pcx_info.planes); |
497 | 1.82k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
498 | 1.82k | "Uncompressed size: %.0f", uncompressed_size); |
499 | 1.82k | if (pcx_info.encoding == 0) |
500 | 183 | { |
501 | | /* Not compressed */ |
502 | 183 | if (uncompressed_size > file_size) |
503 | 5 | ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile, |
504 | 183 | image); |
505 | 178 | } |
506 | 1.64k | else |
507 | 1.64k | { |
508 | | /* RLE compressed */ |
509 | 1.64k | if (uncompressed_size > file_size*254.0) |
510 | 36 | ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile, |
511 | 1.64k | image); |
512 | 1.60k | } |
513 | 1.82k | } |
514 | | |
515 | | |
516 | | /* |
517 | | Read image data. |
518 | | */ |
519 | 1.78k | pcx_packets=MagickArraySize(image->rows, |
520 | 1.78k | MagickArraySize(pcx_info.bytes_per_line, |
521 | 1.78k | pcx_info.planes)); |
522 | 1.78k | if ((0 == pcx_packets) || |
523 | 1.78k | (((size_t) pcx_info.bits_per_pixel*pcx_info.planes*image->columns) > |
524 | 1.78k | ((size_t) pcx_packets*8U))) |
525 | 1.78k | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
526 | 1.78k | pcx_pixels=MagickAllocateResourceLimitedMemory(unsigned char *,pcx_packets); |
527 | 1.78k | if (pcx_pixels == (unsigned char *) NULL) |
528 | 1.78k | ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
529 | 1.78k | scanline_size=MagickArraySize(Max(image->columns, |
530 | 1.78k | (size_t) pcx_info.bytes_per_line), |
531 | 1.78k | Max(pcx_info.planes,8)); |
532 | 1.78k | scanline=MagickAllocateResourceLimitedMemory(unsigned char *,scanline_size); |
533 | 1.78k | if (scanline == (unsigned char *) NULL) |
534 | 1.78k | ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
535 | 1.78k | (void) memset(scanline,0,scanline_size); |
536 | 1.78k | if (pcx_info.encoding == 0) |
537 | 178 | { |
538 | | /* |
539 | | Data is not compressed |
540 | | */ |
541 | 178 | if (ReadBlob(image,pcx_packets,pcx_pixels) != pcx_packets) |
542 | 127 | ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image); |
543 | 51 | } |
544 | 1.60k | else |
545 | 1.60k | { |
546 | | /* |
547 | | Uncompress image data. |
548 | | */ |
549 | 1.60k | p=pcx_pixels; |
550 | 95.2M | while (pcx_packets != 0) |
551 | 95.2M | { |
552 | 95.2M | packet=ReadBlobByte(image); |
553 | 95.2M | if (EOFBlob(image)) |
554 | 95.2M | ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image); |
555 | 95.2M | if ((packet & 0xc0) != 0xc0) |
556 | 88.4M | { |
557 | 88.4M | *p++=packet; |
558 | 88.4M | pcx_packets--; |
559 | 88.4M | continue; |
560 | 88.4M | } |
561 | 6.76M | count=packet & 0x3f; |
562 | 6.76M | packet=ReadBlobByte(image); |
563 | 6.76M | if (EOFBlob(image)) |
564 | 6.76M | ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image); |
565 | 259M | for (; count != 0; count--) |
566 | 253M | { |
567 | 253M | *p++=packet; |
568 | 253M | pcx_packets--; |
569 | 253M | if (pcx_packets == 0) |
570 | 1.23k | break; |
571 | 253M | } |
572 | 6.76M | } |
573 | 1.60k | } |
574 | 1.41k | if (image->storage_class == DirectClass) |
575 | 283 | image->matte=pcx_info.planes > 3; |
576 | 1.12k | else |
577 | 1.12k | if ((pcx_info.version == 5) || |
578 | 1.12k | (((size_t) pcx_info.bits_per_pixel*pcx_info.planes) == 1)) |
579 | 507 | { |
580 | | /* |
581 | | Initialize image colormap. |
582 | | */ |
583 | 507 | if (image->colors > 256) |
584 | 0 | ThrowPCXReaderException(CorruptImageError,ColormapExceedsColorsLimit, |
585 | 507 | image); |
586 | 507 | if (((size_t) pcx_info.bits_per_pixel*pcx_info.planes) == 1) |
587 | 235 | { |
588 | | /* |
589 | | Monochrome colormap. |
590 | | */ |
591 | 235 | image->colormap[0].red=0; |
592 | 235 | image->colormap[0].green=0; |
593 | 235 | image->colormap[0].blue=0; |
594 | 235 | image->colormap[0].opacity=OpaqueOpacity; |
595 | 235 | image->colormap[1].red=MaxRGB; |
596 | 235 | image->colormap[1].green=MaxRGB; |
597 | 235 | image->colormap[1].blue=MaxRGB; |
598 | 235 | image->colormap[1].opacity=OpaqueOpacity; |
599 | 235 | } |
600 | 272 | else |
601 | 272 | if (image->colors > 16) |
602 | 190 | { |
603 | | /* |
604 | | 256 color images have their color map at the end of the file. |
605 | | */ |
606 | 190 | pcx_info.colormap_signature=ReadBlobByte(image); |
607 | 190 | (void) ReadBlob(image, (size_t) 3*image->colors,(char *) pcx_colormap); |
608 | 190 | p=pcx_colormap; |
609 | 19.4k | for (i=0; i < image->colors; i++) |
610 | 19.2k | { |
611 | 19.2k | image->colormap[i].red=ScaleCharToQuantum(*p++); |
612 | 19.2k | image->colormap[i].green=ScaleCharToQuantum(*p++); |
613 | 19.2k | image->colormap[i].blue=ScaleCharToQuantum(*p++); |
614 | 19.2k | image->colormap[i].opacity=OpaqueOpacity; |
615 | 19.2k | } |
616 | 190 | } |
617 | 507 | } |
618 | | /* |
619 | | Convert PCX raster image to pixel packets. |
620 | | */ |
621 | 355k | for (y=0; y < (long) image->rows; y++) |
622 | 354k | { |
623 | 354k | p=pcx_pixels+((size_t) y*pcx_info.bytes_per_line*pcx_info.planes); |
624 | 354k | q=SetImagePixels(image,0,y,image->columns,1); |
625 | 354k | if (q == (PixelPacket *) NULL) |
626 | 0 | break; |
627 | 354k | indexes=AccessMutableIndexes(image); |
628 | 354k | r=scanline; |
629 | 354k | if (image->storage_class == DirectClass) |
630 | 112k | for (i=0; i < (unsigned int) pcx_info.planes; i++) |
631 | 77.0k | { |
632 | 77.0k | r=scanline+i; |
633 | 322k | for (x=0; x < (long) pcx_info.bytes_per_line; x++) |
634 | 245k | { |
635 | 245k | switch (i) |
636 | 245k | { |
637 | 115k | case 0: |
638 | 115k | { |
639 | 115k | *r=ScaleCharToQuantum(*p++); |
640 | 115k | break; |
641 | 0 | } |
642 | 115k | case 1: |
643 | 115k | { |
644 | 115k | *r=ScaleCharToQuantum(*p++); |
645 | 115k | break; |
646 | 0 | } |
647 | 6.79k | case 2: |
648 | 6.79k | { |
649 | 6.79k | *r=ScaleCharToQuantum(*p++); |
650 | 6.79k | break; |
651 | 0 | } |
652 | 6.09k | case 3: |
653 | 8.42k | default: |
654 | 8.42k | { |
655 | 8.42k | *r=ScaleCharToQuantum(*p++); |
656 | 8.42k | break; |
657 | 6.09k | } |
658 | 245k | } |
659 | 245k | r+=(unsigned int) pcx_info.planes; |
660 | 245k | } |
661 | 77.0k | } |
662 | 318k | else |
663 | 318k | if ((unsigned int) pcx_info.planes > 1) |
664 | 108k | { |
665 | 87.1M | for (x=0; x < (long) image->columns; x++) |
666 | 87.0M | *r++=0; |
667 | 649k | for (i=0; i < (unsigned int) pcx_info.planes; i++) |
668 | 540k | { |
669 | 540k | r=scanline; |
670 | 269M | for (x=0; x < (long) pcx_info.bytes_per_line; x++) |
671 | 269M | { |
672 | 269M | bits=(*p++); |
673 | 2.42G | for (mask=0x80; mask != 0; mask>>=1) |
674 | 2.15G | { |
675 | 2.15G | if (bits & mask) |
676 | 922M | *r|=1 << i; |
677 | 2.15G | r++; |
678 | 2.15G | } |
679 | 269M | } |
680 | 540k | } |
681 | 108k | } |
682 | 210k | else |
683 | 210k | switch (pcx_info.bits_per_pixel) |
684 | 210k | { |
685 | 61.6k | case 1: |
686 | 61.6k | { |
687 | 61.6k | register long |
688 | 61.6k | bit; |
689 | | |
690 | 4.57M | for (x=0; x < ((long) image->columns-7); x+=8) |
691 | 4.51M | { |
692 | 40.6M | for (bit=7; bit >= 0; bit--) |
693 | 36.1M | *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00); |
694 | 4.51M | p++; |
695 | 4.51M | } |
696 | 61.6k | if ((image->columns % 8) != 0) |
697 | 34.3k | { |
698 | 168k | for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--) |
699 | 134k | *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00); |
700 | 34.3k | p++; |
701 | 34.3k | } |
702 | 61.6k | break; |
703 | 0 | } |
704 | 119k | case 2: |
705 | 119k | { |
706 | 17.0M | for (x=0; x < ((long) image->columns-3); x+=4) |
707 | 16.9M | { |
708 | 16.9M | *r++=(*p >> 6) & 0x3; |
709 | 16.9M | *r++=(*p >> 4) & 0x3; |
710 | 16.9M | *r++=(*p >> 2) & 0x3; |
711 | 16.9M | *r++=(*p) & 0x3; |
712 | 16.9M | p++; |
713 | 16.9M | } |
714 | 119k | if ((image->columns % 4) != 0) |
715 | 89.2k | { |
716 | 287k | for (i=3; i >= (unsigned int) (4-(image->columns % 4)); i--) |
717 | 197k | *r++=(*p >> (i*2)) & 0x03; |
718 | 89.2k | p++; |
719 | 89.2k | } |
720 | 119k | break; |
721 | 0 | } |
722 | 23.7k | case 4: |
723 | 23.7k | { |
724 | 41.7k | for (x=0; x < ((long) image->columns-1); x+=2) |
725 | 18.0k | { |
726 | 18.0k | *r++=(*p >> 4) & 0xf; |
727 | 18.0k | *r++=(*p) & 0xf; |
728 | 18.0k | p++; |
729 | 18.0k | } |
730 | 23.7k | if ((image->columns % 2) != 0) |
731 | 12.8k | *r++=(*p++ >> 4) & 0xf; |
732 | 23.7k | break; |
733 | 0 | } |
734 | 5.01k | case 8: |
735 | 5.01k | { |
736 | 5.01k | (void) memcpy(r,p,image->columns); |
737 | 5.01k | break; |
738 | 0 | } |
739 | 0 | default: |
740 | 0 | break; |
741 | 210k | } |
742 | | /* |
743 | | Transfer image scanline. |
744 | | */ |
745 | 354k | r=scanline; |
746 | 191M | for (x=0; x < (long) image->columns; x++) |
747 | 191M | { |
748 | 191M | if (image->storage_class == PseudoClass) |
749 | 191M | { |
750 | 191M | index=(*r++); |
751 | 191M | VerifyColormapIndex(image,index); |
752 | 191M | indexes[x]=index; |
753 | 191M | *q=image->colormap[index]; |
754 | 191M | } |
755 | 45.1k | else |
756 | 45.1k | { |
757 | 45.1k | q->red=ScaleCharToQuantum(*r++); |
758 | 45.1k | q->green=ScaleCharToQuantum(*r++); |
759 | 45.1k | q->blue=ScaleCharToQuantum(*r++); |
760 | 45.1k | if (image->matte) |
761 | 4.79k | q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*r++)); |
762 | 40.3k | else |
763 | 40.3k | q->opacity=OpaqueOpacity; |
764 | 45.1k | } |
765 | 191M | q++; |
766 | 191M | } |
767 | 354k | if (!SyncImagePixels(image)) |
768 | 0 | break; |
769 | 354k | if (image->previous == (Image *) NULL) |
770 | 354k | if (QuantumTick(y,image->rows)) |
771 | 74.6k | if (!MagickMonitorFormatted(y,image->rows,exception,LoadImageText, |
772 | 74.6k | image->filename, |
773 | 74.6k | image->columns,image->rows)) |
774 | 0 | break; |
775 | 354k | } |
776 | 1.41k | MagickFreeResourceLimitedMemory(scanline); |
777 | 1.41k | MagickFreeResourceLimitedMemory(pcx_pixels); |
778 | 1.41k | if (EOFBlob(image)) |
779 | 161 | { |
780 | 161 | ThrowException(exception,CorruptImageError,UnexpectedEndOfFile, |
781 | 161 | image->filename); |
782 | 161 | break; |
783 | 161 | } |
784 | 1.24k | StopTimer(&image->timer); |
785 | | /* |
786 | | Proceed to next image. |
787 | | */ |
788 | 1.24k | if (image_info->subrange != 0) |
789 | 1.24k | if (image->scene >= (image_info->subimage+image_info->subrange-1)) |
790 | 1.24k | break; |
791 | 0 | if (page_table == (ExtendedSignedIntegralType *) NULL) |
792 | 0 | break; |
793 | 0 | if (page_table[id] == 0) |
794 | 0 | break; |
795 | 0 | if (SeekBlob(image,(ExtendedSignedIntegralType) page_table[id],SEEK_SET) |
796 | 0 | == -1) |
797 | 0 | ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image); |
798 | 0 | count=ReadBlob(image,1,(char *) &pcx_info.identifier); |
799 | 0 | if ((count != 0) && (pcx_info.identifier == 0x0a)) |
800 | 0 | { |
801 | | /* |
802 | | Allocate next image structure. |
803 | | */ |
804 | 0 | AllocateNextImage(image_info,image); |
805 | 0 | if (image->next == (Image *) NULL) |
806 | 0 | { |
807 | 0 | DestroyImageList(image); |
808 | 0 | return((Image *) NULL); |
809 | 0 | } |
810 | 0 | image=SyncNextImageInList(image); |
811 | 0 | if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception, |
812 | 0 | LoadImagesText,image->filename)) |
813 | 0 | break; |
814 | 0 | } |
815 | 0 | } |
816 | 1.41k | if (page_table != (ExtendedSignedIntegralType *) NULL) |
817 | 569 | MagickFreeResourceLimitedMemory(page_table); |
818 | 1.41k | while (image->previous != (Image *) NULL) |
819 | 0 | image=image->previous; |
820 | 1.41k | CloseBlob(image); |
821 | 1.41k | return(image); |
822 | 3.81k | } |
823 | | |
824 | | /* |
825 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
826 | | % % |
827 | | % % |
828 | | % % |
829 | | % R e g i s t e r P C X I m a g e % |
830 | | % % |
831 | | % % |
832 | | % % |
833 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
834 | | % |
835 | | % Method RegisterPCXImage adds attributes for the PCX image format to |
836 | | % the list of supported formats. The attributes include the image format |
837 | | % tag, a method to read and/or write the format, whether the format |
838 | | % supports the saving of more than one frame to the same file or blob, |
839 | | % whether the format supports native in-memory I/O, and a brief |
840 | | % description of the format. |
841 | | % |
842 | | % The format of the RegisterPCXImage method is: |
843 | | % |
844 | | % RegisterPCXImage(void) |
845 | | % |
846 | | */ |
847 | | ModuleExport void RegisterPCXImage(void) |
848 | 5 | { |
849 | 5 | MagickInfo |
850 | 5 | *entry; |
851 | | |
852 | 5 | entry=SetMagickInfo("DCX"); |
853 | 5 | entry->decoder=(DecoderHandler) ReadPCXImage; |
854 | 5 | entry->encoder=(EncoderHandler) WritePCXImage; |
855 | 5 | entry->seekable_stream=True; |
856 | 5 | entry->magick=(MagickHandler) IsDCX; |
857 | 5 | entry->description="ZSoft IBM PC multi-page Paintbrush"; |
858 | 5 | entry->module="PCX"; |
859 | 5 | (void) RegisterMagickInfo(entry); |
860 | | |
861 | 5 | entry=SetMagickInfo("PCX"); |
862 | 5 | entry->decoder=(DecoderHandler) ReadPCXImage; |
863 | 5 | entry->encoder=(EncoderHandler) WritePCXImage; |
864 | 5 | entry->magick=(MagickHandler) IsPCX; |
865 | 5 | entry->adjoin=False; |
866 | 5 | entry->seekable_stream=True; |
867 | 5 | entry->description="ZSoft IBM PC Paintbrush"; |
868 | 5 | entry->module="PCX"; |
869 | 5 | (void) RegisterMagickInfo(entry); |
870 | 5 | } |
871 | | |
872 | | /* |
873 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
874 | | % % |
875 | | % % |
876 | | % % |
877 | | % U n r e g i s t e r P C X I m a g e % |
878 | | % % |
879 | | % % |
880 | | % % |
881 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
882 | | % |
883 | | % Method UnregisterPCXImage removes format registrations made by the |
884 | | % PCX module from the list of supported formats. |
885 | | % |
886 | | % The format of the UnregisterPCXImage method is: |
887 | | % |
888 | | % UnregisterPCXImage(void) |
889 | | % |
890 | | */ |
891 | | ModuleExport void UnregisterPCXImage(void) |
892 | 0 | { |
893 | 0 | (void) UnregisterMagickInfo("DCX"); |
894 | 0 | (void) UnregisterMagickInfo("PCX"); |
895 | 0 | } |
896 | | |
897 | | /* |
898 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
899 | | % % |
900 | | % % |
901 | | % % |
902 | | % W r i t e P C X I m a g e % |
903 | | % % |
904 | | % % |
905 | | % % |
906 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
907 | | % |
908 | | % Method WritePCXImage writes an image in the ZSoft IBM PC Paintbrush file |
909 | | % format. |
910 | | % |
911 | | % The format of the WritePCXImage method is: |
912 | | % |
913 | | % unsigned int WritePCXImage(const ImageInfo *image_info,Image *image) |
914 | | % |
915 | | % A description of each parameter follows. |
916 | | % |
917 | | % o status: Method WritePCXImage return True if the image is written. |
918 | | % False is returned is there is a memory shortage or if the image file |
919 | | % fails to write. |
920 | | % |
921 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
922 | | % |
923 | | % o image: A pointer to an Image structure. |
924 | | % |
925 | | % |
926 | | */ |
927 | | static MagickPassFail WritePCXPixels(Image *image, |
928 | | PCXInfo *pcx_info, |
929 | | const unsigned char *pcx_row_pixels) |
930 | 248k | { |
931 | 248k | register const unsigned char |
932 | 248k | *q; |
933 | | |
934 | 248k | unsigned char |
935 | 248k | count, |
936 | 248k | packet, |
937 | 248k | previous; |
938 | | |
939 | 248k | register long |
940 | 248k | i, |
941 | 248k | x; |
942 | | |
943 | 248k | q=pcx_row_pixels; |
944 | | |
945 | | /* For each color plane ... */ |
946 | 571k | for (i=0; i < (long) pcx_info->planes; i++) |
947 | 322k | { |
948 | 322k | if (pcx_info->encoding == 0) |
949 | 0 | { |
950 | 0 | for (x=0; x < (long) pcx_info->bytes_per_line; x++) |
951 | 0 | (void) WriteBlobByte(image,(unsigned char) (*q++)); |
952 | 0 | } |
953 | 322k | else |
954 | 322k | { |
955 | 322k | previous=(*q++); |
956 | 322k | count=1; |
957 | | /* For each column ... */ |
958 | 76.3M | for (x=0; x < (long) (pcx_info->bytes_per_line-1); x++) |
959 | 76.0M | { |
960 | 76.0M | packet=(*q++); |
961 | 76.0M | if ((packet == previous) && (count < 63)) |
962 | 36.8M | { |
963 | 36.8M | count++; |
964 | 36.8M | continue; |
965 | 36.8M | } |
966 | 39.2M | if ((count > 1) || ((previous & 0xc0) == 0xc0)) |
967 | 23.3M | { |
968 | 23.3M | count|=0xc0; |
969 | 23.3M | (void) WriteBlobByte(image,count); |
970 | 23.3M | } |
971 | 39.2M | (void) WriteBlobByte(image,previous); |
972 | 39.2M | previous=packet; |
973 | 39.2M | count=1; |
974 | 39.2M | } |
975 | 322k | if ((count > 1) || ((previous & 0xc0) == 0xc0)) |
976 | 195k | { |
977 | 195k | count|=0xc0; |
978 | 195k | (void) WriteBlobByte(image,count); |
979 | 195k | } |
980 | 322k | (void) WriteBlobByte(image,previous); |
981 | 322k | } |
982 | 322k | } |
983 | 248k | return (MagickPass); |
984 | 248k | } |
985 | | |
986 | | #define LiberatePCXAllocations() \ |
987 | 0 | { \ |
988 | 0 | MagickFreeResourceLimitedMemory(pcx_colormap); \ |
989 | 0 | MagickFreeResourceLimitedMemory(pcx_pixels); \ |
990 | 0 | MagickFreeResourceLimitedMemory(page_table); \ |
991 | 0 | } |
992 | | |
993 | 0 | #define ThrowPCXWriterException(code_,reason_,image_) \ |
994 | 0 | { \ |
995 | 0 | LiberatePCXAllocations(); \ |
996 | 0 | ThrowWriterException(code_,reason_,image_); \ |
997 | 0 | } |
998 | | static unsigned int WritePCXImage(const ImageInfo *image_info,Image *image) |
999 | 993 | { |
1000 | 993 | long |
1001 | 993 | y; |
1002 | | |
1003 | 993 | PCXInfo |
1004 | 993 | pcx_info; |
1005 | | |
1006 | 993 | register const PixelPacket |
1007 | 993 | *p; |
1008 | | |
1009 | 993 | register const IndexPacket |
1010 | 993 | *indexes; |
1011 | | |
1012 | 993 | register long |
1013 | 993 | i, |
1014 | 993 | x; |
1015 | | |
1016 | 993 | register unsigned char |
1017 | 993 | *q; |
1018 | | |
1019 | 993 | size_t |
1020 | 993 | bytes_per_line; |
1021 | | |
1022 | 993 | unsigned char |
1023 | 993 | *pcx_colormap = (unsigned char *) NULL, |
1024 | 993 | *pcx_pixels = (unsigned char *) NULL; |
1025 | | |
1026 | 993 | MagickBool |
1027 | 993 | adjoin, |
1028 | 993 | logging, |
1029 | 993 | write_dcx; |
1030 | | |
1031 | 993 | unsigned int |
1032 | 993 | status; |
1033 | | |
1034 | 993 | ExtendedSignedIntegralType |
1035 | 993 | *page_table=(ExtendedSignedIntegralType *) NULL; |
1036 | | |
1037 | 993 | unsigned long |
1038 | 993 | scene; |
1039 | | |
1040 | 993 | const unsigned long |
1041 | 993 | max_scenes = 1024UL; |
1042 | | |
1043 | 993 | ImageCharacteristics |
1044 | 993 | characteristics; |
1045 | | |
1046 | 993 | size_t |
1047 | 993 | image_list_length; |
1048 | | |
1049 | | /* |
1050 | | Open output image file. |
1051 | | */ |
1052 | 993 | assert(image_info != (const ImageInfo *) NULL); |
1053 | 993 | assert(image_info->signature == MagickSignature); |
1054 | 993 | assert(image != (Image *) NULL); |
1055 | 993 | assert(image->signature == MagickSignature); |
1056 | | |
1057 | 993 | image_list_length=GetImageListLength(image); |
1058 | 993 | logging=image->logging; |
1059 | 993 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
1060 | 993 | if (status == False) |
1061 | 993 | ThrowPCXWriterException(FileOpenError,UnableToOpenFile,image); |
1062 | | |
1063 | 993 | write_dcx=MagickFalse; |
1064 | 993 | if (LocaleCompare(image_info->magick,"DCX") == 0) |
1065 | 482 | { |
1066 | | /* |
1067 | | Write the DCX page table. |
1068 | | */ |
1069 | 482 | write_dcx=MagickTrue; |
1070 | 482 | (void) WriteBlobLSBLong(image,0x3ADE68B1L); |
1071 | 482 | page_table=MagickAllocateResourceLimitedClearedArray(ExtendedSignedIntegralType *, |
1072 | 482 | (size_t) max_scenes+1, |
1073 | 482 | sizeof(ExtendedSignedIntegralType)); |
1074 | 482 | if (page_table == (ExtendedSignedIntegralType *) NULL) |
1075 | 482 | ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1076 | 494k | for (scene=0; scene < max_scenes; scene++) |
1077 | 493k | (void) WriteBlobLSBLong(image,0x00000000L); |
1078 | 482 | } |
1079 | 993 | adjoin=(image_info->adjoin) && (image->next != (const Image *) NULL) && (write_dcx); |
1080 | 993 | scene=0; |
1081 | 993 | do |
1082 | 993 | { |
1083 | 993 | if (logging && write_dcx) |
1084 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1085 | 0 | "Writing DCX frame %lu...",scene); |
1086 | | /* |
1087 | | Ensure that image is in RGB space. |
1088 | | */ |
1089 | 993 | (void) TransformColorspace(image,RGBColorspace); |
1090 | | /* |
1091 | | Analyze image to be written. |
1092 | | */ |
1093 | 993 | if (!GetImageCharacteristics(image,&characteristics, |
1094 | 993 | (OptimizeType == image_info->type), |
1095 | 993 | &image->exception)) |
1096 | 0 | { |
1097 | 0 | LiberatePCXAllocations(); |
1098 | 0 | CloseBlob(image); |
1099 | 0 | return MagickFail; |
1100 | 0 | } |
1101 | 993 | if (page_table != (ExtendedSignedIntegralType *) NULL) |
1102 | 482 | page_table[scene]=TellBlob(image); |
1103 | | /* |
1104 | | Initialize PCX raster file header. |
1105 | | */ |
1106 | 993 | pcx_info.identifier=0x0a; |
1107 | 993 | pcx_info.version=5; |
1108 | | /* Please note that uncompressed PCX in quite rare and some applications cannot open it. |
1109 | | So the compressed PCX needs to be default. */ |
1110 | 993 | pcx_info.encoding = (image->compression==RLECompression || image->compression==UndefinedCompression) ? 1 : 0; |
1111 | 993 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1112 | 993 | "Using %s compression", |
1113 | 993 | pcx_info.encoding == 1 ? "RLE" : "No"); |
1114 | 993 | pcx_info.bits_per_pixel=8; |
1115 | 993 | if (characteristics.palette && characteristics.monochrome) |
1116 | 269 | pcx_info.bits_per_pixel=1; |
1117 | 993 | pcx_info.left=0; |
1118 | 993 | pcx_info.top=0; |
1119 | 993 | pcx_info.right=(unsigned short) (image->columns-1); |
1120 | 993 | pcx_info.bottom=(unsigned short) (image->rows-1); |
1121 | 993 | pcx_info.horizontal_resolution=72; |
1122 | 993 | pcx_info.vertical_resolution=72; |
1123 | 993 | switch (image->units) |
1124 | 993 | { |
1125 | 0 | case UndefinedResolution: |
1126 | 993 | case PixelsPerInchResolution: |
1127 | 993 | { |
1128 | 993 | pcx_info.horizontal_resolution=(unsigned short) image->x_resolution; |
1129 | 993 | pcx_info.vertical_resolution=(unsigned short) image->y_resolution; |
1130 | 993 | break; |
1131 | 0 | } |
1132 | 0 | case PixelsPerCentimeterResolution: |
1133 | 0 | { |
1134 | 0 | pcx_info.horizontal_resolution=(unsigned short) (2.54*image->x_resolution+0.5); |
1135 | 0 | pcx_info.vertical_resolution=(unsigned short) (2.54*image->y_resolution+0.5); |
1136 | 0 | break; |
1137 | 0 | } |
1138 | 993 | } |
1139 | 993 | pcx_info.reserved=0; |
1140 | 993 | pcx_info.planes=1; |
1141 | 993 | if (image->storage_class == DirectClass) |
1142 | 153 | { |
1143 | 153 | pcx_info.planes=3; |
1144 | 153 | if (image->matte) |
1145 | 36 | pcx_info.planes++; |
1146 | 153 | } |
1147 | | |
1148 | | /* image->columns*pcx_info.bits_per_pixel+7)/8 */ |
1149 | 993 | bytes_per_line=MagickArraySize(image->columns,pcx_info.bits_per_pixel); |
1150 | 993 | if (bytes_per_line && (~((size_t)0)-7 > bytes_per_line)) |
1151 | 993 | bytes_per_line += 7; |
1152 | 993 | bytes_per_line /= 8; |
1153 | 993 | pcx_info.bytes_per_line=(unsigned short) bytes_per_line; |
1154 | 993 | if ((pcx_info.bytes_per_line == 0) || |
1155 | 993 | ((size_t) pcx_info.bytes_per_line != bytes_per_line)) |
1156 | 993 | ThrowPCXWriterException(CoderError,UnsupportedNumberOfColumns,image); |
1157 | 993 | pcx_info.palette_info=1; |
1158 | 993 | pcx_info.colormap_signature=0x0c; |
1159 | | /* |
1160 | | Write PCX header. |
1161 | | */ |
1162 | 993 | (void) WriteBlobByte(image,pcx_info.identifier); |
1163 | 993 | (void) WriteBlobByte(image,pcx_info.version); |
1164 | 993 | (void) WriteBlobByte(image,pcx_info.encoding); |
1165 | 993 | (void) WriteBlobByte(image,pcx_info.bits_per_pixel); |
1166 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.left); |
1167 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.top); |
1168 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.right); |
1169 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.bottom); |
1170 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) |
1171 | 993 | pcx_info.horizontal_resolution); |
1172 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.vertical_resolution); |
1173 | | /* |
1174 | | Dump colormap to file. |
1175 | | */ |
1176 | 993 | pcx_colormap=MagickAllocateResourceLimitedClearedArray(unsigned char *,3,256); |
1177 | 993 | if (pcx_colormap == (unsigned char *) NULL) |
1178 | 993 | ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1179 | 993 | q=pcx_colormap; |
1180 | 993 | if (image->storage_class == PseudoClass) |
1181 | 14.6k | for (i=0; i < (long) image->colors; i++) |
1182 | 13.7k | { |
1183 | 13.7k | *q++=ScaleQuantumToChar(image->colormap[i].red); |
1184 | 13.7k | *q++=ScaleQuantumToChar(image->colormap[i].green); |
1185 | 13.7k | *q++=ScaleQuantumToChar(image->colormap[i].blue); |
1186 | 13.7k | } |
1187 | 993 | (void) WriteBlob(image,3*16,(char *) pcx_colormap); |
1188 | 993 | (void) WriteBlobByte(image,pcx_info.reserved); |
1189 | 993 | (void) WriteBlobByte(image,pcx_info.planes); |
1190 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.bytes_per_line); |
1191 | 993 | (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.palette_info); |
1192 | 58.5k | for (i=0; i < 58; i++) |
1193 | 57.5k | (void) WriteBlobByte(image,'\0'); |
1194 | | /* Allocate memory for one pixel row. */ |
1195 | 993 | pcx_pixels=MagickAllocateResourceLimitedClearedArray(unsigned char *, |
1196 | 993 | bytes_per_line, |
1197 | 993 | pcx_info.planes); |
1198 | 993 | if (pcx_pixels == (unsigned char *) NULL) |
1199 | 993 | ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1200 | 993 | q=pcx_pixels; |
1201 | 993 | if (image->storage_class == DirectClass) |
1202 | 153 | { |
1203 | | /* |
1204 | | Convert DirectClass image to PCX raster pixels. |
1205 | | */ |
1206 | | |
1207 | | /* For each row ... */ |
1208 | 35.6k | for (y=0; y < (long) image->rows; y++) |
1209 | 35.4k | { |
1210 | 35.4k | const PixelPacket * |
1211 | 35.4k | row_pixels; |
1212 | | |
1213 | 35.4k | row_pixels=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1214 | 35.4k | if (row_pixels == (const PixelPacket *) NULL) |
1215 | 0 | break; |
1216 | | |
1217 | 35.4k | q=pcx_pixels; |
1218 | | |
1219 | | /* For each color plane ... */ |
1220 | 144k | for (i=0; i < pcx_info.planes; i++) |
1221 | 108k | { |
1222 | 108k | p=row_pixels; |
1223 | 108k | switch ((int) i) |
1224 | 108k | { |
1225 | 35.4k | case 0: |
1226 | 35.4k | { |
1227 | | /* For each column ... */ |
1228 | 80.4k | for (x=(long) pcx_info.bytes_per_line; x > 0; x--) |
1229 | 45.0k | *q++=ScaleQuantumToChar(p++->red); |
1230 | 35.4k | break; |
1231 | 0 | } |
1232 | 35.4k | case 1: |
1233 | 35.4k | { |
1234 | | /* For each column ... */ |
1235 | 80.4k | for (x=(long) pcx_info.bytes_per_line; x > 0; x--) |
1236 | 45.0k | *q++=ScaleQuantumToChar(p++->green); |
1237 | 35.4k | break; |
1238 | 0 | } |
1239 | 35.4k | case 2: |
1240 | 35.4k | { |
1241 | | /* For each column ... */ |
1242 | 80.4k | for (x=(long) pcx_info.bytes_per_line; x > 0; x--) |
1243 | 45.0k | *q++=ScaleQuantumToChar(p++->blue); |
1244 | 35.4k | break; |
1245 | 0 | } |
1246 | 2.34k | case 3: |
1247 | 2.34k | default: |
1248 | 2.34k | { |
1249 | | /* For each column ... */ |
1250 | 7.13k | for (x=(long) pcx_info.bytes_per_line; x > 0; x--) |
1251 | 4.79k | *q++=ScaleQuantumToChar(MaxRGB-p++->opacity); |
1252 | 2.34k | break; |
1253 | 2.34k | } |
1254 | 108k | } |
1255 | 108k | } |
1256 | 35.4k | if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail) |
1257 | 0 | break; |
1258 | 35.4k | if (QuantumTick(y,image->rows)) |
1259 | 9.92k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1260 | 9.92k | SaveImageText,image->filename, |
1261 | 9.92k | image->columns,image->rows)) |
1262 | 0 | break; |
1263 | 35.4k | } |
1264 | 153 | } |
1265 | 840 | else |
1266 | | /* |
1267 | | Convert PseudoClass image to a PCX grayscale image. |
1268 | | */ |
1269 | 840 | if (pcx_info.bits_per_pixel > 1) |
1270 | | /* For each row ... */ |
1271 | 143k | for (y=0; y < (long) image->rows; y++) |
1272 | 143k | { |
1273 | 143k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1274 | 143k | if (p == (const PixelPacket *) NULL) |
1275 | 0 | break; |
1276 | 143k | indexes=AccessImmutableIndexes(image); |
1277 | 143k | q=pcx_pixels; |
1278 | | /* For each column ... */ |
1279 | 71.7M | for (x=0; x < (long) image->columns; x++) |
1280 | 71.6M | *q++=indexes[x]; |
1281 | 143k | if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail) |
1282 | 0 | break; |
1283 | 143k | if (image->previous == (Image *) NULL) |
1284 | 143k | if (QuantumTick(y,image->rows)) |
1285 | 33.4k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1286 | 33.4k | SaveImageText,image->filename, |
1287 | 33.4k | image->columns,image->rows)) |
1288 | 0 | break; |
1289 | 143k | } |
1290 | 269 | else |
1291 | 269 | { |
1292 | 269 | register unsigned char |
1293 | 269 | bit, |
1294 | 269 | byte, |
1295 | 269 | polarity; |
1296 | | |
1297 | | /* |
1298 | | Convert PseudoClass image to a PCX monochrome image. |
1299 | | */ |
1300 | 269 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < (MaxRGB/2); |
1301 | 269 | if (image->colors == 2) |
1302 | 235 | polarity=PixelIntensityToQuantum(&image->colormap[0]) < |
1303 | 235 | PixelIntensityToQuantum(&image->colormap[1]); |
1304 | | /* For each row ... */ |
1305 | 70.3k | for (y=0; y < (long) image->rows; y++) |
1306 | 70.0k | { |
1307 | 70.0k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1308 | 70.0k | if (p == (const PixelPacket *) NULL) |
1309 | 0 | break; |
1310 | 70.0k | indexes=AccessImmutableIndexes(image); |
1311 | 70.0k | bit=0; |
1312 | 70.0k | byte=0; |
1313 | 70.0k | q=pcx_pixels; |
1314 | | /* For each column ... */ |
1315 | 36.3M | for (x=0; x < (long) image->columns; x++) |
1316 | 36.2M | { |
1317 | 36.2M | byte<<=1; |
1318 | 36.2M | if (indexes[x] == polarity) |
1319 | 34.4M | byte|=0x01; |
1320 | 36.2M | bit++; |
1321 | 36.2M | if (bit == 8) |
1322 | 4.51M | { |
1323 | 4.51M | *q++=byte; |
1324 | 4.51M | bit=0; |
1325 | 4.51M | byte=0; |
1326 | 4.51M | } |
1327 | 36.2M | p++; |
1328 | 36.2M | } |
1329 | 70.0k | if (bit != 0) |
1330 | 42.8k | *q++=byte << (8-bit); |
1331 | 70.0k | if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail) |
1332 | 0 | break; |
1333 | 70.0k | if (image->previous == (Image *) NULL) |
1334 | 70.0k | if (QuantumTick(y,image->rows)) |
1335 | 17.3k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1336 | 17.3k | SaveImageText,image->filename, |
1337 | 17.3k | image->columns,image->rows)) |
1338 | 0 | break; |
1339 | 70.0k | } |
1340 | 269 | } |
1341 | | |
1342 | 993 | (void) WriteBlobByte(image,pcx_info.colormap_signature); |
1343 | 993 | (void) WriteBlob(image,3*256,(char *) pcx_colormap); |
1344 | 993 | MagickFreeResourceLimitedMemory(pcx_pixels); |
1345 | 993 | MagickFreeResourceLimitedMemory(pcx_colormap); |
1346 | 993 | if (image->next == (Image *) NULL) |
1347 | 993 | break; |
1348 | 0 | image=SyncNextImageInList(image); |
1349 | 0 | status=MagickMonitorFormatted(scene++,Min(max_scenes,image_list_length), |
1350 | 0 | &image->exception,SaveImagesText, |
1351 | 0 | image->filename); |
1352 | 0 | if (status == False) |
1353 | 0 | break; |
1354 | 0 | if (scene >= max_scenes-1) |
1355 | 0 | break; |
1356 | 0 | } while (adjoin); |
1357 | 993 | if (adjoin) |
1358 | 0 | while (image->previous != (Image *) NULL) |
1359 | 0 | image=image->previous; |
1360 | 993 | if (page_table != (ExtendedSignedIntegralType *) NULL) |
1361 | 482 | { |
1362 | | /* |
1363 | | Write the DCX page table. |
1364 | | */ |
1365 | 482 | if (logging && write_dcx && image_list_length > max_scenes) |
1366 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1367 | 0 | "WARNING: DCX truncated to %lu scenes!", |
1368 | 0 | max_scenes-1); |
1369 | 482 | page_table[scene+1]=0; |
1370 | 482 | (void) SeekBlob(image,0L,SEEK_SET); |
1371 | 482 | (void) WriteBlobLSBLong(image,0x3ADE68B1L); |
1372 | 964 | for (i=0; i <= (long) scene; i++) |
1373 | 482 | (void) WriteBlobLSBLong(image,(unsigned long) page_table[i]); |
1374 | 482 | MagickFreeResourceLimitedMemory(page_table); |
1375 | 482 | } |
1376 | 993 | if (status == False) |
1377 | 993 | ThrowPCXWriterException(FileOpenError,UnableToWriteFile,image); |
1378 | 993 | status &= CloseBlob(image); |
1379 | 993 | return(status); |
1380 | 993 | } |