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