1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % PPPP DDDD BBBB % 7 % P P D D B B % 8 % PPPP D D BBBB % 9 % P D D B B % 10 % P DDDD BBBB % 11 % % 12 % % 13 % Read/Write Palm Database ImageViewer 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 % 20071202 TS * rewrote RLE decoder - old version could cause buffer overflows 38 % * failure of RLE decoding now thows error RLEDecoderError 39 % * fixed bug in RLE decoding - now all rows are decoded, not just 40 % the first one 41 % * fixed bug in reader - record offsets now handled correctly 42 % * fixed bug in reader - only bits 0..2 indicate compression type 43 % * in writer: now using image color count instead of depth 44 */ 45 46 /* 48 Include declarations. 49 */ 50 #include "MagickCore/studio.h" 51 #include "MagickCore/attribute.h" 52 #include "MagickCore/blob.h" 53 #include "MagickCore/blob-private.h" 54 #include "MagickCore/cache.h" 55 #include "MagickCore/colormap-private.h" 56 #include "MagickCore/color-private.h" 57 #include "MagickCore/colormap.h" 58 #include "MagickCore/colorspace.h" 59 #include "MagickCore/colorspace-private.h" 60 #include "MagickCore/constitute.h" 61 #include "MagickCore/exception.h" 62 #include "MagickCore/exception-private.h" 63 #include "MagickCore/image.h" 64 #include "MagickCore/image-private.h" 65 #include "MagickCore/list.h" 66 #include "MagickCore/magick.h" 67 #include "MagickCore/memory_.h" 68 #include "MagickCore/monitor.h" 69 #include "MagickCore/monitor-private.h" 70 #include "MagickCore/pixel-accessor.h" 71 #include "MagickCore/property.h" 72 #include "MagickCore/quantum-private.h" 73 #include "MagickCore/quantum-private.h" 74 #include "MagickCore/static.h" 75 #include "MagickCore/string_.h" 76 #include "MagickCore/module.h" 77 78 /* 80 Typedef declarations. 81 */ 82 typedef struct _PDBInfo 83 { 84 char 85 name[32]; 86 87 short int 88 attributes, 89 version; 90 91 size_t 92 create_time, 93 modify_time, 94 archive_time, 95 modify_number, 96 application_info, 97 sort_info; 98 99 char 100 type[4], /* database type identifier "vIMG" */ 101 id[4]; /* database creator identifier "View" */ 102 103 size_t 104 seed, 105 next_record; 106 107 short int 108 number_records; 109 } PDBInfo; 110 111 typedef struct _PDBImage 112 { 113 char 114 name[32], 115 version; 116 117 size_t 118 reserved_1, 119 note; 120 121 short int 122 x_last, 123 y_last; 124 125 size_t 126 reserved_2; 127 128 short int 129 width, 130 height; 131 132 unsigned char 133 type; 134 135 unsigned short 136 x_anchor, 137 y_anchor; 138 } PDBImage; 139 /* 140 Forward declarations. 141 */ 142 static MagickBooleanType 143 WritePDBImage(const ImageInfo *,Image *,ExceptionInfo *); 144 145 /* 147 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 148 % % 149 % % 150 % % 151 % D e c o d e I m a g e % 152 % % 153 % % 154 % % 155 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 156 % 157 % DecodeImage unpacks the packed image pixels into runlength-encoded 158 % pixel packets. 159 % 160 % The format of the DecodeImage method is: 161 % 162 % MagickBooleanType DecodeImage(Image *image,unsigned char *pixels, 163 % const size_t length) 164 % 165 % A description of each parameter follows: 166 % 167 % o image: the address of a structure of type Image. 168 % 169 % o pixels: The address of a byte (8 bits) array of pixel data created by 170 % the decoding process. 171 % 172 % o length: Number of bytes to read into buffer 'pixels'. 173 % 174 */ 175 static MagickBooleanType DecodeImage(Image *image, unsigned char *pixels, 176 const size_t length) 177 { 178 #define RLE_MODE_NONE -1 179 #define RLE_MODE_COPY 0 180 #define RLE_MODE_RUN 1 181 182 int data = 0, count = 0; 183 unsigned char *p; 184 int mode = RLE_MODE_NONE; 185 186 for (p = pixels; p < pixels + length; p++) { 187 if (0 == count) { 188 data = ReadBlobByte( image ); 189 if (-1 == data) return MagickFalse; 190 if (data > 128) { 191 mode = RLE_MODE_RUN; 192 count = data - 128 + 1; 193 data = ReadBlobByte( image ); 194 if (-1 == data) return MagickFalse; 195 } else { 196 mode = RLE_MODE_COPY; 197 count = data + 1; 198 } 199 } 200 201 if (RLE_MODE_COPY == mode) { 202 data = ReadBlobByte( image ); 203 if (-1 == data) return MagickFalse; 204 } 205 *p = (unsigned char)data; 206 --count; 207 } 208 return MagickTrue; 209 } 210 211 /* 213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 214 % % 215 % % 216 % % 217 % I s P D B % 218 % % 219 % % 220 % % 221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 222 % 223 % IsPDB() returns MagickTrue if the image format type, identified by the 224 % magick string, is PDB. 225 % 226 % The format of the ReadPDBImage method is: 227 % 228 % MagickBooleanType IsPDB(const unsigned char *magick,const size_t length) 229 % 230 % A description of each parameter follows: 231 % 232 % o magick: compare image format pattern against these bytes. 233 % 234 % o length: Specifies the length of the magick string. 235 % 236 */ 237 static MagickBooleanType IsPDB(const unsigned char *magick,const size_t length) 238 { 239 if (length < 68) 240 return(MagickFalse); 241 if (memcmp(magick+60,"vIMGView",8) == 0) 242 return(MagickTrue); 243 return(MagickFalse); 244 } 245 246 /* 248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 249 % % 250 % % 251 % % 252 % R e a d P D B I m a g e % 253 % % 254 % % 255 % % 256 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 257 % 258 % ReadPDBImage() reads an Pilot image file and returns it. It 259 % allocates the memory necessary for the new Image structure and returns a 260 % pointer to the new image. 261 % 262 % The format of the ReadPDBImage method is: 263 % 264 % Image *ReadPDBImage(const ImageInfo *image_info,ExceptionInfo *exception) 265 % 266 % A description of each parameter follows: 267 % 268 % o image_info: the image info. 269 % 270 % o exception: return any errors or warnings in this structure. 271 % 272 */ 273 static Image *ReadPDBImage(const ImageInfo *image_info,ExceptionInfo *exception) 274 { 275 unsigned char 276 attributes, 277 tag[3]; 278 279 Image 280 *image; 281 282 MagickBooleanType 283 status; 284 285 PDBImage 286 pdb_image; 287 288 PDBInfo 289 pdb_info; 290 291 Quantum 292 index; 293 294 register ssize_t 295 x; 296 297 register Quantum 298 *q; 299 300 register unsigned char 301 *p; 302 303 size_t 304 bits_per_pixel, 305 num_pad_bytes, 306 one, 307 packets; 308 309 ssize_t 310 count, 311 img_offset, 312 comment_offset = 0, 313 y; 314 315 unsigned char 316 *pixels; 317 318 /* 319 Open image file. 320 */ 321 assert(image_info != (const ImageInfo *) NULL); 322 assert(image_info->signature == MagickCoreSignature); 323 if (image_info->debug != MagickFalse) 324 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 325 image_info->filename); 326 assert(exception != (ExceptionInfo *) NULL); 327 assert(exception->signature == MagickCoreSignature); 328 image=AcquireImage(image_info,exception); 329 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); 330 if (status == MagickFalse) 331 { 332 image=DestroyImageList(image); 333 return((Image *) NULL); 334 } 335 /* 336 Determine if this a PDB image file. 337 */ 338 count=ReadBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name); 339 if (count != sizeof(pdb_info.name)) 340 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 341 pdb_info.attributes=(short) ReadBlobMSBShort(image); 342 pdb_info.version=(short) ReadBlobMSBShort(image); 343 pdb_info.create_time=ReadBlobMSBLong(image); 344 pdb_info.modify_time=ReadBlobMSBLong(image); 345 pdb_info.archive_time=ReadBlobMSBLong(image); 346 pdb_info.modify_number=ReadBlobMSBLong(image); 347 pdb_info.application_info=ReadBlobMSBLong(image); 348 pdb_info.sort_info=ReadBlobMSBLong(image); 349 (void) ReadBlob(image,4,(unsigned char *) pdb_info.type); 350 (void) ReadBlob(image,4,(unsigned char *) pdb_info.id); 351 pdb_info.seed=ReadBlobMSBLong(image); 352 pdb_info.next_record=ReadBlobMSBLong(image); 353 pdb_info.number_records=(short) ReadBlobMSBShort(image); 354 if ((memcmp(pdb_info.type,"vIMG",4) != 0) || 355 (memcmp(pdb_info.id,"View",4) != 0)) 356 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 357 if (pdb_info.next_record != 0) 358 ThrowReaderException(CoderError,"MultipleRecordListNotSupported"); 359 /* 360 Read record header. 361 */ 362 img_offset=(ssize_t) ReadBlobMSBSignedLong(image); 363 attributes=(unsigned char) ReadBlobByte(image); 364 (void) attributes; 365 count=ReadBlob(image,3,(unsigned char *) tag); 366 if (count != 3 || memcmp(tag,"\x6f\x80\x00",3) != 0) 367 ThrowReaderException(CorruptImageError,"CorruptImage"); 368 if (pdb_info.number_records > 1) 369 { 370 comment_offset=(ssize_t) ReadBlobMSBSignedLong(image); 371 attributes=(unsigned char) ReadBlobByte(image); 372 count=ReadBlob(image,3,(unsigned char *) tag); 373 if (count != 3 || memcmp(tag,"\x6f\x80\x01",3) != 0) 374 ThrowReaderException(CorruptImageError,"CorruptImage"); 375 } 376 num_pad_bytes = (size_t) (img_offset - TellBlob( image )); 377 while (num_pad_bytes-- != 0) 378 { 379 int 380 c; 381 382 c=ReadBlobByte(image); 383 if (c == EOF) 384 break; 385 } 386 /* 387 Read image header. 388 */ 389 count=ReadBlob(image,sizeof(pdb_image.name),(unsigned char *) pdb_image.name); 390 if (count != sizeof(pdb_image.name)) 391 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 392 pdb_image.version=ReadBlobByte(image); 393 pdb_image.type=(unsigned char) (ReadBlobByte(image)); 394 pdb_image.reserved_1=ReadBlobMSBLong(image); 395 pdb_image.note=ReadBlobMSBLong(image); 396 pdb_image.x_last=(short) ReadBlobMSBShort(image); 397 pdb_image.y_last=(short) ReadBlobMSBShort(image); 398 pdb_image.reserved_2=ReadBlobMSBLong(image); 399 pdb_image.x_anchor=ReadBlobMSBShort(image); 400 pdb_image.y_anchor=ReadBlobMSBShort(image); 401 pdb_image.width=(short) ReadBlobMSBShort(image); 402 pdb_image.height=(short) ReadBlobMSBShort(image); 403 /* 404 Initialize image structure. 405 */ 406 image->columns=(size_t) pdb_image.width; 407 image->rows=(size_t) pdb_image.height; 408 image->depth=8; 409 image->storage_class=PseudoClass; 410 bits_per_pixel=pdb_image.type == 0 ? 2UL : pdb_image.type == 2 ? 4UL : 1UL; 411 one=1; 412 if (AcquireImageColormap(image,one << bits_per_pixel,exception) == MagickFalse) 413 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 414 if (image_info->ping != MagickFalse) 415 { 416 (void) CloseBlob(image); 417 return(GetFirstImageInList(image)); 418 } 419 status=SetImageExtent(image,image->columns,image->rows,exception); 420 if (status == MagickFalse) 421 return(DestroyImageList(image)); 422 packets=(bits_per_pixel*image->columns+7)/8; 423 pixels=(unsigned char *) AcquireQuantumMemory(packets+257UL,image->rows* 424 sizeof(*pixels)); 425 if (pixels == (unsigned char *) NULL) 426 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 427 switch (pdb_image.version & 0x07) 428 { 429 case 0: 430 { 431 image->compression=NoCompression; 432 count=(ssize_t) ReadBlob(image, packets * image -> rows, pixels); 433 break; 434 } 435 case 1: 436 { 437 image->compression=RLECompression; 438 if (!DecodeImage(image, pixels, packets * image -> rows)) 439 ThrowReaderException( CorruptImageError, "RLEDecoderError" ); 440 break; 441 } 442 default: 443 ThrowReaderException(CorruptImageError, 444 "UnrecognizedImageCompressionType" ); 445 } 446 p=pixels; 447 switch (bits_per_pixel) 448 { 449 case 1: 450 { 451 int 452 bit; 453 454 /* 455 Read 1-bit PDB image. 456 */ 457 for (y=0; y < (ssize_t) image->rows; y++) 458 { 459 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 460 if (q == (Quantum *) NULL) 461 break; 462 for (x=0; x < ((ssize_t) image->columns-7); x+=8) 463 { 464 for (bit=0; bit < 8; bit++) 465 { 466 index=(Quantum) (*p & (0x80 >> bit) ? 0x00 : 0x01); 467 SetPixelIndex(image,index,q); 468 q+=GetPixelChannels(image); 469 } 470 p++; 471 } 472 if (SyncAuthenticPixels(image,exception) == MagickFalse) 473 break; 474 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 475 image->rows); 476 if (status == MagickFalse) 477 break; 478 } 479 (void) SyncImage(image,exception); 480 break; 481 } 482 case 2: 483 { 484 /* 485 Read 2-bit PDB image. 486 */ 487 for (y=0; y < (ssize_t) image->rows; y++) 488 { 489 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 490 if (q == (Quantum *) NULL) 491 break; 492 for (x=0; x < (ssize_t) image->columns-3; x+=4) 493 { 494 index=ConstrainColormapIndex(image,3UL-((*p >> 6) & 0x03),exception); 495 SetPixelIndex(image,index,q); 496 q+=GetPixelChannels(image); 497 index=ConstrainColormapIndex(image,3UL-((*p >> 4) & 0x03),exception); 498 SetPixelIndex(image,index,q); 499 q+=GetPixelChannels(image); 500 index=ConstrainColormapIndex(image,3UL-((*p >> 2) & 0x03),exception); 501 SetPixelIndex(image,index,q); 502 q+=GetPixelChannels(image); 503 index=ConstrainColormapIndex(image,3UL-((*p) & 0x03),exception); 504 SetPixelIndex(image,index,q); 505 p++; 506 q+=GetPixelChannels(image); 507 } 508 if (SyncAuthenticPixels(image,exception) == MagickFalse) 509 break; 510 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 511 image->rows); 512 if (status == MagickFalse) 513 break; 514 } 515 (void) SyncImage(image,exception); 516 break; 517 } 518 case 4: 519 { 520 /* 521 Read 4-bit PDB image. 522 */ 523 for (y=0; y < (ssize_t) image->rows; y++) 524 { 525 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); 526 if (q == (Quantum *) NULL) 527 break; 528 for (x=0; x < (ssize_t) image->columns-1; x+=2) 529 { 530 index=ConstrainColormapIndex(image,15UL-((*p >> 4) & 0x0f),exception); 531 SetPixelIndex(image,index,q); 532 q+=GetPixelChannels(image); 533 index=ConstrainColormapIndex(image,15UL-((*p) & 0x0f),exception); 534 SetPixelIndex(image,index,q); 535 p++; 536 q+=GetPixelChannels(image); 537 } 538 if (SyncAuthenticPixels(image,exception) == MagickFalse) 539 break; 540 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y, 541 image->rows); 542 if (status == MagickFalse) 543 break; 544 } 545 (void) SyncImage(image,exception); 546 break; 547 } 548 default: 549 ThrowReaderException(CorruptImageError,"ImproperImageHeader"); 550 } 551 pixels=(unsigned char *) RelinquishMagickMemory(pixels); 552 if (EOFBlob(image) != MagickFalse) 553 ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile", 554 image->filename); 555 if (pdb_info.number_records > 1) 556 { 557 char 558 *comment; 559 560 int 561 c; 562 563 register char 564 *p; 565 566 size_t 567 length; 568 569 num_pad_bytes = (size_t) (comment_offset - TellBlob( image )); 570 while (num_pad_bytes--) ReadBlobByte( image ); 571 572 /* 573 Read comment. 574 */ 575 c=ReadBlobByte(image); 576 length=MagickPathExtent; 577 comment=AcquireString((char *) NULL); 578 for (p=comment; c != EOF; p++) 579 { 580 if ((size_t) (p-comment+MagickPathExtent) >= length) 581 { 582 *p='\0'; 583 length<<=1; 584 length+=MagickPathExtent; 585 comment=(char *) ResizeQuantumMemory(comment,length+MagickPathExtent, 586 sizeof(*comment)); 587 if (comment == (char *) NULL) 588 break; 589 p=comment+strlen(comment); 590 } 591 *p=c; 592 c=ReadBlobByte(image); 593 } 594 *p='\0'; 595 if (comment == (char *) NULL) 596 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); 597 (void) SetImageProperty(image,"comment",comment,exception); 598 comment=DestroyString(comment); 599 } 600 (void) CloseBlob(image); 601 return(GetFirstImageInList(image)); 602 } 603 604 /* 606 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 607 % % 608 % % 609 % % 610 % R e g i s t e r P D B I m a g e % 611 % % 612 % % 613 % % 614 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 615 % 616 % RegisterPDBImage() adds properties for the PDB image format to 617 % the list of supported formats. The properties include the image format 618 % tag, a method to read and/or write the format, whether the format 619 % supports the saving of more than one frame to the same file or blob, 620 % whether the format supports native in-memory I/O, and a brief 621 % description of the format. 622 % 623 % The format of the RegisterPDBImage method is: 624 % 625 % size_t RegisterPDBImage(void) 626 % 627 */ 628 ModuleExport size_t RegisterPDBImage(void) 629 { 630 MagickInfo 631 *entry; 632 633 entry=AcquireMagickInfo("PDB","PDB","Palm Database ImageViewer Format"); 634 entry->decoder=(DecodeImageHandler *) ReadPDBImage; 635 entry->encoder=(EncodeImageHandler *) WritePDBImage; 636 entry->magick=(IsImageFormatHandler *) IsPDB; 637 (void) RegisterMagickInfo(entry); 638 return(MagickImageCoderSignature); 639 } 640 641 /* 643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 644 % % 645 % % 646 % % 647 % U n r e g i s t e r P D B I m a g e % 648 % % 649 % % 650 % % 651 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 652 % 653 % UnregisterPDBImage() removes format registrations made by the 654 % PDB module from the list of supported formats. 655 % 656 % The format of the UnregisterPDBImage method is: 657 % 658 % UnregisterPDBImage(void) 659 % 660 */ 661 ModuleExport void UnregisterPDBImage(void) 662 { 663 (void) UnregisterMagickInfo("PDB"); 664 } 665 666 /* 668 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 669 % % 670 % % 671 % % 672 % W r i t e P D B I m a g e % 673 % % 674 % % 675 % % 676 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 677 % 678 % WritePDBImage() writes an image 679 % 680 % The format of the WritePDBImage method is: 681 % 682 % MagickBooleanType WritePDBImage(const ImageInfo *image_info, 683 % Image *image,ExceptionInfo *exception) 684 % 685 % A description of each parameter follows. 686 % 687 % o image_info: the image info. 688 % 689 % o image: The image. 690 % 691 % o exception: return any errors or warnings in this structure. 692 % 693 */ 694 695 static unsigned char *EncodeRLE(unsigned char *destination, 696 unsigned char *source,size_t literal,size_t repeat) 697 { 698 if (literal > 0) 699 *destination++=(unsigned char) (literal-1); 700 (void) CopyMagickMemory(destination,source,literal); 701 destination+=literal; 702 if (repeat > 0) 703 { 704 *destination++=(unsigned char) (0x80 | (repeat-1)); 705 *destination++=source[literal]; 706 } 707 return(destination); 708 } 709 710 static MagickBooleanType WritePDBImage(const ImageInfo *image_info,Image *image, 711 ExceptionInfo *exception) 712 { 713 const char 714 *comment; 715 716 int 717 bits; 718 719 MagickBooleanType 720 status; 721 722 PDBImage 723 pdb_image; 724 725 PDBInfo 726 pdb_info; 727 728 QuantumInfo 729 *quantum_info; 730 731 register const Quantum 732 *p; 733 734 register ssize_t 735 x; 736 737 register unsigned char 738 *q; 739 740 size_t 741 bits_per_pixel, 742 literal, 743 packets, 744 packet_size, 745 repeat; 746 747 ssize_t 748 y; 749 750 unsigned char 751 *buffer, 752 *runlength, 753 *scanline; 754 755 /* 756 Open output image file. 757 */ 758 assert(image_info != (const ImageInfo *) NULL); 759 assert(image_info->signature == MagickCoreSignature); 760 assert(image != (Image *) NULL); 761 assert(image->signature == MagickCoreSignature); 762 if (image->debug != MagickFalse) 763 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 764 assert(exception != (ExceptionInfo *) NULL); 765 assert(exception->signature == MagickCoreSignature); 766 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); 767 if (status == MagickFalse) 768 return(status); 769 (void) TransformImageColorspace(image,sRGBColorspace,exception); 770 if (SetImageMonochrome(image,exception) != MagickFalse) { 771 bits_per_pixel=1; 772 } else if (image->colors <= 4) { 773 bits_per_pixel=2; 774 } else if (image->colors <= 8) { 775 bits_per_pixel=3; 776 } else { 777 bits_per_pixel=4; 778 } 779 (void) ResetMagickMemory(&pdb_info,0,sizeof(pdb_info)); 780 (void) CopyMagickString(pdb_info.name,image_info->filename, 781 sizeof(pdb_info.name)); 782 pdb_info.attributes=0; 783 pdb_info.version=0; 784 pdb_info.create_time=time(NULL); 785 pdb_info.modify_time=pdb_info.create_time; 786 pdb_info.archive_time=0; 787 pdb_info.modify_number=0; 788 pdb_info.application_info=0; 789 pdb_info.sort_info=0; 790 (void) CopyMagickMemory(pdb_info.type,"vIMG",4); 791 (void) CopyMagickMemory(pdb_info.id,"View",4); 792 pdb_info.seed=0; 793 pdb_info.next_record=0; 794 comment=GetImageProperty(image,"comment",exception); 795 pdb_info.number_records=(comment == (const char *) NULL ? 1 : 2); 796 (void) WriteBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name); 797 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.attributes); 798 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.version); 799 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.create_time); 800 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_time); 801 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.archive_time); 802 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_number); 803 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.application_info); 804 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.sort_info); 805 (void) WriteBlob(image,4,(unsigned char *) pdb_info.type); 806 (void) WriteBlob(image,4,(unsigned char *) pdb_info.id); 807 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.seed); 808 (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.next_record); 809 (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.number_records); 810 (void) CopyMagickString(pdb_image.name,pdb_info.name,sizeof(pdb_image.name)); 811 pdb_image.version=1; /* RLE Compressed */ 812 switch (bits_per_pixel) 813 { 814 case 1: pdb_image.type=(unsigned char) 0xff; break; /* monochrome */ 815 case 2: pdb_image.type=(unsigned char) 0x00; break; /* 2 bit gray */ 816 default: pdb_image.type=(unsigned char) 0x02; /* 4 bit gray */ 817 } 818 pdb_image.reserved_1=0; 819 pdb_image.note=0; 820 pdb_image.x_last=0; 821 pdb_image.y_last=0; 822 pdb_image.reserved_2=0; 823 pdb_image.x_anchor=(unsigned short) 0xffff; 824 pdb_image.y_anchor=(unsigned short) 0xffff; 825 pdb_image.width=(short) image->columns; 826 if (image->columns % 16) 827 pdb_image.width=(short) (16*(image->columns/16+1)); 828 pdb_image.height=(short) image->rows; 829 packets=((bits_per_pixel*image->columns+7)/8); 830 runlength=(unsigned char *) AcquireQuantumMemory(9UL*packets, 831 image->rows*sizeof(*runlength)); 832 if (runlength == (unsigned char *) NULL) 833 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 834 buffer=(unsigned char *) AcquireQuantumMemory(512,sizeof(*buffer)); 835 if (buffer == (unsigned char *) NULL) 836 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 837 packet_size=(size_t) (image->depth > 8 ? 2: 1); 838 scanline=(unsigned char *) AcquireQuantumMemory(image->columns,packet_size* 839 sizeof(*scanline)); 840 if (scanline == (unsigned char *) NULL) 841 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 842 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) 843 (void) TransformImageColorspace(image,sRGBColorspace,exception); 844 /* 845 Convert to GRAY raster scanline. 846 */ 847 quantum_info=AcquireQuantumInfo(image_info,image); 848 if (quantum_info == (QuantumInfo *) NULL) 849 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); 850 bits=8/(int) bits_per_pixel-1; /* start at most significant bits */ 851 literal=0; 852 repeat=0; 853 q=runlength; 854 buffer[0]=0x00; 855 for (y=0; y < (ssize_t) image->rows; y++) 856 { 857 p=GetVirtualPixels(image,0,y,image->columns,1,exception); 858 if (p == (const Quantum *) NULL) 859 break; 860 (void) ExportQuantumPixels(image,(CacheView *) NULL,quantum_info, 861 GrayQuantum,scanline,exception); 862 for (x=0; x < (ssize_t) pdb_image.width; x++) 863 { 864 if (x < (ssize_t) image->columns) 865 buffer[literal+repeat]|=(0xff-scanline[x*packet_size]) >> 866 (8-bits_per_pixel) << bits*bits_per_pixel; 867 bits--; 868 if (bits < 0) 869 { 870 if (((literal+repeat) > 0) && 871 (buffer[literal+repeat] == buffer[literal+repeat-1])) 872 { 873 if (repeat == 0) 874 { 875 literal--; 876 repeat++; 877 } 878 repeat++; 879 if (0x7f < repeat) 880 { 881 q=EncodeRLE(q,buffer,literal,repeat); 882 literal=0; 883 repeat=0; 884 } 885 } 886 else 887 { 888 if (repeat >= 2) 889 literal+=repeat; 890 else 891 { 892 q=EncodeRLE(q,buffer,literal,repeat); 893 buffer[0]=buffer[literal+repeat]; 894 literal=0; 895 } 896 literal++; 897 repeat=0; 898 if (0x7f < literal) 899 { 900 q=EncodeRLE(q,buffer,(literal < 0x80 ? literal : 0x80),0); 901 (void) CopyMagickMemory(buffer,buffer+literal+repeat,0x80); 902 literal-=0x80; 903 } 904 } 905 bits=8/(int) bits_per_pixel-1; 906 buffer[literal+repeat]=0x00; 907 } 908 } 909 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y, 910 image->rows); 911 if (status == MagickFalse) 912 break; 913 } 914 q=EncodeRLE(q,buffer,literal,repeat); 915 scanline=(unsigned char *) RelinquishMagickMemory(scanline); 916 buffer=(unsigned char *) RelinquishMagickMemory(buffer); 917 quantum_info=DestroyQuantumInfo(quantum_info); 918 /* 919 Write the Image record header. 920 */ 921 (void) WriteBlobMSBLong(image,(unsigned int) 922 (TellBlob(image)+8*pdb_info.number_records)); 923 (void) WriteBlobByte(image,0x40); 924 (void) WriteBlobByte(image,0x6f); 925 (void) WriteBlobByte(image,0x80); 926 (void) WriteBlobByte(image,0); 927 if (pdb_info.number_records > 1) 928 { 929 /* 930 Write the comment record header. 931 */ 932 (void) WriteBlobMSBLong(image,(unsigned int) (TellBlob(image)+8+58+q- 933 runlength)); 934 (void) WriteBlobByte(image,0x40); 935 (void) WriteBlobByte(image,0x6f); 936 (void) WriteBlobByte(image,0x80); 937 (void) WriteBlobByte(image,1); 938 } 939 /* 940 Write the Image data. 941 */ 942 (void) WriteBlob(image,sizeof(pdb_image.name),(unsigned char *) 943 pdb_image.name); 944 (void) WriteBlobByte(image,(unsigned char) pdb_image.version); 945 (void) WriteBlobByte(image,pdb_image.type); 946 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_1); 947 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.note); 948 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.x_last); 949 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.y_last); 950 (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_2); 951 (void) WriteBlobMSBShort(image,pdb_image.x_anchor); 952 (void) WriteBlobMSBShort(image,pdb_image.y_anchor); 953 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.width); 954 (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.height); 955 (void) WriteBlob(image,(size_t) (q-runlength),runlength); 956 runlength=(unsigned char *) RelinquishMagickMemory(runlength); 957 if (pdb_info.number_records > 1) 958 (void) WriteBlobString(image,comment); 959 (void) CloseBlob(image); 960 return(MagickTrue); 961 } 962