Coverage Report

Created: 2025-07-23 08:18

/src/graphicsmagick/coders/pcx.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
% Copyright (C) 2003 - 2020 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                            PPPP    CCCC  X   X                              %
15
%                            P   P  C       X X                               %
16
%                            PPPP   C        X                                %
17
%                            P      C       X X                               %
18
%                            P       CCCC  X   X                              %
19
%                                                                             %
20
%                                                                             %
21
%               Read/Write ZSoft IBM PC Paintbrush Image Format.              %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
*/
34

35
/*
36
  Include declarations.
37
*/
38
#include "magick/studio.h"
39
#include "magick/analyze.h"
40
#include "magick/blob.h"
41
#include "magick/colormap.h"
42
#include "magick/log.h"
43
#include "magick/magick.h"
44
#include "magick/monitor.h"
45
#include "magick/pixel_cache.h"
46
#include "magick/utility.h"
47

48
/*
49
  Typedef declarations.
50
*/
51
typedef struct _PCXInfo
52
{
53
  unsigned char
54
    identifier,
55
    version,
56
    encoding,
57
    bits_per_pixel;
58
59
  unsigned short
60
    left,
61
    top,
62
    right,
63
    bottom,
64
    horizontal_resolution,
65
    vertical_resolution;
66
67
  unsigned char
68
    reserved,
69
    planes;
70
71
  unsigned short
72
    bytes_per_line,
73
    palette_info,
74
    horizontal_screen_size,
75
    vertical_screen_size;
76
77
  unsigned char
78
    colormap_signature;
79
} PCXInfo;
80

81
/*
82
  Forward declarations.
83
*/
84
static unsigned int
85
  WritePCXImage(const ImageInfo *,Image *);
86

87
/*
88
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89
%                                                                             %
90
%                                                                             %
91
%                                                                             %
92
%   I s D C X                                                                 %
93
%                                                                             %
94
%                                                                             %
95
%                                                                             %
96
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
97
%
98
%  Method IsDCX returns True if the image format type, identified by the
99
%  magick string, is DCX.
100
%
101
%  The format of the IsDCX method is:
102
%
103
%      unsigned int IsDCX(const unsigned char *magick,const size_t length)
104
%
105
%  A description of each parameter follows:
106
%
107
%    o status:  Method IsDCX returns True if the image format type is DCX.
108
%
109
%    o magick: This string is generally the first few bytes of an image file
110
%      or blob.
111
%
112
%    o length: Specifies the length of the magick string.
113
%
114
%
115
*/
116
static unsigned int IsDCX(const unsigned char *magick,const size_t length)
117
0
{
118
0
  if (length < 4)
119
0
    return(False);
120
0
  if (memcmp(magick,"\261\150\336\72",4) == 0)
121
0
    return(True);
122
0
  return(False);
123
0
}
124

125
/*
126
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
127
%                                                                             %
128
%                                                                             %
129
%                                                                             %
130
%   I s P C X                                                                 %
131
%                                                                             %
132
%                                                                             %
133
%                                                                             %
134
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
135
%
136
%  Method IsPCX returns True if the image format type, identified by the
137
%  magick string, is PCX.
138
%
139
%  The format of the IsPCX method is:
140
%
141
%      unsigned int IsPCX(const unsigned char *magick,const size_t length)
142
%
143
%  A description of each parameter follows:
144
%
145
%    o status:  Method IsPCX returns True if the image format type is PCX.
146
%
147
%    o magick: This string is generally the first few bytes of an image file
148
%      or blob.
149
%
150
%    o length: Specifies the length of the magick string.
151
%
152
%
153
*/
154
static unsigned int IsPCX(const unsigned char *magick,const size_t length)
155
0
{
156
0
  if (length < 2)
157
0
    return(False);
158
0
  if (memcmp(magick,"\012\002",2) == 0)
159
0
    return(True);
160
0
  if (memcmp(magick,"\012\005",2) == 0)
161
0
    return(True);
162
0
  return(False);
163
0
}
164

165
/*
166
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167
%                                                                             %
168
%                                                                             %
169
%                                                                             %
170
%   R e a d P C X I m a g e                                                   %
171
%                                                                             %
172
%                                                                             %
173
%                                                                             %
174
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
175
%
176
%  Method ReadPCXImage reads a ZSoft IBM PC Paintbrush file and returns it.
177
%  It allocates the memory necessary for the new Image structure and returns
178
%  a pointer to the new image.
179
%
180
%  The format of the ReadPCXImage method is:
181
%
182
%      Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
183
%
184
%  A description of each parameter follows:
185
%
186
%    o image:  Method ReadPCXImage returns a pointer to the image after
187
%      reading.  A null image is returned if there is a memory shortage or
188
%      if the image cannot be read.
189
%
190
%    o image_info: Specifies a pointer to a ImageInfo structure.
191
%
192
%    o exception: return any errors or warnings in this structure.
193
%
194
%
195
*/
196
2.47k
#define ThrowPCXReaderException(code_,reason_,image_) \
197
2.47k
{ \
198
2.47k
  MagickFreeResourceLimitedMemory(page_table) \
199
2.47k
  MagickFreeResourceLimitedMemory(pcx_pixels); \
200
2.47k
  MagickFreeResourceLimitedMemory(scanline); \
201
2.47k
  ThrowReaderException(code_,reason_,image_); \
202
0
}
203
static Image *ReadPCXImage(const ImageInfo *image_info,ExceptionInfo *exception)
204
3.88k
{
205
3.88k
  Image
206
3.88k
    *image;
207
208
3.88k
  ExtendedSignedIntegralType
209
3.88k
    *page_table = (ExtendedSignedIntegralType *) NULL;
210
211
3.88k
  int
212
3.88k
    c,
213
3.88k
    bits,
214
3.88k
    id,
215
3.88k
    mask;
216
217
3.88k
  long
218
3.88k
    y;
219
220
3.88k
  PCXInfo
221
3.88k
    pcx_info;
222
223
3.88k
  register IndexPacket
224
3.88k
    index,
225
3.88k
    *indexes;
226
227
3.88k
  register long
228
3.88k
    x;
229
230
3.88k
  register PixelPacket
231
3.88k
    *q;
232
233
3.88k
  register unsigned int
234
3.88k
    i;
235
236
3.88k
  register unsigned char
237
3.88k
    *p,
238
3.88k
    *r;
239
240
3.88k
  size_t
241
3.88k
    count;
242
243
3.88k
  unsigned char
244
3.88k
    packet,
245
3.88k
    pcx_colormap[256*3],
246
3.88k
    *pcx_pixels = (unsigned char *) NULL,
247
3.88k
    *scanline =  (unsigned char *) NULL;
248
249
3.88k
  unsigned int
250
3.88k
    status;
251
252
3.88k
  size_t
253
3.88k
    scanline_size,
254
3.88k
    pcx_packets;
255
256
3.88k
  magick_off_t
257
3.88k
    file_size;
258
259
  /*
260
    Open image file.
261
  */
262
3.88k
  assert(image_info != (const ImageInfo *) NULL);
263
3.88k
  assert(image_info->signature == MagickSignature);
264
3.88k
  assert(exception != (ExceptionInfo *) NULL);
265
3.88k
  assert(exception->signature == MagickSignature);
266
3.88k
  image=AllocateImage(image_info);
267
3.88k
  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
268
3.88k
  if (status == False)
269
3.88k
    ThrowPCXReaderException(FileOpenError,UnableToOpenFile,image);
270
  /*
271
    Determine if this is a PCX file.
272
  */
273
3.88k
  page_table=(ExtendedSignedIntegralType *) NULL;
274
3.88k
  if (LocaleCompare(image_info->magick,"DCX") == 0)
275
1.02k
    {
276
1.02k
      unsigned long
277
1.02k
        magic;
278
279
      /*
280
        Read the DCX page table.
281
      */
282
1.02k
      magic=ReadBlobLSBLong(image);
283
1.02k
      if (magic != 987654321)
284
963
        ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
285
963
      page_table=MagickAllocateResourceLimitedArray(ExtendedSignedIntegralType *,
286
963
                                     1024,sizeof(ExtendedSignedIntegralType));
287
963
      if (page_table == (ExtendedSignedIntegralType *) NULL)
288
963
        ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image);
289
141k
      for (id=0; id < 1024; id++)
290
141k
      {
291
141k
        page_table[id]=(ExtendedSignedIntegralType) ReadBlobLSBLong(image);
292
141k
        if (page_table[id] == 0)
293
861
          break;
294
141k
      }
295
963
    }
