blob: 444e090ef10bbc36f3eef4e4133032b165dca0d6 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% PPPP CCCC X X %
7% P P C X X %
8% PPPP C X %
9% P C X X %
10% P CCCC X X %
11% %
12% %
13% Read/Write ZSoft IBM PC Paintbrush Image Format %
14% %
15% Software Design %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
Cristy7ce65e72015-12-12 18:03:16 -050020% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/image.h"
55#include "MagickCore/image-private.h"
56#include "MagickCore/list.h"
57#include "MagickCore/magick.h"
58#include "MagickCore/memory_.h"
59#include "MagickCore/monitor.h"
60#include "MagickCore/monitor-private.h"
61#include "MagickCore/pixel-accessor.h"
62#include "MagickCore/quantum-private.h"
63#include "MagickCore/static.h"
64#include "MagickCore/string_.h"
65#include "MagickCore/module.h"
cristy3ed852e2009-09-05 21:47:34 +000066
67/*
68 Typedef declarations.
69*/
70typedef struct _PCXInfo
71{
72 unsigned char
73 identifier,
74 version,
75 encoding,
76 bits_per_pixel;
77
78 unsigned short
79 left,
80 top,
81 right,
82 bottom,
83 horizontal_resolution,
84 vertical_resolution;
85
86 unsigned char
87 reserved,
88 planes;
89
90 unsigned short
91 bytes_per_line,
cristy29d40332014-12-30 23:48:22 +000092 palette_info,
93 horizontal_screensize,
94 vertical_screensize;
cristy3ed852e2009-09-05 21:47:34 +000095
96 unsigned char
97 colormap_signature;
98} PCXInfo;
99
100/*
101 Forward declarations.
102*/
103static MagickBooleanType
cristy1e178e72011-08-28 19:44:34 +0000104 WritePCXImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +0000105
106/*
107%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108% %
109% %
110% %
111% I s D C X %
112% %
113% %
114% %
115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116%
117% IsDCX() returns MagickTrue if the image format type, identified by the
118% magick string, is DCX.
119%
120% The format of the IsDCX method is:
121%
122% MagickBooleanType IsDCX(const unsigned char *magick,const size_t length)
123%
124% A description of each parameter follows:
125%
126% o magick: compare image format pattern against these bytes.
127%
128% o length: Specifies the length of the magick string.
129%
cristy3ed852e2009-09-05 21:47:34 +0000130*/
131static MagickBooleanType IsDCX(const unsigned char *magick,const size_t length)
132{
133 if (length < 4)
134 return(MagickFalse);
135 if (memcmp(magick,"\261\150\336\72",4) == 0)
136 return(MagickTrue);
137 return(MagickFalse);
138}
139
140/*
141%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
142% %
143% %
144% %
145% I s P C X %
146% %
147% %
148% %
149%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
150%
151% IsPCX() returns MagickTrue if the image format type, identified by the
152% magick string, is PCX.
153%
154% The format of the IsPCX method is:
155%
156% MagickBooleanType IsPCX(const unsigned char *magick,const size_t length)
157%
158% A description of each parameter follows:
159%
160% o magick: compare image format pattern against these bytes.
161%
162% o length: Specifies the length of the magick string.
163%
cristy3ed852e2009-09-05 21:47:34 +0000164*/
165static MagickBooleanType IsPCX(const unsigned char *magick,const size_t length)
166{
167 if (length < 2)
168 return(MagickFalse);
169 if (memcmp(magick,"\012\002",2) == 0)
170 return(MagickTrue);
171 if (memcmp(magick,"\012\005",2) == 0)
172 return(MagickTrue);
173 return(MagickFalse);
174}
175
176/*
177%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178% %
179% %
180% %
181% R e a d P C X I m a g e %
182% %
183% %
184% %
185%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186%
187% ReadPCXImage() reads a ZSoft IBM PC Paintbrush file and returns it.
188% It allocates the memory necessary for the new Image structure and returns
189% a pointer to the new image.
190%
191% The format of the ReadPCXImage method is:
192%
193% Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
194%
195% A description of each parameter follows:
196%
197% o image_info: the image info.
198%
199% o exception: return any errors or warnings in this structure.
200%
201*/
cristy3ed852e2009-09-05 21:47:34 +0000202static Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
203{
dirk08f033c2014-10-23 21:53:50 +0000204#define ThrowPCXException(severity,tag) \
205 { \
206 scanline=(unsigned char *) RelinquishMagickMemory(scanline); \
207 pixel_info=RelinquishVirtualMemory(pixel_info); \
208 ThrowReaderException(severity,tag); \
209 }
210
cristy3ed852e2009-09-05 21:47:34 +0000211 Image
212 *image;
213
214 int
215 bits,
216 id,
217 mask;
218
cristy3ed852e2009-09-05 21:47:34 +0000219 MagickBooleanType
220 status;
221
222 MagickOffsetType
223 offset,
224 *page_table;
225
cristy0acf7e12013-06-30 23:35:36 +0000226 MemoryInfo
227 *pixel_info;
228
cristy3ed852e2009-09-05 21:47:34 +0000229 PCXInfo
230 pcx_info;
231
cristybb503372010-05-27 20:51:26 +0000232 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000233 x;
234
cristy4c08aed2011-07-01 19:47:50 +0000235 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000236 *q;
237
cristybb503372010-05-27 20:51:26 +0000238 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000239 i;
240
241 register unsigned char
242 *p,
243 *r;
244
cristyaff6d802011-04-26 01:46:31 +0000245 size_t
246 one,
247 pcx_packets;
248
cristy3ed852e2009-09-05 21:47:34 +0000249 ssize_t
cristyaff6d802011-04-26 01:46:31 +0000250 count,
251 y;
cristy3ed852e2009-09-05 21:47:34 +0000252
253 unsigned char
254 packet,
dirk08f033c2014-10-23 21:53:50 +0000255 pcx_colormap[768],
cristy0553bd52013-06-30 15:53:50 +0000256 *pixels,
cristy3ed852e2009-09-05 21:47:34 +0000257 *scanline;
258
cristy3ed852e2009-09-05 21:47:34 +0000259 /*
260 Open image file.
261 */
262 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000263 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000264 if (image_info->debug != MagickFalse)
265 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
266 image_info->filename);
267 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000268 assert(exception->signature == MagickCoreSignature);
cristy9950d572011-10-01 18:22:35 +0000269 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000270 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
271 if (status == MagickFalse)
272 {
273 image=DestroyImageList(image);
274 return((Image *) NULL);
275 }
276 /*
277 Determine if this a PCX file.
278 */
279 page_table=(MagickOffsetType *) NULL;
280 if (LocaleCompare(image_info->magick,"DCX") == 0)
281 {
cristybb503372010-05-27 20:51:26 +0000282 size_t
cristy3ed852e2009-09-05 21:47:34 +0000283 magic;
284
285 /*
286 Read the DCX page table.
287 */
288 magic=ReadBlobLSBLong(image);
289 if (magic != 987654321)
290 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
291 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
292 sizeof(*page_table));
293 if (page_table == (MagickOffsetType *) NULL)
294 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
295 for (id=0; id < 1024; id++)
296 {
297 page_table[id]=(MagickOffsetType) ReadBlobLSBLong(image);
298 if (page_table[id] == 0)
299 break;
300 }
301 }
302 if (page_table != (MagickOffsetType *) NULL)
303 {
304 offset=SeekBlob(image,(MagickOffsetType) page_table[0],SEEK_SET);
305 if (offset < 0)
306 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
307 }
cristy3ed852e2009-09-05 21:47:34 +0000308 count=ReadBlob(image,1,&pcx_info.identifier);
309 for (id=1; id < 1024; id++)
310 {
cristyc99e4c22014-04-30 23:37:58 +0000311 int
312 bits_per_pixel;
313
cristy3ed852e2009-09-05 21:47:34 +0000314 /*
315 Verify PCX identifier.
316 */
317 pcx_info.version=(unsigned char) ReadBlobByte(image);
cristy29d40332014-12-30 23:48:22 +0000318 if ((count != 1) || (pcx_info.identifier != 0x0a))
cristy3ed852e2009-09-05 21:47:34 +0000319 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
320 pcx_info.encoding=(unsigned char) ReadBlobByte(image);
cristyc99e4c22014-04-30 23:37:58 +0000321 bits_per_pixel=ReadBlobByte(image);
322 if (bits_per_pixel == -1)
323 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
324 pcx_info.bits_per_pixel=(unsigned char) bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000325 pcx_info.left=ReadBlobLSBShort(image);
326 pcx_info.top=ReadBlobLSBShort(image);
327 pcx_info.right=ReadBlobLSBShort(image);
328 pcx_info.bottom=ReadBlobLSBShort(image);
329 pcx_info.horizontal_resolution=ReadBlobLSBShort(image);
330 pcx_info.vertical_resolution=ReadBlobLSBShort(image);
331 /*
332 Read PCX raster colormap.
333 */
cristybb503372010-05-27 20:51:26 +0000334 image->columns=(size_t) MagickAbsoluteValue((ssize_t) pcx_info.right-
cristy3ed852e2009-09-05 21:47:34 +0000335 pcx_info.left)+1UL;
cristybb503372010-05-27 20:51:26 +0000336 image->rows=(size_t) MagickAbsoluteValue((ssize_t) pcx_info.bottom-
cristy3ed852e2009-09-05 21:47:34 +0000337 pcx_info.top)+1UL;
338 if ((image->columns == 0) || (image->rows == 0) ||
cristy29d40332014-12-30 23:48:22 +0000339 ((pcx_info.bits_per_pixel != 1) &&
340 (pcx_info.bits_per_pixel != 2) &&
341 (pcx_info.bits_per_pixel != 4) &&
342 (pcx_info.bits_per_pixel != 8)))
cristy3ed852e2009-09-05 21:47:34 +0000343 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy29d40332014-12-30 23:48:22 +0000344 image->depth=pcx_info.bits_per_pixel;
cristy3ed852e2009-09-05 21:47:34 +0000345 image->units=PixelsPerInchResolution;
cristy2a11bef2011-10-28 18:33:11 +0000346 image->resolution.x=(double) pcx_info.horizontal_resolution;
347 image->resolution.y=(double) pcx_info.vertical_resolution;
cristy3ed852e2009-09-05 21:47:34 +0000348 image->colors=16;
cristy3ed852e2009-09-05 21:47:34 +0000349 count=ReadBlob(image,3*image->colors,pcx_colormap);
cristy7c443542015-01-05 23:19:48 +0000350 if (count != (ssize_t) (3*image->colors))
cristy29d40332014-12-30 23:48:22 +0000351 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000352 pcx_info.reserved=(unsigned char) ReadBlobByte(image);
353 pcx_info.planes=(unsigned char) ReadBlobByte(image);
cristy7163b3e2014-05-24 22:18:20 +0000354 if ((pcx_info.bits_per_pixel*pcx_info.planes) >= 64)
355 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristyeaedf062010-05-29 22:36:02 +0000356 one=1;
cristy3ed852e2009-09-05 21:47:34 +0000357 if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1))
358 if ((pcx_info.version == 3) || (pcx_info.version == 5) ||
359 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
cristyeaedf062010-05-29 22:36:02 +0000360 image->colors=(size_t) MagickMin(one << (1UL*
cristy3ed852e2009-09-05 21:47:34 +0000361 (pcx_info.bits_per_pixel*pcx_info.planes)),256UL);
cristy018f07f2011-09-04 21:15:19 +0000362 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000363 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
364 if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1))
365 image->storage_class=DirectClass;
366 p=pcx_colormap;
cristybb503372010-05-27 20:51:26 +0000367 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000368 {
369 image->colormap[i].red=ScaleCharToQuantum(*p++);
370 image->colormap[i].green=ScaleCharToQuantum(*p++);
371 image->colormap[i].blue=ScaleCharToQuantum(*p++);
372 }
373 pcx_info.bytes_per_line=ReadBlobLSBShort(image);
374 pcx_info.palette_info=ReadBlobLSBShort(image);
cristy29d40332014-12-30 23:48:22 +0000375 pcx_info.horizontal_screensize=ReadBlobLSBShort(image);
376 pcx_info.vertical_screensize=ReadBlobLSBShort(image);
377 for (i=0; i < 54; i++)
cristy3ed852e2009-09-05 21:47:34 +0000378 (void) ReadBlobByte(image);
379 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
380 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
381 break;
cristyacabb842014-12-14 23:36:33 +0000382 status=SetImageExtent(image,image->columns,image->rows,exception);
383 if (status == MagickFalse)
384 return(DestroyImageList(image));
cristy3ed852e2009-09-05 21:47:34 +0000385 /*
386 Read image data.
387 */
cristy0acf7e12013-06-30 23:35:36 +0000388 pcx_packets=(size_t) image->rows*pcx_info.bytes_per_line*pcx_info.planes;
dirk08f033c2014-10-23 21:53:50 +0000389 if ((size_t) (pcx_info.bits_per_pixel*pcx_info.planes*image->columns) >
390 (pcx_packets*8U))
391 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000392 scanline=(unsigned char *) AcquireQuantumMemory(MagickMax(image->columns,
393 pcx_info.bytes_per_line),MagickMax(8,pcx_info.planes)*sizeof(*scanline));
cristyf432c632014-12-07 15:11:28 +0000394 pixel_info=AcquireVirtualMemory(pcx_packets,2*sizeof(*pixels));
cristy0acf7e12013-06-30 23:35:36 +0000395 if ((scanline == (unsigned char *) NULL) ||
396 (pixel_info == (MemoryInfo *) NULL))
397 {
398 if (scanline != (unsigned char *) NULL)
399 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
400 if (pixel_info != (MemoryInfo *) NULL)
401 pixel_info=RelinquishVirtualMemory(pixel_info);
402 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
403 }
404 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +0000405 /*
406 Uncompress image data.
407 */
cristy0553bd52013-06-30 15:53:50 +0000408 p=pixels;
cristy3ed852e2009-09-05 21:47:34 +0000409 if (pcx_info.encoding == 0)
410 while (pcx_packets != 0)
411 {
412 packet=(unsigned char) ReadBlobByte(image);
413 if (EOFBlob(image) != MagickFalse)
dirk08f033c2014-10-23 21:53:50 +0000414 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
cristy3ed852e2009-09-05 21:47:34 +0000415 *p++=packet;
416 pcx_packets--;
417 }
418 else
419 while (pcx_packets != 0)
420 {
421 packet=(unsigned char) ReadBlobByte(image);
422 if (EOFBlob(image) != MagickFalse)
dirk08f033c2014-10-23 21:53:50 +0000423 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
cristy3ed852e2009-09-05 21:47:34 +0000424 if ((packet & 0xc0) != 0xc0)
425 {
426 *p++=packet;
427 pcx_packets--;
428 continue;
429 }
430 count=(ssize_t) (packet & 0x3f);
431 packet=(unsigned char) ReadBlobByte(image);
432 if (EOFBlob(image) != MagickFalse)
dirk08f033c2014-10-23 21:53:50 +0000433 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
cristy3ed852e2009-09-05 21:47:34 +0000434 for ( ; count != 0; count--)
435 {
436 *p++=packet;
437 pcx_packets--;
438 if (pcx_packets == 0)
439 break;
440 }
441 }
442 if (image->storage_class == DirectClass)
cristyb0a657e2012-08-29 00:45:37 +0000443 image->alpha_trait=pcx_info.planes > 3 ? BlendPixelTrait :
444 UndefinedPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000445 else
446 if ((pcx_info.version == 5) ||
447 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
448 {
449 /*
450 Initialize image colormap.
451 */
452 if (image->colors > 256)
dirk08f033c2014-10-23 21:53:50 +0000453 ThrowPCXException(CorruptImageError,"ColormapExceeds256Colors");
cristy3ed852e2009-09-05 21:47:34 +0000454 if ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)
455 {
456 /*
457 Monochrome colormap.
458 */
459 image->colormap[0].red=(Quantum) 0;
460 image->colormap[0].green=(Quantum) 0;
461 image->colormap[0].blue=(Quantum) 0;
cristy6e963d82012-06-19 15:23:24 +0000462 image->colormap[1].red=QuantumRange;
463 image->colormap[1].green=QuantumRange;
464 image->colormap[1].blue=QuantumRange;
cristy3ed852e2009-09-05 21:47:34 +0000465 }
466 else
467 if (image->colors > 16)
468 {
469 /*
470 256 color images have their color map at the end of the file.
471 */
472 pcx_info.colormap_signature=(unsigned char) ReadBlobByte(image);
473 count=ReadBlob(image,3*image->colors,pcx_colormap);
474 p=pcx_colormap;
cristybb503372010-05-27 20:51:26 +0000475 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000476 {
477 image->colormap[i].red=ScaleCharToQuantum(*p++);
478 image->colormap[i].green=ScaleCharToQuantum(*p++);
479 image->colormap[i].blue=ScaleCharToQuantum(*p++);
480 }
481 }
cristy3ed852e2009-09-05 21:47:34 +0000482 }
483 /*
484 Convert PCX raster image to pixel packets.
485 */
cristybb503372010-05-27 20:51:26 +0000486 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000487 {
cristy0553bd52013-06-30 15:53:50 +0000488 p=pixels+(y*pcx_info.bytes_per_line*pcx_info.planes);
cristy3ed852e2009-09-05 21:47:34 +0000489 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000490 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000491 break;
cristy3ed852e2009-09-05 21:47:34 +0000492 r=scanline;
493 if (image->storage_class == DirectClass)
494 for (i=0; i < pcx_info.planes; i++)
495 {
496 r=scanline+i;
cristybb503372010-05-27 20:51:26 +0000497 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +0000498 {
499 switch (i)
500 {
501 case 0:
502 {
503 *r=(*p++);
504 break;
505 }
506 case 1:
507 {
508 *r=(*p++);
509 break;
510 }
511 case 2:
512 {
513 *r=(*p++);
514 break;
515 }
516 case 3:
517 default:
518 {
519 *r=(*p++);
520 break;
521 }
522 }
523 r+=pcx_info.planes;
524 }
525 }
526 else
527 if (pcx_info.planes > 1)
528 {
cristybb503372010-05-27 20:51:26 +0000529 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000530 *r++=0;
531 for (i=0; i < pcx_info.planes; i++)
532 {
533 r=scanline;
cristybb503372010-05-27 20:51:26 +0000534 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +0000535 {
536 bits=(*p++);
537 for (mask=0x80; mask != 0; mask>>=1)
538 {
539 if (bits & mask)
540 *r|=1 << i;
541 r++;
542 }
543 }
544 }
545 }
546 else
547 switch (pcx_info.bits_per_pixel)
548 {
549 case 1:
550 {
cristybb503372010-05-27 20:51:26 +0000551 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000552 bit;
553
cristybb503372010-05-27 20:51:26 +0000554 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
cristy3ed852e2009-09-05 21:47:34 +0000555 {
556 for (bit=7; bit >= 0; bit--)
557 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
558 p++;
559 }
560 if ((image->columns % 8) != 0)
561 {
cristybb503372010-05-27 20:51:26 +0000562 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
cristy3ed852e2009-09-05 21:47:34 +0000563 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
564 p++;
565 }
566 break;
567 }
568 case 2:
569 {
cristybb503372010-05-27 20:51:26 +0000570 for (x=0; x < ((ssize_t) image->columns-3); x+=4)
cristy3ed852e2009-09-05 21:47:34 +0000571 {
572 *r++=(*p >> 6) & 0x3;
573 *r++=(*p >> 4) & 0x3;
574 *r++=(*p >> 2) & 0x3;
575 *r++=(*p) & 0x3;
576 p++;
577 }
578 if ((image->columns % 4) != 0)
579 {
cristybb503372010-05-27 20:51:26 +0000580 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
cristy3ed852e2009-09-05 21:47:34 +0000581 *r++=(unsigned char) ((*p >> (i*2)) & 0x03);
582 p++;
583 }
584 break;
585 }
586 case 4:
587 {
cristybb503372010-05-27 20:51:26 +0000588 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
cristy3ed852e2009-09-05 21:47:34 +0000589 {
590 *r++=(*p >> 4) & 0xf;
591 *r++=(*p) & 0xf;
592 p++;
593 }
594 if ((image->columns % 2) != 0)
595 *r++=(*p++ >> 4) & 0xf;
596 break;
597 }
598 case 8:
599 {
600 (void) CopyMagickMemory(r,p,image->columns);
601 break;
602 }
603 default:
604 break;
605 }
606 /*
607 Transfer image scanline.
608 */
609 r=scanline;
cristybb503372010-05-27 20:51:26 +0000610 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000611 {
612 if (image->storage_class == PseudoClass)
cristy4c08aed2011-07-01 19:47:50 +0000613 SetPixelIndex(image,*r++,q);
cristy3ed852e2009-09-05 21:47:34 +0000614 else
615 {
cristy4c08aed2011-07-01 19:47:50 +0000616 SetPixelRed(image,ScaleCharToQuantum(*r++),q);
617 SetPixelGreen(image,ScaleCharToQuantum(*r++),q);
618 SetPixelBlue(image,ScaleCharToQuantum(*r++),q);
cristy17f11b02014-12-20 19:37:04 +0000619 if (image->alpha_trait != UndefinedPixelTrait)
cristy4c08aed2011-07-01 19:47:50 +0000620 SetPixelAlpha(image,ScaleCharToQuantum(*r++),q);
cristy3ed852e2009-09-05 21:47:34 +0000621 }
cristyed231572011-07-14 02:18:59 +0000622 q+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000623 }
624 if (SyncAuthenticPixels(image,exception) == MagickFalse)
625 break;
626 if (image->previous == (Image *) NULL)
627 {
cristycee97112010-05-28 00:44:52 +0000628 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
cristyaff6d802011-04-26 01:46:31 +0000629 image->rows);
cristy3ed852e2009-09-05 21:47:34 +0000630 if (status == MagickFalse)
631 break;
632 }
633 }
634 if (image->storage_class == PseudoClass)
cristyea1a8aa2011-10-20 13:24:06 +0000635 (void) SyncImage(image,exception);
cristy0acf7e12013-06-30 23:35:36 +0000636 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
637 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +0000638 if (EOFBlob(image) != MagickFalse)
639 {
640 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
641 image->filename);
642 break;
643 }
644 /*
645 Proceed to next image.
646 */
647 if (image_info->number_scenes != 0)
648 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
649 break;
650 if (page_table == (MagickOffsetType *) NULL)
651 break;
652 if (page_table[id] == 0)
653 break;
654 offset=SeekBlob(image,(MagickOffsetType) page_table[id],SEEK_SET);
655 if (offset < 0)
656 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
657 count=ReadBlob(image,1,&pcx_info.identifier);
658 if ((count != 0) && (pcx_info.identifier == 0x0a))
659 {
660 /*
661 Allocate next image structure.
662 */
cristy9950d572011-10-01 18:22:35 +0000663 AcquireNextImage(image_info,image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000664 if (GetNextImageInList(image) == (Image *) NULL)
665 {
666 image=DestroyImageList(image);
667 return((Image *) NULL);
668 }
669 image=SyncNextImageInList(image);
670 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
671 GetBlobSize(image));
672 if (status == MagickFalse)
673 break;
674 }
675 }
676 if (page_table != (MagickOffsetType *) NULL)
677 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
678 (void) CloseBlob(image);
679 return(GetFirstImageInList(image));
680}
681
682/*
683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
684% %
685% %
686% %
687% R e g i s t e r P C X I m a g e %
688% %
689% %
690% %
691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692%
693% RegisterPCXImage() adds attributes for the PCX image format to
694% the list of supported formats. The attributes include the image format
695% tag, a method to read and/or write the format, whether the format
696% supports the saving of more than one frame to the same file or blob,
697% whether the format supports native in-memory I/O, and a brief
698% description of the format.
699%
700% The format of the RegisterPCXImage method is:
701%
cristybb503372010-05-27 20:51:26 +0000702% size_t RegisterPCXImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000703%
704*/
cristybb503372010-05-27 20:51:26 +0000705ModuleExport size_t RegisterPCXImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000706{
707 MagickInfo
708 *entry;
709
dirk06b627a2015-04-06 18:59:17 +0000710 entry=AcquireMagickInfo("PCX","DCX","ZSoft IBM PC multi-page Paintbrush");
cristy3ed852e2009-09-05 21:47:34 +0000711 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
712 entry->encoder=(EncodeImageHandler *) WritePCXImage;
dirk08e9a112015-02-22 01:51:41 +0000713 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000714 entry->magick=(IsImageFormatHandler *) IsDCX;
cristy3ed852e2009-09-05 21:47:34 +0000715 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000716 entry=AcquireMagickInfo("PCX","PCX","ZSoft IBM PC Paintbrush");
cristy3ed852e2009-09-05 21:47:34 +0000717 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
718 entry->encoder=(EncodeImageHandler *) WritePCXImage;
719 entry->magick=(IsImageFormatHandler *) IsPCX;
dirk08e9a112015-02-22 01:51:41 +0000720 entry->flags^=CoderAdjoinFlag;
721 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000722 (void) RegisterMagickInfo(entry);
723 return(MagickImageCoderSignature);
724}
725
726/*
727%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728% %
729% %
730% %
731% U n r e g i s t e r P C X I m a g e %
732% %
733% %
734% %
735%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736%
737% UnregisterPCXImage() removes format registrations made by the
738% PCX module from the list of supported formats.
739%
740% The format of the UnregisterPCXImage method is:
741%
742% UnregisterPCXImage(void)
743%
744*/
745ModuleExport void UnregisterPCXImage(void)
746{
747 (void) UnregisterMagickInfo("DCX");
748 (void) UnregisterMagickInfo("PCX");
749}
750
751/*
752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753% %
754% %
755% %
756% W r i t e P C X I m a g e %
757% %
758% %
759% %
760%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
761%
762% WritePCXImage() writes an image in the ZSoft IBM PC Paintbrush file
763% format.
764%
765% The format of the WritePCXImage method is:
766%
cristy1e178e72011-08-28 19:44:34 +0000767% MagickBooleanType WritePCXImage(const ImageInfo *image_info,
768% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000769%
770% A description of each parameter follows.
771%
772% o image_info: the image info.
773%
774% o image: The image.
775%
cristy1e178e72011-08-28 19:44:34 +0000776% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +0000777%
778*/
cristy1e178e72011-08-28 19:44:34 +0000779
cristy3ed852e2009-09-05 21:47:34 +0000780static MagickBooleanType PCXWritePixels(PCXInfo *pcx_info,
781 const unsigned char *pixels,Image *image)
782{
783 register const unsigned char
784 *q;
785
cristybb503372010-05-27 20:51:26 +0000786 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000787 i,
788 x;
789
790 ssize_t
791 count;
792
793 unsigned char
794 packet,
795 previous;
796
797 q=pixels;
cristybb503372010-05-27 20:51:26 +0000798 for (i=0; i < (ssize_t) pcx_info->planes; i++)
cristy3ed852e2009-09-05 21:47:34 +0000799 {
cristy1f9852b2010-09-04 15:05:36 +0000800 if (pcx_info->encoding == 0)
cristy3ed852e2009-09-05 21:47:34 +0000801 {
cristy1f9852b2010-09-04 15:05:36 +0000802 for (x=0; x < (ssize_t) pcx_info->bytes_per_line; x++)
803 (void) WriteBlobByte(image,(unsigned char) (*q++));
cristy3ed852e2009-09-05 21:47:34 +0000804 }
cristy1f9852b2010-09-04 15:05:36 +0000805 else
806 {
807 previous=(*q++);
808 count=1;
809 for (x=0; x < (ssize_t) (pcx_info->bytes_per_line-1); x++)
810 {
811 packet=(*q++);
812 if ((packet == previous) && (count < 63))
813 {
814 count++;
815 continue;
816 }
817 if ((count > 1) || ((previous & 0xc0) == 0xc0))
818 {
819 count|=0xc0;
820 (void) WriteBlobByte(image,(unsigned char) count);
821 }
822 (void) WriteBlobByte(image,previous);
823 previous=packet;
824 count=1;
825 }
826 if ((count > 1) || ((previous & 0xc0) == 0xc0))
827 {
828 count|=0xc0;
829 (void) WriteBlobByte(image,(unsigned char) count);
830 }
831 (void) WriteBlobByte(image,previous);
832 }
cristy3ed852e2009-09-05 21:47:34 +0000833 }
834 return (MagickTrue);
835}
836
cristy1e178e72011-08-28 19:44:34 +0000837static MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image,
838 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000839{
cristy3ed852e2009-09-05 21:47:34 +0000840 MagickBooleanType
841 status;
842
843 MagickOffsetType
844 offset,
845 *page_table,
846 scene;
847
cristy0553bd52013-06-30 15:53:50 +0000848 MemoryInfo
849 *pixel_info;
850
cristy3ed852e2009-09-05 21:47:34 +0000851 PCXInfo
852 pcx_info;
853
cristy4c08aed2011-07-01 19:47:50 +0000854 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000855 *p;
856
cristybb503372010-05-27 20:51:26 +0000857 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000858 i,
859 x;
860
861 register unsigned char
862 *q;
863
864 size_t
865 length;
866
cristyaff6d802011-04-26 01:46:31 +0000867 ssize_t
868 y;
869
cristy3ed852e2009-09-05 21:47:34 +0000870 unsigned char
871 *pcx_colormap,
cristy0553bd52013-06-30 15:53:50 +0000872 *pixels;
cristy3ed852e2009-09-05 21:47:34 +0000873
874 /*
875 Open output image file.
876 */
877 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000878 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000879 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000880 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000881 if (image->debug != MagickFalse)
882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000883 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000884 assert(exception->signature == MagickCoreSignature);
cristy1e178e72011-08-28 19:44:34 +0000885 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000886 if (status == MagickFalse)
887 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000888 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000889 page_table=(MagickOffsetType *) NULL;
890 if ((LocaleCompare(image_info->magick,"DCX") == 0) ||
891 ((GetNextImageInList(image) != (Image *) NULL) &&
892 (image_info->adjoin != MagickFalse)))
893 {
894 /*
895 Write the DCX page table.
896 */
897 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
898 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
899 sizeof(*page_table));
900 if (page_table == (MagickOffsetType *) NULL)
901 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
902 for (scene=0; scene < 1024; scene++)
903 (void) WriteBlobLSBLong(image,0x00000000L);
904 }
905 scene=0;
906 do
907 {
908 if (page_table != (MagickOffsetType *) NULL)
909 page_table[scene]=TellBlob(image);
910 /*
911 Initialize PCX raster file header.
912 */
913 pcx_info.identifier=0x0a;
914 pcx_info.version=5;
cristy1f9852b2010-09-04 15:05:36 +0000915 pcx_info.encoding=image_info->compression == NoCompression ? 0 : 1;
cristy3ed852e2009-09-05 21:47:34 +0000916 pcx_info.bits_per_pixel=8;
917 if ((image->storage_class == PseudoClass) &&
dirkf1d85482015-04-06 00:36:00 +0000918 (SetImageMonochrome(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000919 pcx_info.bits_per_pixel=1;
920 pcx_info.left=0;
921 pcx_info.top=0;
922 pcx_info.right=(unsigned short) (image->columns-1);
923 pcx_info.bottom=(unsigned short) (image->rows-1);
924 switch (image->units)
925 {
926 case UndefinedResolution:
927 case PixelsPerInchResolution:
928 default:
929 {
cristy2a11bef2011-10-28 18:33:11 +0000930 pcx_info.horizontal_resolution=(unsigned short) image->resolution.x;
931 pcx_info.vertical_resolution=(unsigned short) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000932 break;
933 }
934 case PixelsPerCentimeterResolution:
935 {
936 pcx_info.horizontal_resolution=(unsigned short)
cristy2a11bef2011-10-28 18:33:11 +0000937 (2.54*image->resolution.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000938 pcx_info.vertical_resolution=(unsigned short)
cristy2a11bef2011-10-28 18:33:11 +0000939 (2.54*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000940 break;
941 }
942 }
943 pcx_info.reserved=0;
944 pcx_info.planes=1;
945 if ((image->storage_class == DirectClass) || (image->colors > 256))
946 {
947 pcx_info.planes=3;
cristy17f11b02014-12-20 19:37:04 +0000948 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000949 pcx_info.planes++;
950 }
cristybb503372010-05-27 20:51:26 +0000951 pcx_info.bytes_per_line=(unsigned short) (((size_t) image->columns*
cristy3ed852e2009-09-05 21:47:34 +0000952 pcx_info.bits_per_pixel+7)/8);
953 pcx_info.palette_info=1;
954 pcx_info.colormap_signature=0x0c;
955 /*
956 Write PCX header.
957 */
958 (void) WriteBlobByte(image,pcx_info.identifier);
959 (void) WriteBlobByte(image,pcx_info.version);
960 (void) WriteBlobByte(image,pcx_info.encoding);
961 (void) WriteBlobByte(image,pcx_info.bits_per_pixel);
962 (void) WriteBlobLSBShort(image,pcx_info.left);
963 (void) WriteBlobLSBShort(image,pcx_info.top);
964 (void) WriteBlobLSBShort(image,pcx_info.right);
965 (void) WriteBlobLSBShort(image,pcx_info.bottom);
966 (void) WriteBlobLSBShort(image,pcx_info.horizontal_resolution);
967 (void) WriteBlobLSBShort(image,pcx_info.vertical_resolution);
968 /*
969 Dump colormap to file.
970 */
971 pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL,
972 3*sizeof(*pcx_colormap));
973 if (pcx_colormap == (unsigned char *) NULL)
974 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
975 (void) memset(pcx_colormap,0,3*256*sizeof(*pcx_colormap));
976 q=pcx_colormap;
977 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
cristybb503372010-05-27 20:51:26 +0000978 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000979 {
980 *q++=ScaleQuantumToChar(image->colormap[i].red);
981 *q++=ScaleQuantumToChar(image->colormap[i].green);
982 *q++=ScaleQuantumToChar(image->colormap[i].blue);
983 }
984 (void) WriteBlob(image,3*16,(const unsigned char *) pcx_colormap);
985 (void) WriteBlobByte(image,pcx_info.reserved);
986 (void) WriteBlobByte(image,pcx_info.planes);
987 (void) WriteBlobLSBShort(image,pcx_info.bytes_per_line);
988 (void) WriteBlobLSBShort(image,pcx_info.palette_info);
989 for (i=0; i < 58; i++)
990 (void) WriteBlobByte(image,'\0');
991 length=(size_t) pcx_info.bytes_per_line;
cristy0553bd52013-06-30 15:53:50 +0000992 pixel_info=AcquireVirtualMemory(length,pcx_info.planes*sizeof(*pixels));
993 if (pixel_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000994 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy0553bd52013-06-30 15:53:50 +0000995 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
996 q=pixels;
cristy3ed852e2009-09-05 21:47:34 +0000997 if ((image->storage_class == DirectClass) || (image->colors > 256))
998 {
cristy3ed852e2009-09-05 21:47:34 +0000999 /*
1000 Convert DirectClass image to PCX raster pixels.
1001 */
cristybb503372010-05-27 20:51:26 +00001002 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001003 {
cristy0553bd52013-06-30 15:53:50 +00001004 q=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001005 for (i=0; i < pcx_info.planes; i++)
1006 {
cristy0acf7e12013-06-30 23:35:36 +00001007 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1008 if (p == (const Quantum *) NULL)
1009 break;
cristy3ed852e2009-09-05 21:47:34 +00001010 switch ((int) i)
1011 {
1012 case 0:
1013 {
cristybb503372010-05-27 20:51:26 +00001014 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001015 {
cristy4c08aed2011-07-01 19:47:50 +00001016 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
cristyed231572011-07-14 02:18:59 +00001017 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001018 }
1019 break;
1020 }
1021 case 1:
1022 {
cristybb503372010-05-27 20:51:26 +00001023 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001024 {
cristy4c08aed2011-07-01 19:47:50 +00001025 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
cristyed231572011-07-14 02:18:59 +00001026 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001027 }
1028 break;
1029 }
1030 case 2:
1031 {
cristybb503372010-05-27 20:51:26 +00001032 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001033 {
cristy4c08aed2011-07-01 19:47:50 +00001034 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00001035 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001036 }
1037 break;
1038 }
1039 case 3:
1040 default:
1041 {
cristybb503372010-05-27 20:51:26 +00001042 for (x=(ssize_t) pcx_info.bytes_per_line; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001043 {
cristy4c08aed2011-07-01 19:47:50 +00001044 *q++=ScaleQuantumToChar((Quantum) (GetPixelAlpha(image,p)));
cristyed231572011-07-14 02:18:59 +00001045 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001046 }
1047 break;
1048 }
1049 }
1050 }
cristy0553bd52013-06-30 15:53:50 +00001051 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001052 break;
1053 if (image->previous == (Image *) NULL)
1054 {
cristycee97112010-05-28 00:44:52 +00001055 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1056 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001057 if (status == MagickFalse)
1058 break;
1059 }
1060 }
1061 }
1062 else
1063 {
1064 if (pcx_info.bits_per_pixel > 1)
cristybb503372010-05-27 20:51:26 +00001065 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001066 {
cristy1e178e72011-08-28 19:44:34 +00001067 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001068 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001069 break;
cristy0553bd52013-06-30 15:53:50 +00001070 q=pixels;
cristybb503372010-05-27 20:51:26 +00001071 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001072 {
1073 *q++=(unsigned char) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +00001074 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001075 }
cristy0553bd52013-06-30 15:53:50 +00001076 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001077 break;
1078 if (image->previous == (Image *) NULL)
1079 {
cristycee97112010-05-28 00:44:52 +00001080 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1081 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001082 if (status == MagickFalse)
1083 break;
1084 }
1085 }
1086 else
1087 {
cristy3ed852e2009-09-05 21:47:34 +00001088 register unsigned char
1089 bit,
1090 byte;
1091
1092 /*
1093 Convert PseudoClass image to a PCX monochrome image.
1094 */
cristybb503372010-05-27 20:51:26 +00001095 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001096 {
cristy1e178e72011-08-28 19:44:34 +00001097 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001098 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001099 break;
cristy3ed852e2009-09-05 21:47:34 +00001100 bit=0;
1101 byte=0;
cristy0553bd52013-06-30 15:53:50 +00001102 q=pixels;
cristybb503372010-05-27 20:51:26 +00001103 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001104 {
1105 byte<<=1;
cristy85636be2014-04-04 19:16:53 +00001106 if (GetPixelLuma(image,p) >= (QuantumRange/2.0))
cristy3ed852e2009-09-05 21:47:34 +00001107 byte|=0x01;
1108 bit++;
1109 if (bit == 8)
1110 {
1111 *q++=byte;
1112 bit=0;
1113 byte=0;
1114 }
cristyed231572011-07-14 02:18:59 +00001115 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001116 }
1117 if (bit != 0)
1118 *q++=byte << (8-bit);
cristy0553bd52013-06-30 15:53:50 +00001119 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001120 break;
1121 if (image->previous == (Image *) NULL)
1122 {
cristy1f9852b2010-09-04 15:05:36 +00001123 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1124 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001125 if (status == MagickFalse)
1126 break;
1127 }
1128 }
1129 }
1130 (void) WriteBlobByte(image,pcx_info.colormap_signature);
1131 (void) WriteBlob(image,3*256,pcx_colormap);
1132 }
cristy0553bd52013-06-30 15:53:50 +00001133 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001134 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
cristyc044c482010-03-03 03:21:55 +00001135 if (page_table == (MagickOffsetType *) NULL)
1136 break;
1137 if (scene >= 1023)
1138 break;
cristy3ed852e2009-09-05 21:47:34 +00001139 if (GetNextImageInList(image) == (Image *) NULL)
1140 break;
1141 image=SyncNextImageInList(image);
1142 status=SetImageProgress(image,SaveImagesTag,scene++,
1143 GetImageListLength(image));
1144 if (status == MagickFalse)
1145 break;
cristy3ed852e2009-09-05 21:47:34 +00001146 } while (image_info->adjoin != MagickFalse);
1147 if (page_table != (MagickOffsetType *) NULL)
1148 {
1149 /*
1150 Write the DCX page table.
1151 */
1152 page_table[scene+1]=0;
1153 offset=SeekBlob(image,0L,SEEK_SET);
1154 if (offset < 0)
1155 ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1156 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
cristybb503372010-05-27 20:51:26 +00001157 for (i=0; i <= (ssize_t) scene; i++)
cristy0b29b252010-05-30 01:59:46 +00001158 (void) WriteBlobLSBLong(image,(unsigned int) page_table[i]);
cristy3ed852e2009-09-05 21:47:34 +00001159 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1160 }
1161 if (status == MagickFalse)
1162 {
1163 char
1164 *message;
1165
1166 message=GetExceptionMessage(errno);
cristy1e178e72011-08-28 19:44:34 +00001167 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1168 "UnableToWriteFile","`%s': %s",image->filename,message);
cristy3ed852e2009-09-05 21:47:34 +00001169 message=DestroyString(message);
1170 }
1171 (void) CloseBlob(image);
1172 return(MagickTrue);
1173}