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