296
3.81k
  if (page_table != (ExtendedSignedIntegralType *) NULL)
297
963
    if (SeekBlob(image,(ExtendedSignedIntegralType) page_table[0],SEEK_SET)
298
963
        == -1)
299
3.81k
      ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
300
3.81k
  file_size=GetBlobSize(image);
301
3.81k
  count=ReadBlob(image,1,(char *) &pcx_info.identifier);
302
3.81k
  for (id=1; id < 1024; id++)
303
3.81k
  {
304
3.81k
    MagickBool
305
3.81k
      read_header_ok = MagickFalse;
306
307
    /*
308
      Verify PCX identifier.
309
    */
310
3.81k
    do
311
3.81k
      {
312
3.81k
        if ((c = ReadBlobByte(image)) == EOF)
313
72
          break;
314
3.74k
        pcx_info.version=c;
315
3.74k
        if ((count != 1) || (pcx_info.identifier != 0x0aU))
316
3.71k
          ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
317
3.71k
        if ((c = ReadBlobByte(image)) == EOF)
318
4
          break;
319
3.71k
        pcx_info.encoding=c;
320
3.71k
        if ((c = ReadBlobByte(image)) == EOF)
321
664
          break;
322
3.04k
        pcx_info.bits_per_pixel=c;
323
3.04k
        pcx_info.left=ReadBlobLSBShort(image);
324
3.04k
        pcx_info.top=ReadBlobLSBShort(image);
325
3.04k
        pcx_info.right=ReadBlobLSBShort(image);
326
3.04k
        pcx_info.bottom=ReadBlobLSBShort(image);
327
3.04k
        pcx_info.horizontal_resolution=ReadBlobLSBShort(image);
328
3.04k
        pcx_info.vertical_resolution=ReadBlobLSBShort(image);
329
3.04k
        (void) memset(pcx_colormap,0,sizeof(pcx_colormap));
330
3.04k
        if (ReadBlob(image,3*16,(char *) pcx_colormap) != 3*16)
331
396
          break;
332
2.65k
        if ((c = ReadBlobByte(image)) == EOF)
333
2
          break;
334
2.64k
        pcx_info.reserved=c;
335
2.64k
        if ((c = ReadBlobByte(image)) == EOF)
336
18
          break;
337
2.63k
        pcx_info.planes=c;
338
2.63k
        pcx_info.bytes_per_line=ReadBlobLSBShort(image);
339
2.63k
        pcx_info.palette_info=ReadBlobLSBShort(image);
340
2.63k
        pcx_info.horizontal_screen_size=ReadBlobLSBShort(image);
341
2.63k
        pcx_info.vertical_screen_size=ReadBlobLSBShort(image);
342
2.63k
        if (EOFBlob(image))
343
23
          break;
344
2.60k
        read_header_ok=MagickTrue;
345
2.60k
      } while (0);
346
347
3.78k
    if (!read_header_ok)
348
2.60k
      ThrowPCXReaderException(CorruptImageError,UnexpectedEndOfFile,image);
349
350
2.60k
    if (image->logging)
351
2.60k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
352
2.60k
                            "PCX Header (%d):\n"
353
2.60k
                            "    version=%u\n"
354
2.60k
                            "    encoding=%u\n"
355
2.60k
                            "    bits_per_pixel=%u\n"
356
2.60k
                            "    left=%u\n"
357
2.60k
                            "    top=%u\n"
358
2.60k
                            "    right=%u\n"
359
2.60k
                            "    bottom=%u\n"
360
2.60k
                            "    horizontal_resolution=%u\n"
361
2.60k
                            "    vertical_resolution=%u\n"
362
2.60k
                            "    reserved=%u\n"
363
2.60k
                            "    planes=%u\n"
364
2.60k
                            "    bytes_per_line=%u\n"
365
2.60k
                            "    palette_info=%u\n"
366
2.60k
                            "    horizontal_screen_size=%u\n"
367
2.60k
                            "    vertical_screen_size=%u",
368
2.60k
                            id,
369
2.60k
                            (unsigned int) pcx_info.version,
370
2.60k
                            (unsigned int) pcx_info.encoding,
371
2.60k
                            (unsigned int) pcx_info.bits_per_pixel,
372
2.60k
                            (unsigned int) pcx_info.left,
373
2.60k
                            (unsigned int) pcx_info.top,
374
2.60k
                            (unsigned int) pcx_info.right,
375
2.60k
                            (unsigned int) pcx_info.bottom,
376
2.60k
                            (unsigned int) pcx_info.horizontal_resolution,
377
2.60k
                            (unsigned int) pcx_info.vertical_resolution,
378
2.60k
                            (unsigned int) pcx_info.reserved,
379
2.60k
                            (unsigned int) pcx_info.planes,
380
2.60k
                            (unsigned int) pcx_info.bytes_per_line,
381
2.60k
                            (unsigned int) pcx_info.palette_info,
382
2.60k
                            (unsigned int) pcx_info.horizontal_screen_size,
383
2.60k
                            (unsigned int) pcx_info.vertical_screen_size
384
2.60k
                            );
385
386
    /*
387
      Read PCX raster colormap.
388
    */
389
2.60k
    image->columns=(pcx_info.right-pcx_info.left)+1;
390
2.60k
    image->rows=(pcx_info.bottom-pcx_info.top)+1;
391
2.60k
    image->depth=8; /* or pcx_info.bits_per_pixel */
392
2.60k
    image->units=PixelsPerInchResolution;
