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