Coverage Report

Created: 2026-06-30 07:12

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