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