393
2.60k
    image->x_resolution=pcx_info.horizontal_resolution;
394
2.60k
    image->y_resolution=pcx_info.vertical_resolution;
395
2.60k
    image->colors=16;
396
397
    /*
398
      Validate rows, columns, bits
399
    */
400
2.60k
    if ((pcx_info.right < pcx_info.left) ||
401
2.60k
        (pcx_info.bottom < pcx_info.top) ||
402
2.60k
        (image->columns == 0) ||
403
2.60k
        (image->rows == 0) ||
404
2.60k
        ((pcx_info.bits_per_pixel != 1) &&
405
2.58k
         (pcx_info.bits_per_pixel != 2) &&
406
2.58k
         (pcx_info.bits_per_pixel != 4) &&
407
2.58k
         (pcx_info.bits_per_pixel != 8)))
408
2.56k
      ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
409
410
    /*
411
      Validate bytes per line.  It is ok to have more bytes per line
412
      than the total bits suggest, but not less.
413
    */
414
2.56k
    {
415
2.56k
      size_t bytes_per_line;
416
417
2.56k
      bytes_per_line = MagickArraySize(image->columns,pcx_info.bits_per_pixel);
418
2.56k
      if (bytes_per_line)
419
2.56k
        bytes_per_line += 7U;
420
2.56k
      if (bytes_per_line)
421
2.56k
        bytes_per_line /= 8U;
422
2.56k
      if (image->logging)
423
2.56k
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
424
2.56k
                              "Bytes per line: reqire >= %" MAGICK_SIZE_T_F "u, have %u",
425
2.56k
                              (MAGICK_SIZE_T) bytes_per_line, pcx_info.bytes_per_line);
426
2.56k
      if ((bytes_per_line == 0) || (pcx_info.bytes_per_line < bytes_per_line))
427
2.20k
        ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
428
2.20k
    }
429
    /*
430
      Validate number of planes.  We only support 1, 2, 3, 4 but some
431
      files might have extra planes (which we ignore).
432
    */
433
2.20k
    if (pcx_info.planes == 0)
434
1.90k
      ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
435
1.90k
    if (pcx_info.planes > 6)
436
1.86k
      ThrowPCXReaderException(CorruptImageError,UnsupportedNumberOfPlanes,image);
437
438
1.86k
   if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1))
439
313
      {
440
313
        image->storage_class=DirectClass;
441
313
        if (image->logging)
442
313
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
443
313
                                "DirectClass image");
444
313
      }
445
1.55k
    else
446
1.55k
      {
447
1.55k
        if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1))
448
1.55k
          if ((pcx_info.version == 3) || (pcx_info.version == 5) ||
449
1.55k
              ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
450
762
            {
451
762
              image->colors=1 << (pcx_info.bits_per_pixel*pcx_info.planes);
452
762
              if (image->colors > 256)
453
14
                image->colors = 256;
454
762
            }
455
456
1.55k
        if (!AllocateImageColormap(image,image->colors))
457
1.55k
          ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image);
458
459
        /*
460
          256 color images have their color map at the end of the file.
461
          Colormap for 1 bit/pixel images is explicitly initialized.
462
        */
463
1.55k
        if (image->colors <= 16)
464
1.28k
          {
465
1.28k
            p=pcx_colormap;
466
15.6k
            for (i=0; i < image->colors; i++)
467
14.3k
              {
468
14.3k
                image->colormap[i].red=ScaleCharToQuantum(*p++);
469
14.3k
                image->colormap[i].green=ScaleCharToQuantum(*p++);
470
14.3k
                image->colormap[i].blue=ScaleCharToQuantum(*p++);
471
14.3k
                image->colormap[i].opacity=OpaqueOpacity;
472
14.3k
              }
473
1.28k
          }
474
1.55k
        if (image->logging)
475
1.55k
          (void) LogMagickEvent(CoderEvent,GetMagickModule(),
476
1.55k
                                "PseudoClass image with %u colors", image->colors);
477
1.55k
      }
478
479
102k
    for (i=0; i < 54; i++)
480
100k
      (void) ReadBlobByte(image);
481
1.86k
    if (image_info->ping && (image_info->subrange != 0))
482
0
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
483
0
        break;
484
485
1.86k
    if (CheckImagePixelLimits(image, exception) != MagickPass)
486
1.82k
      ThrowPCXReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
487
488
489
    /*
490
      Check that filesize is reasonable given header
491
    */
492
1.82k
    {
493
1.82k
      double
494
1.82k
        uncompressed_size;
495
496
1.82k
      uncompressed_size=((double) image->rows*pcx_info.bytes_per_line*pcx_info.planes);
497
1.82k
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
498
1.82k
                            "Uncompressed size: %.0f", uncompressed_size);
499
1.82k
      if (pcx_info.encoding == 0)
500
183
        {
501
          /* Not compressed */
502
183
          if (uncompressed_size > file_size)
503
5
            ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,
504
183
                                    image);
505
178
        }
506
1.64k
      else
507
1.64k
        {
508
          /* RLE compressed */
509
1.64k
          if (uncompressed_size > file_size*254.0)
510
36
            ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,
511
1.64k
                                    image);
512
1.60k
        }
513
1.82k
    }
514
515
516
    /*
517
      Read image data.
518
    */
519
1.78k
    pcx_packets=MagickArraySize(image->rows,
520
1.78k
                                MagickArraySize(pcx_info.bytes_per_line,
521
1.78k
                                                pcx_info.planes));
522
1.78k
    if ((0 == pcx_packets) ||
523
1.78k
        (((size_t) pcx_info.bits_per_pixel*pcx_info.planes*image->columns) >
524
1.78k
         ((size_t) pcx_packets*8U)))
525
1.78k
      ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
526
1.78k
    pcx_pixels=MagickAllocateResourceLimitedMemory(unsigned char *,pcx_packets);
527
1.78k
    if (pcx_pixels == (unsigned char *) NULL)
528
1.78k
      ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image);
529
1.78k
    scanline_size=MagickArraySize(Max(image->columns,
530
1.78k
                                      (size_t) pcx_info.bytes_per_line),
531
1.78k
                                  Max(pcx_info.planes,8));
532
1.78k
    scanline=MagickAllocateResourceLimitedMemory(unsigned char *,scanline_size);
533
1.78k
    if (scanline == (unsigned char *) NULL)
534
1.78k
      ThrowPCXReaderException(ResourceLimitError,MemoryAllocationFailed,image);
535
1.78k
    (void) memset(scanline,0,scanline_size);
536
1.78k
    if (pcx_info.encoding == 0)
537
178
      {
538
        /*
539
          Data is not compressed
540
        */
541
178
        if (ReadBlob(image,pcx_packets,pcx_pixels) != pcx_packets)
542
127
          ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image);
543
51
      }
544
1.60k
    else
