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