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