xpm.c revision ab37187df1c707c6eb99e573a9448ce0b164956d
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 ((IssRGBColorspace(image->colorspace) == MagickFalse) &&
635      (IsRGBColorspace(image->colorspace) == MagickFalse))
636    (void) TransformImageColorspace(image,sRGBColorspace,exception);
637  SetGeometry(image,&geometry);
638  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
639    &geometry.width,&geometry.height);
640  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
641    exception);
642  blob_info=CloneImageInfo(image_info);
643  (void) AcquireUniqueFilename(blob_info->filename);
644  if ((image_info->type != TrueColorType) &&
645      (IsImageGray(image,exception) != MagickFalse))
646    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
647  else
648    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
649  (void) RelinquishUniqueFileResource(blob_info->filename);
650  blob_info=DestroyImageInfo(blob_info);
651  if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
652    return(MagickFalse);
653  quantize_info=AcquireQuantizeInfo(image_info);
654  status=RemapImage(quantize_info,picon,affinity_image,exception);
655  quantize_info=DestroyQuantizeInfo(quantize_info);
656  affinity_image=DestroyImage(affinity_image);
657  transparent=MagickFalse;
658  if (picon->storage_class == PseudoClass)
659    {
660      (void) CompressImageColormap(picon,exception);
661      if (picon->matte != MagickFalse)
662        transparent=MagickTrue;
663    }
664  else
665    {
666      /*
667        Convert DirectClass to PseudoClass picon.
668      */
669      if (picon->matte != MagickFalse)
670        {
671          /*
672            Map all the transparent pixels.
673          */
674          for (y=0; y < (ssize_t) picon->rows; y++)
675          {
676            q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
677            if (q == (Quantum *) NULL)
678              break;
679            for (x=0; x < (ssize_t) picon->columns; x++)
680            {
681              if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
682                transparent=MagickTrue;
683              else
684                SetPixelAlpha(picon,OpaqueAlpha,q);
685              q+=GetPixelChannels(picon);
686            }
687            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
688              break;
689          }
690        }
691      (void) SetImageType(picon,PaletteType,exception);
692    }
693  colors=picon->colors;
694  if (transparent != MagickFalse)
695    {
696      colors++;
697      picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
698        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
699      if (picon->colormap == (PixelInfo *) NULL)
700        ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
701      for (y=0; y < (ssize_t) picon->rows; y++)
702      {
703        q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
704        if (q == (Quantum *) NULL)
705          break;
706        for (x=0; x < (ssize_t) picon->columns; x++)
707        {
708          if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
709            SetPixelIndex(picon,picon->colors,q);
710          q+=GetPixelChannels(picon);
711        }
712        if (SyncAuthenticPixels(picon,exception) == MagickFalse)
713          break;
714      }
715    }
716  /*
717    Compute the character per pixel.
718  */
719  characters_per_pixel=1;
720  for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
721    characters_per_pixel++;
722  /*
723    XPM header.
724  */
725  (void) WriteBlobString(image,"/* XPM */\n");
726  GetPathComponent(picon->filename,BasePath,basename);
727  (void) FormatLocaleString(buffer,MaxTextExtent,
728    "static char *%s[] = {\n",basename);
729  (void) WriteBlobString(image,buffer);
730  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
731  (void) FormatLocaleString(buffer,MaxTextExtent,
732    "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
733    picon->rows,(double) colors,(double) characters_per_pixel);
734  (void) WriteBlobString(image,buffer);
735  GetPixelInfo(image,&pixel);
736  for (i=0; i < (ssize_t) colors; i++)
737  {
738    /*
739      Define XPM color.
740    */
741    pixel=picon->colormap[i];
742    pixel.colorspace=sRGBColorspace;
743    pixel.depth=8;
744    pixel.alpha=(MagickRealType) OpaqueAlpha;
745    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
746    if (transparent != MagickFalse)
747      {
748        if (i == (ssize_t) (colors-1))
749          (void) CopyMagickString(name,"grey75",MaxTextExtent);
750      }
751    /*
752      Write XPM color.
753    */
754    k=i % MaxCixels;
755    symbol[0]=Cixel[k];
756    for (j=1; j < (ssize_t) characters_per_pixel; j++)
757    {
758      k=((i-k)/MaxCixels) % MaxCixels;
759      symbol[j]=Cixel[k];
760    }
761    symbol[j]='\0';
762    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
763       symbol,name);
764    (void) WriteBlobString(image,buffer);
765  }
766  /*
767    Define XPM pixels.
768  */
769  (void) WriteBlobString(image,"/* pixels */\n");
770  for (y=0; y < (ssize_t) picon->rows; y++)
771  {
772    p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
773    if (p == (const Quantum *) NULL)
774      break;
775    (void) WriteBlobString(image,"\"");
776    for (x=0; x < (ssize_t) picon->columns; x++)
777    {
778      k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
779      symbol[0]=Cixel[k];
780      for (j=1; j < (ssize_t) characters_per_pixel; j++)
781      {
782        k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
783        symbol[j]=Cixel[k];
784      }
785      symbol[j]='\0';
786      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
787      (void) WriteBlobString(image,buffer);
788      p+=GetPixelChannels(image);
789    }
790    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
791      y == (ssize_t) (picon->rows-1) ? "" : ",");
792    (void) WriteBlobString(image,buffer);
793    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
794      picon->rows);
795    if (status == MagickFalse)
796      break;
797  }
798  picon=DestroyImage(picon);
799  (void) WriteBlobString(image,"};\n");
800  (void) CloseBlob(image);
801  return(MagickTrue);
802}
803
804/*
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806%                                                                             %
807%                                                                             %
808%                                                                             %
809%   W r i t e X P M I m a g e                                                 %
810%                                                                             %
811%                                                                             %
812%                                                                             %
813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814%
815%  WriteXPMImage() writes an image to a file in the X pixmap format.
816%
817%  The format of the WriteXPMImage method is:
818%
819%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
820%        Image *image,ExceptionInfo *exception)
821%
822%  A description of each parameter follows.
823%
824%    o image_info: the image info.
825%
826%    o image:  The image.
827%
828%    o exception: return any errors or warnings in this structure.
829%
830*/
831static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
832  ExceptionInfo *exception)
833{
834#define MaxCixels  92
835
836  static const char
837    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
838                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
839
840  char
841    buffer[MaxTextExtent],
842    basename[MaxTextExtent],
843    name[MaxTextExtent],
844    symbol[MaxTextExtent];
845
846  MagickBooleanType
847    status;
848
849  PixelInfo
850    pixel;
851
852  register const Quantum
853    *p;
854
855  register ssize_t
856    i,
857    x;
858
859  size_t
860    characters_per_pixel;
861
862  ssize_t
863    j,
864    k,
865    opacity,
866    y;
867
868  /*
869    Open output image file.
870  */
871  assert(image_info != (const ImageInfo *) NULL);
872  assert(image_info->signature == MagickSignature);
873  assert(image != (Image *) NULL);
874  assert(image->signature == MagickSignature);
875  if (image->debug != MagickFalse)
876    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
877  assert(exception != (ExceptionInfo *) NULL);
878  assert(exception->signature == MagickSignature);
879  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
880  if (status == MagickFalse)
881    return(status);
882  if (IssRGBColorspace(image->colorspace) == MagickFalse)
883    (void) TransformImageColorspace(image,sRGBColorspace,exception);
884  opacity=(-1);
885  if (image->matte == MagickFalse)
886    {
887      if ((image->storage_class == DirectClass) || (image->colors > 256))
888        (void) SetImageType(image,PaletteType,exception);
889    }
890  else
891    {
892      MagickRealType
893        alpha,
894        beta;
895
896      /*
897        Identify transparent colormap index.
898      */
899      if ((image->storage_class == DirectClass) || (image->colors > 256))
900        (void) SetImageType(image,PaletteBilevelMatteType,exception);
901      for (i=0; i < (ssize_t) image->colors; i++)
902        if (image->colormap[i].alpha != OpaqueAlpha)
903          {
904            if (opacity < 0)
905              {
906                opacity=i;
907                continue;
908              }
909            alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
910              image->colormap[i].alpha;
911            beta=(MagickRealType) TransparentAlpha-(MagickRealType)
912              image->colormap[opacity].alpha;
913            if (alpha < beta)
914              opacity=i;
915          }
916      if (opacity == -1)
917        {
918          (void) SetImageType(image,PaletteBilevelMatteType,exception);
919          for (i=0; i < (ssize_t) image->colors; i++)
920            if (image->colormap[i].alpha != OpaqueAlpha)
921              {
922                if (opacity < 0)
923                  {
924                    opacity=i;
925                    continue;
926                  }
927                alpha=(Quantum) TransparentAlpha-(MagickRealType)
928                  image->colormap[i].alpha;
929                beta=(Quantum) TransparentAlpha-(MagickRealType)
930                  image->colormap[opacity].alpha;
931                if (alpha < beta)
932                  opacity=i;
933              }
934        }
935      if (opacity >= 0)
936        {
937          image->colormap[opacity].red=image->transparent_color.red;
938          image->colormap[opacity].green=image->transparent_color.green;
939          image->colormap[opacity].blue=image->transparent_color.blue;
940        }
941    }
942  /*
943    Compute the character per pixel.
944  */
945  characters_per_pixel=1;
946  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
947    characters_per_pixel++;
948  /*
949    XPM header.
950  */
951  (void) WriteBlobString(image,"/* XPM */\n");
952  GetPathComponent(image->filename,BasePath,basename);
953  if (isalnum((int) ((unsigned char) *basename)) == 0)
954    {
955      (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
956      (void) CopyMagickString(basename,buffer,MaxTextExtent);
957    }
958  if (isalpha((int) ((unsigned char) basename[0])) == 0)
959    basename[0]='_';
960  for (i=1; basename[i] != '\0'; i++)
961    if (isalnum((int) ((unsigned char) basename[i])) == 0)
962      basename[i]='_';
963  (void) FormatLocaleString(buffer,MaxTextExtent,
964    "static char *%s[] = {\n",basename);
965  (void) WriteBlobString(image,buffer);
966  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
967  (void) FormatLocaleString(buffer,MaxTextExtent,
968    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
969    image->rows,(double) image->colors,(double) characters_per_pixel);
970  (void) WriteBlobString(image,buffer);
971  GetPixelInfo(image,&pixel);
972  for (i=0; i < (ssize_t) image->colors; i++)
973  {
974    /*
975      Define XPM color.
976    */
977    pixel=image->colormap[i];
978    pixel.colorspace=sRGBColorspace;
979    pixel.depth=8;
980    pixel.alpha=(MagickRealType) OpaqueAlpha;
981    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
982    if (i == opacity)
983      (void) CopyMagickString(name,"None",MaxTextExtent);
984    /*
985      Write XPM color.
986    */
987    k=i % MaxCixels;
988    symbol[0]=Cixel[k];
989    for (j=1; j < (ssize_t) characters_per_pixel; j++)
990    {
991      k=((i-k)/MaxCixels) % MaxCixels;
992      symbol[j]=Cixel[k];
993    }
994    symbol[j]='\0';
995    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
996      name);
997    (void) WriteBlobString(image,buffer);
998  }
999  /*
1000    Define XPM pixels.
1001  */
1002  (void) WriteBlobString(image,"/* pixels */\n");
1003  for (y=0; y < (ssize_t) image->rows; y++)
1004  {
1005    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1006    if (p == (const Quantum *) NULL)
1007      break;
1008    (void) WriteBlobString(image,"\"");
1009    for (x=0; x < (ssize_t) image->columns; x++)
1010    {
1011      k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1012      symbol[0]=Cixel[k];
1013      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1014      {
1015        k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1016        symbol[j]=Cixel[k];
1017      }
1018      symbol[j]='\0';
1019      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1020      (void) WriteBlobString(image,buffer);
1021      p+=GetPixelChannels(image);
1022    }
1023    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1024      (y == (ssize_t) (image->rows-1) ? "" : ","));
1025    (void) WriteBlobString(image,buffer);
1026    if (image->previous == (Image *) NULL)
1027      {
1028        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1029          image->rows);
1030        if (status == MagickFalse)
1031          break;
1032      }
1033  }
1034  (void) WriteBlobString(image,"};\n");
1035  (void) CloseBlob(image);
1036  return(MagickTrue);
1037}
1038