545
1.60k
      {
546
        /*
547
          Uncompress image data.
548
        */
549
1.60k
        p=pcx_pixels;
550
95.2M
        while (pcx_packets != 0)
551
95.2M
          {
552
95.2M
            packet=ReadBlobByte(image);
553
95.2M
            if (EOFBlob(image))
554
95.2M
              ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image);
555
95.2M
            if ((packet & 0xc0) != 0xc0)
556
88.4M
              {
557
88.4M
                *p++=packet;
558
88.4M
                pcx_packets--;
559
88.4M
                continue;
560
88.4M
              }
561
6.76M
            count=packet & 0x3f;
562
6.76M
            packet=ReadBlobByte(image);
563
6.76M
            if (EOFBlob(image))
564
6.76M
              ThrowPCXReaderException(CorruptImageError,InsufficientImageDataInFile,image);
565
259M
            for (; count != 0; count--)
566
253M
              {
567
253M
                *p++=packet;
568
253M
                pcx_packets--;
569
253M
                if (pcx_packets == 0)
570
1.23k
                  break;
571
253M
              }
572
6.76M
          }
573
1.60k
      }
574
1.41k
    if (image->storage_class == DirectClass)
575
283
      image->matte=pcx_info.planes > 3;
576
1.12k
    else
577
1.12k
      if ((pcx_info.version == 5) ||
578
1.12k
          (((size_t) pcx_info.bits_per_pixel*pcx_info.planes) == 1))
579
507
        {
580
          /*
581
            Initialize image colormap.
582
          */
583
507
          if (image->colors > 256)
584
0
            ThrowPCXReaderException(CorruptImageError,ColormapExceedsColorsLimit,
585
507
              image);
586
507
          if (((size_t) pcx_info.bits_per_pixel*pcx_info.planes) == 1)
587
235
            {
588
              /*
589
                Monochrome colormap.
590
              */
591
235
              image->colormap[0].red=0;
592
235
              image->colormap[0].green=0;
593
235
              image->colormap[0].blue=0;
594
235
              image->colormap[0].opacity=OpaqueOpacity;
595
235
              image->colormap[1].red=MaxRGB;
596
235
              image->colormap[1].green=MaxRGB;
597
235
              image->colormap[1].blue=MaxRGB;
598
235
              image->colormap[1].opacity=OpaqueOpacity;
599
235
            }
600
272
          else
601
272
            if (image->colors > 16)
602
190
              {
603
                /*
604
                  256 color images have their color map at the end of the file.
605
                */
606
190
                pcx_info.colormap_signature=ReadBlobByte(image);
607
190
                (void) ReadBlob(image, (size_t) 3*image->colors,(char *) pcx_colormap);
608
190
                p=pcx_colormap;
609
19.4k
                for (i=0; i < image->colors; i++)
610
19.2k
                {
611
19.2k
                  image->colormap[i].red=ScaleCharToQuantum(*p++);
612
19.2k
                  image->colormap[i].green=ScaleCharToQuantum(*p++);
613
19.2k
                  image->colormap[i].blue=ScaleCharToQuantum(*p++);
614
19.2k
                  image->colormap[i].opacity=OpaqueOpacity;
615
19.2k
                }
616
190
            }
617
507
        }
618
    /*
619
      Convert PCX raster image to pixel packets.
620
    */
621
355k
    for (y=0; y < (long) image->rows; y++)
622
354k
    {
623
354k
      p=pcx_pixels+((size_t) y*pcx_info.bytes_per_line*pcx_info.planes);
624
354k
      q=SetImagePixels(image,0,y,image->columns,1);
625
354k
      if (q == (PixelPacket *) NULL)
626
0
        break;
627
354k
      indexes=AccessMutableIndexes(image);
628
354k
      r=scanline;
629
354k
      if (image->storage_class == DirectClass)
630
112k
        for (i=0; i < (unsigned int) pcx_info.planes; i++)
631
77.0k
        {
632
77.0k
          r=scanline+i;
633
322k
          for (x=0; x < (long) pcx_info.bytes_per_line; x++)
634
245k
          {
635
245k
            switch (i)
636
245k
            {
637
115k
              case 0:
638
115k
              {
639
115k
                *r=ScaleCharToQuantum(*p++);
640
115k
                break;
641
0
              }
642
115k
              case 1:
643
115k
              {
644
115k
                *r=ScaleCharToQuantum(*p++);
645
115k
                break;
646
0
              }
647
6.79k
              case 2:
648
6.79k
              {
649
6.79k
                *r=ScaleCharToQuantum(*p++);
650
6.79k
                break;
651
0
              }
652
6.09k
              case 3:
653
8.42k
              default:
654
8.42k
              {
655
8.42k
                *r=ScaleCharToQuantum(*p++);
656
8.42k
                break;
657
6.09k
              }
658
245k
            }
659
245k
            r+=(unsigned int) pcx_info.planes;
660
245k
          }
661
77.0k
        }
662
318k
      else
663
318k
        if ((unsigned int) pcx_info.planes > 1)
664
108k
          {
665
87.1M
            for (x=0; x < (long) image->columns; x++)
666
87.0M
              *r++=0;
667
649k
            for (i=0; i < (unsigned int) pcx_info.planes; i++)
668
540k
            {
669
540k
              r=scanline;
670
269M
              for (x=0; x < (long) pcx_info.bytes_per_line; x++)
671
269M
              {
672
269M
                 bits=(*p++);
673
2.42G
                 for (mask=0x80; mask != 0; mask>>=1)
674
2.15G
                 {
675
2.15G
                   if (bits & mask)
676
922M
                     *r|=1 << i;
677
2.15G
                   r++;
678
2.15G
                 }
679
269M
               }
680
540k
            }
681
108k
          }
682
210k
        else
683
210k
          switch (pcx_info.bits_per_pixel)
684
210k
          {
685
61.6k
            case 1:
686
61.6k
            {
687
61.6k
              register long
688
61.6k
                bit;
689
690
4.57M
              for (x=0; x < ((long) image->columns-7); x+=8)
691
4.51M
              {
692
40.6M
                for (bit=7; bit >= 0; bit--)
693
36.1M
                  *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
694
4.51M
                p++;
695
4.51M
              }
696
61.6k
              if ((image->columns % 8) != 0)
697
34.3k
                {
698
168k
                  for (bit=7; bit >= (long) (8-(image->columns % 8)); bit--)
699
134k
                    *r++=((*p) & (0x01 << bit) ? 0x01 : 0x00);
700
34.3k
                  p++;
701
34.3k
                }
702
61.6k
              break;
703
0
            }
704
119k
            case 2:
705
119k
            {
706
17.0M
              for (x=0; x < ((long) image->columns-3); x+=4)
707
16.9M
              {
708
16.9M
                *r++=(*p >> 6) & 0x3;
709
16.9M
                *r++=(*p >> 4) & 0x3;
710
16.9M
                *r++=(*p >> 2) & 0x3;
711
16.9M
                *r++=(*p) & 0x3;
712
16.9M
                p++;
713
16.9M
              }
714
119k
              if ((image->columns % 4) != 0)
715
89.2k
                {
716
287k
                  for (i=3; i >= (unsigned int) (4-(image->columns % 4)); i--)
717
197k
                    *r++=(*p >> (i*2)) & 0x03;
718
89.2k
                  p++;
719
89.2k
                }
720
119k
              break;
721
0
            }
722
23.7k
            case 4:
723
23.7k
            {
724
41.7k
              for (x=0; x < ((long) image->columns-1); x+=2)
725
18.0k
              {
726
18.0k
                *r++=(*p >> 4) & 0xf;
727
18.0k
                *r++=(*p) & 0xf;
728
18.0k
                p++;
729
18.0k
              }
730
23.7k
              if ((image->columns % 2) != 0)
731
12.8k
                *r++=(*p++ >> 4) & 0xf;
732
23.7k
              break;
733
0
            }
734
5.01k
            case 8:
735
5.01k
            {
736
5.01k
              (void) memcpy(r,p,image->columns);
737
5.01k
              break;
738
0
            }
739
0
            default:
740
0
              break;
741
210k
          }
742
      /*
743
        Transfer image scanline.
744
      */
745
354k
      r=scanline;
746
191M
      for (x=0; x < (long) image->columns; x++)
747
191M
      {
748
191M
        if (image->storage_class == PseudoClass)
749
191M
          {
750
191M
            index=(*r++);
751
191M
            VerifyColormapIndex(image,index);
752
191M
            indexes[x]=index;
753
191M
            *q=image->colormap[index];
754
191M
          }
755
45.1k
        else
756
45.1k
          {
757
45.1k
            q->red=ScaleCharToQuantum(*r++);
758
45.1k
            q->green=ScaleCharToQuantum(*r++);
759
45.1k
            q->blue=ScaleCharToQuantum(*r++);
760
45.1k
            if (image->matte)
761
4.79k
              q->opacity=(Quantum) (MaxRGB-ScaleCharToQuantum(*r++));
762
40.3k
            else
763
40.3k
              q->opacity=OpaqueOpacity;
764
45.1k
          }
765
191M
        q++;
766
191M
      }
767
354k
      if (!SyncImagePixels(image))
768
0
        break;
769
354k
      if (image->previous == (Image *) NULL)
770
354k
        if (QuantumTick(y,image->rows))
771
74.6k
          if (!MagickMonitorFormatted(y,image->rows,exception,LoadImageText,
772
74.6k
                                      image->filename,
773
74.6k
                                      image->columns,image->rows))
774
0
            break;
775
354k
    }
