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