]> granicus.if.org Git - imagemagick/blob - coders/pdb.c
1b0773d145cc6a186e922b42daa02b73cc8c319c
[imagemagick] / coders / pdb.c
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 %                                John Cristy                                  %
17 %                                 July 1992                                   %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2014 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 \f
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 \f
77 /*
78   Typedef declarations.
79 */
80 typedef 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
109 typedef 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 */
140 static MagickBooleanType
141   WritePDBImage(const ImageInfo *,Image *,ExceptionInfo *);
142 \f
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 */
172 static 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 \f
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 */
233 static 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 \f
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 */
268 static Image *ReadPDBImage(const ImageInfo *image_info,ExceptionInfo *exception)
269 {
270   unsigned char
271     attributes, /* TS */
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, /* TS */
301     one,
302     packets;
303
304   ssize_t
305     count,
306     img_offset, /* TS */
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 == MagickSignature);
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 == MagickSignature);
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,32,(unsigned char *) pdb_info.name);
334   pdb_info.attributes=(short) ReadBlobMSBShort(image);
335   pdb_info.version=(short) ReadBlobMSBShort(image);
336   pdb_info.create_time=ReadBlobMSBLong(image);
337   pdb_info.modify_time=ReadBlobMSBLong(image);
338   pdb_info.archive_time=ReadBlobMSBLong(image);
339   pdb_info.modify_number=ReadBlobMSBLong(image);
340   pdb_info.application_info=ReadBlobMSBLong(image);
341   pdb_info.sort_info=ReadBlobMSBLong(image);
342   count=ReadBlob(image,4,(unsigned char *) pdb_info.type);
343   count=ReadBlob(image,4,(unsigned char *) pdb_info.id);
344   if ((count == 0) || (memcmp(pdb_info.type,"vIMG",4) != 0) ||
345       (memcmp(pdb_info.id,"View",4) != 0))
346     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
347   pdb_info.seed=ReadBlobMSBLong(image);
348   pdb_info.next_record=ReadBlobMSBLong(image);
349   pdb_info.number_records=(short) ReadBlobMSBShort(image);
350   if (pdb_info.next_record != 0)
351     ThrowReaderException(CoderError,"MultipleRecordListNotSupported");
352   /*
353     Read record header.
354   */
355   img_offset=(int) ReadBlobMSBLong(image); /* TS */
356   attributes=(unsigned char) ReadBlobByte(image);
357   (void) attributes;
358   count=ReadBlob(image,3,(unsigned char *) tag);
359   if (count != 3  ||  memcmp(tag,"\x6f\x80\x00",3) != 0)
360     ThrowReaderException(CorruptImageError,"CorruptImage");
361   if (pdb_info.number_records > 1)
362     {
363       comment_offset=(int) ReadBlobMSBLong(image);
364       attributes=(unsigned char) ReadBlobByte(image);
365       count=ReadBlob(image,3,(unsigned char *) tag);
366       if (count != 3  ||  memcmp(tag,"\x6f\x80\x01",3) != 0)
367         ThrowReaderException(CorruptImageError,"CorruptImage");
368     }
369   num_pad_bytes = (size_t) (img_offset - TellBlob( image ));
370   while (num_pad_bytes--) ReadBlobByte( image );
371   /*
372     Read image header.
373   */
374   count=ReadBlob(image,32,(unsigned char *) pdb_image.name);
375   pdb_image.version=ReadBlobByte(image);
376   pdb_image.type=ReadBlobByte(image);
377   pdb_image.reserved_1=ReadBlobMSBLong(image);
378   pdb_image.note=ReadBlobMSBLong(image);
379   pdb_image.x_last=(short) ReadBlobMSBShort(image);
380   pdb_image.y_last=(short) ReadBlobMSBShort(image);
381   pdb_image.reserved_2=ReadBlobMSBLong(image);
382   pdb_image.x_anchor=ReadBlobMSBShort(image);
383   pdb_image.y_anchor=ReadBlobMSBShort(image);
384   pdb_image.width=(short) ReadBlobMSBShort(image);
385   pdb_image.height=(short) ReadBlobMSBShort(image);
386   /*
387     Initialize image structure.
388   */
389   image->columns=(size_t) pdb_image.width;
390   image->rows=(size_t) pdb_image.height;
391   image->depth=8;
392   image->storage_class=PseudoClass;
393   bits_per_pixel=pdb_image.type == 0 ? 2UL : pdb_image.type == 2 ? 4UL : 1UL;
394   one=1;
395   if (AcquireImageColormap(image,one << bits_per_pixel,exception) == MagickFalse)
396     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
397   if (image_info->ping != MagickFalse)
398     {
399       (void) CloseBlob(image);
400       return(GetFirstImageInList(image));
401     }
402   packets=bits_per_pixel*image->columns/8;
403   pixels=(unsigned char *) AcquireQuantumMemory(packets+256UL,image->rows*
404     sizeof(*pixels));
405   if (pixels == (unsigned char *) NULL)
406     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
407
408   switch (pdb_image.version & 7) /* TS */
409   {
410     case 0:
411     {
412       image->compression=NoCompression;
413       count=(ssize_t) ReadBlob(image, packets * image -> rows, pixels);
414       break;
415     }
416     case 1:
417     {
418       image->compression=RLECompression;
419       if (!DecodeImage(image, pixels, packets * image -> rows))
420         ThrowReaderException( CorruptImageError, "RLEDecoderError" ); /* TS */
421       break;
422     }
423     default:
424       ThrowReaderException(CorruptImageError,
425          "UnrecognizedImageCompressionType" );
426   }
427   p=pixels;
428   switch (bits_per_pixel)
429   {
430     case 1:
431     {
432       int
433         bit;
434
435       /*
436         Read 1-bit PDB image.
437       */
438       for (y=0; y < (ssize_t) image->rows; y++)
439       {
440         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
441         if (q == (Quantum *) NULL)
442           break;
443         for (x=0; x < ((ssize_t) image->columns-7); x+=8)
444         {
445           for (bit=0; bit < 8; bit++)
446           {
447             index=(Quantum) (*p & (0x80 >> bit) ? 0x00 : 0x01);
448             SetPixelIndex(image,index,q);
449             q+=GetPixelChannels(image);
450           }
451           p++;
452         }
453         if (SyncAuthenticPixels(image,exception) == MagickFalse)
454           break;
455         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
456           image->rows);
457         if (status == MagickFalse)
458           break;
459       }
460       (void) SyncImage(image,exception);
461       break;
462     }
463     case 2:
464     {
465       /*
466         Read 2-bit PDB image.
467       */
468       for (y=0; y < (ssize_t) image->rows; y++)
469       {
470         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
471         if (q == (Quantum *) NULL)
472           break;
473         for (x=0; x < (ssize_t) image->columns; x+=4)
474         {
475           index=ConstrainColormapIndex(image,3UL-((*p >> 6) & 0x03),exception);
476           SetPixelIndex(image,index,q);
477           q+=GetPixelChannels(image);
478           index=ConstrainColormapIndex(image,3UL-((*p >> 4) & 0x03),exception);
479           SetPixelIndex(image,index,q);
480           q+=GetPixelChannels(image);
481           index=ConstrainColormapIndex(image,3UL-((*p >> 2) & 0x03),exception);
482           SetPixelIndex(image,index,q);
483           q+=GetPixelChannels(image);
484           index=ConstrainColormapIndex(image,3UL-((*p) & 0x03),exception);
485           SetPixelIndex(image,index,q);
486           p++;
487           q+=GetPixelChannels(image);
488         }
489         if (SyncAuthenticPixels(image,exception) == MagickFalse)
490           break;
491         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
492           image->rows);
493         if (status == MagickFalse)
494           break;
495       }
496       (void) SyncImage(image,exception);
497       break;
498     }
499     case 4:
500     {
501       /*
502         Read 4-bit PDB image.
503       */
504       for (y=0; y < (ssize_t) image->rows; y++)
505       {
506         q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
507         if (q == (Quantum *) NULL)
508           break;
509         for (x=0; x < (ssize_t) image->columns; x+=2)
510         {
511           index=ConstrainColormapIndex(image,15UL-((*p >> 4) & 0x0f),exception);
512           SetPixelIndex(image,index,q);
513           q+=GetPixelChannels(image);
514           index=ConstrainColormapIndex(image,15UL-((*p) & 0x0f),exception);
515           SetPixelIndex(image,index,q);
516           p++;
517           q+=GetPixelChannels(image);
518         }
519         if (SyncAuthenticPixels(image,exception) == MagickFalse)
520           break;
521         status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
522           image->rows);
523         if (status == MagickFalse)
524           break;
525       }
526       (void) SyncImage(image,exception);
527       break;
528     }
529     default:
530       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
531   }
532
533   pixels=(unsigned char *) RelinquishMagickMemory(pixels);
534   if (EOFBlob(image) != MagickFalse)
535     ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
536       image->filename);
537   if (pdb_info.number_records > 1) /* TS */
538     {
539       char
540         *comment;
541
542       int
543         c;
544
545       register char
546         *p;
547
548       size_t
549         length;
550
551       num_pad_bytes = (size_t) (comment_offset - TellBlob( image ));
552       while (num_pad_bytes--) ReadBlobByte( image );
553
554       /*
555         Read comment.
556       */
557       c=ReadBlobByte(image);
558       length=MaxTextExtent;
559       comment=AcquireString((char *) NULL);
560       for (p=comment; c != EOF; p++)
561       {
562         if ((size_t) (p-comment+MaxTextExtent) >= length)
563           {
564             *p='\0';
565             length<<=1;
566             length+=MaxTextExtent;
567             comment=(char *) ResizeQuantumMemory(comment,length+MaxTextExtent,
568               sizeof(*comment));
569             if (comment == (char *) NULL)
570               break;
571             p=comment+strlen(comment);
572           }
573         *p=c;
574         c=ReadBlobByte(image);
575       }
576       *p='\0';
577       if (comment == (char *) NULL)
578         ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
579       (void) SetImageProperty(image,"comment",comment,exception);
580       comment=DestroyString(comment);
581     }
582   (void) CloseBlob(image);
583   return(GetFirstImageInList(image));
584 }
585 \f
586 /*
587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
588 %                                                                             %
589 %                                                                             %
590 %                                                                             %
591 %   R e g i s t e r P D B I m a g e                                           %
592 %                                                                             %
593 %                                                                             %
594 %                                                                             %
595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596 %
597 %  RegisterPDBImage() adds properties for the PDB image format to
598 %  the list of supported formats.  The properties include the image format
599 %  tag, a method to read and/or write the format, whether the format
600 %  supports the saving of more than one frame to the same file or blob,
601 %  whether the format supports native in-memory I/O, and a brief
602 %  description of the format.
603 %
604 %  The format of the RegisterPDBImage method is:
605 %
606 %      size_t RegisterPDBImage(void)
607 %
608 */
609 ModuleExport size_t RegisterPDBImage(void)
610 {
611   MagickInfo
612     *entry;
613
614   entry=SetMagickInfo("PDB");
615   entry->decoder=(DecodeImageHandler *) ReadPDBImage;
616   entry->encoder=(EncodeImageHandler *) WritePDBImage;
617   entry->magick=(IsImageFormatHandler *) IsPDB;
618   entry->description=ConstantString("Palm Database ImageViewer Format");
619   entry->module=ConstantString("PDB");
620   (void) RegisterMagickInfo(entry);
621   return(MagickImageCoderSignature);
622 }
623 \f
624 /*
625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
626 %                                                                             %
627 %                                                                             %
628 %                                                                             %
629 %   U n r e g i s t e r P D B I m a g e                                       %
630 %                                                                             %
631 %                                                                             %
632 %                                                                             %
633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634 %
635 %  UnregisterPDBImage() removes format registrations made by the
636 %  PDB module from the list of supported formats.
637 %
638 %  The format of the UnregisterPDBImage method is:
639 %
640 %      UnregisterPDBImage(void)
641 %
642 */
643 ModuleExport void UnregisterPDBImage(void)
644 {
645   (void) UnregisterMagickInfo("PDB");
646 }
647 \f
648 /*
649 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
650 %                                                                             %
651 %                                                                             %
652 %                                                                             %
653 %   W r i t e P D B I m a g e                                                 %
654 %                                                                             %
655 %                                                                             %
656 %                                                                             %
657 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
658 %
659 %  WritePDBImage() writes an image
660 %
661 %  The format of the WritePDBImage method is:
662 %
663 %      MagickBooleanType WritePDBImage(const ImageInfo *image_info,
664 %        Image *image,ExceptionInfo *exception)
665 %
666 %  A description of each parameter follows.
667 %
668 %    o image_info: the image info.
669 %
670 %    o image:  The image.
671 %
672 %    o exception: return any errors or warnings in this structure.
673 %
674 */
675
676 static unsigned char *EncodeRLE(unsigned char *destination,
677   unsigned char *source,size_t literal,size_t repeat)
678 {
679   if (literal > 0)
680     *destination++=(unsigned char) (literal-1);
681   (void) CopyMagickMemory(destination,source,literal);
682   destination+=literal;
683   if (repeat > 0)
684     {
685       *destination++=(unsigned char) (0x80 | (repeat-1));
686       *destination++=source[literal];
687     }
688   return(destination);
689 }
690
691 static MagickBooleanType WritePDBImage(const ImageInfo *image_info,Image *image,
692   ExceptionInfo *exception)
693 {
694   const char
695     *comment;
696
697   int
698     bits;
699
700   MagickBooleanType
701     status;
702
703   PDBImage
704     pdb_image;
705
706   PDBInfo
707     pdb_info;
708
709   QuantumInfo
710     *quantum_info;
711
712   register const Quantum
713     *p;
714
715   register ssize_t
716     x;
717
718   register unsigned char
719     *q;
720
721   size_t
722     bits_per_pixel,
723     literal,
724     packets,
725     packet_size,
726     repeat;
727
728   ssize_t
729     y;
730
731   unsigned char
732     *buffer,
733     *runlength,
734     *scanline;
735
736   /*
737     Open output image file.
738   */
739   assert(image_info != (const ImageInfo *) NULL);
740   assert(image_info->signature == MagickSignature);
741   assert(image != (Image *) NULL);
742   assert(image->signature == MagickSignature);
743   if (image->debug != MagickFalse)
744     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
745   assert(exception != (ExceptionInfo *) NULL);
746   assert(exception->signature == MagickSignature);
747   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
748   if (status == MagickFalse)
749     return(status);
750   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
751     (void) TransformImageColorspace(image,sRGBColorspace,exception);
752   if (image -> colors <= 2  ||  GetImageType( image, exception ) == BilevelType) { /* TS */
753     bits_per_pixel = 1;
754   } else if (image -> colors <= 4) {
755     bits_per_pixel = 2;
756   } else if (image -> colors <= 8) {
757     bits_per_pixel = 3;
758   } else {
759     bits_per_pixel = 4;
760   }
761
762   (void) ResetMagickMemory(pdb_info.name,0,32);
763   (void) CopyMagickString(pdb_info.name,image_info->filename,32);
764   pdb_info.attributes=0;
765   pdb_info.version=0;
766   pdb_info.create_time=time(NULL);
767   pdb_info.modify_time=pdb_info.create_time;
768   pdb_info.archive_time=0;
769   pdb_info.modify_number=0;
770   pdb_info.application_info=0;
771   pdb_info.sort_info=0;
772   (void) CopyMagickMemory(pdb_info.type,"vIMG",4);
773   (void) CopyMagickMemory(pdb_info.id,"View",4);
774   pdb_info.seed=0;
775   pdb_info.next_record=0;
776   comment=GetImageProperty(image,"comment",exception);
777   pdb_info.number_records=(comment == (const char *) NULL ? 1 : 2);
778   (void) WriteBlob(image,32,(unsigned char *) pdb_info.name);
779   (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.attributes);
780   (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.version);
781   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.create_time);
782   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_time);
783   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.archive_time);
784   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_number);
785   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.application_info);
786   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.sort_info);
787   (void) WriteBlob(image,4,(unsigned char *) pdb_info.type);
788   (void) WriteBlob(image,4,(unsigned char *) pdb_info.id);
789   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.seed);
790   (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.next_record);
791   (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.number_records);
792   (void) CopyMagickString(pdb_image.name,pdb_info.name,32);
793   pdb_image.version=1;  /* RLE Compressed */
794   switch (bits_per_pixel)
795   {
796     case 1: pdb_image.type=(unsigned char) 0xff; break;  /* monochrome */
797     case 2: pdb_image.type=(unsigned char) 0x00; break;  /* 2 bit gray */
798     default: pdb_image.type=(unsigned char) 0x02;  /* 4 bit gray */
799   }
800   pdb_image.reserved_1=0;
801   pdb_image.note=0;
802   pdb_image.x_last=0;
803   pdb_image.y_last=0;
804   pdb_image.reserved_2=0;
805   pdb_image.x_anchor=(unsigned short) 0xffff;
806   pdb_image.y_anchor=(unsigned short) 0xffff;
807   pdb_image.width=(short) image->columns;
808   if (image->columns % 16)
809     pdb_image.width=(short) (16*(image->columns/16+1));
810   pdb_image.height=(short) image->rows;
811   packets=(bits_per_pixel*image->columns/8)*image->rows;
812   runlength=(unsigned char *) AcquireQuantumMemory(2UL*packets,
813     sizeof(*runlength));
814   if (runlength == (unsigned char *) NULL)
815     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
816   buffer=(unsigned char *) AcquireQuantumMemory(256UL,sizeof(*buffer));
817   if (buffer == (unsigned char *) NULL)
818     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
819   packet_size=(size_t) (image->depth > 8 ? 2: 1);
820   scanline=(unsigned char *) AcquireQuantumMemory(image->columns,packet_size*
821     sizeof(*scanline));
822   if (scanline == (unsigned char *) NULL)
823     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
824   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
825     (void) TransformImageColorspace(image,sRGBColorspace,exception);
826   /*
827     Convert to GRAY raster scanline.
828   */
829   quantum_info=AcquireQuantumInfo(image_info,image);
830   if (quantum_info == (QuantumInfo *) NULL)
831     ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
832   bits=8/(int) bits_per_pixel-1;  /* start at most significant bits */
833   literal=0;
834   repeat=0;
835   q=runlength;
836   buffer[0]=0x00;
837   for (y=0; y < (ssize_t) image->rows; y++)
838   {
839     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
840     if (p == (const Quantum *) NULL)
841       break;
842     (void) ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
843       GrayQuantum,scanline,exception);
844     for (x=0; x < pdb_image.width; x++)
845     {
846       if (x < (ssize_t) image->columns)
847         buffer[literal+repeat]|=(0xff-scanline[x*packet_size]) >>
848           (8-bits_per_pixel) << bits*bits_per_pixel;
849       bits--;
850       if (bits < 0)
851         {
852           if (((literal+repeat) > 0) &&
853               (buffer[literal+repeat] == buffer[literal+repeat-1]))
854             {
855               if (repeat == 0)
856                 {
857                   literal--;
858                   repeat++;
859                 }
860               repeat++;
861               if (0x7f < repeat)
862                 {
863                   q=EncodeRLE(q,buffer,literal,repeat);
864                   literal=0;
865                   repeat=0;
866                 }
867             }
868           else
869             {
870               if (repeat >= 2)
871                 literal+=repeat;
872               else
873                 {
874                   q=EncodeRLE(q,buffer,literal,repeat);
875                   buffer[0]=buffer[literal+repeat];
876                   literal=0;
877                 }
878               literal++;
879               repeat=0;
880               if (0x7f < literal)
881                 {
882                   q=EncodeRLE(q,buffer,(literal < 0x80 ? literal : 0x80),0);
883                   (void) CopyMagickMemory(buffer,buffer+literal+repeat,0x80);
884                   literal-=0x80;
885                 }
886             }
887         bits=8/(int) bits_per_pixel-1;
888         buffer[literal+repeat]=0x00;
889       }
890     }
891     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
892                 image->rows);
893     if (status == MagickFalse)
894       break;
895   }
896   q=EncodeRLE(q,buffer,literal,repeat);
897   scanline=(unsigned char *) RelinquishMagickMemory(scanline);
898   buffer=(unsigned char *) RelinquishMagickMemory(buffer);
899   quantum_info=DestroyQuantumInfo(quantum_info);
900   /*
901     Write the Image record header.
902   */
903   (void) WriteBlobMSBLong(image,(unsigned int)
904     (TellBlob(image)+8*pdb_info.number_records));
905   (void) WriteBlobByte(image,0x40);
906   (void) WriteBlobByte(image,0x6f);
907   (void) WriteBlobByte(image,0x80);
908   (void) WriteBlobByte(image,0);
909   if (pdb_info.number_records > 1)
910     {
911       /*
912         Write the comment record header.
913       */
914       (void) WriteBlobMSBLong(image,(unsigned int) (TellBlob(image)+8+58+q-
915         runlength));
916       (void) WriteBlobByte(image,0x40);
917       (void) WriteBlobByte(image,0x6f);
918       (void) WriteBlobByte(image,0x80);
919       (void) WriteBlobByte(image,1);
920     }
921   /*
922     Write the Image data.
923   */
924   (void) WriteBlob(image,32,(unsigned char *) pdb_image.name);
925   (void) WriteBlobByte(image,(unsigned char) pdb_image.version);
926   (void) WriteBlobByte(image,pdb_image.type);
927   (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_1);
928   (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.note);
929   (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.x_last);
930   (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.y_last);
931   (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_2);
932   (void) WriteBlobMSBShort(image,pdb_image.x_anchor);
933   (void) WriteBlobMSBShort(image,pdb_image.y_anchor);
934   (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.width);
935   (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.height);
936   (void) WriteBlob(image,(size_t) (q-runlength),runlength);
937   runlength=(unsigned char *) RelinquishMagickMemory(runlength);
938   if (pdb_info.number_records > 1)
939     (void) WriteBlobString(image,comment);
940   (void) CloseBlob(image);
941   return(MagickTrue);
942 }