776
1.41k
    MagickFreeResourceLimitedMemory(scanline);
777
1.41k
    MagickFreeResourceLimitedMemory(pcx_pixels);
778
1.41k
    if (EOFBlob(image))
779
161
      {
780
161
        ThrowException(exception,CorruptImageError,UnexpectedEndOfFile,
781
161
          image->filename);
782
161
        break;
783
161
      }
784
1.24k
    StopTimer(&image->timer);
785
    /*
786
      Proceed to next image.
787
    */
788
1.24k
    if (image_info->subrange != 0)
789
1.24k
      if (image->scene >= (image_info->subimage+image_info->subrange-1))
790
1.24k
        break;
791
0
    if (page_table == (ExtendedSignedIntegralType *) NULL)
792
0
      break;
793
0
    if (page_table[id] == 0)
794
0
      break;
795
0
    if (SeekBlob(image,(ExtendedSignedIntegralType) page_table[id],SEEK_SET)
796
0
        == -1)
797
0
      ThrowPCXReaderException(CorruptImageError,ImproperImageHeader,image);
798
0
    count=ReadBlob(image,1,(char *) &pcx_info.identifier);
799
0
    if ((count != 0) && (pcx_info.identifier == 0x0a))
800
0
      {
801
        /*
802
          Allocate next image structure.
803
        */
804
0
        AllocateNextImage(image_info,image);
805
0
        if (image->next == (Image *) NULL)
806
0
          {
807
0
            DestroyImageList(image);
808
0
            return((Image *) NULL);
809
0
          }
810
0
        image=SyncNextImageInList(image);
811
0
        if (!MagickMonitorFormatted(TellBlob(image),GetBlobSize(image),exception,
812
0
                                    LoadImagesText,image->filename))
813
0
          break;
814
0
      }
815
0
  }
816
1.41k
  if (page_table != (ExtendedSignedIntegralType *) NULL)
817
569
    MagickFreeResourceLimitedMemory(page_table);
818
1.41k
  while (image->previous != (Image *) NULL)
819
0
    image=image->previous;
820
1.41k
  CloseBlob(image);
821
1.41k
  return(image);
822
3.81k
}
823

824
/*
825
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
826
%                                                                             %
827
%                                                                             %
828
%                                                                             %
829
%   R e g i s t e r P C X I m a g e                                           %
830
%                                                                             %
831
%                                                                             %
832
%                                                                             %
833
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
834
%
835
%  Method RegisterPCXImage adds attributes for the PCX image format to
836
%  the list of supported formats.  The attributes include the image format
837
%  tag, a method to read and/or write the format, whether the format
838
%  supports the saving of more than one frame to the same file or blob,
839
%  whether the format supports native in-memory I/O, and a brief
840
%  description of the format.
841
%
842
%  The format of the RegisterPCXImage method is:
843
%
844
%      RegisterPCXImage(void)
845
%
846
*/
847
ModuleExport void RegisterPCXImage(void)
848
5
{
849
5
  MagickInfo
850
5
    *entry;
851
852
5
  entry=SetMagickInfo("DCX");
853
5
  entry->decoder=(DecoderHandler) ReadPCXImage;
854
5
  entry->encoder=(EncoderHandler) WritePCXImage;
855
5
  entry->seekable_stream=True;
856
5
  entry->magick=(MagickHandler) IsDCX;
857
5
  entry->description="ZSoft IBM PC multi-page Paintbrush";
858
5
  entry->module="PCX";
859
5
  (void) RegisterMagickInfo(entry);
860
861
5
  entry=SetMagickInfo("PCX");
862
5
  entry->decoder=(DecoderHandler) ReadPCXImage;
863
5
  entry->encoder=(EncoderHandler) WritePCXImage;
864
5
  entry->magick=(MagickHandler) IsPCX;
865
5
  entry->adjoin=False;
866
5
  entry->seekable_stream=True;
867
5
  entry->description="ZSoft IBM PC Paintbrush";
868
5
  entry->module="PCX";
869
5
  (void) RegisterMagickInfo(entry);
870
5
}
871

872
/*
873
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
874
%                                                                             %
875
%                                                                             %
876
%                                                                             %
877
%   U n r e g i s t e r P C X I m a g e                                       %
878
%                                                                             %
879
%                                                                             %
880
%                                                                             %
881
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
882
%
883
%  Method UnregisterPCXImage removes format registrations made by the
884
%  PCX module from the list of supported formats.
885
%
886
%  The format of the UnregisterPCXImage method is:
887
%
888
%      UnregisterPCXImage(void)
889
%
890
*/
891
ModuleExport void UnregisterPCXImage(void)
892
0
{
893
0
  (void) UnregisterMagickInfo("DCX");
894
0
  (void) UnregisterMagickInfo("PCX");
895
0
}
896

