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