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