897
/*
898
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
899
%                                                                             %
900
%                                                                             %
901
%                                                                             %
902
%   W r i t e P C X I m a g e                                                 %
903
%                                                                             %
904
%                                                                             %
905
%                                                                             %
906
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
907
%
908
%  Method WritePCXImage writes an image in the ZSoft IBM PC Paintbrush file
909
%  format.
910
%
911
%  The format of the WritePCXImage method is:
912
%
913
%      unsigned int WritePCXImage(const ImageInfo *image_info,Image *image)
914
%
915
%  A description of each parameter follows.
916
%
917
%    o status: Method WritePCXImage return True if the image is written.
918
%      False is returned is there is a memory shortage or if the image file
919
%      fails to write.
920
%
921
%    o image_info: Specifies a pointer to a ImageInfo structure.
922
%
923
%    o image:  A pointer to an Image structure.
924
%
925
%
926
*/
927
static MagickPassFail WritePCXPixels(Image *image,
928
                                     PCXInfo *pcx_info,
929
                                     const unsigned char *pcx_row_pixels)
930
248k
{
931
248k
  register const unsigned char
932
248k
    *q;
933
934
248k
  unsigned char
935
248k
    count,
936
248k
    packet,
937
248k
    previous;
938
939
248k
  register long
940
248k
    i,
941
248k
    x;
942
943
248k
  q=pcx_row_pixels;
944
945
  /* For each color plane ... */
946
571k
  for (i=0; i < (long) pcx_info->planes; i++)
947
322k
    {
948
322k
      if (pcx_info->encoding == 0)
949
0
        {
950
0
          for (x=0; x < (long) pcx_info->bytes_per_line; x++)
951
0
            (void) WriteBlobByte(image,(unsigned char) (*q++));
952
0
        }
953
322k
      else
954
322k
        {
955
322k
          previous=(*q++);
956
322k
          count=1;
957
          /* For each column ... */
958
76.3M
          for (x=0; x < (long) (pcx_info->bytes_per_line-1); x++)
959
76.0M
            {
960
76.0M
              packet=(*q++);
961
76.0M
              if ((packet == previous) && (count < 63))
962
36.8M
                {
963
36.8M
                  count++;
964
36.8M
                  continue;
965
36.8M
                }
966
39.2M
              if ((count > 1) || ((previous & 0xc0) == 0xc0))
967
23.3M
                {
968
23.3M
                  count|=0xc0;
969
23.3M
                  (void) WriteBlobByte(image,count);
970
23.3M
                }
971
39.2M
              (void) WriteBlobByte(image,previous);
972
39.2M
              previous=packet;
973
39.2M
              count=1;
974
39.2M
            }
975
322k
          if ((count > 1) || ((previous & 0xc0) == 0xc0))
976
195k
            {
977
195k
              count|=0xc0;
978
195k
              (void) WriteBlobByte(image,count);
979
195k
            }
980
322k
          (void) WriteBlobByte(image,previous);
981
322k
        }
982
322k
    }
983
248k
  return (MagickPass);
984
248k
}
985
986
#define LiberatePCXAllocations()                \
987
0
  {                                             \
988
0
    MagickFreeResourceLimitedMemory(pcx_colormap);             \
989
0
    MagickFreeResourceLimitedMemory(pcx_pixels);               \
990
0
    MagickFreeResourceLimitedMemory(page_table);               \
991
0
  }
992
993
0
#define ThrowPCXWriterException(code_,reason_,image_) \
994
0
{ \
995
0
  LiberatePCXAllocations();                   \
996
0
  ThrowWriterException(code_,reason_,image_); \
997
0
}
998
static unsigned int WritePCXImage(const ImageInfo *image_info,Image *image)
999
993
{
1000
993
  long
1001
993
    y;
1002
1003
993
  PCXInfo
1004
993
    pcx_info;
1005
1006
993
  register const PixelPacket
1007
993
    *p;
1008
1009
993
  register const IndexPacket
1010
993
    *indexes;
1011
1012
993
  register long
1013
993
    i,
1014
993
    x;
1015
1016
993
  register unsigned char
1017
993
    *q;
1018
1019
993
  size_t
1020
993
    bytes_per_line;
1021
1022
993
  unsigned char
1023
993
    *pcx_colormap = (unsigned char *) NULL,
1024
993
    *pcx_pixels = (unsigned char *) NULL;
1025
1026
993
  MagickBool
1027
993
    adjoin,
1028
993
    logging,
1029
993
    write_dcx;
1030
1031
993
  unsigned int
1032
993
    status;
1033
1034
993
  ExtendedSignedIntegralType
1035
993
    *page_table=(ExtendedSignedIntegralType *) NULL;
1036
1037
993
  unsigned long
1038
993
    scene;
1039
1040
993
  const unsigned long
1041
993
    max_scenes = 1024UL;
1042
1043
993
  ImageCharacteristics
1044
993
    characteristics;
1045
1046
993
  size_t
1047
993
    image_list_length;
1048
1049
  /*
1050
    Open output image file.
1051
  */
1052
993
  assert(image_info != (const ImageInfo *) NULL);
1053
993
  assert(image_info->signature == MagickSignature);
1054
993
  assert(image != (Image *) NULL);
1055
993
  assert(image->signature == MagickSignature);
1056
1057
993
  image_list_length=GetImageListLength(image);
1058
993
  logging=image->logging;
1059
993
  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
1060
993
  if (status == False)
1061
993
    ThrowPCXWriterException(FileOpenError,UnableToOpenFile,image);
1062
1063
993
  write_dcx=MagickFalse;
1064
993
  if (LocaleCompare(image_info->magick,"DCX") == 0)
1065
482
    {
1066
      /*
1067
        Write the DCX page table.
1068
      */
1069
482
      write_dcx=MagickTrue;
1070
482
      (void) WriteBlobLSBLong(image,0x3ADE68B1L);
1071
482
      page_table=MagickAllocateResourceLimitedClearedArray(ExtendedSignedIntegralType *,
1072
482
                                                           (size_t) max_scenes+1,
1073
482
                                                           sizeof(ExtendedSignedIntegralType));
1074
482
      if (page_table == (ExtendedSignedIntegralType *) NULL)
1075
482
        ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1076
494k
      for (scene=0; scene < max_scenes; scene++)
1077
493k
        (void) WriteBlobLSBLong(image,0x00000000L);
1078
482
    }
1079
993
  adjoin=(image_info->adjoin) && (image->next != (const Image *) NULL) && (write_dcx);
1080
993
  scene=0;
1081
993
  do
1082
993
  {
1083
993
    if (logging && write_dcx)
1084
0
      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1085
0
                            "Writing DCX frame %lu...",scene);
1086
    /*
1087
      Ensure that image is in RGB space.
1088
    */
1089
993
    (void) TransformColorspace(image,RGBColorspace);
1090
    /*
1091
      Analyze image to be written.
1092
    */
1093
993
    if (!GetImageCharacteristics(image,&characteristics,
1094
993
                                 (OptimizeType == image_info->type),
1095
993
                                 &image->exception))
1096
0
      {
1097
0
        LiberatePCXAllocations();
1098
0
        CloseBlob(image);
1099
0
        return MagickFail;
1100
0
      }
1101
993
    if (page_table != (ExtendedSignedIntegralType *) NULL)
1102
482
      page_table[scene]=TellBlob(image);
1103
    /*
1104
      Initialize PCX raster file header.
1105
    */
1106
993
    pcx_info.identifier=0x0a;
1107
993
    pcx_info.version=5;
1108
    /* Please note that uncompressed PCX in quite rare and some applications cannot open it.
1109
       So the compressed PCX needs to be default. */
1110
993
    pcx_info.encoding = (image->compression==RLECompression || image->compression==UndefinedCompression) ? 1 : 0;
1111
993
    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1112
993
                          "Using %s compression",
1113
993
                          pcx_info.encoding == 1 ? "RLE" : "No");
