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