Home | History | Annotate | Download | only in coders
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %                            X   X  PPPP   M   M                              %
      7 %                             X X   P   P  MM MM                              %
      8 %                              X    PPPP   M M M                              %
      9 %                             X X   P      M   M                              %
     10 %                            X   X  P      M   M                              %
     11 %                                                                             %
     12 %                                                                             %
     13 %                  Read/Write X Windows system Pixmap 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 */
     38 
     39 /*
     41   Include declarations.
     42 */
     43 #include "MagickCore/studio.h"
     44 #include "MagickCore/attribute.h"
     45 #include "MagickCore/blob.h"
     46 #include "MagickCore/blob-private.h"
     47 #include "MagickCore/cache.h"
     48 #include "MagickCore/color.h"
     49 #include "MagickCore/color-private.h"
     50 #include "MagickCore/colormap.h"
     51 #include "MagickCore/colorspace.h"
     52 #include "MagickCore/colorspace-private.h"
     53 #include "MagickCore/exception.h"
     54 #include "MagickCore/exception-private.h"
     55 #include "MagickCore/geometry.h"
     56 #include "MagickCore/image.h"
     57 #include "MagickCore/image-private.h"
     58 #include "MagickCore/list.h"
     59 #include "MagickCore/magick.h"
     60 #include "MagickCore/memory_.h"
     61 #include "MagickCore/monitor.h"
     62 #include "MagickCore/monitor-private.h"
     63 #include "MagickCore/pixel-accessor.h"
     64 #include "MagickCore/quantize.h"
     65 #include "MagickCore/quantum-private.h"
     66 #include "MagickCore/resize.h"
     67 #include "MagickCore/resource_.h"
     68 #include "MagickCore/splay-tree.h"
     69 #include "MagickCore/static.h"
     70 #include "MagickCore/string_.h"
     71 #include "MagickCore/module.h"
     72 #include "MagickCore/threshold.h"
     73 #include "MagickCore/utility.h"
     74 
     75 /*
     77   Forward declarations.
     78 */
     79 static MagickBooleanType
     80   WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
     81   WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
     82 
     83 /*
     85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     86 %                                                                             %
     87 %                                                                             %
     88 %                                                                             %
     89 %   I s X P M                                                                 %
     90 %                                                                             %
     91 %                                                                             %
     92 %                                                                             %
     93 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     94 %
     95 %  IsXPM() returns MagickTrue if the image format type, identified by the
     96 %  magick string, is XPM.
     97 %
     98 %  The format of the IsXPM method is:
     99 %
    100 %      MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
    101 %
    102 %  A description of each parameter follows:
    103 %
    104 %    o magick: compare image format pattern against these bytes. or
    105 %      blob.
    106 %
    107 %    o length: Specifies the length of the magick string.
    108 %
    109 */
    110 static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
    111 {
    112   if (length < 9)
    113     return(MagickFalse);
    114   if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
    115     return(MagickTrue);
    116   return(MagickFalse);
    117 }
    118 
    119 /*
    121 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    122 %                                                                             %
    123 %                                                                             %
    124 %                                                                             %
    125 %   R e a d X P M I m a g e                                                   %
    126 %                                                                             %
    127 %                                                                             %
    128 %                                                                             %
    129 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    130 %
    131 %  ReadXPMImage() reads an X11 pixmap image file and returns it.  It
    132 %  allocates the memory necessary for the new Image structure and returns a
    133 %  pointer to the new image.
    134 %
    135 %  The format of the ReadXPMImage method is:
    136 %
    137 %      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
    138 %
    139 %  A description of each parameter follows:
    140 %
    141 %    o image_info: the image info.
    142 %
    143 %    o exception: return any errors or warnings in this structure.
    144 %
    145 */
    146 
    147 static int CompareXPMColor(const void *target,const void *source)
    148 {
    149   const char
    150     *p,
    151     *q;
    152 
    153   p=(const char *) target;
    154   q=(const char *) source;
    155   return(strcmp(p,q));
    156 }
    157 
    158 static ssize_t CopyXPMColor(char *destination,const char *source,size_t length)
    159 {
    160   register const char
    161     *p;
    162 
    163   p=source;
    164   while (length-- && (*p != '\0'))
    165     *destination++=(*p++);
    166   if (length != 0)
    167     *destination='\0';
    168   return((ssize_t) (p-source));
    169 }
    170 
    171 static char *NextXPMLine(char *p)
    172 {
    173   assert(p != (char *) NULL);
    174   p=strchr(p,'\n');
    175   if (p != (char *) NULL)
    176     p++;
    177   return(p);
    178 }
    179 
    180 static char *ParseXPMColor(char *color,MagickBooleanType search_start)
    181 {
    182 #define NumberTargets  6
    183 
    184   register char
    185     *p,
    186     *r;
    187 
    188   register const char
    189     *q;
    190 
    191   register ssize_t
    192     i;
    193 
    194   static const char
    195     *const targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
    196 
    197   if (search_start != MagickFalse)
    198     {
    199       for (i=0; i < NumberTargets; i++)
    200       {
    201         p=color;
    202         for (q=targets[i]; *p != '\0'; p++)
    203         {
    204           if (*p == '\n')
    205             break;
    206           if (*p != *q)
    207             continue;
    208           if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
    209             continue;
    210           r=p;
    211           for ( ; ; )
    212           {
    213             if (*q == '\0')
    214               return(p);
    215             if (*r++ != *q++)
    216               break;
    217           }
    218           q=targets[i];
    219         }
    220       }
    221       return((char *) NULL);
    222     }
    223   for (p=color+1; *p != '\0'; p++)
    224   {
    225     if (*p == '\n')
    226       break;
    227     if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
    228       continue;
    229     if (isspace((int) ((unsigned char) (*p))) != 0)
    230       continue;
    231     for (i=0; i < NumberTargets; i++)
    232     {
    233       if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
    234         return(p);
    235     }
    236   }
    237   return(p);
    238 }
    239 
    240 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
    241 {
    242   char
    243     *grey,
    244     key[MagickPathExtent],
    245     target[MagickPathExtent],
    246     *xpm_buffer;
    247 
    248   Image
    249     *image;
    250 
    251   MagickBooleanType
    252     active,
    253     status;
    254 
    255   register char
    256     *next,
    257     *p,
    258     *q;
    259 
    260   register ssize_t
    261     x;
    262 
    263   register Quantum
    264     *r;
    265 
    266   size_t
    267     length;
    268 
    269   SplayTreeInfo
    270     *xpm_colors;
    271 
    272   ssize_t
    273     count,
    274     j,
    275     y;
    276 
    277   unsigned long
    278     colors,
    279     columns,
    280     rows,
    281     width;
    282 
    283   /*
    284     Open image file.
    285   */
    286   assert(image_info != (const ImageInfo *) NULL);
    287   assert(image_info->signature == MagickCoreSignature);
    288   if (image_info->debug != MagickFalse)
    289     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
    290       image_info->filename);
    291   assert(exception != (ExceptionInfo *) NULL);
    292   assert(exception->signature == MagickCoreSignature);
    293   image=AcquireImage(image_info,exception);
    294   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    295   if (status == MagickFalse)
    296     {
    297       image=DestroyImageList(image);
    298       return((Image *) NULL);
    299     }
    300   /*
    301     Read XPM file.
    302   */
    303   length=MagickPathExtent;
    304   xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
    305   if (xpm_buffer == (char *) NULL)
    306     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    307   *xpm_buffer='\0';
    308   p=xpm_buffer;
    309   while (ReadBlobString(image,p) != (char *) NULL)
    310   {
    311     if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
    312       continue;
    313     if ((*p == '}') && (*(p+1) == ';'))
    314       break;
    315     p+=strlen(p);
    316     if ((size_t) (p-xpm_buffer+MagickPathExtent) < length)
    317       continue;
    318     length<<=1;
    319     xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MagickPathExtent,
    320       sizeof(*xpm_buffer));
    321     if (xpm_buffer == (char *) NULL)
    322       break;
    323     p=xpm_buffer+strlen(xpm_buffer);
    324   }
    325   if (xpm_buffer == (char *) NULL)
    326     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    327   /*
    328     Remove comments.
    329   */
    330   count=0;
    331   width=0;
    332   for (p=xpm_buffer; *p != '\0'; p++)
    333   {
    334     if (*p != '"')
    335       continue;
    336     count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
    337     image->columns=columns;
    338     image->rows=rows;
    339     image->colors=colors;
    340     if (count == 4)
    341       break;
    342   }
    343   if ((count != 4) || (width == 0) || (width > 2) ||
    344       (image->columns == 0) || (image->rows == 0) ||
    345       (image->colors == 0) || (image->colors > MaxColormapSize))
    346     {
    347       xpm_buffer=DestroyString(xpm_buffer);
    348       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
    349     }
    350   /*
    351     Remove unquoted characters.
    352   */
    353   active=MagickFalse;
    354   for (q=xpm_buffer; *p != '\0'; )
    355   {
    356     if (*p++ == '"')
    357       {
    358         if (active != MagickFalse)
    359           *q++='\n';
    360         active=active != MagickFalse ? MagickFalse : MagickTrue;
    361       }
    362     if (active != MagickFalse)
    363       *q++=(*p);
    364   }
    365   *q='\0';
    366   /*
    367     Initialize image structure.
    368   */
    369   xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
    370     (void *(*)(void *)) NULL);
    371   if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
    372     {
    373       xpm_buffer=DestroyString(xpm_buffer);
    374       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    375     }
    376   /*
    377     Read image colormap.
    378   */
    379   image->depth=1;
    380   next=NextXPMLine(xpm_buffer);
    381   for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
    382   {
    383     p=next;
    384     next=NextXPMLine(p);
    385     (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1));
    386     status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
    387     /*
    388       Parse color.
    389     */
    390     (void) CopyMagickString(target,"gray",MagickPathExtent);
    391     q=ParseXPMColor(p+width,MagickTrue);
    392     if (q != (char *) NULL)
    393       {
    394         while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
    395           q++;
    396         if ((next-q) < 0)
    397           break;
    398         if (next != (char *) NULL)
    399           (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
    400             MagickPathExtent-1));
    401         else
    402           (void) CopyMagickString(target,q,MagickPathExtent);
    403         q=ParseXPMColor(target,MagickFalse);
    404         if (q != (char *) NULL)
    405           *q='\0';
    406       }
    407     StripString(target);
    408     grey=strstr(target,"grey");
    409     if (grey != (char *) NULL)
    410       grey[2]='a';
    411     if (LocaleCompare(target,"none") == 0)
    412       {
    413         image->storage_class=DirectClass;
    414         image->alpha_trait=BlendPixelTrait;
    415       }
    416     status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
    417       exception);
    418     if (status == MagickFalse)
    419       break;
    420     if (image->depth < image->colormap[j].depth)
    421       image->depth=image->colormap[j].depth;
    422   }
    423   if (j < (ssize_t) image->colors)
    424     {
    425       xpm_colors=DestroySplayTree(xpm_colors);
    426       xpm_buffer=DestroyString(xpm_buffer);
    427       ThrowReaderException(CorruptImageError,"CorruptImage");
    428     }
    429   j=0;
    430   if (image_info->ping == MagickFalse)
    431     {
    432       /*
    433         Read image pixels.
    434       */
    435       status=SetImageExtent(image,image->columns,image->rows,exception);
    436       if (status == MagickFalse)
    437         return(DestroyImageList(image));
    438       for (y=0; y < (ssize_t) image->rows; y++)
    439       {
    440         p=NextXPMLine(p);
    441         if (p == (char *) NULL)
    442           break;
    443         r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
    444         if (r == (Quantum *) NULL)
    445           break;
    446         for (x=0; x < (ssize_t) image->columns; x++)
    447         {
    448           ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1));
    449           if (count != (ssize_t) width)
    450             break;
    451           j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
    452           if (image->storage_class == PseudoClass)
    453             SetPixelIndex(image,(Quantum) j,r);
    454           SetPixelViaPixelInfo(image,image->colormap+j,r);
    455           p+=count;
    456           r+=GetPixelChannels(image);
    457         }
    458         if (x < (ssize_t) image->columns)
    459           break;
    460         if (SyncAuthenticPixels(image,exception) == MagickFalse)
    461           break;
    462       }
    463       if (y < (ssize_t) image->rows)
    464         {
    465           xpm_colors=DestroySplayTree(xpm_colors);
    466           xpm_buffer=DestroyString(xpm_buffer);
    467           ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
    468         }
    469     }
    470   /*
    471     Relinquish resources.
    472   */
    473   xpm_buffer=DestroyString(xpm_buffer);
    474   xpm_colors=DestroySplayTree(xpm_colors);
    475   (void) CloseBlob(image);
    476   return(GetFirstImageInList(image));
    477 }
    478 
    479 /*
    481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    482 %                                                                             %
    483 %                                                                             %
    484 %                                                                             %
    485 %   R e g i s t e r X P M I m a g e                                           %
    486 %                                                                             %
    487 %                                                                             %
    488 %                                                                             %
    489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    490 %
    491 %  RegisterXPMImage() adds attributes for the XPM image format to
    492 %  the list of supported formats.  The attributes include the image format
    493 %  tag, a method to read and/or write the format, whether the format
    494 %  supports the saving of more than one frame to the same file or blob,
    495 %  whether the format supports native in-memory I/O, and a brief
    496 %  description of the format.
    497 %
    498 %  The format of the RegisterXPMImage method is:
    499 %
    500 %      size_t RegisterXPMImage(void)
    501 %
    502 */
    503 ModuleExport size_t RegisterXPMImage(void)
    504 {
    505   MagickInfo
    506     *entry;
    507 
    508   entry=AcquireMagickInfo("XPM","PICON","Personal Icon");
    509   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
    510   entry->encoder=(EncodeImageHandler *) WritePICONImage;
    511   entry->flags^=CoderAdjoinFlag;
    512   (void) RegisterMagickInfo(entry);
    513   entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)");
    514   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
    515   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
    516   entry->flags^=CoderAdjoinFlag;
    517   entry->flags|=CoderStealthFlag;
    518   (void) RegisterMagickInfo(entry);
    519   entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)");
    520   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
    521   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
    522   entry->magick=(IsImageFormatHandler *) IsXPM;
    523   entry->flags^=CoderAdjoinFlag;
    524   (void) RegisterMagickInfo(entry);
    525   return(MagickImageCoderSignature);
    526 }
    527 
    528 /*
    530 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    531 %                                                                             %
    532 %                                                                             %
    533 %                                                                             %
    534 %   U n r e g i s t e r X P M I m a g e                                       %
    535 %                                                                             %
    536 %                                                                             %
    537 %                                                                             %
    538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    539 %
    540 %  UnregisterXPMImage() removes format registrations made by the
    541 %  XPM module from the list of supported formats.
    542 %
    543 %  The format of the UnregisterXPMImage method is:
    544 %
    545 %      UnregisterXPMImage(void)
    546 %
    547 */
    548 ModuleExport void UnregisterXPMImage(void)
    549 {
    550   (void) UnregisterMagickInfo("PICON");
    551   (void) UnregisterMagickInfo("PM");
    552   (void) UnregisterMagickInfo("XPM");
    553 }
    554 
    555 /*
    557 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    558 %                                                                             %
    559 %                                                                             %
    560 %                                                                             %
    561 %   W r i t e P I C O N I m a g e                                             %
    562 %                                                                             %
    563 %                                                                             %
    564 %                                                                             %
    565 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    566 %
    567 %  WritePICONImage() writes an image to a file in the Personal Icon format.
    568 %
    569 %  The format of the WritePICONImage method is:
    570 %
    571 %      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
    572 %        Image *image,ExceptionInfo *exception)
    573 %
    574 %  A description of each parameter follows.
    575 %
    576 %    o image_info: the image info.
    577 %
    578 %    o image:  The image.
    579 %
    580 %    o exception: return any errors or warnings in this structure.
    581 %
    582 */
    583 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
    584   Image *image,ExceptionInfo *exception)
    585 {
    586 #define ColormapExtent  155
    587 #define GraymapExtent  95
    588 #define PiconGeometry  "48x48>"
    589 
    590   static unsigned char
    591     Colormap[]=
    592     {
    593       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
    594       0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
    595       0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
    596       0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
    597       0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
    598       0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
    599       0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
    600       0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
    601       0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
    602       0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
    603       0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
    604       0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
    605       0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
    606     },
    607     Graymap[]=
    608     {
    609       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
    610       0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
    611       0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
    612       0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
    613       0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
    614       0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
    615       0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
    616       0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
    617     };
    618 
    619 #define MaxCixels  92
    620 
    621   static const char
    622     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
    623                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
    624 
    625   char
    626     buffer[MagickPathExtent],
    627     basename[MagickPathExtent],
    628     name[MagickPathExtent],
    629     symbol[MagickPathExtent];
    630 
    631   Image
    632     *affinity_image,
    633     *picon;
    634 
    635   ImageInfo
    636     *blob_info;
    637 
    638   MagickBooleanType
    639     status,
    640     transparent;
    641 
    642   PixelInfo
    643     pixel;
    644 
    645   QuantizeInfo
    646     *quantize_info;
    647 
    648   RectangleInfo
    649     geometry;
    650 
    651   register const Quantum
    652     *p;
    653 
    654   register ssize_t
    655     i,
    656     x;
    657 
    658   register Quantum
    659     *q;
    660 
    661   size_t
    662     characters_per_pixel,
    663     colors;
    664 
    665   ssize_t
    666     j,
    667     k,
    668     y;
    669 
    670   /*
    671     Open output image file.
    672   */
    673   assert(image_info != (const ImageInfo *) NULL);
    674   assert(image_info->signature == MagickCoreSignature);
    675   assert(image != (Image *) NULL);
    676   assert(image->signature == MagickCoreSignature);
    677   if (image->debug != MagickFalse)
    678     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    679   assert(exception != (ExceptionInfo *) NULL);
    680   assert(exception->signature == MagickCoreSignature);
    681   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    682   if (status == MagickFalse)
    683     return(status);
    684   (void) TransformImageColorspace(image,sRGBColorspace,exception);
    685   SetGeometry(image,&geometry);
    686   (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
    687     &geometry.width,&geometry.height);
    688   picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
    689     exception);
    690   blob_info=CloneImageInfo(image_info);
    691   (void) AcquireUniqueFilename(blob_info->filename);
    692   if ((image_info->type != TrueColorType) &&
    693       (SetImageGray(image,exception) != MagickFalse))
    694     affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
    695   else
    696     affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
    697   (void) RelinquishUniqueFileResource(blob_info->filename);
    698   blob_info=DestroyImageInfo(blob_info);
    699   if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
    700     return(MagickFalse);
    701   quantize_info=AcquireQuantizeInfo(image_info);
    702   status=RemapImage(quantize_info,picon,affinity_image,exception);
    703   quantize_info=DestroyQuantizeInfo(quantize_info);
    704   affinity_image=DestroyImage(affinity_image);
    705   transparent=MagickFalse;
    706   if (picon->storage_class == PseudoClass)
    707     {
    708       (void) CompressImageColormap(picon,exception);
    709       if (picon->alpha_trait != UndefinedPixelTrait)
    710         transparent=MagickTrue;
    711     }
    712   else
    713     {
    714       /*
    715         Convert DirectClass to PseudoClass picon.
    716       */
    717       if (picon->alpha_trait != UndefinedPixelTrait)
    718         {
    719           /*
    720             Map all the transparent pixels.
    721           */
    722           for (y=0; y < (ssize_t) picon->rows; y++)
    723           {
    724             q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
    725             if (q == (Quantum *) NULL)
    726               break;
    727             for (x=0; x < (ssize_t) picon->columns; x++)
    728             {
    729               if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
    730                 transparent=MagickTrue;
    731               else
    732                 SetPixelAlpha(picon,OpaqueAlpha,q);
    733               q+=GetPixelChannels(picon);
    734             }
    735             if (SyncAuthenticPixels(picon,exception) == MagickFalse)
    736               break;
    737           }
    738         }
    739       (void) SetImageType(picon,PaletteType,exception);
    740     }
    741   colors=picon->colors;
    742   if (transparent != MagickFalse)
    743     {
    744       colors++;
    745       picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
    746         picon->colormap,(size_t) colors,sizeof(*picon->colormap));
    747       if (picon->colormap == (PixelInfo *) NULL)
    748         ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
    749       for (y=0; y < (ssize_t) picon->rows; y++)
    750       {
    751         q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
    752         if (q == (Quantum *) NULL)
    753           break;
    754         for (x=0; x < (ssize_t) picon->columns; x++)
    755         {
    756           if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
    757             SetPixelIndex(picon,(Quantum) picon->colors,q);
    758           q+=GetPixelChannels(picon);
    759         }
    760         if (SyncAuthenticPixels(picon,exception) == MagickFalse)
    761           break;
    762       }
    763     }
    764   /*
    765     Compute the character per pixel.
    766   */
    767   characters_per_pixel=1;
    768   for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
    769     characters_per_pixel++;
    770   /*
    771     XPM header.
    772   */
    773   (void) WriteBlobString(image,"/* XPM */\n");
    774   GetPathComponent(picon->filename,BasePath,basename);
    775   (void) FormatLocaleString(buffer,MagickPathExtent,
    776     "static char *%s[] = {\n",basename);
    777   (void) WriteBlobString(image,buffer);
    778   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
    779   (void) FormatLocaleString(buffer,MagickPathExtent,
    780     "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
    781     picon->rows,(double) colors,(double) characters_per_pixel);
    782   (void) WriteBlobString(image,buffer);
    783   GetPixelInfo(image,&pixel);
    784   for (i=0; i < (ssize_t) colors; i++)
    785   {
    786     /*
    787       Define XPM color.
    788     */
    789     pixel=picon->colormap[i];
    790     pixel.colorspace=sRGBColorspace;
    791     pixel.depth=8;
    792     pixel.alpha=(double) OpaqueAlpha;
    793     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
    794     if (transparent != MagickFalse)
    795       {
    796         if (i == (ssize_t) (colors-1))
    797           (void) CopyMagickString(name,"grey75",MagickPathExtent);
    798       }
    799     /*
    800       Write XPM color.
    801     */
    802     k=i % MaxCixels;
    803     symbol[0]=Cixel[k];
    804     for (j=1; j < (ssize_t) characters_per_pixel; j++)
    805     {
    806       k=((i-k)/MaxCixels) % MaxCixels;
    807       symbol[j]=Cixel[k];
    808     }
    809     symbol[j]='\0';
    810     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",
    811        symbol,name);
    812     (void) WriteBlobString(image,buffer);
    813   }
    814   /*
    815     Define XPM pixels.
    816   */
    817   (void) WriteBlobString(image,"/* pixels */\n");
    818   for (y=0; y < (ssize_t) picon->rows; y++)
    819   {
    820     p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
    821     if (p == (const Quantum *) NULL)
    822       break;
    823     (void) WriteBlobString(image,"\"");
    824     for (x=0; x < (ssize_t) picon->columns; x++)
    825     {
    826       k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
    827       symbol[0]=Cixel[k];
    828       for (j=1; j < (ssize_t) characters_per_pixel; j++)
    829       {
    830         k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
    831         symbol[j]=Cixel[k];
    832       }
    833       symbol[j]='\0';
    834       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
    835       (void) WriteBlobString(image,buffer);
    836       p+=GetPixelChannels(image);
    837     }
    838     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
    839       y == (ssize_t) (picon->rows-1) ? "" : ",");
    840     (void) WriteBlobString(image,buffer);
    841     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
    842       picon->rows);
    843     if (status == MagickFalse)
    844       break;
    845   }
    846   picon=DestroyImage(picon);
    847   (void) WriteBlobString(image,"};\n");
    848   (void) CloseBlob(image);
    849   return(MagickTrue);
    850 }
    851 
    852 /*
    854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    855 %                                                                             %
    856 %                                                                             %
    857 %                                                                             %
    858 %   W r i t e X P M I m a g e                                                 %
    859 %                                                                             %
    860 %                                                                             %
    861 %                                                                             %
    862 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    863 %
    864 %  WriteXPMImage() writes an image to a file in the X pixmap format.
    865 %
    866 %  The format of the WriteXPMImage method is:
    867 %
    868 %      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
    869 %        Image *image,ExceptionInfo *exception)
    870 %
    871 %  A description of each parameter follows.
    872 %
    873 %    o image_info: the image info.
    874 %
    875 %    o image:  The image.
    876 %
    877 %    o exception: return any errors or warnings in this structure.
    878 %
    879 */
    880 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
    881   ExceptionInfo *exception)
    882 {
    883 #define MaxCixels  92
    884 
    885   static const char
    886     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
    887                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
    888 
    889   char
    890     buffer[MagickPathExtent],
    891     basename[MagickPathExtent],
    892     name[MagickPathExtent],
    893     symbol[MagickPathExtent];
    894 
    895   MagickBooleanType
    896     status;
    897 
    898   PixelInfo
    899     pixel;
    900 
    901   register const Quantum
    902     *p;
    903 
    904   register ssize_t
    905     i,
    906     x;
    907 
    908   size_t
    909     characters_per_pixel;
    910 
    911   ssize_t
    912     j,
    913     k,
    914     opacity,
    915     y;
    916 
    917   /*
    918     Open output image file.
    919   */
    920   assert(image_info != (const ImageInfo *) NULL);
    921   assert(image_info->signature == MagickCoreSignature);
    922   assert(image != (Image *) NULL);
    923   assert(image->signature == MagickCoreSignature);
    924   if (image->debug != MagickFalse)
    925     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    926   assert(exception != (ExceptionInfo *) NULL);
    927   assert(exception->signature == MagickCoreSignature);
    928   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    929   if (status == MagickFalse)
    930     return(status);
    931   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
    932     (void) TransformImageColorspace(image,sRGBColorspace,exception);
    933   opacity=(-1);
    934   if (image->alpha_trait == UndefinedPixelTrait)
    935     {
    936       if ((image->storage_class == DirectClass) || (image->colors > 256))
    937         (void) SetImageType(image,PaletteType,exception);
    938     }
    939   else
    940     {
    941       double
    942         alpha,
    943         beta;
    944 
    945       /*
    946         Identify transparent colormap index.
    947       */
    948       if ((image->storage_class == DirectClass) || (image->colors > 256))
    949         (void) SetImageType(image,PaletteBilevelAlphaType,exception);
    950       for (i=0; i < (ssize_t) image->colors; i++)
    951         if (image->colormap[i].alpha != OpaqueAlpha)
    952           {
    953             if (opacity < 0)
    954               {
    955                 opacity=i;
    956                 continue;
    957               }
    958             alpha=(double) TransparentAlpha-(double)
    959               image->colormap[i].alpha;
    960             beta=(double) TransparentAlpha-(double)
    961               image->colormap[opacity].alpha;
    962             if (alpha < beta)
    963               opacity=i;
    964           }
    965       if (opacity == -1)
    966         {
    967           (void) SetImageType(image,PaletteBilevelAlphaType,exception);
    968           for (i=0; i < (ssize_t) image->colors; i++)
    969             if (image->colormap[i].alpha != OpaqueAlpha)
    970               {
    971                 if (opacity < 0)
    972                   {
    973                     opacity=i;
    974                     continue;
    975                   }
    976                 alpha=(Quantum) TransparentAlpha-(double)
    977                   image->colormap[i].alpha;
    978                 beta=(Quantum) TransparentAlpha-(double)
    979                   image->colormap[opacity].alpha;
    980                 if (alpha < beta)
    981                   opacity=i;
    982               }
    983         }
    984       if (opacity >= 0)
    985         {
    986           image->colormap[opacity].red=image->transparent_color.red;
    987           image->colormap[opacity].green=image->transparent_color.green;
    988           image->colormap[opacity].blue=image->transparent_color.blue;
    989         }
    990     }
    991   /*
    992     Compute the character per pixel.
    993   */
    994   characters_per_pixel=1;
    995   for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
    996     characters_per_pixel++;
    997   /*
    998     XPM header.
    999   */
   1000   (void) WriteBlobString(image,"/* XPM */\n");
   1001   GetPathComponent(image->filename,BasePath,basename);
   1002   if (isalnum((int) ((unsigned char) *basename)) == 0)
   1003     {
   1004       (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename);
   1005       (void) CopyMagickString(basename,buffer,MagickPathExtent);
   1006     }
   1007   if (isalpha((int) ((unsigned char) basename[0])) == 0)
   1008     basename[0]='_';
   1009   for (i=1; basename[i] != '\0'; i++)
   1010     if (isalnum((int) ((unsigned char) basename[i])) == 0)
   1011       basename[i]='_';
   1012   (void) FormatLocaleString(buffer,MagickPathExtent,
   1013     "static char *%s[] = {\n",basename);
   1014   (void) WriteBlobString(image,buffer);
   1015   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
   1016   (void) FormatLocaleString(buffer,MagickPathExtent,
   1017     "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
   1018     image->rows,(double) image->colors,(double) characters_per_pixel);
   1019   (void) WriteBlobString(image,buffer);
   1020   GetPixelInfo(image,&pixel);
   1021   for (i=0; i < (ssize_t) image->colors; i++)
   1022   {
   1023     /*
   1024       Define XPM color.
   1025     */
   1026     pixel=image->colormap[i];
   1027     pixel.colorspace=sRGBColorspace;
   1028     pixel.depth=8;
   1029     pixel.alpha=(double) OpaqueAlpha;
   1030     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
   1031     if (i == opacity)
   1032       (void) CopyMagickString(name,"None",MagickPathExtent);
   1033     /*
   1034       Write XPM color.
   1035     */
   1036     k=i % MaxCixels;
   1037     symbol[0]=Cixel[k];
   1038     for (j=1; j < (ssize_t) characters_per_pixel; j++)
   1039     {
   1040       k=((i-k)/MaxCixels) % MaxCixels;
   1041       symbol[j]=Cixel[k];
   1042     }
   1043     symbol[j]='\0';
   1044     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol,
   1045       name);
   1046     (void) WriteBlobString(image,buffer);
   1047   }
   1048   /*
   1049     Define XPM pixels.
   1050   */
   1051   (void) WriteBlobString(image,"/* pixels */\n");
   1052   for (y=0; y < (ssize_t) image->rows; y++)
   1053   {
   1054     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
   1055     if (p == (const Quantum *) NULL)
   1056       break;
   1057     (void) WriteBlobString(image,"\"");
   1058     for (x=0; x < (ssize_t) image->columns; x++)
   1059     {
   1060       k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
   1061       symbol[0]=Cixel[k];
   1062       for (j=1; j < (ssize_t) characters_per_pixel; j++)
   1063       {
   1064         k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
   1065         symbol[j]=Cixel[k];
   1066       }
   1067       symbol[j]='\0';
   1068       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
   1069       (void) WriteBlobString(image,buffer);
   1070       p+=GetPixelChannels(image);
   1071     }
   1072     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
   1073       (y == (ssize_t) (image->rows-1) ? "" : ","));
   1074     (void) WriteBlobString(image,buffer);
   1075     if (image->previous == (Image *) NULL)
   1076       {
   1077         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
   1078           image->rows);
   1079         if (status == MagickFalse)
   1080           break;
   1081       }
   1082   }
   1083   (void) WriteBlobString(image,"};\n");
   1084   (void) CloseBlob(image);
   1085   return(MagickTrue);
   1086 }
   1087