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