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