1114
993
    pcx_info.bits_per_pixel=8;
1115
993
    if (characteristics.palette && characteristics.monochrome)
1116
269
      pcx_info.bits_per_pixel=1;
1117
993
    pcx_info.left=0;
1118
993
    pcx_info.top=0;
1119
993
    pcx_info.right=(unsigned short) (image->columns-1);
1120
993
    pcx_info.bottom=(unsigned short) (image->rows-1);
1121
993
    pcx_info.horizontal_resolution=72;
1122
993
    pcx_info.vertical_resolution=72;
1123
993
    switch (image->units)
1124
993
      {
1125
0
      case UndefinedResolution:
1126
993
      case PixelsPerInchResolution:
1127
993
        {
1128
993
          pcx_info.horizontal_resolution=(unsigned short) image->x_resolution;
1129
993
          pcx_info.vertical_resolution=(unsigned short) image->y_resolution;
1130
993
          break;
1131
0
        }
1132
0
      case PixelsPerCentimeterResolution:
1133
0
        {
1134
0
          pcx_info.horizontal_resolution=(unsigned short) (2.54*image->x_resolution+0.5);
1135
0
          pcx_info.vertical_resolution=(unsigned short) (2.54*image->y_resolution+0.5);
1136
0
          break;
1137
0
        }
1138
993
      }
1139
993
    pcx_info.reserved=0;
1140
993
    pcx_info.planes=1;
1141
993
    if (image->storage_class == DirectClass)
1142
153
      {
1143
153
        pcx_info.planes=3;
1144
153
        if (image->matte)
1145
36
          pcx_info.planes++;
1146
153
      }
1147
1148
    /* image->columns*pcx_info.bits_per_pixel+7)/8 */
1149
993
    bytes_per_line=MagickArraySize(image->columns,pcx_info.bits_per_pixel);
1150
993
    if (bytes_per_line && (~((size_t)0)-7 > bytes_per_line))
1151
993
      bytes_per_line += 7;
1152
993
    bytes_per_line /= 8;
1153
993
    pcx_info.bytes_per_line=(unsigned short) bytes_per_line;
1154
993
    if ((pcx_info.bytes_per_line == 0) ||
1155
993
        ((size_t) pcx_info.bytes_per_line != bytes_per_line))
1156
993
      ThrowPCXWriterException(CoderError,UnsupportedNumberOfColumns,image);
1157
993
    pcx_info.palette_info=1;
1158
993
    pcx_info.colormap_signature=0x0c;
1159
    /*
1160
      Write PCX header.
1161
    */
1162
993
    (void) WriteBlobByte(image,pcx_info.identifier);
1163
993
    (void) WriteBlobByte(image,pcx_info.version);
1164
993
    (void) WriteBlobByte(image,pcx_info.encoding);
1165
993
    (void) WriteBlobByte(image,pcx_info.bits_per_pixel);
1166
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.left);
1167
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.top);
1168
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.right);
1169
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.bottom);
1170
993
    (void) WriteBlobLSBShort(image,(unsigned int)
1171
993
      pcx_info.horizontal_resolution);
1172
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.vertical_resolution);
1173
    /*
1174
      Dump colormap to file.
1175
    */
1176
993
    pcx_colormap=MagickAllocateResourceLimitedClearedArray(unsigned char *,3,256);
1177
993
    if (pcx_colormap == (unsigned char *) NULL)
1178
993
      ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1179
993
    q=pcx_colormap;
1180
993
    if (image->storage_class == PseudoClass)
1181
14.6k
      for (i=0; i < (long) image->colors; i++)
1182
13.7k
      {
1183
13.7k
        *q++=ScaleQuantumToChar(image->colormap[i].red);
1184
13.7k
        *q++=ScaleQuantumToChar(image->colormap[i].green);
1185
13.7k
        *q++=ScaleQuantumToChar(image->colormap[i].blue);
1186
13.7k
      }
1187
993
    (void) WriteBlob(image,3*16,(char *) pcx_colormap);
1188
993
    (void) WriteBlobByte(image,pcx_info.reserved);
1189
993
    (void) WriteBlobByte(image,pcx_info.planes);
1190
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.bytes_per_line);
1191
993
    (void) WriteBlobLSBShort(image,(unsigned int) pcx_info.palette_info);
1192
58.5k
    for (i=0; i < 58; i++)
1193
57.5k
      (void) WriteBlobByte(image,'\0');
1194
    /* Allocate memory for one pixel row. */
1195
993
    pcx_pixels=MagickAllocateResourceLimitedClearedArray(unsigned char *,
1196
993
                                                         bytes_per_line,
1197
993
                                                         pcx_info.planes);
1198
993
    if (pcx_pixels == (unsigned char *) NULL)
1199
993
      ThrowPCXWriterException(ResourceLimitError,MemoryAllocationFailed,image);
1200
993
    q=pcx_pixels;
1201
993
    if (image->storage_class == DirectClass)
