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