blob: 8505d50c3082de583f5ea2d8f1856c5e96d43efe [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% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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);
263 assert(image_info->signature == MagickSignature);
264 if (image_info->debug != MagickFalse)
265 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
266 image_info->filename);
267 assert(exception != (ExceptionInfo *) NULL);
268 assert(exception->signature == MagickSignature);
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
710 entry=SetMagickInfo("DCX");
711 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;
715 entry->description=ConstantString("ZSoft IBM PC multi-page Paintbrush");
716 entry->module=ConstantString("PCX");
717 (void) RegisterMagickInfo(entry);
718 entry=SetMagickInfo("PCX");
719 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
720 entry->encoder=(EncodeImageHandler *) WritePCXImage;
721 entry->magick=(IsImageFormatHandler *) IsPCX;
dirk08e9a112015-02-22 01:51:41 +0000722 entry->flags^=CoderAdjoinFlag;
723 entry->flags|=CoderSeekableStreamFlag;
cristy3ed852e2009-09-05 21:47:34 +0000724 entry->description=ConstantString("ZSoft IBM PC Paintbrush");
725 entry->module=ConstantString("PCX");
726 (void) RegisterMagickInfo(entry);
727 return(MagickImageCoderSignature);
728}
729
730/*
731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732% %
733% %
734% %
735% U n r e g i s t e r P C X I m a g e %
736% %
737% %
738% %
739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740%
741% UnregisterPCXImage() removes format registrations made by the
742% PCX module from the list of supported formats.
743%
744% The format of the UnregisterPCXImage method is:
745%
746% UnregisterPCXImage(void)
747%
748*/
749ModuleExport void UnregisterPCXImage(void)
750{
751 (void) UnregisterMagickInfo("DCX");
752 (void) UnregisterMagickInfo("PCX");
753}
754
755/*
756%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757% %
758% %
759% %
760% W r i t e P C X I m a g e %
761% %
762% %
763% %
764%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
765%
766% WritePCXImage() writes an image in the ZSoft IBM PC Paintbrush file
767% format.
768%
769% The format of the WritePCXImage method is:
770%
cristy1e178e72011-08-28 19:44:34 +0000771% MagickBooleanType WritePCXImage(const ImageInfo *image_info,
772% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000773%
774% A description of each parameter follows.
775%
776% o image_info: the image info.
777%
778% o image: The image.
779%
cristy1e178e72011-08-28 19:44:34 +0000780% o exception: return any errors or warnings in this structure.
cristy3ed852e2009-09-05 21:47:34 +0000781%
782*/
cristy1e178e72011-08-28 19:44:34 +0000783
cristy3ed852e2009-09-05 21:47:34 +0000784static MagickBooleanType PCXWritePixels(PCXInfo *pcx_info,
785 const unsigned char *pixels,Image *image)
786{
787 register const unsigned char
788 *q;
789
cristybb503372010-05-27 20:51:26 +0000790 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000791 i,
792 x;
793
794 ssize_t
795 count;
796
797 unsigned char
798 packet,
799 previous;
800
801 q=pixels;
cristybb503372010-05-27 20:51:26 +0000802 for (i=0; i < (ssize_t) pcx_info->planes; i++)
cristy3ed852e2009-09-05 21:47:34 +0000803 {
cristy1f9852b2010-09-04 15:05:36 +0000804 if (pcx_info->encoding == 0)
cristy3ed852e2009-09-05 21:47:34 +0000805 {
cristy1f9852b2010-09-04 15:05:36 +0000806 for (x=0; x < (ssize_t) pcx_info->bytes_per_line; x++)
807 (void) WriteBlobByte(image,(unsigned char) (*q++));
cristy3ed852e2009-09-05 21:47:34 +0000808 }
cristy1f9852b2010-09-04 15:05:36 +0000809 else
810 {
811 previous=(*q++);
812 count=1;
813 for (x=0; x < (ssize_t) (pcx_info->bytes_per_line-1); x++)
814 {
815 packet=(*q++);
816 if ((packet == previous) && (count < 63))
817 {
818 count++;
819 continue;
820 }
821 if ((count > 1) || ((previous & 0xc0) == 0xc0))
822 {
823 count|=0xc0;
824 (void) WriteBlobByte(image,(unsigned char) count);
825 }
826 (void) WriteBlobByte(image,previous);
827 previous=packet;
828 count=1;
829 }
830 if ((count > 1) || ((previous & 0xc0) == 0xc0))
831 {
832 count|=0xc0;
833 (void) WriteBlobByte(image,(unsigned char) count);
834 }
835 (void) WriteBlobByte(image,previous);
836 }
cristy3ed852e2009-09-05 21:47:34 +0000837 }
838 return (MagickTrue);
839}
840
cristy1e178e72011-08-28 19:44:34 +0000841static MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image,
842 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000843{
cristy3ed852e2009-09-05 21:47:34 +0000844 MagickBooleanType
845 status;
846
847 MagickOffsetType
848 offset,
849 *page_table,
850 scene;
851
cristy0553bd52013-06-30 15:53:50 +0000852 MemoryInfo
853 *pixel_info;
854
cristy3ed852e2009-09-05 21:47:34 +0000855 PCXInfo
856 pcx_info;
857
cristy4c08aed2011-07-01 19:47:50 +0000858 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000859 *p;
860
cristybb503372010-05-27 20:51:26 +0000861 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000862 i,
863 x;
864
865 register unsigned char
866 *q;
867
868 size_t
869 length;
870
cristyaff6d802011-04-26 01:46:31 +0000871 ssize_t
872 y;
873
cristy3ed852e2009-09-05 21:47:34 +0000874 unsigned char
875 *pcx_colormap,
cristy0553bd52013-06-30 15:53:50 +0000876 *pixels;
cristy3ed852e2009-09-05 21:47:34 +0000877
878 /*
879 Open output image file.
880 */
881 assert(image_info != (const ImageInfo *) NULL);
882 assert(image_info->signature == MagickSignature);
883 assert(image != (Image *) NULL);
884 assert(image->signature == MagickSignature);
885 if (image->debug != MagickFalse)
886 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000887 assert(exception != (ExceptionInfo *) NULL);
888 assert(exception->signature == MagickSignature);
cristy1e178e72011-08-28 19:44:34 +0000889 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000890 if (status == MagickFalse)
891 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000892 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000893 page_table=(MagickOffsetType *) NULL;
894 if ((LocaleCompare(image_info->magick,"DCX") == 0) ||
895 ((GetNextImageInList(image) != (Image *) NULL) &&
896 (image_info->adjoin != MagickFalse)))
897 {
898 /*
899 Write the DCX page table.
900 */
901 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
902 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
903 sizeof(*page_table));
904 if (page_table == (MagickOffsetType *) NULL)
905 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
906 for (scene=0; scene < 1024; scene++)
907 (void) WriteBlobLSBLong(image,0x00000000L);
908 }
909 scene=0;
910 do
911 {
912 if (page_table != (MagickOffsetType *) NULL)
913 page_table[scene]=TellBlob(image);
914 /*
915 Initialize PCX raster file header.
916 */
917 pcx_info.identifier=0x0a;
918 pcx_info.version=5;
cristy1f9852b2010-09-04 15:05:36 +0000919 pcx_info.encoding=image_info->compression == NoCompression ? 0 : 1;
cristy3ed852e2009-09-05 21:47:34 +0000920 pcx_info.bits_per_pixel=8;
921 if ((image->storage_class == PseudoClass) &&
dirkf1d85482015-04-06 00:36:00 +0000922 (SetImageMonochrome(image,exception) != MagickFalse))
cristy3ed852e2009-09-05 21:47:34 +0000923 pcx_info.bits_per_pixel=1;
924 pcx_info.left=0;
925 pcx_info.top=0;
926 pcx_info.right=(unsigned short) (image->columns-1);
927 pcx_info.bottom=(unsigned short) (image->rows-1);
928 switch (image->units)
929 {
930 case UndefinedResolution:
931 case PixelsPerInchResolution:
932 default:
933 {
cristy2a11bef2011-10-28 18:33:11 +0000934 pcx_info.horizontal_resolution=(unsigned short) image->resolution.x;
935 pcx_info.vertical_resolution=(unsigned short) image->resolution.y;
cristy3ed852e2009-09-05 21:47:34 +0000936 break;
937 }
938 case PixelsPerCentimeterResolution:
939 {
940 pcx_info.horizontal_resolution=(unsigned short)
cristy2a11bef2011-10-28 18:33:11 +0000941 (2.54*image->resolution.x+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000942 pcx_info.vertical_resolution=(unsigned short)
cristy2a11bef2011-10-28 18:33:11 +0000943 (2.54*image->resolution.y+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000944 break;
945 }
946 }
947 pcx_info.reserved=0;
948 pcx_info.planes=1;
949 if ((image->storage_class == DirectClass) || (image->colors > 256))
950 {
951 pcx_info.planes=3;
cristy17f11b02014-12-20 19:37:04 +0000952 if (image->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000953 pcx_info.planes++;
954 }
cristybb503372010-05-27 20:51:26 +0000955 pcx_info.bytes_per_line=(unsigned short) (((size_t) image->columns*
cristy3ed852e2009-09-05 21:47:34 +0000956 pcx_info.bits_per_pixel+7)/8);
957 pcx_info.palette_info=1;
958 pcx_info.colormap_signature=0x0c;
959 /*
960 Write PCX header.
961 */
962 (void) WriteBlobByte(image,pcx_info.identifier);
963 (void) WriteBlobByte(image,pcx_info.version);
964 (void) WriteBlobByte(image,pcx_info.encoding);
965 (void) WriteBlobByte(image,pcx_info.bits_per_pixel);
966 (void) WriteBlobLSBShort(image,pcx_info.left);
967 (void) WriteBlobLSBShort(image,pcx_info.top);
968 (void) WriteBlobLSBShort(image,pcx_info.right);
969 (void) WriteBlobLSBShort(image,pcx_info.bottom);
970 (void) WriteBlobLSBShort(image,pcx_info.horizontal_resolution);
971 (void) WriteBlobLSBShort(image,pcx_info.vertical_resolution);
972 /*
973 Dump colormap to file.
974 */
975 pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL,
976 3*sizeof(*pcx_colormap));
977 if (pcx_colormap == (unsigned char *) NULL)
978 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
979 (void) memset(pcx_colormap,0,3*256*sizeof(*pcx_colormap));
980 q=pcx_colormap;
981 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
cristybb503372010-05-27 20:51:26 +0000982 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000983 {
984 *q++=ScaleQuantumToChar(image->colormap[i].red);
985 *q++=ScaleQuantumToChar(image->colormap[i].green);
986 *q++=ScaleQuantumToChar(image->colormap[i].blue);
987 }
988 (void) WriteBlob(image,3*16,(const unsigned char *) pcx_colormap);
989 (void) WriteBlobByte(image,pcx_info.reserved);
990 (void) WriteBlobByte(image,pcx_info.planes);
991 (void) WriteBlobLSBShort(image,pcx_info.bytes_per_line);
992 (void) WriteBlobLSBShort(image,pcx_info.palette_info);
993 for (i=0; i < 58; i++)
994 (void) WriteBlobByte(image,'\0');
995 length=(size_t) pcx_info.bytes_per_line;
cristy0553bd52013-06-30 15:53:50 +0000996 pixel_info=AcquireVirtualMemory(length,pcx_info.planes*sizeof(*pixels));
997 if (pixel_info == (MemoryInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000998 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
cristy0553bd52013-06-30 15:53:50 +0000999 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1000 q=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001001 if ((image->storage_class == DirectClass) || (image->colors > 256))
1002 {
cristy3ed852e2009-09-05 21:47:34 +00001003 /*
1004 Convert DirectClass image to PCX raster pixels.
1005 */
cristybb503372010-05-27 20:51:26 +00001006 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001007 {
cristy0553bd52013-06-30 15:53:50 +00001008 q=pixels;
cristy3ed852e2009-09-05 21:47:34 +00001009 for (i=0; i < pcx_info.planes; i++)
1010 {
cristy0acf7e12013-06-30 23:35:36 +00001011 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1012 if (p == (const Quantum *) NULL)
1013 break;
cristy3ed852e2009-09-05 21:47:34 +00001014 switch ((int) i)
1015 {
1016 case 0:
1017 {
cristybb503372010-05-27 20:51:26 +00001018 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001019 {
cristy4c08aed2011-07-01 19:47:50 +00001020 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
cristyed231572011-07-14 02:18:59 +00001021 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001022 }
1023 break;
1024 }
1025 case 1:
1026 {
cristybb503372010-05-27 20:51:26 +00001027 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001028 {
cristy4c08aed2011-07-01 19:47:50 +00001029 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
cristyed231572011-07-14 02:18:59 +00001030 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001031 }
1032 break;
1033 }
1034 case 2:
1035 {
cristybb503372010-05-27 20:51:26 +00001036 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
cristy3ed852e2009-09-05 21:47:34 +00001037 {
cristy4c08aed2011-07-01 19:47:50 +00001038 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
cristyed231572011-07-14 02:18:59 +00001039 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001040 }
1041 break;
1042 }
1043 case 3:
1044 default:
1045 {
cristybb503372010-05-27 20:51:26 +00001046 for (x=(ssize_t) pcx_info.bytes_per_line; x != 0; x--)
cristy3ed852e2009-09-05 21:47:34 +00001047 {
cristy4c08aed2011-07-01 19:47:50 +00001048 *q++=ScaleQuantumToChar((Quantum) (GetPixelAlpha(image,p)));
cristyed231572011-07-14 02:18:59 +00001049 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001050 }
1051 break;
1052 }
1053 }
1054 }
cristy0553bd52013-06-30 15:53:50 +00001055 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001056 break;
1057 if (image->previous == (Image *) NULL)
1058 {
cristycee97112010-05-28 00:44:52 +00001059 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1060 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001061 if (status == MagickFalse)
1062 break;
1063 }
1064 }
1065 }
1066 else
1067 {
1068 if (pcx_info.bits_per_pixel > 1)
cristybb503372010-05-27 20:51:26 +00001069 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001070 {
cristy1e178e72011-08-28 19:44:34 +00001071 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001072 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001073 break;
cristy0553bd52013-06-30 15:53:50 +00001074 q=pixels;
cristybb503372010-05-27 20:51:26 +00001075 for (x=0; x < (ssize_t) image->columns; x++)
cristy4c08aed2011-07-01 19:47:50 +00001076 {
1077 *q++=(unsigned char) GetPixelIndex(image,p);
cristyed231572011-07-14 02:18:59 +00001078 p+=GetPixelChannels(image);
cristy4c08aed2011-07-01 19:47:50 +00001079 }
cristy0553bd52013-06-30 15:53:50 +00001080 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001081 break;
1082 if (image->previous == (Image *) NULL)
1083 {
cristycee97112010-05-28 00:44:52 +00001084 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1085 image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001086 if (status == MagickFalse)
1087 break;
1088 }
1089 }
1090 else
1091 {
cristy3ed852e2009-09-05 21:47:34 +00001092 register unsigned char
1093 bit,
1094 byte;
1095
1096 /*
1097 Convert PseudoClass image to a PCX monochrome image.
1098 */
cristybb503372010-05-27 20:51:26 +00001099 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001100 {
cristy1e178e72011-08-28 19:44:34 +00001101 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001102 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001103 break;
cristy3ed852e2009-09-05 21:47:34 +00001104 bit=0;
1105 byte=0;
cristy0553bd52013-06-30 15:53:50 +00001106 q=pixels;
cristybb503372010-05-27 20:51:26 +00001107 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001108 {
1109 byte<<=1;
cristy85636be2014-04-04 19:16:53 +00001110 if (GetPixelLuma(image,p) >= (QuantumRange/2.0))
cristy3ed852e2009-09-05 21:47:34 +00001111 byte|=0x01;
1112 bit++;
1113 if (bit == 8)
1114 {
1115 *q++=byte;
1116 bit=0;
1117 byte=0;
1118 }
cristyed231572011-07-14 02:18:59 +00001119 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001120 }
1121 if (bit != 0)
1122 *q++=byte << (8-bit);
cristy0553bd52013-06-30 15:53:50 +00001123 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +00001124 break;
1125 if (image->previous == (Image *) NULL)
1126 {
cristy1f9852b2010-09-04 15:05:36 +00001127 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1128 y,image->rows);
cristy3ed852e2009-09-05 21:47:34 +00001129 if (status == MagickFalse)
1130 break;
1131 }
1132 }
1133 }
1134 (void) WriteBlobByte(image,pcx_info.colormap_signature);
1135 (void) WriteBlob(image,3*256,pcx_colormap);
1136 }
cristy0553bd52013-06-30 15:53:50 +00001137 pixel_info=RelinquishVirtualMemory(pixel_info);
cristy3ed852e2009-09-05 21:47:34 +00001138 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
cristyc044c482010-03-03 03:21:55 +00001139 if (page_table == (MagickOffsetType *) NULL)
1140 break;
1141 if (scene >= 1023)
1142 break;
cristy3ed852e2009-09-05 21:47:34 +00001143 if (GetNextImageInList(image) == (Image *) NULL)
1144 break;
1145 image=SyncNextImageInList(image);
1146 status=SetImageProgress(image,SaveImagesTag,scene++,
1147 GetImageListLength(image));
1148 if (status == MagickFalse)
1149 break;
cristy3ed852e2009-09-05 21:47:34 +00001150 } while (image_info->adjoin != MagickFalse);
1151 if (page_table != (MagickOffsetType *) NULL)
1152 {
1153 /*
1154 Write the DCX page table.
1155 */
1156 page_table[scene+1]=0;
1157 offset=SeekBlob(image,0L,SEEK_SET);
1158 if (offset < 0)
1159 ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1160 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
cristybb503372010-05-27 20:51:26 +00001161 for (i=0; i <= (ssize_t) scene; i++)
cristy0b29b252010-05-30 01:59:46 +00001162 (void) WriteBlobLSBLong(image,(unsigned int) page_table[i]);
cristy3ed852e2009-09-05 21:47:34 +00001163 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1164 }
1165 if (status == MagickFalse)
1166 {
1167 char
1168 *message;
1169
1170 message=GetExceptionMessage(errno);
cristy1e178e72011-08-28 19:44:34 +00001171 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1172 "UnableToWriteFile","`%s': %s",image->filename,message);
cristy3ed852e2009-09-05 21:47:34 +00001173 message=DestroyString(message);
1174 }
1175 (void) CloseBlob(image);
1176 return(MagickTrue);
1177}