xpm.c revision 1d92f7952ace182649f73e84b7ecbba1798e8fff
1024598e40c84666cc311a42c256bbf880db3ac99sewardj/*
2024598e40c84666cc311a42c256bbf880db3ac99sewardj%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
4024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
5024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
6024598e40c84666cc311a42c256bbf880db3ac99sewardj%                            X   X  PPPP   M   M                              %
7024598e40c84666cc311a42c256bbf880db3ac99sewardj%                             X X   P   P  MM MM                              %
8024598e40c84666cc311a42c256bbf880db3ac99sewardj%                              X    PPPP   M M M                              %
9024598e40c84666cc311a42c256bbf880db3ac99sewardj%                             X X   P      M   M                              %
10024598e40c84666cc311a42c256bbf880db3ac99sewardj%                            X   X  P      M   M                              %
11024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
12024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
13024598e40c84666cc311a42c256bbf880db3ac99sewardj%                  Read/Write X Windows system Pixmap Format                  %
14024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
15024598e40c84666cc311a42c256bbf880db3ac99sewardj%                              Software Design                                %
16024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                John Cristy                                  %
17024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                 July 1992                                   %
18024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
19024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
20024598e40c84666cc311a42c256bbf880db3ac99sewardj%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
21024598e40c84666cc311a42c256bbf880db3ac99sewardj%  dedicated to making software imaging solutions freely available.           %
22024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
23024598e40c84666cc311a42c256bbf880db3ac99sewardj%  You may not use this file except in compliance with the License.  You may  %
24024598e40c84666cc311a42c256bbf880db3ac99sewardj%  obtain a copy of the License at                                            %
25024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
26024598e40c84666cc311a42c256bbf880db3ac99sewardj%    http://www.imagemagick.org/script/license.php                            %
27024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
28024598e40c84666cc311a42c256bbf880db3ac99sewardj%  Unless required by applicable law or agreed to in writing, software        %
29024598e40c84666cc311a42c256bbf880db3ac99sewardj%  distributed under the License is distributed on an "AS IS" BASIS,          %
30024598e40c84666cc311a42c256bbf880db3ac99sewardj%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31024598e40c84666cc311a42c256bbf880db3ac99sewardj%  See the License for the specific language governing permissions and        %
32024598e40c84666cc311a42c256bbf880db3ac99sewardj%  limitations under the License.                                             %
33024598e40c84666cc311a42c256bbf880db3ac99sewardj%                                                                             %
34024598e40c84666cc311a42c256bbf880db3ac99sewardj%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35024598e40c84666cc311a42c256bbf880db3ac99sewardj%
36024598e40c84666cc311a42c256bbf880db3ac99sewardj%
37024598e40c84666cc311a42c256bbf880db3ac99sewardj*/
38024598e40c84666cc311a42c256bbf880db3ac99sewardj
39024598e40c84666cc311a42c256bbf880db3ac99sewardj/*
40024598e40c84666cc311a42c256bbf880db3ac99sewardj  Include declarations.
41024598e40c84666cc311a42c256bbf880db3ac99sewardj*/
42024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/studio.h"
43024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/attribute.h"
44024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/blob.h"
45024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/blob-private.h"
46024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/cache.h"
47024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/color.h"
48024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/color-private.h"
49024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colormap.h"
50024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colorspace.h"
51024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/colorspace-private.h"
52024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/exception.h"
53024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/exception-private.h"
54024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/geometry.h"
55024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/image.h"
56024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/image-private.h"
57024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/list.h"
58024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/magick.h"
59024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/memory_.h"
60024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/monitor.h"
61024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/monitor-private.h"
62024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/pixel-accessor.h"
63024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/quantize.h"
64024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/quantum-private.h"
65024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/resize.h"
66024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/resource_.h"
67024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/splay-tree.h"
68024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/static.h"
69024598e40c84666cc311a42c256bbf880db3ac99sewardj#include "MagickCore/string_.h"
70024598e40c84666cc311a42c256bbf880db3ac99sewardj#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  /*
323    Remove unquoted characters.
324  */
325  active=MagickFalse;
326  q=xpm_buffer;
327  while (*p != '\0')
328  {
329    if (*p++ == '"')
330      {
331        if (active != MagickFalse)
332          *q++='\n';
333        active=active != MagickFalse ? MagickFalse : MagickTrue;
334      }
335    if (active != MagickFalse)
336      *q++=(*p);
337  }
338  *q='\0';
339  /*
340    Initialize image structure.
341  */
342  xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
343    (void *(*)(void *)) NULL);
344  if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
345    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
346  /*
347    Read image colormap.
348  */
349  image->depth=1;
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->alpha_trait=BlendPixelTrait;
380      }
381    status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
382      exception);
383    if (status == MagickFalse)
384      break;
385    if (image->depth < image->colormap[j].depth)
386      image->depth=image->colormap[j].depth;
387  }
388  if (j < (ssize_t) image->colors)
389    ThrowReaderException(CorruptImageError,"CorruptImage");
390  j=0;
391  if (image_info->ping == MagickFalse)
392    {
393      /*
394        Read image pixels.
395      */
396      for (y=0; y < (ssize_t) image->rows; y++)
397      {
398        p=NextXPMLine(p);
399        if (p == (char *) NULL)
400          break;
401        r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
402        if (r == (Quantum *) NULL)
403          break;
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            SetPixelIndex(image,j,r);
410          SetPixelInfoPixel(image,image->colormap+j,r);
411          p+=width;
412          r+=GetPixelChannels(image);
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,ExceptionInfo *exception)
525%
526%  A description of each parameter follows.
527%
528%    o image_info: the image info.
529%
530%    o image:  The image.
531%
532%    o exception: return any errors or warnings in this structure.
533%
534*/
535static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
536  Image *image,ExceptionInfo *exception)
537{
538#define ColormapExtent  155
539#define GraymapExtent  95
540#define PiconGeometry  "48x48>"
541
542  static unsigned char
543    Colormap[]=
544    {
545      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
546      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
547      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
548      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
549      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
550      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
551      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
552      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
553      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
554      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
555      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
556      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
557      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
558    },
559    Graymap[]=
560    {
561      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
562      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
563      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
564      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
565      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
566      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
567      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
568      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
569    };
570
571#define MaxCixels  92
572
573  static const char
574    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
575                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
576
577  char
578    buffer[MaxTextExtent],
579    basename[MaxTextExtent],
580    name[MaxTextExtent],
581    symbol[MaxTextExtent];
582
583  Image
584    *affinity_image,
585    *picon;
586
587  ImageInfo
588    *blob_info;
589
590  MagickBooleanType
591    status,
592    transparent;
593
594  PixelInfo
595    pixel;
596
597  QuantizeInfo
598    *quantize_info;
599
600  RectangleInfo
601    geometry;
602
603  register const Quantum
604    *p;
605
606  register ssize_t
607    i,
608    x;
609
610  register Quantum
611    *q;
612
613  size_t
614    characters_per_pixel,
615    colors;
616
617  ssize_t
618    j,
619    k,
620    y;
621
622  /*
623    Open output image file.
624  */
625  assert(image_info != (const ImageInfo *) NULL);
626  assert(image_info->signature == MagickSignature);
627  assert(image != (Image *) NULL);
628  assert(image->signature == MagickSignature);
629  if (image->debug != MagickFalse)
630    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
631  assert(exception != (ExceptionInfo *) NULL);
632  assert(exception->signature == MagickSignature);
633  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
634  if (status == MagickFalse)
635    return(status);
636  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
637    (void) TransformImageColorspace(image,sRGBColorspace,exception);
638  SetGeometry(image,&geometry);
639  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
640    &geometry.width,&geometry.height);
641  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
642    exception);
643  blob_info=CloneImageInfo(image_info);
644  (void) AcquireUniqueFilename(blob_info->filename);
645  if ((image_info->type != TrueColorType) &&
646      (IsImageGray(image,exception) != MagickFalse))
647    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
648  else
649    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
650  (void) RelinquishUniqueFileResource(blob_info->filename);
651  blob_info=DestroyImageInfo(blob_info);
652  if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
653    return(MagickFalse);
654  quantize_info=AcquireQuantizeInfo(image_info);
655  status=RemapImage(quantize_info,picon,affinity_image,exception);
656  quantize_info=DestroyQuantizeInfo(quantize_info);
657  affinity_image=DestroyImage(affinity_image);
658  transparent=MagickFalse;
659  if (picon->storage_class == PseudoClass)
660    {
661      (void) CompressImageColormap(picon,exception);
662      if (picon->alpha_trait == BlendPixelTrait)
663        transparent=MagickTrue;
664    }
665  else
666    {
667      /*
668        Convert DirectClass to PseudoClass picon.
669      */
670      if (picon->alpha_trait == BlendPixelTrait)
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 == (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+=GetPixelChannels(picon);
687            }
688            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
689              break;
690          }
691        }
692      (void) SetImageType(picon,PaletteType,exception);
693    }
694  colors=picon->colors;
695  if (transparent != MagickFalse)
696    {
697      colors++;
698      picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
699        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
700      if (picon->colormap == (PixelInfo *) 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 == (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+=GetPixelChannels(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    pixel=picon->colormap[i];
743    pixel.colorspace=sRGBColorspace;
744    pixel.depth=8;
745    pixel.alpha=(double) OpaqueAlpha;
746    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
747    if (transparent != MagickFalse)
748      {
749        if (i == (ssize_t) (colors-1))
750          (void) CopyMagickString(name,"grey75",MaxTextExtent);
751      }
752    /*
753      Write XPM color.
754    */
755    k=i % MaxCixels;
756    symbol[0]=Cixel[k];
757    for (j=1; j < (ssize_t) characters_per_pixel; j++)
758    {
759      k=((i-k)/MaxCixels) % MaxCixels;
760      symbol[j]=Cixel[k];
761    }
762    symbol[j]='\0';
763    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
764       symbol,name);
765    (void) WriteBlobString(image,buffer);
766  }
767  /*
768    Define XPM pixels.
769  */
770  (void) WriteBlobString(image,"/* pixels */\n");
771  for (y=0; y < (ssize_t) picon->rows; y++)
772  {
773    p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
774    if (p == (const Quantum *) NULL)
775      break;
776    (void) WriteBlobString(image,"\"");
777    for (x=0; x < (ssize_t) picon->columns; x++)
778    {
779      k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
780      symbol[0]=Cixel[k];
781      for (j=1; j < (ssize_t) characters_per_pixel; j++)
782      {
783        k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
784        symbol[j]=Cixel[k];
785      }
786      symbol[j]='\0';
787      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
788      (void) WriteBlobString(image,buffer);
789      p+=GetPixelChannels(image);
790    }
791    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
792      y == (ssize_t) (picon->rows-1) ? "" : ",");
793    (void) WriteBlobString(image,buffer);
794    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
795      picon->rows);
796    if (status == MagickFalse)
797      break;
798  }
799  picon=DestroyImage(picon);
800  (void) WriteBlobString(image,"};\n");
801  (void) CloseBlob(image);
802  return(MagickTrue);
803}
804
805/*
806%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807%                                                                             %
808%                                                                             %
809%                                                                             %
810%   W r i t e X P M I m a g e                                                 %
811%                                                                             %
812%                                                                             %
813%                                                                             %
814%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
815%
816%  WriteXPMImage() writes an image to a file in the X pixmap format.
817%
818%  The format of the WriteXPMImage method is:
819%
820%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
821%        Image *image,ExceptionInfo *exception)
822%
823%  A description of each parameter follows.
824%
825%    o image_info: the image info.
826%
827%    o image:  The image.
828%
829%    o exception: return any errors or warnings in this structure.
830%
831*/
832static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
833  ExceptionInfo *exception)
834{
835#define MaxCixels  92
836
837  static const char
838    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
839                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
840
841  char
842    buffer[MaxTextExtent],
843    basename[MaxTextExtent],
844    name[MaxTextExtent],
845    symbol[MaxTextExtent];
846
847  MagickBooleanType
848    status;
849
850  PixelInfo
851    pixel;
852
853  register const Quantum
854    *p;
855
856  register ssize_t
857    i,
858    x;
859
860  size_t
861    characters_per_pixel;
862
863  ssize_t
864    j,
865    k,
866    opacity,
867    y;
868
869  /*
870    Open output image file.
871  */
872  assert(image_info != (const ImageInfo *) NULL);
873  assert(image_info->signature == MagickSignature);
874  assert(image != (Image *) NULL);
875  assert(image->signature == MagickSignature);
876  if (image->debug != MagickFalse)
877    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
878  assert(exception != (ExceptionInfo *) NULL);
879  assert(exception->signature == MagickSignature);
880  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
881  if (status == MagickFalse)
882    return(status);
883  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
884    (void) TransformImageColorspace(image,sRGBColorspace,exception);
885  opacity=(-1);
886  if (image->alpha_trait != BlendPixelTrait)
887    {
888      if ((image->storage_class == DirectClass) || (image->colors > 256))
889        (void) SetImageType(image,PaletteType,exception);
890    }
891  else
892    {
893      double
894        alpha,
895        beta;
896
897      /*
898        Identify transparent colormap index.
899      */
900      if ((image->storage_class == DirectClass) || (image->colors > 256))
901        (void) SetImageType(image,PaletteBilevelMatteType,exception);
902      for (i=0; i < (ssize_t) image->colors; i++)
903        if (image->colormap[i].alpha != OpaqueAlpha)
904          {
905            if (opacity < 0)
906              {
907                opacity=i;
908                continue;
909              }
910            alpha=(double) TransparentAlpha-(double)
911              image->colormap[i].alpha;
912            beta=(double) TransparentAlpha-(double)
913              image->colormap[opacity].alpha;
914            if (alpha < beta)
915              opacity=i;
916          }
917      if (opacity == -1)
918        {
919          (void) SetImageType(image,PaletteBilevelMatteType,exception);
920          for (i=0; i < (ssize_t) image->colors; i++)
921            if (image->colormap[i].alpha != OpaqueAlpha)
922              {
923                if (opacity < 0)
924                  {
925                    opacity=i;
926                    continue;
927                  }
928                alpha=(Quantum) TransparentAlpha-(double)
929                  image->colormap[i].alpha;
930                beta=(Quantum) TransparentAlpha-(double)
931                  image->colormap[opacity].alpha;
932                if (alpha < beta)
933                  opacity=i;
934              }
935        }
936      if (opacity >= 0)
937        {
938          image->colormap[opacity].red=image->transparent_color.red;
939          image->colormap[opacity].green=image->transparent_color.green;
940          image->colormap[opacity].blue=image->transparent_color.blue;
941        }
942    }
943  /*
944    Compute the character per pixel.
945  */
946  characters_per_pixel=1;
947  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
948    characters_per_pixel++;
949  /*
950    XPM header.
951  */
952  (void) WriteBlobString(image,"/* XPM */\n");
953  GetPathComponent(image->filename,BasePath,basename);
954  if (isalnum((int) ((unsigned char) *basename)) == 0)
955    {
956      (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
957      (void) CopyMagickString(basename,buffer,MaxTextExtent);
958    }
959  if (isalpha((int) ((unsigned char) basename[0])) == 0)
960    basename[0]='_';
961  for (i=1; basename[i] != '\0'; i++)
962    if (isalnum((int) ((unsigned char) basename[i])) == 0)
963      basename[i]='_';
964  (void) FormatLocaleString(buffer,MaxTextExtent,
965    "static char *%s[] = {\n",basename);
966  (void) WriteBlobString(image,buffer);
967  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
968  (void) FormatLocaleString(buffer,MaxTextExtent,
969    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
970    image->rows,(double) image->colors,(double) characters_per_pixel);
971  (void) WriteBlobString(image,buffer);
972  GetPixelInfo(image,&pixel);
973  for (i=0; i < (ssize_t) image->colors; i++)
974  {
975    /*
976      Define XPM color.
977    */
978    pixel=image->colormap[i];
979    pixel.colorspace=sRGBColorspace;
980    pixel.depth=8;
981    pixel.alpha=(double) OpaqueAlpha;
982    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
983    if (i == opacity)
984      (void) CopyMagickString(name,"None",MaxTextExtent);
985    /*
986      Write XPM color.
987    */
988    k=i % MaxCixels;
989    symbol[0]=Cixel[k];
990    for (j=1; j < (ssize_t) characters_per_pixel; j++)
991    {
992      k=((i-k)/MaxCixels) % MaxCixels;
993      symbol[j]=Cixel[k];
994    }
995    symbol[j]='\0';
996    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
997      name);
998    (void) WriteBlobString(image,buffer);
999  }
1000  /*
1001    Define XPM pixels.
1002  */
1003  (void) WriteBlobString(image,"/* pixels */\n");
1004  for (y=0; y < (ssize_t) image->rows; y++)
1005  {
1006    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1007    if (p == (const Quantum *) NULL)
1008      break;
1009    (void) WriteBlobString(image,"\"");
1010    for (x=0; x < (ssize_t) image->columns; x++)
1011    {
1012      k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1013      symbol[0]=Cixel[k];
1014      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1015      {
1016        k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1017        symbol[j]=Cixel[k];
1018      }
1019      symbol[j]='\0';
1020      (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1021      (void) WriteBlobString(image,buffer);
1022      p+=GetPixelChannels(image);
1023    }
1024    (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1025      (y == (ssize_t) (image->rows-1) ? "" : ","));
1026    (void) WriteBlobString(image,buffer);
1027    if (image->previous == (Image *) NULL)
1028      {
1029        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1030          image->rows);
1031        if (status == MagickFalse)
1032          break;
1033      }
1034  }
1035  (void) WriteBlobString(image,"};\n");
1036  (void) CloseBlob(image);
1037  return(MagickTrue);
1038}
1039