xpm.c revision dcfc1ad7e0ad199ccd86e9508e9c5775e4c4bbdb
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-2011 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 *),
79  WriteXPMImage(const ImageInfo *,Image *);
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);
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) == 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          SetPixelPacket(image,image->colormap+j,r);
409          p+=width;
410          r+=GetPixelComponents(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)
523%
524%  A description of each parameter follows.
525%
526%    o image_info: the image info.
527%
528%    o image:  The image.
529%
530*/
531static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
532  Image *image)
533{
534#define ColormapExtent  155
535#define GraymapExtent  95
536#define PiconGeometry  "48x48>"
537
538  static unsigned char
539    Colormap[]=
540    {
541      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
542      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
543      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
544      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
545      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
546      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
547      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
548      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
549      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
550      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
551      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
552      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
553      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
554    },
555    Graymap[]=
556    {
557      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
558      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
559      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
560      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
561      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
562      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
563      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
564      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
565    };
566
567#define MaxCixels  92
568
569  static const char
570    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
571                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
572
573  char
574    buffer[MaxTextExtent],
575    basename[MaxTextExtent],
576    name[MaxTextExtent],
577    symbol[MaxTextExtent];
578
579  ExceptionInfo
580    *exception;
581
582  Image
583    *affinity_image,
584    *picon;
585
586  ImageInfo
587    *blob_info;
588
589  MagickBooleanType
590    status,
591    transparent;
592
593  PixelInfo
594    pixel;
595
596  QuantizeInfo
597    *quantize_info;
598
599  RectangleInfo
600    geometry;
601
602  register const Quantum
603    *p;
604
605  register ssize_t
606    i,
607    x;
608
609  register Quantum
610    *q;
611
612  size_t
613    characters_per_pixel,
614    colors;
615
616  ssize_t
617    j,
618    k,
619    y;
620
621  /*
622    Open output image file.
623  */
624  assert(image_info != (const ImageInfo *) NULL);
625  assert(image_info->signature == MagickSignature);
626  assert(image != (Image *) NULL);
627  assert(image->signature == MagickSignature);
628  if (image->debug != MagickFalse)
629    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
630  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
631  if (status == MagickFalse)
632    return(status);
633  if (IsRGBColorspace(image->colorspace) == MagickFalse)
634    (void) TransformImageColorspace(image,RGBColorspace);
635  SetGeometry(image,&geometry);
636  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
637    &geometry.width,&geometry.height);
638  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
639    &image->exception);
640  blob_info=CloneImageInfo(image_info);
641  (void) AcquireUniqueFilename(blob_info->filename);
642  if ((image_info->type != TrueColorType) &&
643      (IsImageGray(image,&image->exception) != MagickFalse))
644    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,
645      &image->exception);
646  else
647    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
648      &image->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);
655  quantize_info=DestroyQuantizeInfo(quantize_info);
656  affinity_image=DestroyImage(affinity_image);
657  transparent=MagickFalse;
658  exception=(&image->exception);
659  if (picon->storage_class == PseudoClass)
660    {
661      (void) CompressImageColormap(picon);
662      if (picon->matte != MagickFalse)
663        transparent=MagickTrue;
664    }
665  else
666    {
667      /*
668        Convert DirectClass to PseudoClass picon.
669      */
670      if (picon->matte != MagickFalse)
671        {
672          /*
673            Map all the transparent pixels.
674          */
675          for (y=0; y < (ssize_t) picon->rows; y++)
676          {
677            q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
678            if (q == (const Quantum *) NULL)
679              break;
680            for (x=0; x < (ssize_t) picon->columns; x++)
681            {
682              if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
683                transparent=MagickTrue;
684              else
685                SetPixelAlpha(picon,OpaqueAlpha,q);
686              q+=GetPixelComponents(picon);
687            }
688            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
689              break;
690          }
691        }
692      (void) SetImageType(picon,PaletteType);
693    }
694  colors=picon->colors;
695  if (transparent != MagickFalse)
696    {
697      colors++;
698      picon->colormap=(PixelPacket *) ResizeQuantumMemory((void **)
699        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
700      if (picon->colormap == (PixelPacket *) NULL)
701        ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
702      for (y=0; y < (ssize_t) picon->rows; y++)
703      {
704        q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
705        if (q == (const Quantum *) NULL)
706          break;
707        for (x=0; x < (ssize_t) picon->columns; x++)
708        {
709          if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
710            SetPixelIndex(picon,picon->colors,q);
711          q+=GetPixelComponents(picon);
712        }
713        if (SyncAuthenticPixels(picon,exception) == MagickFalse)
714          break;
715      }
716    }
717  /*
718    Compute the character per pixel.
719  */
720  characters_per_pixel=1;
721  for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
722    characters_per_pixel++;
723  /*
724    XPM header.
725  */
726  (void) WriteBlobString(image,"/* XPM */\n");
727  GetPathComponent(picon->filename,BasePath,basename);
728  (void) FormatLocaleString(buffer,MaxTextExtent,
729    "static char *%s[] = {\n",basename);
730  (void) WriteBlobString(image,buffer);
731  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
732  (void) FormatLocaleString(buffer,MaxTextExtent,
733    "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
734    picon->rows,(double) colors,(double) characters_per_pixel);
735  (void) WriteBlobString(image,buffer);
736  GetPixelInfo(image,&pixel);
737  for (i=0; i < (ssize_t) colors; i++)
738  {
739    /*
740      Define XPM color.
741    */
742    SetPixelInfoPacket(image,picon->colormap+i,&pixel);
743    pixel.colorspace=RGBColorspace;
744    pixel.depth=8;
745    pixel.alpha=(MagickRealType) OpaqueAlpha;
746    (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
747      &image->exception);
748    if (transparent != MagickFalse)
749      {
750        if (i == (ssize_t) (colors-1))
751          (void) CopyMagickString(name,"grey75",MaxTextExtent);
752      }
753    /*
754      Write XPM color.
755    */
756    k=i % MaxCixels;
757    symbol[0]=Cixel[k];
758    for (j=1; j < (ssize_t) characters_per_pixel; j++)
759    {
760      k=((i-k)/MaxCixels) % MaxCixels;
761      symbol[j]=Cixel[k];
762    }
763    symbol[j]='\0';
764    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
765       symbol,name);
766    (void) WriteBlobString(image,buffer);
767  }
768  /*
769    Define XPM pixels.
770  */
771  (void) WriteBlobString(image,"/* pixels */\n");
772  for (y=0; y < (ssize_t) picon->rows; y++)
773  {
774    p=GetVirtualPixels(picon,0,y,picon->columns,1,&picon->exception);
775    if (p == (const Quantum *) NULL)
776      break;
777    (void) WriteBlobString(image,"\"");
778    for (x=0; x < (ssize_t) picon->columns; x++)
779    {
780      k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
781      symbol[0]=Cixel[k];
782      for (j=1; j < (ssize_t) characters_per_pixel; j++)
783      {
784        k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
785        symbol[j]=Cixel[k];
786      }
787      symbol[j]='\0';
788      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
789      (void) WriteBlobString(image,buffer);
790      p+=GetPixelComponents(image);
791    }
792    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
793      y == (ssize_t) (picon->rows-1) ? "" : ",");
794    (void) WriteBlobString(image,buffer);
795    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
796      picon->rows);
797    if (status == MagickFalse)
798      break;
799  }
800  picon=DestroyImage(picon);
801  (void) WriteBlobString(image,"};\n");
802  (void) CloseBlob(image);
803  return(MagickTrue);
804}
805
806/*
807%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
808%                                                                             %
809%                                                                             %
810%                                                                             %
811%   W r i t e X P M I m a g e                                                 %
812%                                                                             %
813%                                                                             %
814%                                                                             %
815%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
816%
817%  WriteXPMImage() writes an image to a file in the X pixmap format.
818%
819%  The format of the WriteXPMImage method is:
820%
821%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
822%
823%  A description of each parameter follows.
824%
825%    o image_info: the image info.
826%
827%    o image:  The image.
828%
829*/
830static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
831{
832#define MaxCixels  92
833
834  static const char
835    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
836                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
837
838  char
839    buffer[MaxTextExtent],
840    basename[MaxTextExtent],
841    name[MaxTextExtent],
842    symbol[MaxTextExtent];
843
844  MagickBooleanType
845    status;
846
847  PixelInfo
848    pixel;
849
850  register const Quantum
851    *p;
852
853  register ssize_t
854    i,
855    x;
856
857  size_t
858    characters_per_pixel;
859
860  ssize_t
861    j,
862    k,
863    opacity,
864    y;
865
866  /*
867    Open output image file.
868  */
869  assert(image_info != (const ImageInfo *) NULL);
870  assert(image_info->signature == MagickSignature);
871  assert(image != (Image *) NULL);
872  assert(image->signature == MagickSignature);
873  if (image->debug != MagickFalse)
874    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
875  status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
876  if (status == MagickFalse)
877    return(status);
878  if (IsRGBColorspace(image->colorspace) == MagickFalse)
879    (void) TransformImageColorspace(image,RGBColorspace);
880  opacity=(-1);
881  if (image->matte == MagickFalse)
882    {
883      if ((image->storage_class == DirectClass) || (image->colors > 256))
884        (void) SetImageType(image,PaletteType);
885    }
886  else
887    {
888      MagickRealType
889        alpha,
890        beta;
891
892      /*
893        Identify transparent colormap index.
894      */
895      if ((image->storage_class == DirectClass) || (image->colors > 256))
896        (void) SetImageType(image,PaletteBilevelMatteType);
897      for (i=0; i < (ssize_t) image->colors; i++)
898        if (image->colormap[i].alpha != OpaqueAlpha)
899          {
900            if (opacity < 0)
901              {
902                opacity=i;
903                continue;
904              }
905            alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
906              image->colormap[i].alpha;
907            beta=(MagickRealType) TransparentAlpha-(MagickRealType)
908              image->colormap[opacity].alpha;
909            if (alpha < beta)
910              opacity=i;
911          }
912      if (opacity == -1)
913        {
914          (void) SetImageType(image,PaletteBilevelMatteType);
915          for (i=0; i < (ssize_t) image->colors; i++)
916            if (image->colormap[i].alpha != OpaqueAlpha)
917              {
918                if (opacity < 0)
919                  {
920                    opacity=i;
921                    continue;
922                  }
923                alpha=(Quantum) TransparentAlpha-(MagickRealType)
924                  image->colormap[i].alpha;
925                beta=(Quantum) TransparentAlpha-(MagickRealType)
926                  image->colormap[opacity].alpha;
927                if (alpha < beta)
928                  opacity=i;
929              }
930        }
931      if (opacity >= 0)
932        {
933          image->colormap[opacity].red=image->transparent_color.red;
934          image->colormap[opacity].green=image->transparent_color.green;
935          image->colormap[opacity].blue=image->transparent_color.blue;
936        }
937    }
938  /*
939    Compute the character per pixel.
940  */
941  characters_per_pixel=1;
942  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
943    characters_per_pixel++;
944  /*
945    XPM header.
946  */
947  (void) WriteBlobString(image,"/* XPM */\n");
948  GetPathComponent(image->filename,BasePath,basename);
949  if (isalnum((int) ((unsigned char) *basename)) == 0)
950    {
951      (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
952      (void) CopyMagickString(basename,buffer,MaxTextExtent);
953    }
954  if (isalpha((int) ((unsigned char) basename[0])) == 0)
955    basename[0]='_';
956  for (i=1; basename[i] != '\0'; i++)
957    if (isalnum((int) ((unsigned char) basename[i])) == 0)
958      basename[i]='_';
959  (void) FormatLocaleString(buffer,MaxTextExtent,
960    "static char *%s[] = {\n",basename);
961  (void) WriteBlobString(image,buffer);
962  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
963  (void) FormatLocaleString(buffer,MaxTextExtent,
964    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
965    image->rows,(double) image->colors,(double) characters_per_pixel);
966  (void) WriteBlobString(image,buffer);
967  GetPixelInfo(image,&pixel);
968  for (i=0; i < (ssize_t) image->colors; i++)
969  {
970    /*
971      Define XPM color.
972    */
973    SetPixelInfoPacket(image,image->colormap+i,&pixel);
974    pixel.colorspace=RGBColorspace;
975    pixel.depth=8;
976    pixel.alpha=(MagickRealType) OpaqueAlpha;
977    (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
978      &image->exception);
979    if (i == opacity)
980      (void) CopyMagickString(name,"None",MaxTextExtent);
981    /*
982      Write XPM color.
983    */
984    k=i % MaxCixels;
985    symbol[0]=Cixel[k];
986    for (j=1; j < (ssize_t) characters_per_pixel; j++)
987    {
988      k=((i-k)/MaxCixels) % MaxCixels;
989      symbol[j]=Cixel[k];
990    }
991    symbol[j]='\0';
992    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
993      name);
994    (void) WriteBlobString(image,buffer);
995  }
996  /*
997    Define XPM pixels.
998  */
999  (void) WriteBlobString(image,"/* pixels */\n");
1000  for (y=0; y < (ssize_t) image->rows; y++)
1001  {
1002    p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1003    if (p == (const Quantum *) NULL)
1004      break;
1005    (void) WriteBlobString(image,"\"");
1006    for (x=0; x < (ssize_t) image->columns; x++)
1007    {
1008      k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1009      symbol[0]=Cixel[k];
1010      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1011      {
1012        k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1013        symbol[j]=Cixel[k];
1014      }
1015      symbol[j]='\0';
1016      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1017      (void) WriteBlobString(image,buffer);
1018      p+=GetPixelComponents(image);
1019    }
1020    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1021      (y == (ssize_t) (image->rows-1) ? "" : ","));
1022    (void) WriteBlobString(image,buffer);
1023    if (image->previous == (Image *) NULL)
1024      {
1025        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1026          image->rows);
1027        if (status == MagickFalse)
1028          break;
1029      }
1030  }
1031  (void) WriteBlobString(image,"};\n");
1032  (void) CloseBlob(image);
1033  return(MagickTrue);
1034}
1035