blob: 992966352771ed6d5358712d516c0e21fffec37e [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 %
16% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 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*/
42#include "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
cristye7e40552010-04-24 21:34:22 +000048#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000049#include "magick/colorspace.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/image.h"
53#include "magick/image-private.h"
54#include "magick/list.h"
55#include "magick/magick.h"
56#include "magick/memory_.h"
57#include "magick/monitor.h"
58#include "magick/monitor-private.h"
59#include "magick/quantum-private.h"
60#include "magick/static.h"
61#include "magick/string_.h"
62#include "magick/module.h"
63
64/*
65 Typedef declarations.
66*/
67typedef struct _PCXInfo
68{
69 unsigned char
70 identifier,
71 version,
72 encoding,
73 bits_per_pixel;
74
75 unsigned short
76 left,
77 top,
78 right,
79 bottom,
80 horizontal_resolution,
81 vertical_resolution;
82
83 unsigned char
84 reserved,
85 planes;
86
87 unsigned short
88 bytes_per_line,
89 palette_info;
90
91 unsigned char
92 colormap_signature;
93} PCXInfo;
94
95/*
96 Forward declarations.
97*/
98static MagickBooleanType
99 WritePCXImage(const ImageInfo *,Image *);
100
101/*
102%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
103% %
104% %
105% %
106% I s D C X %
107% %
108% %
109% %
110%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
111%
112% IsDCX() returns MagickTrue if the image format type, identified by the
113% magick string, is DCX.
114%
115% The format of the IsDCX method is:
116%
117% MagickBooleanType IsDCX(const unsigned char *magick,const size_t length)
118%
119% A description of each parameter follows:
120%
121% o magick: compare image format pattern against these bytes.
122%
123% o length: Specifies the length of the magick string.
124%
125%
126*/
127static MagickBooleanType IsDCX(const unsigned char *magick,const size_t length)
128{
129 if (length < 4)
130 return(MagickFalse);
131 if (memcmp(magick,"\261\150\336\72",4) == 0)
132 return(MagickTrue);
133 return(MagickFalse);
134}
135
136/*
137%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
138% %
139% %
140% %
141% I s P C X %
142% %
143% %
144% %
145%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
146%
147% IsPCX() returns MagickTrue if the image format type, identified by the
148% magick string, is PCX.
149%
150% The format of the IsPCX method is:
151%
152% MagickBooleanType IsPCX(const unsigned char *magick,const size_t length)
153%
154% A description of each parameter follows:
155%
156% o magick: compare image format pattern against these bytes.
157%
158% o length: Specifies the length of the magick string.
159%
160%
161*/
162static MagickBooleanType IsPCX(const unsigned char *magick,const size_t length)
163{
164 if (length < 2)
165 return(MagickFalse);
166 if (memcmp(magick,"\012\002",2) == 0)
167 return(MagickTrue);
168 if (memcmp(magick,"\012\005",2) == 0)
169 return(MagickTrue);
170 return(MagickFalse);
171}
172
173/*
174%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
175% %
176% %
177% %
178% R e a d P C X I m a g e %
179% %
180% %
181% %
182%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
183%
184% ReadPCXImage() reads a ZSoft IBM PC Paintbrush file and returns it.
185% It allocates the memory necessary for the new Image structure and returns
186% a pointer to the new image.
187%
188% The format of the ReadPCXImage method is:
189%
190% Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
191%
192% A description of each parameter follows:
193%
194% o image_info: the image info.
195%
196% o exception: return any errors or warnings in this structure.
197%
198*/
199
200static inline long MagickAbsoluteValue(const long x)
201{
202 if (x < 0)
203 return(-x);
204 return(x);
205}
206
207static inline size_t MagickMax(const size_t x,const size_t y)
208{
209 if (x > y)
210 return(x);
211 return(y);
212}
213
214static inline size_t MagickMin(const size_t x,const size_t y)
215{
216 if (x < y)
217 return(x);
218 return(y);
219}
220
221static Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
222{
223 Image
224 *image;
225
226 int
227 bits,
228 id,
229 mask;
230
231 long
232 y;
233
234 MagickBooleanType
235 status;
236
237 MagickOffsetType
238 offset,
239 *page_table;
240
241 PCXInfo
242 pcx_info;
243
244 register IndexPacket
245 *indexes;
246
247 register long
248 x;
249
250 register PixelPacket
251 *q;
252
253 register long
254 i;
255
256 register unsigned char
257 *p,
258 *r;
259
260 ssize_t
261 count;
262
263 unsigned char
264 packet,
265 *pcx_colormap,
266 *pcx_pixels,
267 *scanline;
268
269 unsigned long
270 pcx_packets;
271
272 /*
273 Open image file.
274 */
275 assert(image_info != (const ImageInfo *) NULL);
276 assert(image_info->signature == MagickSignature);
277 if (image_info->debug != MagickFalse)
278 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
279 image_info->filename);
280 assert(exception != (ExceptionInfo *) NULL);
281 assert(exception->signature == MagickSignature);
282 image=AcquireImage(image_info);
283 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
284 if (status == MagickFalse)
285 {
286 image=DestroyImageList(image);
287 return((Image *) NULL);
288 }
289 /*
290 Determine if this a PCX file.
291 */
292 page_table=(MagickOffsetType *) NULL;
293 if (LocaleCompare(image_info->magick,"DCX") == 0)
294 {
295 unsigned long
296 magic;
297
298 /*
299 Read the DCX page table.
300 */
301 magic=ReadBlobLSBLong(image);
302 if (magic != 987654321)
303 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
304 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
305 sizeof(*page_table));
306 if (page_table == (MagickOffsetType *) NULL)
307 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
308 for (id=0; id < 1024; id++)
309 {
310 page_table[id]=(MagickOffsetType) ReadBlobLSBLong(image);
311 if (page_table[id] == 0)
312 break;
313 }
314 }
315 if (page_table != (MagickOffsetType *) NULL)
316 {
317 offset=SeekBlob(image,(MagickOffsetType) page_table[0],SEEK_SET);
318 if (offset < 0)
319 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
320 }
321 pcx_colormap=(unsigned char *) NULL;
322 count=ReadBlob(image,1,&pcx_info.identifier);
323 for (id=1; id < 1024; id++)
324 {
325 /*
326 Verify PCX identifier.
327 */
328 pcx_info.version=(unsigned char) ReadBlobByte(image);
329 if ((count == 0) || (pcx_info.identifier != 0x0a))
330 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
331 pcx_info.encoding=(unsigned char) ReadBlobByte(image);
332 pcx_info.bits_per_pixel=(unsigned char) ReadBlobByte(image);
333 pcx_info.left=ReadBlobLSBShort(image);
334 pcx_info.top=ReadBlobLSBShort(image);
335 pcx_info.right=ReadBlobLSBShort(image);
336 pcx_info.bottom=ReadBlobLSBShort(image);
337 pcx_info.horizontal_resolution=ReadBlobLSBShort(image);
338 pcx_info.vertical_resolution=ReadBlobLSBShort(image);
339 /*
340 Read PCX raster colormap.
341 */
342 image->columns=(unsigned long) MagickAbsoluteValue((long) pcx_info.right-
343 pcx_info.left)+1UL;
344 image->rows=(unsigned long) MagickAbsoluteValue((long) pcx_info.bottom-
345 pcx_info.top)+1UL;
346 if ((image->columns == 0) || (image->rows == 0) ||
347 (pcx_info.bits_per_pixel == 0))
348 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
349 image->depth=pcx_info.bits_per_pixel <= 8 ? 8U : MAGICKCORE_QUANTUM_DEPTH;
350 image->units=PixelsPerInchResolution;
351 image->x_resolution=(double) pcx_info.horizontal_resolution;
352 image->y_resolution=(double) pcx_info.vertical_resolution;
353 image->colors=16;
354 pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL,
355 3*sizeof(*pcx_colormap));
356 if (pcx_colormap == (unsigned char *) NULL)
357 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
358 count=ReadBlob(image,3*image->colors,pcx_colormap);
359 pcx_info.reserved=(unsigned char) ReadBlobByte(image);
360 pcx_info.planes=(unsigned char) ReadBlobByte(image);
361 if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1))
362 if ((pcx_info.version == 3) || (pcx_info.version == 5) ||
363 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
364 image->colors=(unsigned long) MagickMin(1UL << (1UL*
365 (pcx_info.bits_per_pixel*pcx_info.planes)),256UL);
366 if (AcquireImageColormap(image,image->colors) == MagickFalse)
367 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
368 if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1))
369 image->storage_class=DirectClass;
370 p=pcx_colormap;
371 for (i=0; i < (long) image->colors; i++)
372 {
373 image->colormap[i].red=ScaleCharToQuantum(*p++);
374 image->colormap[i].green=ScaleCharToQuantum(*p++);
375 image->colormap[i].blue=ScaleCharToQuantum(*p++);
376 }
377 pcx_info.bytes_per_line=ReadBlobLSBShort(image);
378 pcx_info.palette_info=ReadBlobLSBShort(image);
379 for (i=0; i < 58; i++)
380 (void) ReadBlobByte(image);
381 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
382 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
383 break;
384 /*
385 Read image data.
386 */
387 pcx_packets=(unsigned long) image->rows*pcx_info.bytes_per_line*
388 pcx_info.planes;
389 pcx_pixels=(unsigned char *) AcquireQuantumMemory(pcx_packets,
390 sizeof(*pcx_pixels));
391 scanline=(unsigned char *) AcquireQuantumMemory(MagickMax(image->columns,
392 pcx_info.bytes_per_line),MagickMax(8,pcx_info.planes)*sizeof(*scanline));
393 if ((pcx_pixels == (unsigned char *) NULL) ||
394 (scanline == (unsigned char *) NULL))
395 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
396 /*
397 Uncompress image data.
398 */
399 p=pcx_pixels;
400 if (pcx_info.encoding == 0)
401 while (pcx_packets != 0)
402 {
403 packet=(unsigned char) ReadBlobByte(image);
404 if (EOFBlob(image) != MagickFalse)
405 break;
406 *p++=packet;
407 pcx_packets--;
408 }
409 else
410 while (pcx_packets != 0)
411 {
412 packet=(unsigned char) ReadBlobByte(image);
413 if (EOFBlob(image) != MagickFalse)
414 break;
415 if ((packet & 0xc0) != 0xc0)
416 {
417 *p++=packet;
418 pcx_packets--;
419 continue;
420 }
421 count=(ssize_t) (packet & 0x3f);
422 packet=(unsigned char) ReadBlobByte(image);
423 if (EOFBlob(image) != MagickFalse)
424 break;
425 for ( ; count != 0; count--)
426 {
427 *p++=packet;
428 pcx_packets--;
429 if (pcx_packets == 0)
430 break;
431 }
432 }
433 if (image->storage_class == DirectClass)
434 image->matte=pcx_info.planes > 3 ? MagickTrue : MagickFalse;
435 else
436 if ((pcx_info.version == 5) ||
437 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
438 {
439 /*
440 Initialize image colormap.
441 */
442 if (image->colors > 256)
443 ThrowReaderException(CorruptImageError,"ColormapExceeds256Colors");
444 if ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)
445 {
446 /*
447 Monochrome colormap.
448 */
449 image->colormap[0].red=(Quantum) 0;
450 image->colormap[0].green=(Quantum) 0;
451 image->colormap[0].blue=(Quantum) 0;
452 image->colormap[1].red=(Quantum) QuantumRange;
453 image->colormap[1].green=(Quantum) QuantumRange;
454 image->colormap[1].blue=(Quantum) QuantumRange;
455 }
456 else
457 if (image->colors > 16)
458 {
459 /*
460 256 color images have their color map at the end of the file.
461 */
462 pcx_info.colormap_signature=(unsigned char) ReadBlobByte(image);
463 count=ReadBlob(image,3*image->colors,pcx_colormap);
464 p=pcx_colormap;
465 for (i=0; i < (long) image->colors; i++)
466 {
467 image->colormap[i].red=ScaleCharToQuantum(*p++);
468 image->colormap[i].green=ScaleCharToQuantum(*p++);
469 image->colormap[i].blue=ScaleCharToQuantum(*p++);
470 }
471 }
472 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
473 }
474 /*
475 Convert PCX raster image to pixel packets.
476 */
477 for (y=0; y < (long) image->rows; y++)
478 {
479 p=pcx_pixels+(y*pcx_info.bytes_per_line*pcx_info.planes);
480 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
481 if (q == (PixelPacket *) NULL)
482 break;
483 indexes=GetAuthenticIndexQueue(image);
484 r=scanline;
485 if (image->storage_class == DirectClass)
486 for (i=0; i < pcx_info.planes; i++)
487 {
488 r=scanline+i;
489 for (x=0; x < (long) pcx_info.bytes_per_line; x++)
490 {
491 switch (i)
492 {
493 case 0:
494 {
495 *r=(*p++);
496 break;
497 }
498 case 1:
499 {
500 *r=(*p++);
501 break;
502 }
503 case 2:
504 {
505 *r=(*p++);
506 break;
507 }
508 case 3:
509 default:
510 {
511 *r=(*p++);
512 break;
513 }
514 }
515 r+=pcx_info.planes;
516 }
517 }
518 else
519 if (pcx_info.planes > 1)
520 {
521 for (x=0; x < (long) image->columns; x++)
522 *r++=0;
523 for (i=0; i < pcx_info.planes; i++)
524 {
525 r=scanline;
526 for (x=0; x < (long) pcx_info.bytes_per_line; x++)
527 {
528 bits=(*p++);
529 for (mask=0x80; mask != 0; mask>>=1)
530 {
531 if (bits & mask)
532 *r|=1 << i;
533 r++;
534 }
535 }
536 }
537 }
538 else
539 switch (pcx_info.bits_per_pixel)
540 {
541 case 1:
542 {
543 register long
544 bit;
545
546 for (x=0; x < ((long) image->columns-7); x+=8)
547 {
548 for (bit=7; bit >= 0; bit--)
549 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
550 p++;
551 }
552 if ((image->columns % 8) != 0)
553 {
554 for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--)
555 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
556 p++;
557 }
558 break;
559 }
560 case 2:
561 {
562 for (x=0; x < ((long) image->columns-3); x+=4)
563 {
564 *r++=(*p >> 6) & 0x3;
565 *r++=(*p >> 4) & 0x3;
566 *r++=(*p >> 2) & 0x3;
567 *r++=(*p) & 0x3;
568 p++;
569 }
570 if ((image->columns % 4) != 0)
571 {
572 for (i=3; i >= (long) (4-(image->columns % 4)); i--)
573 *r++=(unsigned char) ((*p >> (i*2)) & 0x03);
574 p++;
575 }
576 break;
577 }
578 case 4:
579 {
580 for (x=0; x < ((long) image->columns-1); x+=2)
581 {
582 *r++=(*p >> 4) & 0xf;
583 *r++=(*p) & 0xf;
584 p++;
585 }
586 if ((image->columns % 2) != 0)
587 *r++=(*p++ >> 4) & 0xf;
588 break;
589 }
590 case 8:
591 {
592 (void) CopyMagickMemory(r,p,image->columns);
593 break;
594 }
595 default:
596 break;
597 }
598 /*
599 Transfer image scanline.
600 */
601 r=scanline;
602 for (x=0; x < (long) image->columns; x++)
603 {
604 if (image->storage_class == PseudoClass)
605 indexes[x]=(IndexPacket) (*r++);
606 else
607 {
608 q->red=ScaleCharToQuantum(*r++);
609 q->green=ScaleCharToQuantum(*r++);
610 q->blue=ScaleCharToQuantum(*r++);
611 if (image->matte != MagickFalse)
612 q->opacity=(Quantum) (QuantumRange-ScaleCharToQuantum(*r++));
613 }
614 q++;
615 }
616 if (SyncAuthenticPixels(image,exception) == MagickFalse)
617 break;
618 if (image->previous == (Image *) NULL)
619 {
620 status=SetImageProgress(image,LoadImageTag,y,image->rows);
621 if (status == MagickFalse)
622 break;
623 }
624 }
625 if (image->storage_class == PseudoClass)
626 (void) SyncImage(image);
627 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
628 if (pcx_colormap != (unsigned char *) NULL)
629 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
630 pcx_pixels=(unsigned char *) RelinquishMagickMemory(pcx_pixels);
631 if (EOFBlob(image) != MagickFalse)
632 {
633 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
634 image->filename);
635 break;
636 }
637 /*
638 Proceed to next image.
639 */
640 if (image_info->number_scenes != 0)
641 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
642 break;
643 if (page_table == (MagickOffsetType *) NULL)
644 break;
645 if (page_table[id] == 0)
646 break;
647 offset=SeekBlob(image,(MagickOffsetType) page_table[id],SEEK_SET);
648 if (offset < 0)
649 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
650 count=ReadBlob(image,1,&pcx_info.identifier);
651 if ((count != 0) && (pcx_info.identifier == 0x0a))
652 {
653 /*
654 Allocate next image structure.
655 */
656 AcquireNextImage(image_info,image);
657 if (GetNextImageInList(image) == (Image *) NULL)
658 {
659 image=DestroyImageList(image);
660 return((Image *) NULL);
661 }
662 image=SyncNextImageInList(image);
663 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
664 GetBlobSize(image));
665 if (status == MagickFalse)
666 break;
667 }
668 }
669 if (page_table != (MagickOffsetType *) NULL)
670 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
671 (void) CloseBlob(image);
672 return(GetFirstImageInList(image));
673}
674
675/*
676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677% %
678% %
679% %
680% R e g i s t e r P C X I m a g e %
681% %
682% %
683% %
684%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
685%
686% RegisterPCXImage() adds attributes for the PCX image format to
687% the list of supported formats. The attributes include the image format
688% tag, a method to read and/or write the format, whether the format
689% supports the saving of more than one frame to the same file or blob,
690% whether the format supports native in-memory I/O, and a brief
691% description of the format.
692%
693% The format of the RegisterPCXImage method is:
694%
695% unsigned long RegisterPCXImage(void)
696%
697*/
698ModuleExport unsigned long RegisterPCXImage(void)
699{
700 MagickInfo
701 *entry;
702
703 entry=SetMagickInfo("DCX");
704 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
705 entry->encoder=(EncodeImageHandler *) WritePCXImage;
706 entry->seekable_stream=MagickTrue;
707 entry->magick=(IsImageFormatHandler *) IsDCX;
708 entry->description=ConstantString("ZSoft IBM PC multi-page Paintbrush");
709 entry->module=ConstantString("PCX");
710 (void) RegisterMagickInfo(entry);
711 entry=SetMagickInfo("PCX");
712 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
713 entry->encoder=(EncodeImageHandler *) WritePCXImage;
714 entry->magick=(IsImageFormatHandler *) IsPCX;
715 entry->adjoin=MagickFalse;
716 entry->seekable_stream=MagickTrue;
717 entry->description=ConstantString("ZSoft IBM PC Paintbrush");
718 entry->module=ConstantString("PCX");
719 (void) RegisterMagickInfo(entry);
720 return(MagickImageCoderSignature);
721}
722
723/*
724%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725% %
726% %
727% %
728% U n r e g i s t e r P C X I m a g e %
729% %
730% %
731% %
732%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
733%
734% UnregisterPCXImage() removes format registrations made by the
735% PCX module from the list of supported formats.
736%
737% The format of the UnregisterPCXImage method is:
738%
739% UnregisterPCXImage(void)
740%
741*/
742ModuleExport void UnregisterPCXImage(void)
743{
744 (void) UnregisterMagickInfo("DCX");
745 (void) UnregisterMagickInfo("PCX");
746}
747
748/*
749%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
750% %
751% %
752% %
753% W r i t e P C X I m a g e %
754% %
755% %
756% %
757%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758%
759% WritePCXImage() writes an image in the ZSoft IBM PC Paintbrush file
760% format.
761%
762% The format of the WritePCXImage method is:
763%
764% MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image)
765%
766% A description of each parameter follows.
767%
768% o image_info: the image info.
769%
770% o image: The image.
771%
772%
773*/
774static MagickBooleanType PCXWritePixels(PCXInfo *pcx_info,
775 const unsigned char *pixels,Image *image)
776{
777 register const unsigned char
778 *q;
779
780 register long
781 i,
782 x;
783
784 ssize_t
785 count;
786
787 unsigned char
788 packet,
789 previous;
790
791 q=pixels;
792 for (i=0; i < (long) pcx_info->planes; i++)
793 {
794 previous=(*q++);
795 count=1;
796 for (x=0; x < (long) (pcx_info->bytes_per_line-1); x++)
797 {
798 packet=(*q++);
799 if ((packet == previous) && (count < 63))
800 {
801 count++;
802 continue;
803 }
804 if ((count > 1) || ((previous & 0xc0) == 0xc0))
805 {
806 count|=0xc0;
807 (void) WriteBlobByte(image,(unsigned char) count);
808 }
809 (void) WriteBlobByte(image,previous);
810 previous=packet;
811 count=1;
812 }
813 if ((count > 1) || ((previous & 0xc0) == 0xc0))
814 {
815 count|=0xc0;
816 (void) WriteBlobByte(image,(unsigned char) count);
817 }
818 (void) WriteBlobByte(image,previous);
819 }
820 return (MagickTrue);
821}
822
823static MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image)
824{
825 long
826 y;
827
828 MagickBooleanType
829 status;
830
831 MagickOffsetType
832 offset,
833 *page_table,
834 scene;
835
836 PCXInfo
837 pcx_info;
838
839 register const IndexPacket
840 *indexes;
841
842 register const PixelPacket
843 *p;
844
845 register long
846 i,
847 x;
848
849 register unsigned char
850 *q;
851
852 size_t
853 length;
854
855 unsigned char
856 *pcx_colormap,
857 *pcx_pixels;
858
859 /*
860 Open output image file.
861 */
862 assert(image_info != (const ImageInfo *) NULL);
863 assert(image_info->signature == MagickSignature);
864 assert(image != (Image *) NULL);
865 assert(image->signature == MagickSignature);
866 if (image->debug != MagickFalse)
867 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
868 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
869 if (status == MagickFalse)
870 return(status);
871 if (image->colorspace != RGBColorspace)
872 (void) TransformImageColorspace(image,RGBColorspace);
873 page_table=(MagickOffsetType *) NULL;
874 if ((LocaleCompare(image_info->magick,"DCX") == 0) ||
875 ((GetNextImageInList(image) != (Image *) NULL) &&
876 (image_info->adjoin != MagickFalse)))
877 {
878 /*
879 Write the DCX page table.
880 */
881 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
882 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
883 sizeof(*page_table));
884 if (page_table == (MagickOffsetType *) NULL)
885 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
886 for (scene=0; scene < 1024; scene++)
887 (void) WriteBlobLSBLong(image,0x00000000L);
888 }
889 scene=0;
890 do
891 {
892 if (page_table != (MagickOffsetType *) NULL)
893 page_table[scene]=TellBlob(image);
894 /*
895 Initialize PCX raster file header.
896 */
897 pcx_info.identifier=0x0a;
898 pcx_info.version=5;
899 pcx_info.encoding=1;
900 pcx_info.bits_per_pixel=8;
901 if ((image->storage_class == PseudoClass) &&
902 (IsMonochromeImage(image,&image->exception) != MagickFalse))
903 pcx_info.bits_per_pixel=1;
904 pcx_info.left=0;
905 pcx_info.top=0;
906 pcx_info.right=(unsigned short) (image->columns-1);
907 pcx_info.bottom=(unsigned short) (image->rows-1);
908 switch (image->units)
909 {
910 case UndefinedResolution:
911 case PixelsPerInchResolution:
912 default:
913 {
914 pcx_info.horizontal_resolution=(unsigned short) image->x_resolution;
915 pcx_info.vertical_resolution=(unsigned short) image->y_resolution;
916 break;
917 }
918 case PixelsPerCentimeterResolution:
919 {
920 pcx_info.horizontal_resolution=(unsigned short)
921 (2.54*image->x_resolution+0.5);
922 pcx_info.vertical_resolution=(unsigned short)
923 (2.54*image->y_resolution+0.5);
924 break;
925 }
926 }
927 pcx_info.reserved=0;
928 pcx_info.planes=1;
929 if ((image->storage_class == DirectClass) || (image->colors > 256))
930 {
931 pcx_info.planes=3;
932 if (image->matte != MagickFalse)
933 pcx_info.planes++;
934 }
935 pcx_info.bytes_per_line=(unsigned short) (((unsigned long) image->columns*
936 pcx_info.bits_per_pixel+7)/8);
937 pcx_info.palette_info=1;
938 pcx_info.colormap_signature=0x0c;
939 /*
940 Write PCX header.
941 */
942 (void) WriteBlobByte(image,pcx_info.identifier);
943 (void) WriteBlobByte(image,pcx_info.version);
944 (void) WriteBlobByte(image,pcx_info.encoding);
945 (void) WriteBlobByte(image,pcx_info.bits_per_pixel);
946 (void) WriteBlobLSBShort(image,pcx_info.left);
947 (void) WriteBlobLSBShort(image,pcx_info.top);
948 (void) WriteBlobLSBShort(image,pcx_info.right);
949 (void) WriteBlobLSBShort(image,pcx_info.bottom);
950 (void) WriteBlobLSBShort(image,pcx_info.horizontal_resolution);
951 (void) WriteBlobLSBShort(image,pcx_info.vertical_resolution);
952 /*
953 Dump colormap to file.
954 */
955 pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL,
956 3*sizeof(*pcx_colormap));
957 if (pcx_colormap == (unsigned char *) NULL)
958 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
959 (void) memset(pcx_colormap,0,3*256*sizeof(*pcx_colormap));
960 q=pcx_colormap;
961 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
962 for (i=0; i < (long) image->colors; i++)
963 {
964 *q++=ScaleQuantumToChar(image->colormap[i].red);
965 *q++=ScaleQuantumToChar(image->colormap[i].green);
966 *q++=ScaleQuantumToChar(image->colormap[i].blue);
967 }
968 (void) WriteBlob(image,3*16,(const unsigned char *) pcx_colormap);
969 (void) WriteBlobByte(image,pcx_info.reserved);
970 (void) WriteBlobByte(image,pcx_info.planes);
971 (void) WriteBlobLSBShort(image,pcx_info.bytes_per_line);
972 (void) WriteBlobLSBShort(image,pcx_info.palette_info);
973 for (i=0; i < 58; i++)
974 (void) WriteBlobByte(image,'\0');
975 length=(size_t) pcx_info.bytes_per_line;
976 pcx_pixels=(unsigned char *) AcquireQuantumMemory(length,
977 pcx_info.planes*sizeof(*pcx_pixels));
978 if (pcx_pixels == (unsigned char *) NULL)
979 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
980 q=pcx_pixels;
981 if ((image->storage_class == DirectClass) || (image->colors > 256))
982 {
983 const PixelPacket
984 *pixels;
985
986 /*
987 Convert DirectClass image to PCX raster pixels.
988 */
989 for (y=0; y < (long) image->rows; y++)
990 {
991 pixels=GetVirtualPixels(image,0,y,image->columns,1,
992 &image->exception);
993 if (pixels == (const PixelPacket *) NULL)
994 break;
995 q=pcx_pixels;
996 for (i=0; i < pcx_info.planes; i++)
997 {
998 p=pixels;
999 switch ((int) i)
1000 {
1001 case 0:
1002 {
1003 for (x=0; x < (long) pcx_info.bytes_per_line; x++)
1004 {
cristyce70c172010-01-07 17:15:30 +00001005 *q++=ScaleQuantumToChar(GetRedPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001006 p++;
1007 }
1008 break;
1009 }
1010 case 1:
1011 {
1012 for (x=0; x < (long) pcx_info.bytes_per_line; x++)
1013 {
cristyce70c172010-01-07 17:15:30 +00001014 *q++=ScaleQuantumToChar(GetGreenPixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001015 p++;
1016 }
1017 break;
1018 }
1019 case 2:
1020 {
1021 for (x=0; x < (long) pcx_info.bytes_per_line; x++)
1022 {
cristyce70c172010-01-07 17:15:30 +00001023 *q++=ScaleQuantumToChar(GetBluePixelComponent(p));
cristy3ed852e2009-09-05 21:47:34 +00001024 p++;
1025 }
1026 break;
1027 }
1028 case 3:
1029 default:
1030 {
1031 for (x=(long) pcx_info.bytes_per_line; x != 0; x--)
1032 {
cristy46f08202010-01-10 04:04:21 +00001033 *q++=ScaleQuantumToChar((Quantum) (GetAlphaPixelComponent(p)));
cristy3ed852e2009-09-05 21:47:34 +00001034 p++;
1035 }
1036 break;
1037 }
1038 }
1039 }
1040 if (PCXWritePixels(&pcx_info,pcx_pixels,image) == MagickFalse)
1041 break;
1042 if (image->previous == (Image *) NULL)
1043 {
1044 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1045 if (status == MagickFalse)
1046 break;
1047 }
1048 }
1049 }
1050 else
1051 {
1052 if (pcx_info.bits_per_pixel > 1)
1053 for (y=0; y < (long) image->rows; y++)
1054 {
1055 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1056 if (p == (const PixelPacket *) NULL)
1057 break;
1058 indexes=GetVirtualIndexQueue(image);
1059 q=pcx_pixels;
1060 for (x=0; x < (long) image->columns; x++)
1061 *q++=(unsigned char) indexes[x];
1062 if (PCXWritePixels(&pcx_info,pcx_pixels,image) == MagickFalse)
1063 break;
1064 if (image->previous == (Image *) NULL)
1065 {
1066 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1067 if (status == MagickFalse)
1068 break;
1069 }
1070 }
1071 else
1072 {
1073 IndexPacket
1074 polarity;
1075
1076 register unsigned char
1077 bit,
1078 byte;
1079
1080 /*
1081 Convert PseudoClass image to a PCX monochrome image.
1082 */
1083 polarity=(IndexPacket) (PixelIntensityToQuantum(
1084 &image->colormap[0]) < ((Quantum) QuantumRange/2) ? 1 : 0);
1085 if (image->colors == 2)
1086 polarity=(IndexPacket) (
1087 PixelIntensityToQuantum(&image->colormap[0]) <
1088 PixelIntensityToQuantum(&image->colormap[1]) ? 1 : 0);
1089 for (y=0; y < (long) image->rows; y++)
1090 {
1091 p=GetVirtualPixels(image,0,y,image->columns,1,
1092 &image->exception);
1093 if (p == (const PixelPacket *) NULL)
1094 break;
1095 indexes=GetVirtualIndexQueue(image);
1096 bit=0;
1097 byte=0;
1098 q=pcx_pixels;
1099 for (x=0; x < (long) image->columns; x++)
1100 {
1101 byte<<=1;
1102 if (indexes[x] == polarity)
1103 byte|=0x01;
1104 bit++;
1105 if (bit == 8)
1106 {
1107 *q++=byte;
1108 bit=0;
1109 byte=0;
1110 }
1111 p++;
1112 }
1113 if (bit != 0)
1114 *q++=byte << (8-bit);
1115 if (PCXWritePixels(&pcx_info,pcx_pixels,image) == MagickFalse)
1116 break;
1117 if (image->previous == (Image *) NULL)
1118 {
1119 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1120 if (status == MagickFalse)
1121 break;
1122 }
1123 }
1124 }
1125 (void) WriteBlobByte(image,pcx_info.colormap_signature);
1126 (void) WriteBlob(image,3*256,pcx_colormap);
1127 }
1128 pcx_pixels=(unsigned char *) RelinquishMagickMemory(pcx_pixels);
1129 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
cristyc044c482010-03-03 03:21:55 +00001130 if (page_table == (MagickOffsetType *) NULL)
1131 break;
1132 if (scene >= 1023)
1133 break;
cristy3ed852e2009-09-05 21:47:34 +00001134 if (GetNextImageInList(image) == (Image *) NULL)
1135 break;
1136 image=SyncNextImageInList(image);
1137 status=SetImageProgress(image,SaveImagesTag,scene++,
1138 GetImageListLength(image));
1139 if (status == MagickFalse)
1140 break;
cristy3ed852e2009-09-05 21:47:34 +00001141 } while (image_info->adjoin != MagickFalse);
1142 if (page_table != (MagickOffsetType *) NULL)
1143 {
1144 /*
1145 Write the DCX page table.
1146 */
1147 page_table[scene+1]=0;
1148 offset=SeekBlob(image,0L,SEEK_SET);
1149 if (offset < 0)
1150 ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1151 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
1152 for (i=0; i <= (long) scene; i++)
1153 (void) WriteBlobLSBLong(image,(unsigned long) page_table[i]);
1154 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1155 }
1156 if (status == MagickFalse)
1157 {
1158 char
1159 *message;
1160
1161 message=GetExceptionMessage(errno);
1162 (void) ThrowMagickException(&image->exception,GetMagickModule(),
1163 FileOpenError,"UnableToWriteFile","`%s': %s",image->filename,message);
1164 message=DestroyString(message);
1165 }
1166 (void) CloseBlob(image);
1167 return(MagickTrue);
1168}