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-2019 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 register ssize_t
238 x;
239
240 register Quantum
241 *q;
242
243 register ssize_t
244 i;
245
246 register 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 image->columns=(size_t) MagickAbsoluteValue((ssize_t) pcx_info.right-
344 pcx_info.left)+1UL;
345 image->rows=(size_t) MagickAbsoluteValue((ssize_t) pcx_info.bottom-
346 pcx_info.top)+1UL;
347 if ((image->columns == 0) || (image->rows == 0) ||
348 ((pcx_info.bits_per_pixel != 1) && (pcx_info.bits_per_pixel != 2) &&
349 (pcx_info.bits_per_pixel != 4) && (pcx_info.bits_per_pixel != 8)))
350 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
351 image->depth=pcx_info.bits_per_pixel;
352 image->units=PixelsPerInchResolution;
353 image->resolution.x=(double) pcx_info.horizontal_resolution;
354 image->resolution.y=(double) pcx_info.vertical_resolution;
355 image->colors=16;
356 if ((image_info->ping != MagickFalse) && (image_info->number_scenes != 0))
357 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
358 break;
359 if ((MagickSizeType) (image->columns*image->rows/255) > GetBlobSize(image))
360 ThrowPCXException(CorruptImageError,"InsufficientImageDataInFile");
361 status=SetImageExtent(image,image->columns,image->rows,exception);
362 if (status == MagickFalse)
363 ThrowPCXException(exception->severity,exception->reason);
364 (void) SetImageBackgroundColor(image,exception);
365 (void) memset(pcx_colormap,0,sizeof(pcx_colormap));
366 count=ReadBlob(image,3*image->colors,pcx_colormap);
367 if (count != (ssize_t) (3*image->colors))
368 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
369 pcx_info.reserved=(unsigned char) ReadBlobByte(image);
370 pcx_info.planes=(unsigned char) ReadBlobByte(image);
371 if (pcx_info.planes == 0)
372 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
373 if (pcx_info.planes > 6)
374 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
375 if ((pcx_info.bits_per_pixel*pcx_info.planes) >= 64)
376 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
377 one=1;
378 if ((pcx_info.bits_per_pixel != 8) || (pcx_info.planes == 1))
379 if ((pcx_info.version == 3) || (pcx_info.version == 5) ||
380 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
381 image->colors=(size_t) MagickMin(one << (1UL*
382 (pcx_info.bits_per_pixel*pcx_info.planes)),256UL);
383 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
384 ThrowPCXException(ResourceLimitError,"MemoryAllocationFailed");
385 if ((pcx_info.bits_per_pixel >= 8) && (pcx_info.planes != 1))
386 image->storage_class=DirectClass;
387 p=pcx_colormap;
388 for (i=0; i < (ssize_t) image->colors; i++)
389 {
390 image->colormap[i].red=ScaleCharToQuantum(*p++);
391 image->colormap[i].green=ScaleCharToQuantum(*p++);
392 image->colormap[i].blue=ScaleCharToQuantum(*p++);
393 }
394 pcx_info.bytes_per_line=ReadBlobLSBShort(image);
395 pcx_info.palette_info=ReadBlobLSBShort(image);
396 pcx_info.horizontal_screensize=ReadBlobLSBShort(image);
397 pcx_info.vertical_screensize=ReadBlobLSBShort(image);
398 for (i=0; i < 54; i++)
399 (void) ReadBlobByte(image);
400 /*
401 Read image data.
402 */
403 if (HeapOverflowSanityCheck(image->rows, (size_t) pcx_info.bytes_per_line) != MagickFalse)
404 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
405 pcx_packets=(size_t) image->rows*pcx_info.bytes_per_line;
406 if (HeapOverflowSanityCheck(pcx_packets, (size_t) pcx_info.planes) != MagickFalse)
407 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
408 pcx_packets=(size_t) pcx_packets*pcx_info.planes;
409 if ((size_t) (pcx_info.bits_per_pixel*pcx_info.planes*image->columns) > (pcx_packets*8U))
410 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
411 if ((MagickSizeType) (pcx_packets/32+128) > GetBlobSize(image))
412 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
413 scanline=(unsigned char *) AcquireQuantumMemory(MagickMax(image->columns,
414 pcx_info.bytes_per_line),MagickMax(pcx_info.planes,8)*sizeof(*scanline));
415 pixel_info=AcquireVirtualMemory(pcx_packets,2*sizeof(*pixels));
416 if ((scanline == (unsigned char *) NULL) ||
417 (pixel_info == (MemoryInfo *) NULL))
418 {
419 if (scanline != (unsigned char *) NULL)
420 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
421 if (pixel_info != (MemoryInfo *) NULL)
422 pixel_info=RelinquishVirtualMemory(pixel_info);
423 ThrowPCXException(ResourceLimitError,"MemoryAllocationFailed");
424 }
425 (void) memset(scanline,0,(size_t) MagickMax(image->columns,
426 pcx_info.bytes_per_line)*MagickMax(pcx_info.planes,8)*sizeof(*scanline));
427 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
428 (void) memset(pixels,0,(size_t) pcx_packets*(2*sizeof(*pixels)));
429 /*
430 Uncompress image data.
431 */
432 p=pixels;
433 if (pcx_info.encoding == 0)
434 while (pcx_packets != 0)
435 {
436 packet=(unsigned char) ReadBlobByte(image);
437 if (EOFBlob(image) != MagickFalse)
438 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
439 *p++=packet;
440 pcx_packets--;
441 }
442 else
443 while (pcx_packets != 0)
444 {
445 packet=(unsigned char) ReadBlobByte(image);
446 if (EOFBlob(image) != MagickFalse)
447 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
448 if ((packet & 0xc0) != 0xc0)
449 {
450 *p++=packet;
451 pcx_packets--;
452 continue;
453 }
454 count=(ssize_t) (packet & 0x3f);
455 packet=(unsigned char) ReadBlobByte(image);
456 if (EOFBlob(image) != MagickFalse)
457 ThrowPCXException(CorruptImageError,"UnexpectedEndOfFile");
458 for ( ; count != 0; count--)
459 {
460 *p++=packet;
461 pcx_packets--;
462 if (pcx_packets == 0)
463 break;
464 }
465 }
466 if (image->storage_class == DirectClass)
467 image->alpha_trait=pcx_info.planes > 3 ? BlendPixelTrait :
468 UndefinedPixelTrait;
469 else
470 if ((pcx_info.version == 5) ||
471 ((pcx_info.bits_per_pixel*pcx_info.planes) == 1))
472 {
473 /*
474 Initialize image colormap.
475 */
476 if (image->colors > 256)
477 ThrowPCXException(CorruptImageError,"ColormapExceeds256Colors");
478 if ((pcx_info.bits_per_pixel*pcx_info.planes) == 1)
479 {
480 /*
481 Monochrome colormap.
482 */
483 image->colormap[0].red=(Quantum) 0;
484 image->colormap[0].green=(Quantum) 0;
485 image->colormap[0].blue=(Quantum) 0;
486 image->colormap[1].red=QuantumRange;
487 image->colormap[1].green=QuantumRange;
488 image->colormap[1].blue=QuantumRange;
489 }
490 else
491 if (image->colors > 16)
492 {
493 /*
494 256 color images have their color map at the end of the file.
495 */
496 pcx_info.colormap_signature=(unsigned char) ReadBlobByte(image);
497 count=ReadBlob(image,3*image->colors,pcx_colormap);
498 p=pcx_colormap;
499 for (i=0; i < (ssize_t) image->colors; i++)
500 {
501 image->colormap[i].red=ScaleCharToQuantum(*p++);
502 image->colormap[i].green=ScaleCharToQuantum(*p++);
503 image->colormap[i].blue=ScaleCharToQuantum(*p++);
504 }
505 }
506 }
507 /*
508 Convert PCX raster image to pixel packets.
509 */
510 for (y=0; y < (ssize_t) image->rows; y++)
511 {
512 p=pixels+(y*pcx_info.bytes_per_line*pcx_info.planes);
513 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
514 if (q == (Quantum *) NULL)
515 break;
516 r=scanline;
517 if (image->storage_class == DirectClass)
518 for (i=0; i < pcx_info.planes; i++)
519 {
520 r=scanline+i;
521 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
522 {
523 switch (i)
524 {
525 case 0:
526 {
527 *r=(*p++);
528 break;
529 }
530 case 1:
531 {
532 *r=(*p++);
533 break;
534 }
535 case 2:
536 {
537 *r=(*p++);
538 break;
539 }
540 case 3:
541 default:
542 {
543 *r=(*p++);
544 break;
545 }
546 }
547 r+=pcx_info.planes;
548 }
549 }
550 else
551 if (pcx_info.planes > 1)
552 {
553 for (x=0; x < (ssize_t) image->columns; x++)
554 *r++=0;
555 for (i=0; i < pcx_info.planes; i++)
556 {
557 r=scanline;
558 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
559 {
560 bits=(*p++);
561 for (mask=0x80; mask != 0; mask>>=1)
562 {
563 if (bits & mask)
564 *r|=1 << i;
565 r++;
566 }
567 }
568 }
569 }
570 else
571 switch (pcx_info.bits_per_pixel)
572 {
573 case 1:
574 {
575 register ssize_t
576 bit;
577
578 for (x=0; x < ((ssize_t) image->columns-7); x+=8)
579 {
580 for (bit=7; bit >= 0; bit--)
581 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
582 p++;
583 }
584 if ((image->columns % 8) != 0)
585 {
586 for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
587 *r++=(unsigned char) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
588 p++;
589 }
590 break;
591 }
592 case 2:
593 {
594 for (x=0; x < ((ssize_t) image->columns-3); x+=4)
595 {
596 *r++=(*p >> 6) & 0x3;
597 *r++=(*p >> 4) & 0x3;
598 *r++=(*p >> 2) & 0x3;
599 *r++=(*p) & 0x3;
600 p++;
601 }
602 if ((image->columns % 4) != 0)
603 {
604 for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
605 *r++=(unsigned char) ((*p >> (i*2)) & 0x03);
606 p++;
607 }
608 break;
609 }
610 case 4:
611 {
612 for (x=0; x < ((ssize_t) image->columns-1); x+=2)
613 {
614 *r++=(*p >> 4) & 0xf;
615 *r++=(*p) & 0xf;
616 p++;
617 }
618 if ((image->columns % 2) != 0)
619 *r++=(*p++ >> 4) & 0xf;
620 break;
621 }
622 case 8:
623 {
624 (void) memcpy(r,p,image->columns);
625 break;
626 }
627 default:
628 break;
629 }
630 /*
631 Transfer image scanline.
632 */
633 r=scanline;
634 for (x=0; x < (ssize_t) image->columns; x++)
635 {
636 if (image->storage_class == PseudoClass)
637 SetPixelIndex(image,*r++,q);
638 else
639 {
640 SetPixelRed(image,ScaleCharToQuantum(*r++),q);
641 SetPixelGreen(image,ScaleCharToQuantum(*r++),q);
642 SetPixelBlue(image,ScaleCharToQuantum(*r++),q);
643 if (image->alpha_trait != UndefinedPixelTrait)
644 SetPixelAlpha(image,ScaleCharToQuantum(*r++),q);
645 }
646 q+=GetPixelChannels(image);
647 }
648 if (SyncAuthenticPixels(image,exception) == MagickFalse)
649 break;
650 if (image->previous == (Image *) NULL)
651 {
652 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
653 image->rows);
654 if (status == MagickFalse)
655 break;
656 }
657 }
658 if (image->storage_class == PseudoClass)
659 (void) SyncImage(image,exception);
660 scanline=(unsigned char *) RelinquishMagickMemory(scanline);
661 pixel_info=RelinquishVirtualMemory(pixel_info);
662 if (EOFBlob(image) != MagickFalse)
663 {
664 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
665 image->filename);
666 break;
667 }
668 /*
669 Proceed to next image.
670 */
671 if (image_info->number_scenes != 0)
672 if (image->scene >= (image_info->scene+image_info->number_scenes-1))
673 break;
674 if (page_table == (MagickOffsetType *) NULL)
675 break;
676 if (page_table[id] == 0)
677 break;
678 offset=SeekBlob(image,(MagickOffsetType) page_table[id],SEEK_SET);
679 if (offset < 0)
680 ThrowPCXException(CorruptImageError,"ImproperImageHeader");
681 count=ReadBlob(image,1,&pcx_info.identifier);
682 if ((count != 0) && (pcx_info.identifier == 0x0a))
683 {
684 /*
685 Allocate next image structure.
686 */
687 AcquireNextImage(image_info,image,exception);
688 if (GetNextImageInList(image) == (Image *) NULL)
689 {
690 status=MagickFalse;
691 break;
692 }
693 image=SyncNextImageInList(image);
694 status=SetImageProgress(image,LoadImagesTag,TellBlob(image),
695 GetBlobSize(image));
696 if (status == MagickFalse)
697 break;
698 }
699 }
700 if (page_table != (MagickOffsetType *) NULL)
701 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
702 (void) CloseBlob(image);
703 if (status == MagickFalse)
704 return(DestroyImageList(image));
705 return(GetFirstImageInList(image));
706 }
707
708 /*
709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710 % %
711 % %
712 % %
713 % R e g i s t e r P C X I m a g e %
714 % %
715 % %
716 % %
717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718 %
719 % RegisterPCXImage() adds attributes for the PCX image format to
720 % the list of supported formats. The attributes include the image format
721 % tag, a method to read and/or write the format, whether the format
722 % supports the saving of more than one frame to the same file or blob,
723 % whether the format supports native in-memory I/O, and a brief
724 % description of the format.
725 %
726 % The format of the RegisterPCXImage method is:
727 %
728 % size_t RegisterPCXImage(void)
729 %
730 */
RegisterPCXImage(void)731 ModuleExport size_t RegisterPCXImage(void)
732 {
733 MagickInfo
734 *entry;
735
736 entry=AcquireMagickInfo("PCX","DCX","ZSoft IBM PC multi-page Paintbrush");
737 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
738 entry->encoder=(EncodeImageHandler *) WritePCXImage;
739 entry->flags|=CoderDecoderSeekableStreamFlag;
740 entry->flags|=CoderEncoderSeekableStreamFlag;
741 entry->magick=(IsImageFormatHandler *) IsDCX;
742 (void) RegisterMagickInfo(entry);
743 entry=AcquireMagickInfo("PCX","PCX","ZSoft IBM PC Paintbrush");
744 entry->decoder=(DecodeImageHandler *) ReadPCXImage;
745 entry->encoder=(EncodeImageHandler *) WritePCXImage;
746 entry->magick=(IsImageFormatHandler *) IsPCX;
747 entry->flags^=CoderAdjoinFlag;
748 entry->flags|=CoderDecoderSeekableStreamFlag;
749 entry->flags|=CoderEncoderSeekableStreamFlag;
750 (void) RegisterMagickInfo(entry);
751 return(MagickImageCoderSignature);
752 }
753
754 /*
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756 % %
757 % %
758 % %
759 % U n r e g i s t e r P C X I m a g e %
760 % %
761 % %
762 % %
763 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
764 %
765 % UnregisterPCXImage() removes format registrations made by the
766 % PCX module from the list of supported formats.
767 %
768 % The format of the UnregisterPCXImage method is:
769 %
770 % UnregisterPCXImage(void)
771 %
772 */
UnregisterPCXImage(void)773 ModuleExport void UnregisterPCXImage(void)
774 {
775 (void) UnregisterMagickInfo("DCX");
776 (void) UnregisterMagickInfo("PCX");
777 }
778
779 /*
780 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781 % %
782 % %
783 % %
784 % W r i t e P C X I m a g e %
785 % %
786 % %
787 % %
788 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
789 %
790 % WritePCXImage() writes an image in the ZSoft IBM PC Paintbrush file
791 % format.
792 %
793 % The format of the WritePCXImage method is:
794 %
795 % MagickBooleanType WritePCXImage(const ImageInfo *image_info,
796 % Image *image,ExceptionInfo *exception)
797 %
798 % A description of each parameter follows.
799 %
800 % o image_info: the image info.
801 %
802 % o image: The image.
803 %
804 % o exception: return any errors or warnings in this structure.
805 %
806 */
807
PCXWritePixels(PCXInfo * pcx_info,const unsigned char * pixels,Image * image)808 static MagickBooleanType PCXWritePixels(PCXInfo *pcx_info,
809 const unsigned char *pixels,Image *image)
810 {
811 register const unsigned char
812 *q;
813
814 register ssize_t
815 i,
816 x;
817
818 ssize_t
819 count;
820
821 unsigned char
822 packet,
823 previous;
824
825 q=pixels;
826 for (i=0; i < (ssize_t) pcx_info->planes; i++)
827 {
828 if (pcx_info->encoding == 0)
829 {
830 for (x=0; x < (ssize_t) pcx_info->bytes_per_line; x++)
831 (void) WriteBlobByte(image,(unsigned char) (*q++));
832 }
833 else
834 {
835 previous=(*q++);
836 count=1;
837 for (x=0; x < (ssize_t) (pcx_info->bytes_per_line-1); x++)
838 {
839 packet=(*q++);
840 if ((packet == previous) && (count < 63))
841 {
842 count++;
843 continue;
844 }
845 if ((count > 1) || ((previous & 0xc0) == 0xc0))
846 {
847 count|=0xc0;
848 (void) WriteBlobByte(image,(unsigned char) count);
849 }
850 (void) WriteBlobByte(image,previous);
851 previous=packet;
852 count=1;
853 }
854 if ((count > 1) || ((previous & 0xc0) == 0xc0))
855 {
856 count|=0xc0;
857 (void) WriteBlobByte(image,(unsigned char) count);
858 }
859 (void) WriteBlobByte(image,previous);
860 }
861 }
862 return (MagickTrue);
863 }
864
WritePCXImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)865 static MagickBooleanType WritePCXImage(const ImageInfo *image_info,Image *image,
866 ExceptionInfo *exception)
867 {
868 MagickBooleanType
869 status;
870
871 MagickOffsetType
872 offset,
873 *page_table,
874 scene;
875
876 MemoryInfo
877 *pixel_info;
878
879 PCXInfo
880 pcx_info;
881
882 register const Quantum
883 *p;
884
885 register ssize_t
886 i,
887 x;
888
889 register unsigned char
890 *q;
891
892 size_t
893 imageListLength,
894 length;
895
896 ssize_t
897 y;
898
899 unsigned char
900 *pcx_colormap,
901 *pixels;
902
903 /*
904 Open output image file.
905 */
906 assert(image_info != (const ImageInfo *) NULL);
907 assert(image_info->signature == MagickCoreSignature);
908 assert(image != (Image *) NULL);
909 assert(image->signature == MagickCoreSignature);
910 if (image->debug != MagickFalse)
911 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
912 assert(exception != (ExceptionInfo *) NULL);
913 assert(exception->signature == MagickCoreSignature);
914 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
915 if (status == MagickFalse)
916 return(status);
917 if ((image->columns > 65535UL) || (image->rows > 65535UL))
918 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
919 page_table=(MagickOffsetType *) NULL;
920 if ((LocaleCompare(image_info->magick,"DCX") == 0) ||
921 ((GetNextImageInList(image) != (Image *) NULL) &&
922 (image_info->adjoin != MagickFalse)))
923 {
924 /*
925 Write the DCX page table.
926 */
927 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
928 page_table=(MagickOffsetType *) AcquireQuantumMemory(1024UL,
929 sizeof(*page_table));
930 if (page_table == (MagickOffsetType *) NULL)
931 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
932 for (scene=0; scene < 1024; scene++)
933 (void) WriteBlobLSBLong(image,0x00000000L);
934 }
935 scene=0;
936 imageListLength=GetImageListLength(image);
937 do
938 {
939 if (page_table != (MagickOffsetType *) NULL)
940 page_table[scene]=TellBlob(image);
941 /*
942 Initialize PCX raster file header.
943 */
944 pcx_info.identifier=0x0a;
945 pcx_info.version=5;
946 pcx_info.encoding=image_info->compression == NoCompression ? 0 : 1;
947 pcx_info.bits_per_pixel=8;
948 if ((image->storage_class == PseudoClass) &&
949 (SetImageMonochrome(image,exception) != MagickFalse))
950 pcx_info.bits_per_pixel=1;
951 else
952 (void) TransformImageColorspace(image,sRGBColorspace,exception);
953 pcx_info.left=0;
954 pcx_info.top=0;
955 pcx_info.right=(unsigned short) (image->columns-1);
956 pcx_info.bottom=(unsigned short) (image->rows-1);
957 switch (image->units)
958 {
959 case UndefinedResolution:
960 case PixelsPerInchResolution:
961 default:
962 {
963 pcx_info.horizontal_resolution=(unsigned short) image->resolution.x;
964 pcx_info.vertical_resolution=(unsigned short) image->resolution.y;
965 break;
966 }
967 case PixelsPerCentimeterResolution:
968 {
969 pcx_info.horizontal_resolution=(unsigned short)
970 (2.54*image->resolution.x+0.5);
971 pcx_info.vertical_resolution=(unsigned short)
972 (2.54*image->resolution.y+0.5);
973 break;
974 }
975 }
976 pcx_info.reserved=0;
977 pcx_info.planes=1;
978 if ((image->storage_class == DirectClass) || (image->colors > 256))
979 {
980 pcx_info.planes=3;
981 if (image->alpha_trait != UndefinedPixelTrait)
982 pcx_info.planes++;
983 }
984 length=(((size_t) image->columns*pcx_info.bits_per_pixel+7)/8);
985 if (length > 65535UL)
986 {
987 if (page_table != (MagickOffsetType *) NULL)
988 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
989 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
990 }
991 pcx_info.bytes_per_line=(unsigned short) length;
992 pcx_info.palette_info=1;
993 pcx_info.colormap_signature=0x0c;
994 /*
995 Write PCX header.
996 */
997 (void) WriteBlobByte(image,pcx_info.identifier);
998 (void) WriteBlobByte(image,pcx_info.version);
999 (void) WriteBlobByte(image,pcx_info.encoding);
1000 (void) WriteBlobByte(image,pcx_info.bits_per_pixel);
1001 (void) WriteBlobLSBShort(image,pcx_info.left);
1002 (void) WriteBlobLSBShort(image,pcx_info.top);
1003 (void) WriteBlobLSBShort(image,pcx_info.right);
1004 (void) WriteBlobLSBShort(image,pcx_info.bottom);
1005 (void) WriteBlobLSBShort(image,pcx_info.horizontal_resolution);
1006 (void) WriteBlobLSBShort(image,pcx_info.vertical_resolution);
1007 /*
1008 Dump colormap to file.
1009 */
1010 pcx_colormap=(unsigned char *) AcquireQuantumMemory(256UL,
1011 3*sizeof(*pcx_colormap));
1012 if (pcx_colormap == (unsigned char *) NULL)
1013 {
1014 if (page_table != (MagickOffsetType *) NULL)
1015 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1016 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1017 }
1018 (void) memset(pcx_colormap,0,3*256*sizeof(*pcx_colormap));
1019 q=pcx_colormap;
1020 if ((image->storage_class == PseudoClass) && (image->colors <= 256))
1021 for (i=0; i < (ssize_t) image->colors; i++)
1022 {
1023 *q++=ScaleQuantumToChar(image->colormap[i].red);
1024 *q++=ScaleQuantumToChar(image->colormap[i].green);
1025 *q++=ScaleQuantumToChar(image->colormap[i].blue);
1026 }
1027 (void) WriteBlob(image,3*16,(const unsigned char *) pcx_colormap);
1028 (void) WriteBlobByte(image,pcx_info.reserved);
1029 (void) WriteBlobByte(image,pcx_info.planes);
1030 (void) WriteBlobLSBShort(image,pcx_info.bytes_per_line);
1031 (void) WriteBlobLSBShort(image,pcx_info.palette_info);
1032 for (i=0; i < 58; i++)
1033 (void) WriteBlobByte(image,'\0');
1034 length=(size_t) pcx_info.bytes_per_line;
1035 pixel_info=AcquireVirtualMemory(length,pcx_info.planes*sizeof(*pixels));
1036 if (pixel_info == (MemoryInfo *) NULL)
1037 {
1038 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
1039 if (page_table != (MagickOffsetType *) NULL)
1040 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1041 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1042 }
1043 pixels=(unsigned char *) GetVirtualMemoryBlob(pixel_info);
1044 q=pixels;
1045 if ((image->storage_class == DirectClass) || (image->colors > 256))
1046 {
1047 /*
1048 Convert DirectClass image to PCX raster pixels.
1049 */
1050 for (y=0; y < (ssize_t) image->rows; y++)
1051 {
1052 q=pixels;
1053 for (i=0; i < pcx_info.planes; i++)
1054 {
1055 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1056 if (p == (const Quantum *) NULL)
1057 break;
1058 switch ((int) i)
1059 {
1060 case 0:
1061 {
1062 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
1063 {
1064 *q++=ScaleQuantumToChar(GetPixelRed(image,p));
1065 p+=GetPixelChannels(image);
1066 }
1067 break;
1068 }
1069 case 1:
1070 {
1071 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
1072 {
1073 *q++=ScaleQuantumToChar(GetPixelGreen(image,p));
1074 p+=GetPixelChannels(image);
1075 }
1076 break;
1077 }
1078 case 2:
1079 {
1080 for (x=0; x < (ssize_t) pcx_info.bytes_per_line; x++)
1081 {
1082 *q++=ScaleQuantumToChar(GetPixelBlue(image,p));
1083 p+=GetPixelChannels(image);
1084 }
1085 break;
1086 }
1087 case 3:
1088 default:
1089 {
1090 for (x=(ssize_t) pcx_info.bytes_per_line; x != 0; x--)
1091 {
1092 *q++=ScaleQuantumToChar((Quantum) (GetPixelAlpha(image,p)));
1093 p+=GetPixelChannels(image);
1094 }
1095 break;
1096 }
1097 }
1098 }
1099 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
1100 break;
1101 if (image->previous == (Image *) NULL)
1102 {
1103 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1104 image->rows);
1105 if (status == MagickFalse)
1106 break;
1107 }
1108 }
1109 }
1110 else
1111 {
1112 if (pcx_info.bits_per_pixel > 1)
1113 for (y=0; y < (ssize_t) image->rows; y++)
1114 {
1115 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1116 if (p == (const Quantum *) NULL)
1117 break;
1118 q=pixels;
1119 for (x=0; x < (ssize_t) image->columns; x++)
1120 {
1121 *q++=(unsigned char) GetPixelIndex(image,p);
1122 p+=GetPixelChannels(image);
1123 }
1124 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
1125 break;
1126 if (image->previous == (Image *) NULL)
1127 {
1128 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1129 y,image->rows);
1130 if (status == MagickFalse)
1131 break;
1132 }
1133 }
1134 else
1135 {
1136 register unsigned char
1137 bit,
1138 byte;
1139
1140 /*
1141 Convert PseudoClass image to a PCX monochrome image.
1142 */
1143 for (y=0; y < (ssize_t) image->rows; y++)
1144 {
1145 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1146 if (p == (const Quantum *) NULL)
1147 break;
1148 bit=0;
1149 byte=0;
1150 q=pixels;
1151 for (x=0; x < (ssize_t) image->columns; x++)
1152 {
1153 byte<<=1;
1154 if (GetPixelLuma(image,p) >= (QuantumRange/2.0))
1155 byte|=0x01;
1156 bit++;
1157 if (bit == 8)
1158 {
1159 *q++=byte;
1160 bit=0;
1161 byte=0;
1162 }
1163 p+=GetPixelChannels(image);
1164 }
1165 if (bit != 0)
1166 *q++=byte << (8-bit);
1167 if (PCXWritePixels(&pcx_info,pixels,image) == MagickFalse)
1168 break;
1169 if (image->previous == (Image *) NULL)
1170 {
1171 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType)
1172 y,image->rows);
1173 if (status == MagickFalse)
1174 break;
1175 }
1176 }
1177 }
1178 (void) WriteBlobByte(image,pcx_info.colormap_signature);
1179 (void) WriteBlob(image,3*256,pcx_colormap);
1180 }
1181 pixel_info=RelinquishVirtualMemory(pixel_info);
1182 pcx_colormap=(unsigned char *) RelinquishMagickMemory(pcx_colormap);
1183 if (page_table == (MagickOffsetType *) NULL)
1184 break;
1185 if (scene >= 1023)
1186 break;
1187 if (GetNextImageInList(image) == (Image *) NULL)
1188 break;
1189 image=SyncNextImageInList(image);
1190 status=SetImageProgress(image,SaveImagesTag,scene++,imageListLength);
1191 if (status == MagickFalse)
1192 break;
1193 } while (image_info->adjoin != MagickFalse);
1194 if (page_table != (MagickOffsetType *) NULL)
1195 {
1196 /*
1197 Write the DCX page table.
1198 */
1199 page_table[scene+1]=0;
1200 offset=SeekBlob(image,0L,SEEK_SET);
1201 if (offset < 0)
1202 {
1203 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1204 ThrowWriterException(CorruptImageError,"ImproperImageHeader");
1205 }
1206 (void) WriteBlobLSBLong(image,0x3ADE68B1L);
1207 for (i=0; i <= (ssize_t) scene; i++)
1208 (void) WriteBlobLSBLong(image,(unsigned int) page_table[i]);
1209 page_table=(MagickOffsetType *) RelinquishMagickMemory(page_table);
1210 }
1211 if (status == MagickFalse)
1212 {
1213 char
1214 *message;
1215
1216 message=GetExceptionMessage(errno);
1217 (void) ThrowMagickException(exception,GetMagickModule(),FileOpenError,
1218 "UnableToWriteFile","`%s': %s",image->filename,message);
1219 message=DestroyString(message);
1220 }
1221 (void) CloseBlob(image);
1222 return(MagickTrue);
1223 }
1224