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