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/*
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
77/*
78  Typedef declarations.
79*/
80typedef 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
109typedef 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*/
140static MagickBooleanType
141  WritePDBImage(const ImageInfo *,Image *,ExceptionInfo *);
142
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*/
172static 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
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*/
233static 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
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*/
268static 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  count=ReadBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name);
334  if (count != sizeof(pdb_info.name))
335    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
336  pdb_info.attributes=(short) ReadBlobMSBShort(image);
337  pdb_info.version=(short) ReadBlobMSBShort(image);
338  pdb_info.create_time=ReadBlobMSBLong(image);
339  pdb_info.modify_time=ReadBlobMSBLong(image);
340  pdb_info.archive_time=ReadBlobMSBLong(image);
341  pdb_info.modify_number=ReadBlobMSBLong(image);
342  pdb_info.application_info=ReadBlobMSBLong(image);
343  pdb_info.sort_info=ReadBlobMSBLong(image);
344  (void) ReadBlob(image,4,(unsigned char *) pdb_info.type);
345  (void) ReadBlob(image,4,(unsigned char *) pdb_info.id);
346  pdb_info.seed=ReadBlobMSBLong(image);
347  pdb_info.next_record=ReadBlobMSBLong(image);
348  pdb_info.number_records=(short) ReadBlobMSBShort(image);
349  if ((memcmp(pdb_info.type,"vIMG",4) != 0) ||
350      (memcmp(pdb_info.id,"View",4) != 0))
351    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
352  if (pdb_info.next_record != 0)
353    ThrowReaderException(CoderError,"MultipleRecordListNotSupported");
354  /*
355    Read record header.
356  */
357  img_offset=(ssize_t) ReadBlobMSBSignedLong(image);
358  attributes=(unsigned char) ReadBlobByte(image);
359  (void) attributes;
360  count=ReadBlob(image,3,(unsigned char *) tag);
361  if (count != 3  ||  memcmp(tag,"\x6f\x80\x00",3) != 0)
362    ThrowReaderException(CorruptImageError,"CorruptImage");
363  if (pdb_info.number_records > 1)
364    {
365      comment_offset=(ssize_t) ReadBlobMSBSignedLong(image);
366      attributes=(unsigned char) ReadBlobByte(image);
367      count=ReadBlob(image,3,(unsigned char *) tag);
368      if (count != 3  ||  memcmp(tag,"\x6f\x80\x01",3) != 0)
369        ThrowReaderException(CorruptImageError,"CorruptImage");
370    }
371  num_pad_bytes = (size_t) (img_offset - TellBlob( image ));
372  while (num_pad_bytes-- != 0)
373  {
374    int
375      c;
376
377    c=ReadBlobByte(image);
378    if (c == EOF)
379      break;
380  }
381  /*
382    Read image header.
383  */
384  count=ReadBlob(image,sizeof(pdb_image.name),(unsigned char *) pdb_image.name);
385  if (count != sizeof(pdb_image.name))
386    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
387  pdb_image.version=ReadBlobByte(image);
388  pdb_image.type=(unsigned char) (ReadBlobByte(image));
389  pdb_image.reserved_1=ReadBlobMSBLong(image);
390  pdb_image.note=ReadBlobMSBLong(image);
391  pdb_image.x_last=(short) ReadBlobMSBShort(image);
392  pdb_image.y_last=(short) ReadBlobMSBShort(image);
393  pdb_image.reserved_2=ReadBlobMSBLong(image);
394  pdb_image.x_anchor=ReadBlobMSBShort(image);
395  pdb_image.y_anchor=ReadBlobMSBShort(image);
396  pdb_image.width=(short) ReadBlobMSBShort(image);
397  pdb_image.height=(short) ReadBlobMSBShort(image);
398  /*
399    Initialize image structure.
400  */
401  image->columns=(size_t) pdb_image.width;
402  image->rows=(size_t) pdb_image.height;
403  image->depth=8;
404  image->storage_class=PseudoClass;
405  bits_per_pixel=pdb_image.type == 0 ? 2UL : pdb_image.type == 2 ? 4UL : 1UL;
406  one=1;
407  if (AcquireImageColormap(image,one << bits_per_pixel,exception) == MagickFalse)
408    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
409  if (image_info->ping != MagickFalse)
410    {
411      (void) CloseBlob(image);
412      return(GetFirstImageInList(image));
413    }
414  status=SetImageExtent(image,image->columns,image->rows,exception);
415  if (status == MagickFalse)
416    return(DestroyImageList(image));
417  packets=(bits_per_pixel*image->columns+7)/8;
418  pixels=(unsigned char *) AcquireQuantumMemory(packets+257UL,image->rows*
419    sizeof(*pixels));
420  if (pixels == (unsigned char *) NULL)
421    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
422  switch (pdb_image.version & 0x07)
423  {
424    case 0:
425    {
426      image->compression=NoCompression;
427      count=(ssize_t) ReadBlob(image, packets * image -> rows, pixels);
428      break;
429    }
430    case 1:
431    {
432      image->compression=RLECompression;
433      if (!DecodeImage(image, pixels, packets * image -> rows))
434        ThrowReaderException( CorruptImageError, "RLEDecoderError" );
435      break;
436    }
437    default:
438      ThrowReaderException(CorruptImageError,
439         "UnrecognizedImageCompressionType" );
440  }
441  p=pixels;
442  switch (bits_per_pixel)
443  {
444    case 1:
445    {
446      int
447        bit;
448
449      /*
450        Read 1-bit PDB image.
451      */
452      for (y=0; y < (ssize_t) image->rows; y++)
453      {
454        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
455        if (q == (Quantum *) NULL)
456          break;
457        for (x=0; x < ((ssize_t) image->columns-7); x+=8)
458        {
459          for (bit=0; bit < 8; bit++)
460          {
461            index=(Quantum) (*p & (0x80 >> bit) ? 0x00 : 0x01);
462            SetPixelIndex(image,index,q);
463            q+=GetPixelChannels(image);
464          }
465          p++;
466        }
467        if (SyncAuthenticPixels(image,exception) == MagickFalse)
468          break;
469        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
470          image->rows);
471        if (status == MagickFalse)
472          break;
473      }
474      (void) SyncImage(image,exception);
475      break;
476    }
477    case 2:
478    {
479      /*
480        Read 2-bit PDB image.
481      */
482      for (y=0; y < (ssize_t) image->rows; y++)
483      {
484        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
485        if (q == (Quantum *) NULL)
486          break;
487        for (x=0; x < (ssize_t) image->columns-3; x+=4)
488        {
489          index=ConstrainColormapIndex(image,3UL-((*p >> 6) & 0x03),exception);
490          SetPixelIndex(image,index,q);
491          q+=GetPixelChannels(image);
492          index=ConstrainColormapIndex(image,3UL-((*p >> 4) & 0x03),exception);
493          SetPixelIndex(image,index,q);
494          q+=GetPixelChannels(image);
495          index=ConstrainColormapIndex(image,3UL-((*p >> 2) & 0x03),exception);
496          SetPixelIndex(image,index,q);
497          q+=GetPixelChannels(image);
498          index=ConstrainColormapIndex(image,3UL-((*p) & 0x03),exception);
499          SetPixelIndex(image,index,q);
500          p++;
501          q+=GetPixelChannels(image);
502        }
503        if (SyncAuthenticPixels(image,exception) == MagickFalse)
504          break;
505        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
506          image->rows);
507        if (status == MagickFalse)
508          break;
509      }
510      (void) SyncImage(image,exception);
511      break;
512    }
513    case 4:
514    {
515      /*
516        Read 4-bit PDB image.
517      */
518      for (y=0; y < (ssize_t) image->rows; y++)
519      {
520        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
521        if (q == (Quantum *) NULL)
522          break;
523        for (x=0; x < (ssize_t) image->columns-1; x+=2)
524        {
525          index=ConstrainColormapIndex(image,15UL-((*p >> 4) & 0x0f),exception);
526          SetPixelIndex(image,index,q);
527          q+=GetPixelChannels(image);
528          index=ConstrainColormapIndex(image,15UL-((*p) & 0x0f),exception);
529          SetPixelIndex(image,index,q);
530          p++;
531          q+=GetPixelChannels(image);
532        }
533        if (SyncAuthenticPixels(image,exception) == MagickFalse)
534          break;
535        status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
536          image->rows);
537        if (status == MagickFalse)
538          break;
539      }
540      (void) SyncImage(image,exception);
541      break;
542    }
543    default:
544      ThrowReaderException(CorruptImageError,"ImproperImageHeader");
545  }
546  pixels=(unsigned char *) RelinquishMagickMemory(pixels);
547  if (EOFBlob(image) != MagickFalse)
548    ThrowFileException(exception,CorruptImageError,"UnexpectedEndOfFile",
549      image->filename);
550  if (pdb_info.number_records > 1)
551    {
552      char
553        *comment;
554
555      int
556        c;
557
558      register char
559        *p;
560
561      size_t
562        length;
563
564      num_pad_bytes = (size_t) (comment_offset - TellBlob( image ));
565      while (num_pad_bytes--) ReadBlobByte( image );
566
567      /*
568        Read comment.
569      */
570      c=ReadBlobByte(image);
571      length=MagickPathExtent;
572      comment=AcquireString((char *) NULL);
573      for (p=comment; c != EOF; p++)
574      {
575        if ((size_t) (p-comment+MagickPathExtent) >= length)
576          {
577            *p='\0';
578            length<<=1;
579            length+=MagickPathExtent;
580            comment=(char *) ResizeQuantumMemory(comment,length+MagickPathExtent,
581              sizeof(*comment));
582            if (comment == (char *) NULL)
583              break;
584            p=comment+strlen(comment);
585          }
586        *p=c;
587        c=ReadBlobByte(image);
588      }
589      *p='\0';
590      if (comment == (char *) NULL)
591        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
592      (void) SetImageProperty(image,"comment",comment,exception);
593      comment=DestroyString(comment);
594    }
595  (void) CloseBlob(image);
596  return(GetFirstImageInList(image));
597}
598
599/*
600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601%                                                                             %
602%                                                                             %
603%                                                                             %
604%   R e g i s t e r P D B I m a g e                                           %
605%                                                                             %
606%                                                                             %
607%                                                                             %
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609%
610%  RegisterPDBImage() adds properties for the PDB image format to
611%  the list of supported formats.  The properties include the image format
612%  tag, a method to read and/or write the format, whether the format
613%  supports the saving of more than one frame to the same file or blob,
614%  whether the format supports native in-memory I/O, and a brief
615%  description of the format.
616%
617%  The format of the RegisterPDBImage method is:
618%
619%      size_t RegisterPDBImage(void)
620%
621*/
622ModuleExport size_t RegisterPDBImage(void)
623{
624  MagickInfo
625    *entry;
626
627  entry=AcquireMagickInfo("PDB","PDB","Palm Database ImageViewer Format");
628  entry->decoder=(DecodeImageHandler *) ReadPDBImage;
629  entry->encoder=(EncodeImageHandler *) WritePDBImage;
630  entry->magick=(IsImageFormatHandler *) IsPDB;
631  (void) RegisterMagickInfo(entry);
632  return(MagickImageCoderSignature);
633}
634
635/*
636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
637%                                                                             %
638%                                                                             %
639%                                                                             %
640%   U n r e g i s t e r P D B I m a g e                                       %
641%                                                                             %
642%                                                                             %
643%                                                                             %
644%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
645%
646%  UnregisterPDBImage() removes format registrations made by the
647%  PDB module from the list of supported formats.
648%
649%  The format of the UnregisterPDBImage method is:
650%
651%      UnregisterPDBImage(void)
652%
653*/
654ModuleExport void UnregisterPDBImage(void)
655{
656  (void) UnregisterMagickInfo("PDB");
657}
658
659/*
660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661%                                                                             %
662%                                                                             %
663%                                                                             %
664%   W r i t e P D B I m a g e                                                 %
665%                                                                             %
666%                                                                             %
667%                                                                             %
668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669%
670%  WritePDBImage() writes an image
671%
672%  The format of the WritePDBImage method is:
673%
674%      MagickBooleanType WritePDBImage(const ImageInfo *image_info,
675%        Image *image,ExceptionInfo *exception)
676%
677%  A description of each parameter follows.
678%
679%    o image_info: the image info.
680%
681%    o image:  The image.
682%
683%    o exception: return any errors or warnings in this structure.
684%
685*/
686
687static unsigned char *EncodeRLE(unsigned char *destination,
688  unsigned char *source,size_t literal,size_t repeat)
689{
690  if (literal > 0)
691    *destination++=(unsigned char) (literal-1);
692  (void) CopyMagickMemory(destination,source,literal);
693  destination+=literal;
694  if (repeat > 0)
695    {
696      *destination++=(unsigned char) (0x80 | (repeat-1));
697      *destination++=source[literal];
698    }
699  return(destination);
700}
701
702static MagickBooleanType WritePDBImage(const ImageInfo *image_info,Image *image,
703  ExceptionInfo *exception)
704{
705  const char
706    *comment;
707
708  int
709    bits;
710
711  MagickBooleanType
712    status;
713
714  PDBImage
715    pdb_image;
716
717  PDBInfo
718    pdb_info;
719
720  QuantumInfo
721    *quantum_info;
722
723  register const Quantum
724    *p;
725
726  register ssize_t
727    x;
728
729  register unsigned char
730    *q;
731
732  size_t
733    bits_per_pixel,
734    literal,
735    packets,
736    packet_size,
737    repeat;
738
739  ssize_t
740    y;
741
742  unsigned char
743    *buffer,
744    *runlength,
745    *scanline;
746
747  /*
748    Open output image file.
749  */
750  assert(image_info != (const ImageInfo *) NULL);
751  assert(image_info->signature == MagickCoreSignature);
752  assert(image != (Image *) NULL);
753  assert(image->signature == MagickCoreSignature);
754  if (image->debug != MagickFalse)
755    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
756  assert(exception != (ExceptionInfo *) NULL);
757  assert(exception->signature == MagickCoreSignature);
758  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
759  if (status == MagickFalse)
760    return(status);
761  (void) TransformImageColorspace(image,sRGBColorspace,exception);
762  if (SetImageMonochrome(image,exception) != MagickFalse) {
763    bits_per_pixel=1;
764  } else if (image->colors <= 4) {
765    bits_per_pixel=2;
766  } else if (image->colors <= 8) {
767    bits_per_pixel=3;
768  } else {
769    bits_per_pixel=4;
770  }
771  (void) ResetMagickMemory(&pdb_info,0,sizeof(pdb_info));
772  (void) CopyMagickString(pdb_info.name,image_info->filename,
773    sizeof(pdb_info.name));
774  pdb_info.attributes=0;
775  pdb_info.version=0;
776  pdb_info.create_time=time(NULL);
777  pdb_info.modify_time=pdb_info.create_time;
778  pdb_info.archive_time=0;
779  pdb_info.modify_number=0;
780  pdb_info.application_info=0;
781  pdb_info.sort_info=0;
782  (void) CopyMagickMemory(pdb_info.type,"vIMG",4);
783  (void) CopyMagickMemory(pdb_info.id,"View",4);
784  pdb_info.seed=0;
785  pdb_info.next_record=0;
786  comment=GetImageProperty(image,"comment",exception);
787  pdb_info.number_records=(comment == (const char *) NULL ? 1 : 2);
788  (void) WriteBlob(image,sizeof(pdb_info.name),(unsigned char *) pdb_info.name);
789  (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.attributes);
790  (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.version);
791  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.create_time);
792  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_time);
793  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.archive_time);
794  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.modify_number);
795  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.application_info);
796  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.sort_info);
797  (void) WriteBlob(image,4,(unsigned char *) pdb_info.type);
798  (void) WriteBlob(image,4,(unsigned char *) pdb_info.id);
799  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.seed);
800  (void) WriteBlobMSBLong(image,(unsigned int) pdb_info.next_record);
801  (void) WriteBlobMSBShort(image,(unsigned short) pdb_info.number_records);
802  (void) CopyMagickString(pdb_image.name,pdb_info.name,sizeof(pdb_image.name));
803  pdb_image.version=1;  /* RLE Compressed */
804  switch (bits_per_pixel)
805  {
806    case 1: pdb_image.type=(unsigned char) 0xff; break;  /* monochrome */
807    case 2: pdb_image.type=(unsigned char) 0x00; break;  /* 2 bit gray */
808    default: pdb_image.type=(unsigned char) 0x02;  /* 4 bit gray */
809  }
810  pdb_image.reserved_1=0;
811  pdb_image.note=0;
812  pdb_image.x_last=0;
813  pdb_image.y_last=0;
814  pdb_image.reserved_2=0;
815  pdb_image.x_anchor=(unsigned short) 0xffff;
816  pdb_image.y_anchor=(unsigned short) 0xffff;
817  pdb_image.width=(short) image->columns;
818  if (image->columns % 16)
819    pdb_image.width=(short) (16*(image->columns/16+1));
820  pdb_image.height=(short) image->rows;
821  packets=((bits_per_pixel*image->columns+7)/8);
822  runlength=(unsigned char *) AcquireQuantumMemory(9UL*packets,
823    image->rows*sizeof(*runlength));
824  if (runlength == (unsigned char *) NULL)
825    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
826  buffer=(unsigned char *) AcquireQuantumMemory(512,sizeof(*buffer));
827  if (buffer == (unsigned char *) NULL)
828    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
829  packet_size=(size_t) (image->depth > 8 ? 2: 1);
830  scanline=(unsigned char *) AcquireQuantumMemory(image->columns,packet_size*
831    sizeof(*scanline));
832  if (scanline == (unsigned char *) NULL)
833    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
834  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
835    (void) TransformImageColorspace(image,sRGBColorspace,exception);
836  /*
837    Convert to GRAY raster scanline.
838  */
839  quantum_info=AcquireQuantumInfo(image_info,image);
840  if (quantum_info == (QuantumInfo *) NULL)
841    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
842  bits=8/(int) bits_per_pixel-1;  /* start at most significant bits */
843  literal=0;
844  repeat=0;
845  q=runlength;
846  buffer[0]=0x00;
847  for (y=0; y < (ssize_t) image->rows; y++)
848  {
849    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
850    if (p == (const Quantum *) NULL)
851      break;
852    (void) ExportQuantumPixels(image,(CacheView *) NULL,quantum_info,
853      GrayQuantum,scanline,exception);
854    for (x=0; x < (ssize_t) pdb_image.width; x++)
855    {
856      if (x < (ssize_t) image->columns)
857        buffer[literal+repeat]|=(0xff-scanline[x*packet_size]) >>
858          (8-bits_per_pixel) << bits*bits_per_pixel;
859      bits--;
860      if (bits < 0)
861        {
862          if (((literal+repeat) > 0) &&
863              (buffer[literal+repeat] == buffer[literal+repeat-1]))
864            {
865              if (repeat == 0)
866                {
867                  literal--;
868                  repeat++;
869                }
870              repeat++;
871              if (0x7f < repeat)
872                {
873                  q=EncodeRLE(q,buffer,literal,repeat);
874                  literal=0;
875                  repeat=0;
876                }
877            }
878          else
879            {
880              if (repeat >= 2)
881                literal+=repeat;
882              else
883                {
884                  q=EncodeRLE(q,buffer,literal,repeat);
885                  buffer[0]=buffer[literal+repeat];
886                  literal=0;
887                }
888              literal++;
889              repeat=0;
890              if (0x7f < literal)
891                {
892                  q=EncodeRLE(q,buffer,(literal < 0x80 ? literal : 0x80),0);
893                  (void) CopyMagickMemory(buffer,buffer+literal+repeat,0x80);
894                  literal-=0x80;
895                }
896            }
897        bits=8/(int) bits_per_pixel-1;
898        buffer[literal+repeat]=0x00;
899      }
900    }
901    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
902      image->rows);
903    if (status == MagickFalse)
904      break;
905  }
906  q=EncodeRLE(q,buffer,literal,repeat);
907  scanline=(unsigned char *) RelinquishMagickMemory(scanline);
908  buffer=(unsigned char *) RelinquishMagickMemory(buffer);
909  quantum_info=DestroyQuantumInfo(quantum_info);
910  /*
911    Write the Image record header.
912  */
913  (void) WriteBlobMSBLong(image,(unsigned int)
914    (TellBlob(image)+8*pdb_info.number_records));
915  (void) WriteBlobByte(image,0x40);
916  (void) WriteBlobByte(image,0x6f);
917  (void) WriteBlobByte(image,0x80);
918  (void) WriteBlobByte(image,0);
919  if (pdb_info.number_records > 1)
920    {
921      /*
922        Write the comment record header.
923      */
924      (void) WriteBlobMSBLong(image,(unsigned int) (TellBlob(image)+8+58+q-
925        runlength));
926      (void) WriteBlobByte(image,0x40);
927      (void) WriteBlobByte(image,0x6f);
928      (void) WriteBlobByte(image,0x80);
929      (void) WriteBlobByte(image,1);
930    }
931  /*
932    Write the Image data.
933  */
934  (void) WriteBlob(image,sizeof(pdb_image.name),(unsigned char *)
935    pdb_image.name);
936  (void) WriteBlobByte(image,(unsigned char) pdb_image.version);
937  (void) WriteBlobByte(image,pdb_image.type);
938  (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_1);
939  (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.note);
940  (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.x_last);
941  (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.y_last);
942  (void) WriteBlobMSBLong(image,(unsigned int) pdb_image.reserved_2);
943  (void) WriteBlobMSBShort(image,pdb_image.x_anchor);
944  (void) WriteBlobMSBShort(image,pdb_image.y_anchor);
945  (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.width);
946  (void) WriteBlobMSBShort(image,(unsigned short) pdb_image.height);
947  (void) WriteBlob(image,(size_t) (q-runlength),runlength);
948  runlength=(unsigned char *) RelinquishMagickMemory(runlength);
949  if (pdb_info.number_records > 1)
950    (void) WriteBlobString(image,comment);
951  (void) CloseBlob(image);
952  return(MagickTrue);
953}
954