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