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