1202
153
      {
1203
        /*
1204
          Convert DirectClass image to PCX raster pixels.
1205
        */
1206
1207
        /* For each row ... */
1208
35.6k
        for (y=0; y < (long) image->rows; y++)
1209
35.4k
        {
1210
35.4k
          const PixelPacket *
1211
35.4k
            row_pixels;
1212
1213
35.4k
          row_pixels=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1214
35.4k
          if (row_pixels == (const PixelPacket *) NULL)
1215
0
            break;
1216
1217
35.4k
          q=pcx_pixels;
1218
1219
          /* For each color plane ... */
1220
144k
          for (i=0; i < pcx_info.planes; i++)
1221
108k
          {
1222
108k
            p=row_pixels;
1223
108k
            switch ((int) i)
1224
108k
              {
1225
35.4k
              case 0:
1226
35.4k
                {
1227
                  /* For each column ... */
1228
80.4k
                  for (x=(long) pcx_info.bytes_per_line; x > 0; x--)
1229
45.0k
                    *q++=ScaleQuantumToChar(p++->red);
1230
35.4k
                  break;
1231
0
                }
1232
35.4k
              case 1:
1233
35.4k
                {
1234
                  /* For each column ... */
1235
80.4k
                  for (x=(long) pcx_info.bytes_per_line; x > 0; x--)
1236
45.0k
                    *q++=ScaleQuantumToChar(p++->green);
1237
35.4k
                  break;
1238
0
                }
1239
35.4k
              case 2:
1240
35.4k
                {
1241
                  /* For each column ... */
1242
80.4k
                  for (x=(long) pcx_info.bytes_per_line; x > 0; x--)
1243
45.0k
                    *q++=ScaleQuantumToChar(p++->blue);
1244
35.4k
                  break;
1245
0
                }
1246
2.34k
              case 3:
1247
2.34k
              default:
1248
2.34k
                {
1249
                  /* For each column ... */
1250
7.13k
                  for (x=(long) pcx_info.bytes_per_line; x > 0; x--)
1251
4.79k
                    *q++=ScaleQuantumToChar(MaxRGB-p++->opacity);
1252
2.34k
                  break;
1253
2.34k
                }
1254
108k
              }
1255
108k
          }
1256
35.4k
          if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail)
1257
0
            break;
1258
35.4k
          if (QuantumTick(y,image->rows))
1259
9.92k
            if (!MagickMonitorFormatted(y,image->rows,&image->exception,
1260
9.92k
                                        SaveImageText,image->filename,
1261
9.92k
                                        image->columns,image->rows))
1262
0
              break;
1263
35.4k
        }
1264
153
      }
1265
840
    else
1266
      /*
1267
        Convert PseudoClass image to a PCX grayscale image.
1268
      */
1269
840
      if (pcx_info.bits_per_pixel > 1)
1270
        /* For each row ... */
1271
143k
        for (y=0; y < (long) image->rows; y++)
1272
143k
        {
1273
143k
          p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1274
143k
          if (p == (const PixelPacket *) NULL)
1275
0
            break;
1276
143k
          indexes=AccessImmutableIndexes(image);
1277
143k
          q=pcx_pixels;
1278
          /* For each column ... */
1279
71.7M
          for (x=0; x < (long) image->columns; x++)
1280
71.6M
            *q++=indexes[x];
1281
143k
          if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail)
1282
0
            break;
1283
143k
          if (image->previous == (Image *) NULL)
1284
143k
            if (QuantumTick(y,image->rows))
1285
33.4k
              if (!MagickMonitorFormatted(y,image->rows,&image->exception,
1286
33.4k
                                          SaveImageText,image->filename,
1287
33.4k
                                          image->columns,image->rows))
1288
0
                break;
1289
143k
        }
1290
269
      else
1291
269
        {
1292
269
          register unsigned char
1293
269
            bit,
1294
269
            byte,
1295
269
            polarity;
1296
1297
          /*
1298
            Convert PseudoClass image to a PCX monochrome image.
1299
          */
1300
269
          polarity=PixelIntensityToQuantum(&image->colormap[0]) < (MaxRGB/2);
1301
269
          if (image->colors == 2)
1302
235
            polarity=PixelIntensityToQuantum(&image->colormap[0]) <
1303
235
              PixelIntensityToQuantum(&image->colormap[1]);
1304
          /* For each row ... */
1305
70.3k
          for (y=0; y < (long) image->rows; y++)
1306
70.0k
          {
1307
70.0k
            p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception);
1308
70.0k
            if (p == (const PixelPacket *) NULL)
1309
0
              break;
1310
70.0k
            indexes=AccessImmutableIndexes(image);
1311
70.0k
            bit=0;
1312
70.0k
            byte=0;
1313
70.0k
            q=pcx_pixels;
1314
            /* For each column ... */
1315
36.3M
            for (x=0; x < (long) image->columns; x++)
1316
36.2M
            {
1317
36.2M
              byte<<=1;
1318
36.2M
              if (indexes[x] == polarity)
1319
34.4M
                byte|=0x01;
1320
36.2M
              bit++;
1321
36.2M
              if (bit == 8)
1322
4.51M
                {
1323
4.51M
                  *q++=byte;
1324
4.51M
                  bit=0;
1325
4.51M
                  byte=0;
1326
4.51M
                }
1327
36.2M
              p++;
1328
36.2M
            }
1329
70.0k
            if (bit != 0)
1330
42.8k
              *q++=byte << (8-bit);
1331
70.0k
            if (WritePCXPixels(image,&pcx_info,pcx_pixels) == MagickFail)
1332
0
            break;
1333
70.0k
            if (image->previous == (Image *) NULL)
1334
70.0k
              if (QuantumTick(y,image->rows))
1335
17.3k
                if (!MagickMonitorFormatted(y,image->rows,&image->exception,
1336
17.3k
                                            SaveImageText,image->filename,
1337
17.3k
                                            image->columns,image->rows))
1338
0
                  break;
1339
70.0k
          }
1340
269
        }
1341
1342
993
    (void) WriteBlobByte(image,pcx_info.colormap_signature);
1343
993
    (void) WriteBlob(image,3*256,(char *) pcx_colormap);
1344
993
    MagickFreeResourceLimitedMemory(pcx_pixels);
1345
993
    MagickFreeResourceLimitedMemory(pcx_colormap);
1346
993
    if (image->next == (Image *) NULL)
1347
993
      break;
1348
0
    image=SyncNextImageInList(image);
1349
0
    status=MagickMonitorFormatted(scene++,Min(max_scenes,image_list_length),
1350
0
                                  &image->exception,SaveImagesText,
1351
0
                                  image->filename);
1352
0
    if (status == False)
1353
0
      break;
1354
0
    if (scene >= max_scenes-1)
1355
0
      break;
1356
0
  } while (adjoin);
1357
993
  if (adjoin)
1358
0
    while (image->previous != (Image *) NULL)
1359
0
      image=image->previous;
1360
993
  if (page_table != (ExtendedSignedIntegralType *) NULL)
1361
482
    {
1362
      /*
1363
        Write the DCX page table.
1364
      */
1365
482
      if (logging && write_dcx && image_list_length > max_scenes)
1366
0
        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1367
0
                              "WARNING: DCX truncated to %lu scenes!",
1368
0
                              max_scenes-1);
1369
482
      page_table[scene+1]=0;
1370
482
      (void) SeekBlob(image,0L,SEEK_SET);
1371
482
      (void) WriteBlobLSBLong(image,0x3ADE68B1L);
1372
964
      for (i=0; i <= (long) scene; i++)
1373
482
        (void) WriteBlobLSBLong(image,(unsigned long) page_table[i]);
1374
482
      MagickFreeResourceLimitedMemory(page_table);
1375
482
    }
1376
993
  if (status == False)
1377
993
    ThrowPCXWriterException(FileOpenError,UnableToWriteFile,image);
1378
993
  status &= CloseBlob(image);
1379
993
  return(status);
1380
993
}