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