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