xpm.c revision e1c94d9d25db6b0dd7a5028ffee31d1057855d73
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%                                   Cristy                                    %
17%                                 July 1992                                   %
18%                                                                             %
19%                                                                             %
20%  Copyright 1999-2015 ImageMagick Studio LLC, a non-profit organization      %
21%  dedicated to making software imaging solutions freely available.           %
22%                                                                             %
23%  You may not use this file except in compliance with the License.  You may  %
24%  obtain a copy of the License at                                            %
25%                                                                             %
26%    http://www.imagemagick.org/script/license.php                            %
27%                                                                             %
28%  Unless required by applicable law or agreed to in writing, software        %
29%  distributed under the License is distributed on an "AS IS" BASIS,          %
30%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31%  See the License for the specific language governing permissions and        %
32%  limitations under the License.                                             %
33%                                                                             %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40  Include declarations.
41*/
42#include "MagickCore/studio.h"
43#include "MagickCore/attribute.h"
44#include "MagickCore/blob.h"
45#include "MagickCore/blob-private.h"
46#include "MagickCore/cache.h"
47#include "MagickCore/color.h"
48#include "MagickCore/color-private.h"
49#include "MagickCore/colormap.h"
50#include "MagickCore/colorspace.h"
51#include "MagickCore/colorspace-private.h"
52#include "MagickCore/exception.h"
53#include "MagickCore/exception-private.h"
54#include "MagickCore/geometry.h"
55#include "MagickCore/image.h"
56#include "MagickCore/image-private.h"
57#include "MagickCore/list.h"
58#include "MagickCore/magick.h"
59#include "MagickCore/memory_.h"
60#include "MagickCore/monitor.h"
61#include "MagickCore/monitor-private.h"
62#include "MagickCore/pixel-accessor.h"
63#include "MagickCore/quantize.h"
64#include "MagickCore/quantum-private.h"
65#include "MagickCore/resize.h"
66#include "MagickCore/resource_.h"
67#include "MagickCore/splay-tree.h"
68#include "MagickCore/static.h"
69#include "MagickCore/string_.h"
70#include "MagickCore/module.h"
71#include "MagickCore/threshold.h"
72#include "MagickCore/utility.h"
73
74/*
75  Forward declarations.
76*/
77static MagickBooleanType
78  WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79  WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
80
81/*
82%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83%                                                                             %
84%                                                                             %
85%                                                                             %
86%   I s X P M                                                                 %
87%                                                                             %
88%                                                                             %
89%                                                                             %
90%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91%
92%  IsXPM() returns MagickTrue if the image format type, identified by the
93%  magick string, is XPM.
94%
95%  The format of the IsXPM method is:
96%
97%      MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
98%
99%  A description of each parameter follows:
100%
101%    o magick: compare image format pattern against these bytes. or
102%      blob.
103%
104%    o length: Specifies the length of the magick string.
105%
106*/
107static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
108{
109  if (length < 9)
110    return(MagickFalse);
111  if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
112    return(MagickTrue);
113  return(MagickFalse);
114}
115
116/*
117%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118%                                                                             %
119%                                                                             %
120%                                                                             %
121%   R e a d X P M I m a g e                                                   %
122%                                                                             %
123%                                                                             %
124%                                                                             %
125%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126%
127%  ReadXPMImage() reads an X11 pixmap image file and returns it.  It
128%  allocates the memory necessary for the new Image structure and returns a
129%  pointer to the new image.
130%
131%  The format of the ReadXPMImage method is:
132%
133%      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
134%
135%  A description of each parameter follows:
136%
137%    o image_info: the image info.
138%
139%    o exception: return any errors or warnings in this structure.
140%
141*/
142
143static int CompareXPMColor(const void *target,const void *source)
144{
145  const char
146    *p,
147    *q;
148
149  p=(const char *) target;
150  q=(const char *) source;
151  return(strcmp(p,q));
152}
153
154static ssize_t CopyXPMColor(char *destination,const char *source,size_t length)
155{
156  register const char
157    *p;
158
159  p=source;
160  while (length-- && (*p != '\0'))
161    *destination++=(*p++);
162  if (length != 0)
163    *destination='\0';
164  return((ssize_t) (p-source));
165}
166
167static char *NextXPMLine(char *p)
168{
169  assert(p != (char *) NULL);
170  p=strchr(p,'\n');
171  if (p != (char *) NULL)
172    p++;
173  return(p);
174}
175
176static char *ParseXPMColor(char *color,MagickBooleanType search_start)
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  if (search_start != MagickFalse)
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  for (p=color+1; *p != '\0'; p++)
220  {
221    if (*p == '\n')
222      break;
223    if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
224      continue;
225    if (isspace((int) ((unsigned char) (*p))) != 0)
226      continue;
227    for (i=0; i < NumberTargets; i++)
228    {
229      if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
230        return(p);
231    }
232  }
233  return(p);
234}
235
236static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
237{
238  char
239    *grey,
240    key[MagickPathExtent],
241    target[MagickPathExtent],
242    *xpm_buffer;
243
244  Image
245    *image;
246
247  MagickBooleanType
248    active,
249    status;
250
251  register char
252    *next,
253    *p,
254    *q;
255
256  register ssize_t
257    x;
258
259  register Quantum
260    *r;
261
262  size_t
263    length;
264
265  SplayTreeInfo
266    *xpm_colors;
267
268  ssize_t
269    count,
270    j,
271    y;
272
273  unsigned long
274    colors,
275    columns,
276    rows,
277    width;
278
279  /*
280    Open image file.
281  */
282  assert(image_info != (const ImageInfo *) NULL);
283  assert(image_info->signature == MagickCoreSignature);
284  if (image_info->debug != MagickFalse)
285    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
286      image_info->filename);
287  assert(exception != (ExceptionInfo *) NULL);
288  assert(exception->signature == MagickCoreSignature);
289  image=AcquireImage(image_info,exception);
290  status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
291  if (status == MagickFalse)
292    {
293      image=DestroyImageList(image);
294      return((Image *) NULL);
295    }
296  /*
297    Read XPM file.
298  */
299  length=MagickPathExtent;
300  xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
301  if (xpm_buffer == (char *) NULL)
302    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
303  *xpm_buffer='\0';
304  p=xpm_buffer;
305  while (ReadBlobString(image,p) != (char *) NULL)
306  {
307    if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
308      continue;
309    if ((*p == '}') && (*(p+1) == ';'))
310      break;
311    p+=strlen(p);
312    if ((size_t) (p-xpm_buffer+MagickPathExtent) < length)
313      continue;
314    length<<=1;
315    xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MagickPathExtent,
316      sizeof(*xpm_buffer));
317    if (xpm_buffer == (char *) NULL)
318      break;
319    p=xpm_buffer+strlen(xpm_buffer);
320  }
321  if (xpm_buffer == (char *) NULL)
322    ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
323  /*
324    Remove comments.
325  */
326  count=0;
327  width=0;
328  for (p=xpm_buffer; *p != '\0'; p++)
329  {
330    if (*p != '"')
331      continue;
332    count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
333    image->columns=columns;
334    image->rows=rows;
335    image->colors=colors;
336    if (count == 4)
337      break;
338  }
339  if ((count != 4) || (width > 10) || (image->columns == 0) ||
340      (image->rows == 0) || (image->colors == 0))
341    ThrowReaderException(CorruptImageError,"ImproperImageHeader");
342  /*
343    Remove unquoted characters.
344  */
345  active=MagickFalse;
346  q=xpm_buffer;
347  while (*p != '\0')
348  {
349    if (*p++ == '"')
350      {
351        if (active != MagickFalse)
352          *q++='\n';
353        active=active != MagickFalse ? MagickFalse : MagickTrue;
354      }
355    if (active != MagickFalse)
356      *q++=(*p);
357  }
358  *q='\0';
359  /*
360    Initialize image structure.
361  */
362  xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
363    (void *(*)(void *)) NULL);
364  if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
365    {
366      xpm_buffer=DestroyString(xpm_buffer);
367      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
368    }
369  /*
370    Read image colormap.
371  */
372  image->depth=1;
373  next=NextXPMLine(xpm_buffer);
374  for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
375  {
376    p=next;
377    next=NextXPMLine(p);
378    (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1));
379    status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
380    /*
381      Parse color.
382    */
383    (void) CopyMagickString(target,"gray",MagickPathExtent);
384    q=ParseXPMColor(p+width,MagickTrue);
385    if (q != (char *) NULL)
386      {
387        while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
388          q++;
389        if ((next-q) < 0)
390          break;
391        if (next != (char *) NULL)
392          (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
393            MagickPathExtent-1));
394        else
395          (void) CopyMagickString(target,q,MagickPathExtent);
396        q=ParseXPMColor(target,MagickFalse);
397        if (q != (char *) NULL)
398          *q='\0';
399      }
400    StripString(target);
401    grey=strstr(target,"grey");
402    if (grey != (char *) NULL)
403      grey[2]='a';
404    if (LocaleCompare(target,"none") == 0)
405      {
406        image->storage_class=DirectClass;
407        image->alpha_trait=BlendPixelTrait;
408      }
409    status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
410      exception);
411    if (status == MagickFalse)
412      break;
413    if (image->depth < image->colormap[j].depth)
414      image->depth=image->colormap[j].depth;
415  }
416  if (j < (ssize_t) image->colors)
417    {
418      xpm_colors=DestroySplayTree(xpm_colors);
419      xpm_buffer=DestroyString(xpm_buffer);
420      ThrowReaderException(CorruptImageError,"CorruptImage");
421    }
422  j=0;
423  if (image_info->ping == MagickFalse)
424    {
425      /*
426        Read image pixels.
427      */
428      status=SetImageExtent(image,image->columns,image->rows,exception);
429      if (status == MagickFalse)
430        return(DestroyImageList(image));
431      for (y=0; y < (ssize_t) image->rows; y++)
432      {
433        p=NextXPMLine(p);
434        if (p == (char *) NULL)
435          break;
436        r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
437        if (r == (Quantum *) NULL)
438          break;
439        for (x=0; x < (ssize_t) image->columns; x++)
440        {
441          ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1));
442          if (count != (ssize_t) width)
443            break;
444          j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
445          if (image->storage_class == PseudoClass)
446            SetPixelIndex(image,(Quantum) j,r);
447          SetPixelViaPixelInfo(image,image->colormap+j,r);
448          p+=count;
449          r+=GetPixelChannels(image);
450        }
451        if (x < (ssize_t) image->columns)
452          break;
453        if (SyncAuthenticPixels(image,exception) == MagickFalse)
454          break;
455      }
456      if (y < (ssize_t) image->rows)
457        {
458          xpm_colors=DestroySplayTree(xpm_colors);
459          xpm_buffer=DestroyString(xpm_buffer);
460          ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
461        }
462    }
463  /*
464    Relinquish resources.
465  */
466  xpm_buffer=DestroyString(xpm_buffer);
467  xpm_colors=DestroySplayTree(xpm_colors);
468  (void) CloseBlob(image);
469  return(GetFirstImageInList(image));
470}
471
472/*
473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474%                                                                             %
475%                                                                             %
476%                                                                             %
477%   R e g i s t e r X P M I m a g e                                           %
478%                                                                             %
479%                                                                             %
480%                                                                             %
481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482%
483%  RegisterXPMImage() adds attributes for the XPM image format to
484%  the list of supported formats.  The attributes include the image format
485%  tag, a method to read and/or write the format, whether the format
486%  supports the saving of more than one frame to the same file or blob,
487%  whether the format supports native in-memory I/O, and a brief
488%  description of the format.
489%
490%  The format of the RegisterXPMImage method is:
491%
492%      size_t RegisterXPMImage(void)
493%
494*/
495ModuleExport size_t RegisterXPMImage(void)
496{
497  MagickInfo
498    *entry;
499
500  entry=AcquireMagickInfo("XPM","PICON","Personal Icon");
501  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
502  entry->encoder=(EncodeImageHandler *) WritePICONImage;
503  entry->flags^=CoderAdjoinFlag;
504  (void) RegisterMagickInfo(entry);
505  entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)");
506  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
507  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
508  entry->flags^=CoderAdjoinFlag;
509  entry->flags|=CoderStealthFlag;
510  (void) RegisterMagickInfo(entry);
511  entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)");
512  entry->decoder=(DecodeImageHandler *) ReadXPMImage;
513  entry->encoder=(EncodeImageHandler *) WriteXPMImage;
514  entry->magick=(IsImageFormatHandler *) IsXPM;
515  entry->flags^=CoderAdjoinFlag;
516  (void) RegisterMagickInfo(entry);
517  return(MagickImageCoderSignature);
518}
519
520/*
521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522%                                                                             %
523%                                                                             %
524%                                                                             %
525%   U n r e g i s t e r X P M I m a g e                                       %
526%                                                                             %
527%                                                                             %
528%                                                                             %
529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530%
531%  UnregisterXPMImage() removes format registrations made by the
532%  XPM module from the list of supported formats.
533%
534%  The format of the UnregisterXPMImage method is:
535%
536%      UnregisterXPMImage(void)
537%
538*/
539ModuleExport void UnregisterXPMImage(void)
540{
541  (void) UnregisterMagickInfo("PICON");
542  (void) UnregisterMagickInfo("PM");
543  (void) UnregisterMagickInfo("XPM");
544}
545
546/*
547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548%                                                                             %
549%                                                                             %
550%                                                                             %
551%   W r i t e P I C O N I m a g e                                             %
552%                                                                             %
553%                                                                             %
554%                                                                             %
555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556%
557%  WritePICONImage() writes an image to a file in the Personal Icon format.
558%
559%  The format of the WritePICONImage method is:
560%
561%      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
562%        Image *image,ExceptionInfo *exception)
563%
564%  A description of each parameter follows.
565%
566%    o image_info: the image info.
567%
568%    o image:  The image.
569%
570%    o exception: return any errors or warnings in this structure.
571%
572*/
573static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
574  Image *image,ExceptionInfo *exception)
575{
576#define ColormapExtent  155
577#define GraymapExtent  95
578#define PiconGeometry  "48x48>"
579
580  static unsigned char
581    Colormap[]=
582    {
583      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
584      0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
585      0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
586      0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
587      0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
588      0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
589      0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
590      0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
591      0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
592      0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
593      0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
594      0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
595      0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
596    },
597    Graymap[]=
598    {
599      0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
600      0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
601      0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
602      0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
603      0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
604      0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
605      0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
606      0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
607    };
608
609#define MaxCixels  92
610
611  static const char
612    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
613                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
614
615  char
616    buffer[MagickPathExtent],
617    basename[MagickPathExtent],
618    name[MagickPathExtent],
619    symbol[MagickPathExtent];
620
621  Image
622    *affinity_image,
623    *picon;
624
625  ImageInfo
626    *blob_info;
627
628  MagickBooleanType
629    status,
630    transparent;
631
632  PixelInfo
633    pixel;
634
635  QuantizeInfo
636    *quantize_info;
637
638  RectangleInfo
639    geometry;
640
641  register const Quantum
642    *p;
643
644  register ssize_t
645    i,
646    x;
647
648  register Quantum
649    *q;
650
651  size_t
652    characters_per_pixel,
653    colors;
654
655  ssize_t
656    j,
657    k,
658    y;
659
660  /*
661    Open output image file.
662  */
663  assert(image_info != (const ImageInfo *) NULL);
664  assert(image_info->signature == MagickCoreSignature);
665  assert(image != (Image *) NULL);
666  assert(image->signature == MagickCoreSignature);
667  if (image->debug != MagickFalse)
668    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
669  assert(exception != (ExceptionInfo *) NULL);
670  assert(exception->signature == MagickCoreSignature);
671  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
672  if (status == MagickFalse)
673    return(status);
674  (void) TransformImageColorspace(image,sRGBColorspace,exception);
675  SetGeometry(image,&geometry);
676  (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
677    &geometry.width,&geometry.height);
678  picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
679    exception);
680  blob_info=CloneImageInfo(image_info);
681  (void) AcquireUniqueFilename(blob_info->filename);
682  if ((image_info->type != TrueColorType) &&
683      (SetImageGray(image,exception) != MagickFalse))
684    affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
685  else
686    affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
687  (void) RelinquishUniqueFileResource(blob_info->filename);
688  blob_info=DestroyImageInfo(blob_info);
689  if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
690    return(MagickFalse);
691  quantize_info=AcquireQuantizeInfo(image_info);
692  status=RemapImage(quantize_info,picon,affinity_image,exception);
693  quantize_info=DestroyQuantizeInfo(quantize_info);
694  affinity_image=DestroyImage(affinity_image);
695  transparent=MagickFalse;
696  if (picon->storage_class == PseudoClass)
697    {
698      (void) CompressImageColormap(picon,exception);
699      if (picon->alpha_trait != UndefinedPixelTrait)
700        transparent=MagickTrue;
701    }
702  else
703    {
704      /*
705        Convert DirectClass to PseudoClass picon.
706      */
707      if (picon->alpha_trait != UndefinedPixelTrait)
708        {
709          /*
710            Map all the transparent pixels.
711          */
712          for (y=0; y < (ssize_t) picon->rows; y++)
713          {
714            q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
715            if (q == (Quantum *) NULL)
716              break;
717            for (x=0; x < (ssize_t) picon->columns; x++)
718            {
719              if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
720                transparent=MagickTrue;
721              else
722                SetPixelAlpha(picon,OpaqueAlpha,q);
723              q+=GetPixelChannels(picon);
724            }
725            if (SyncAuthenticPixels(picon,exception) == MagickFalse)
726              break;
727          }
728        }
729      (void) SetImageType(picon,PaletteType,exception);
730    }
731  colors=picon->colors;
732  if (transparent != MagickFalse)
733    {
734      colors++;
735      picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
736        picon->colormap,(size_t) colors,sizeof(*picon->colormap));
737      if (picon->colormap == (PixelInfo *) NULL)
738        ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
739      for (y=0; y < (ssize_t) picon->rows; y++)
740      {
741        q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
742        if (q == (Quantum *) NULL)
743          break;
744        for (x=0; x < (ssize_t) picon->columns; x++)
745        {
746          if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
747            SetPixelIndex(picon,(Quantum) picon->colors,q);
748          q+=GetPixelChannels(picon);
749        }
750        if (SyncAuthenticPixels(picon,exception) == MagickFalse)
751          break;
752      }
753    }
754  /*
755    Compute the character per pixel.
756  */
757  characters_per_pixel=1;
758  for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
759    characters_per_pixel++;
760  /*
761    XPM header.
762  */
763  (void) WriteBlobString(image,"/* XPM */\n");
764  GetPathComponent(picon->filename,BasePath,basename);
765  (void) FormatLocaleString(buffer,MagickPathExtent,
766    "static char *%s[] = {\n",basename);
767  (void) WriteBlobString(image,buffer);
768  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
769  (void) FormatLocaleString(buffer,MagickPathExtent,
770    "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
771    picon->rows,(double) colors,(double) characters_per_pixel);
772  (void) WriteBlobString(image,buffer);
773  GetPixelInfo(image,&pixel);
774  for (i=0; i < (ssize_t) colors; i++)
775  {
776    /*
777      Define XPM color.
778    */
779    pixel=picon->colormap[i];
780    pixel.colorspace=sRGBColorspace;
781    pixel.depth=8;
782    pixel.alpha=(double) OpaqueAlpha;
783    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
784    if (transparent != MagickFalse)
785      {
786        if (i == (ssize_t) (colors-1))
787          (void) CopyMagickString(name,"grey75",MagickPathExtent);
788      }
789    /*
790      Write XPM color.
791    */
792    k=i % MaxCixels;
793    symbol[0]=Cixel[k];
794    for (j=1; j < (ssize_t) characters_per_pixel; j++)
795    {
796      k=((i-k)/MaxCixels) % MaxCixels;
797      symbol[j]=Cixel[k];
798    }
799    symbol[j]='\0';
800    (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",
801       symbol,name);
802    (void) WriteBlobString(image,buffer);
803  }
804  /*
805    Define XPM pixels.
806  */
807  (void) WriteBlobString(image,"/* pixels */\n");
808  for (y=0; y < (ssize_t) picon->rows; y++)
809  {
810    p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
811    if (p == (const Quantum *) NULL)
812      break;
813    (void) WriteBlobString(image,"\"");
814    for (x=0; x < (ssize_t) picon->columns; x++)
815    {
816      k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
817      symbol[0]=Cixel[k];
818      for (j=1; j < (ssize_t) characters_per_pixel; j++)
819      {
820        k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
821        symbol[j]=Cixel[k];
822      }
823      symbol[j]='\0';
824      (void) CopyMagickString(buffer,symbol,MagickPathExtent);
825      (void) WriteBlobString(image,buffer);
826      p+=GetPixelChannels(image);
827    }
828    (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
829      y == (ssize_t) (picon->rows-1) ? "" : ",");
830    (void) WriteBlobString(image,buffer);
831    status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
832      picon->rows);
833    if (status == MagickFalse)
834      break;
835  }
836  picon=DestroyImage(picon);
837  (void) WriteBlobString(image,"};\n");
838  (void) CloseBlob(image);
839  return(MagickTrue);
840}
841
842/*
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844%                                                                             %
845%                                                                             %
846%                                                                             %
847%   W r i t e X P M I m a g e                                                 %
848%                                                                             %
849%                                                                             %
850%                                                                             %
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852%
853%  WriteXPMImage() writes an image to a file in the X pixmap format.
854%
855%  The format of the WriteXPMImage method is:
856%
857%      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
858%        Image *image,ExceptionInfo *exception)
859%
860%  A description of each parameter follows.
861%
862%    o image_info: the image info.
863%
864%    o image:  The image.
865%
866%    o exception: return any errors or warnings in this structure.
867%
868*/
869static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
870  ExceptionInfo *exception)
871{
872#define MaxCixels  92
873
874  static const char
875    Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
876                         "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
877
878  char
879    buffer[MagickPathExtent],
880    basename[MagickPathExtent],
881    name[MagickPathExtent],
882    symbol[MagickPathExtent];
883
884  MagickBooleanType
885    status;
886
887  PixelInfo
888    pixel;
889
890  register const Quantum
891    *p;
892
893  register ssize_t
894    i,
895    x;
896
897  size_t
898    characters_per_pixel;
899
900  ssize_t
901    j,
902    k,
903    opacity,
904    y;
905
906  /*
907    Open output image file.
908  */
909  assert(image_info != (const ImageInfo *) NULL);
910  assert(image_info->signature == MagickCoreSignature);
911  assert(image != (Image *) NULL);
912  assert(image->signature == MagickCoreSignature);
913  if (image->debug != MagickFalse)
914    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
915  assert(exception != (ExceptionInfo *) NULL);
916  assert(exception->signature == MagickCoreSignature);
917  status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
918  if (status == MagickFalse)
919    return(status);
920  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
921    (void) TransformImageColorspace(image,sRGBColorspace,exception);
922  opacity=(-1);
923  if (image->alpha_trait == UndefinedPixelTrait)
924    {
925      if ((image->storage_class == DirectClass) || (image->colors > 256))
926        (void) SetImageType(image,PaletteType,exception);
927    }
928  else
929    {
930      double
931        alpha,
932        beta;
933
934      /*
935        Identify transparent colormap index.
936      */
937      if ((image->storage_class == DirectClass) || (image->colors > 256))
938        (void) SetImageType(image,PaletteBilevelAlphaType,exception);
939      for (i=0; i < (ssize_t) image->colors; i++)
940        if (image->colormap[i].alpha != OpaqueAlpha)
941          {
942            if (opacity < 0)
943              {
944                opacity=i;
945                continue;
946              }
947            alpha=(double) TransparentAlpha-(double)
948              image->colormap[i].alpha;
949            beta=(double) TransparentAlpha-(double)
950              image->colormap[opacity].alpha;
951            if (alpha < beta)
952              opacity=i;
953          }
954      if (opacity == -1)
955        {
956          (void) SetImageType(image,PaletteBilevelAlphaType,exception);
957          for (i=0; i < (ssize_t) image->colors; i++)
958            if (image->colormap[i].alpha != OpaqueAlpha)
959              {
960                if (opacity < 0)
961                  {
962                    opacity=i;
963                    continue;
964                  }
965                alpha=(Quantum) TransparentAlpha-(double)
966                  image->colormap[i].alpha;
967                beta=(Quantum) TransparentAlpha-(double)
968                  image->colormap[opacity].alpha;
969                if (alpha < beta)
970                  opacity=i;
971              }
972        }
973      if (opacity >= 0)
974        {
975          image->colormap[opacity].red=image->transparent_color.red;
976          image->colormap[opacity].green=image->transparent_color.green;
977          image->colormap[opacity].blue=image->transparent_color.blue;
978        }
979    }
980  /*
981    Compute the character per pixel.
982  */
983  characters_per_pixel=1;
984  for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
985    characters_per_pixel++;
986  /*
987    XPM header.
988  */
989  (void) WriteBlobString(image,"/* XPM */\n");
990  GetPathComponent(image->filename,BasePath,basename);
991  if (isalnum((int) ((unsigned char) *basename)) == 0)
992    {
993      (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename);
994      (void) CopyMagickString(basename,buffer,MagickPathExtent);
995    }
996  if (isalpha((int) ((unsigned char) basename[0])) == 0)
997    basename[0]='_';
998  for (i=1; basename[i] != '\0'; i++)
999    if (isalnum((int) ((unsigned char) basename[i])) == 0)
1000      basename[i]='_';
1001  (void) FormatLocaleString(buffer,MagickPathExtent,
1002    "static char *%s[] = {\n",basename);
1003  (void) WriteBlobString(image,buffer);
1004  (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
1005  (void) FormatLocaleString(buffer,MagickPathExtent,
1006    "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1007    image->rows,(double) image->colors,(double) characters_per_pixel);
1008  (void) WriteBlobString(image,buffer);
1009  GetPixelInfo(image,&pixel);
1010  for (i=0; i < (ssize_t) image->colors; i++)
1011  {
1012    /*
1013      Define XPM color.
1014    */
1015    pixel=image->colormap[i];
1016    pixel.colorspace=sRGBColorspace;
1017    pixel.depth=8;
1018    pixel.alpha=(double) OpaqueAlpha;
1019    (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1020    if (i == opacity)
1021      (void) CopyMagickString(name,"None",MagickPathExtent);
1022    /*
1023      Write XPM color.
1024    */
1025    k=i % MaxCixels;
1026    symbol[0]=Cixel[k];
1027    for (j=1; j < (ssize_t) characters_per_pixel; j++)
1028    {
1029      k=((i-k)/MaxCixels) % MaxCixels;
1030      symbol[j]=Cixel[k];
1031    }
1032    symbol[j]='\0';
1033    (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol,
1034      name);
1035    (void) WriteBlobString(image,buffer);
1036  }
1037  /*
1038    Define XPM pixels.
1039  */
1040  (void) WriteBlobString(image,"/* pixels */\n");
1041  for (y=0; y < (ssize_t) image->rows; y++)
1042  {
1043    p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1044    if (p == (const Quantum *) NULL)
1045      break;
1046    (void) WriteBlobString(image,"\"");
1047    for (x=0; x < (ssize_t) image->columns; x++)
1048    {
1049      k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1050      symbol[0]=Cixel[k];
1051      for (j=1; j < (ssize_t) characters_per_pixel; j++)
1052      {
1053        k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1054        symbol[j]=Cixel[k];
1055      }
1056      symbol[j]='\0';
1057      (void) CopyMagickString(buffer,symbol,MagickPathExtent);
1058      (void) WriteBlobString(image,buffer);
1059      p+=GetPixelChannels(image);
1060    }
1061    (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
1062      (y == (ssize_t) (image->rows-1) ? "" : ","));
1063    (void) WriteBlobString(image,buffer);
1064    if (image->previous == (Image *) NULL)
1065      {
1066        status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1067          image->rows);
1068        if (status == MagickFalse)
1069          break;
1070      }
1071  }
1072  (void) WriteBlobString(image,"};\n");
1073  (void) CloseBlob(image);
1074  return(MagickTrue);
1075}
1076