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