root / ImageMagick / trunk / coders / pcx.c

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