]> granicus.if.org Git - imagemagick/blob - coders/xpm.c
Fixed memory leak reported in #456.
[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-2017 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://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 \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   Forward declarations.
76 */
77 static MagickBooleanType
78   WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79   WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
80 \f
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 */
107 static 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 \f
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
143 static 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
154 static 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
167 static 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
176 static 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     *const 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
236 static 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 == 0) || (width > 3) ||
340       (image->columns == 0) || (image->rows == 0) ||
341       (image->colors == 0) || (image->colors > MaxColormapSize))
342     {
343       xpm_buffer=DestroyString(xpm_buffer);
344       ThrowReaderException(CorruptImageError,"ImproperImageHeader");
345     }
346   /*
347     Remove unquoted characters.
348   */
349   active=MagickFalse;
350   for (q=xpm_buffer; *p != '\0'; )
351   {
352     if (*p++ == '"')
353       {
354         if (active != MagickFalse)
355           *q++='\n';
356         active=active != MagickFalse ? MagickFalse : MagickTrue;
357       }
358     if (active != MagickFalse)
359       *q++=(*p);
360   }
361   *q='\0';
362   /*
363     Initialize image structure.
364   */
365   xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
366     (void *(*)(void *)) NULL);
367   if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
368     {
369       xpm_buffer=DestroyString(xpm_buffer);
370       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
371     }
372   /*
373     Read image colormap.
374   */
375   image->depth=1;
376   next=NextXPMLine(xpm_buffer);
377   for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
378   {
379     p=next;
380     next=NextXPMLine(p);
381     (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1));
382     status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
383     /*
384       Parse color.
385     */
386     (void) CopyMagickString(target,"gray",MagickPathExtent);
387     q=ParseXPMColor(p+width,MagickTrue);
388     if (q != (char *) NULL)
389       {
390         while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
391           q++;
392         if ((next-q) < 0)
393           break;
394         if (next != (char *) NULL)
395           (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
396             MagickPathExtent-1));
397         else
398           (void) CopyMagickString(target,q,MagickPathExtent);
399         q=ParseXPMColor(target,MagickFalse);
400         if (q != (char *) NULL)
401           *q='\0';
402       }
403     StripString(target);
404     grey=strstr(target,"grey");
405     if (grey != (char *) NULL)
406       grey[2]='a';
407     if (LocaleCompare(target,"none") == 0)
408       {
409         image->storage_class=DirectClass;
410         image->alpha_trait=BlendPixelTrait;
411       }
412     status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
413       exception);
414     if (status == MagickFalse)
415       break;
416     if (image->depth < image->colormap[j].depth)
417       image->depth=image->colormap[j].depth;
418   }
419   if (j < (ssize_t) image->colors)
420     {
421       xpm_colors=DestroySplayTree(xpm_colors);
422       xpm_buffer=DestroyString(xpm_buffer);
423       ThrowReaderException(CorruptImageError,"CorruptImage");
424     }
425   j=0;
426   if (image_info->ping == MagickFalse)
427     {
428       /*
429         Read image pixels.
430       */
431       status=SetImageExtent(image,image->columns,image->rows,exception);
432       if (status == MagickFalse)
433         return(DestroyImageList(image));
434       for (y=0; y < (ssize_t) image->rows; y++)
435       {
436         p=NextXPMLine(p);
437         if (p == (char *) NULL)
438           break;
439         r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
440         if (r == (Quantum *) NULL)
441           break;
442         for (x=0; x < (ssize_t) image->columns; x++)
443         {
444           ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1));
445           if (count != (ssize_t) width)
446             break;
447           j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
448           if (image->storage_class == PseudoClass)
449             SetPixelIndex(image,(Quantum) j,r);
450           SetPixelViaPixelInfo(image,image->colormap+j,r);
451           p+=count;
452           r+=GetPixelChannels(image);
453         }
454         if (x < (ssize_t) image->columns)
455           break;
456         if (SyncAuthenticPixels(image,exception) == MagickFalse)
457           break;
458       }
459       if (y < (ssize_t) image->rows)
460         {
461           xpm_colors=DestroySplayTree(xpm_colors);
462           xpm_buffer=DestroyString(xpm_buffer);
463           ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
464         }
465     }
466   /*
467     Relinquish resources.
468   */
469   xpm_buffer=DestroyString(xpm_buffer);
470   xpm_colors=DestroySplayTree(xpm_colors);
471   (void) CloseBlob(image);
472   return(GetFirstImageInList(image));
473 }
474 \f
475 /*
476 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477 %                                                                             %
478 %                                                                             %
479 %                                                                             %
480 %   R e g i s t e r X P M I m a g e                                           %
481 %                                                                             %
482 %                                                                             %
483 %                                                                             %
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 %
486 %  RegisterXPMImage() adds attributes for the XPM image format to
487 %  the list of supported formats.  The attributes include the image format
488 %  tag, a method to read and/or write the format, whether the format
489 %  supports the saving of more than one frame to the same file or blob,
490 %  whether the format supports native in-memory I/O, and a brief
491 %  description of the format.
492 %
493 %  The format of the RegisterXPMImage method is:
494 %
495 %      size_t RegisterXPMImage(void)
496 %
497 */
498 ModuleExport size_t RegisterXPMImage(void)
499 {
500   MagickInfo
501     *entry;
502
503   entry=AcquireMagickInfo("XPM","PICON","Personal Icon");
504   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
505   entry->encoder=(EncodeImageHandler *) WritePICONImage;
506   entry->flags^=CoderAdjoinFlag;
507   (void) RegisterMagickInfo(entry);
508   entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)");
509   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
510   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
511   entry->flags^=CoderAdjoinFlag;
512   entry->flags|=CoderStealthFlag;
513   (void) RegisterMagickInfo(entry);
514   entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)");
515   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
516   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
517   entry->magick=(IsImageFormatHandler *) IsXPM;
518   entry->flags^=CoderAdjoinFlag;
519   (void) RegisterMagickInfo(entry);
520   return(MagickImageCoderSignature);
521 }
522 \f
523 /*
524 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525 %                                                                             %
526 %                                                                             %
527 %                                                                             %
528 %   U n r e g i s t e r X P M I m a g e                                       %
529 %                                                                             %
530 %                                                                             %
531 %                                                                             %
532 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533 %
534 %  UnregisterXPMImage() removes format registrations made by the
535 %  XPM module from the list of supported formats.
536 %
537 %  The format of the UnregisterXPMImage method is:
538 %
539 %      UnregisterXPMImage(void)
540 %
541 */
542 ModuleExport void UnregisterXPMImage(void)
543 {
544   (void) UnregisterMagickInfo("PICON");
545   (void) UnregisterMagickInfo("PM");
546   (void) UnregisterMagickInfo("XPM");
547 }
548 \f
549 /*
550 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
551 %                                                                             %
552 %                                                                             %
553 %                                                                             %
554 %   W r i t e P I C O N I m a g e                                             %
555 %                                                                             %
556 %                                                                             %
557 %                                                                             %
558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559 %
560 %  WritePICONImage() writes an image to a file in the Personal Icon format.
561 %
562 %  The format of the WritePICONImage method is:
563 %
564 %      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
565 %        Image *image,ExceptionInfo *exception)
566 %
567 %  A description of each parameter follows.
568 %
569 %    o image_info: the image info.
570 %
571 %    o image:  The image.
572 %
573 %    o exception: return any errors or warnings in this structure.
574 %
575 */
576 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
577   Image *image,ExceptionInfo *exception)
578 {
579 #define ColormapExtent  155
580 #define GraymapExtent  95
581 #define PiconGeometry  "48x48>"
582
583   static unsigned char
584     Colormap[]=
585     {
586       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
587       0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
588       0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
589       0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
590       0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
591       0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
592       0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
593       0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
594       0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
595       0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
596       0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
597       0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
598       0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
599     },
600     Graymap[]=
601     {
602       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
603       0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
604       0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
605       0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
606       0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
607       0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
608       0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
609       0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
610     };
611
612 #define MaxCixels  92
613
614   static const char
615     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
616                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
617
618   char
619     buffer[MagickPathExtent],
620     basename[MagickPathExtent],
621     name[MagickPathExtent],
622     symbol[MagickPathExtent];
623
624   Image
625     *affinity_image,
626     *picon;
627
628   ImageInfo
629     *blob_info;
630
631   MagickBooleanType
632     status,
633     transparent;
634
635   PixelInfo
636     pixel;
637
638   QuantizeInfo
639     *quantize_info;
640
641   RectangleInfo
642     geometry;
643
644   register const Quantum
645     *p;
646
647   register ssize_t
648     i,
649     x;
650
651   register Quantum
652     *q;
653
654   size_t
655     characters_per_pixel,
656     colors;
657
658   ssize_t
659     j,
660     k,
661     y;
662
663   /*
664     Open output image file.
665   */
666   assert(image_info != (const ImageInfo *) NULL);
667   assert(image_info->signature == MagickCoreSignature);
668   assert(image != (Image *) NULL);
669   assert(image->signature == MagickCoreSignature);
670   if (image->debug != MagickFalse)
671     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
672   assert(exception != (ExceptionInfo *) NULL);
673   assert(exception->signature == MagickCoreSignature);
674   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
675   if (status == MagickFalse)
676     return(status);
677   (void) TransformImageColorspace(image,sRGBColorspace,exception);
678   SetGeometry(image,&geometry);
679   (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
680     &geometry.width,&geometry.height);
681   picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
682     exception);
683   blob_info=CloneImageInfo(image_info);
684   (void) AcquireUniqueFilename(blob_info->filename);
685   if ((image_info->type != TrueColorType) &&
686       (SetImageGray(image,exception) != MagickFalse))
687     affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
688   else
689     affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
690   (void) RelinquishUniqueFileResource(blob_info->filename);
691   blob_info=DestroyImageInfo(blob_info);
692   if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
693     return(MagickFalse);
694   quantize_info=AcquireQuantizeInfo(image_info);
695   status=RemapImage(quantize_info,picon,affinity_image,exception);
696   quantize_info=DestroyQuantizeInfo(quantize_info);
697   affinity_image=DestroyImage(affinity_image);
698   transparent=MagickFalse;
699   if (picon->storage_class == PseudoClass)
700     {
701       (void) CompressImageColormap(picon,exception);
702       if (picon->alpha_trait != UndefinedPixelTrait)
703         transparent=MagickTrue;
704     }
705   else
706     {
707       /*
708         Convert DirectClass to PseudoClass picon.
709       */
710       if (picon->alpha_trait != UndefinedPixelTrait)
711         {
712           /*
713             Map all the transparent pixels.
714           */
715           for (y=0; y < (ssize_t) picon->rows; y++)
716           {
717             q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
718             if (q == (Quantum *) NULL)
719               break;
720             for (x=0; x < (ssize_t) picon->columns; x++)
721             {
722               if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
723                 transparent=MagickTrue;
724               else
725                 SetPixelAlpha(picon,OpaqueAlpha,q);
726               q+=GetPixelChannels(picon);
727             }
728             if (SyncAuthenticPixels(picon,exception) == MagickFalse)
729               break;
730           }
731         }
732       (void) SetImageType(picon,PaletteType,exception);
733     }
734   colors=picon->colors;
735   if (transparent != MagickFalse)
736     {
737       colors++;
738       picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
739         picon->colormap,(size_t) colors,sizeof(*picon->colormap));
740       if (picon->colormap == (PixelInfo *) NULL)
741         ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
742       for (y=0; y < (ssize_t) picon->rows; y++)
743       {
744         q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
745         if (q == (Quantum *) NULL)
746           break;
747         for (x=0; x < (ssize_t) picon->columns; x++)
748         {
749           if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
750             SetPixelIndex(picon,(Quantum) picon->colors,q);
751           q+=GetPixelChannels(picon);
752         }
753         if (SyncAuthenticPixels(picon,exception) == MagickFalse)
754           break;
755       }
756     }
757   /*
758     Compute the character per pixel.
759   */
760   characters_per_pixel=1;
761   for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
762     characters_per_pixel++;
763   /*
764     XPM header.
765   */
766   (void) WriteBlobString(image,"/* XPM */\n");
767   GetPathComponent(picon->filename,BasePath,basename);
768   (void) FormatLocaleString(buffer,MagickPathExtent,
769     "static char *%s[] = {\n",basename);
770   (void) WriteBlobString(image,buffer);
771   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
772   (void) FormatLocaleString(buffer,MagickPathExtent,
773     "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
774     picon->rows,(double) colors,(double) characters_per_pixel);
775   (void) WriteBlobString(image,buffer);
776   GetPixelInfo(image,&pixel);
777   for (i=0; i < (ssize_t) colors; i++)
778   {
779     /*
780       Define XPM color.
781     */
782     pixel=picon->colormap[i];
783     pixel.colorspace=sRGBColorspace;
784     pixel.depth=8;
785     pixel.alpha=(double) OpaqueAlpha;
786     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
787     if (transparent != MagickFalse)
788       {
789         if (i == (ssize_t) (colors-1))
790           (void) CopyMagickString(name,"grey75",MagickPathExtent);
791       }
792     /*
793       Write XPM color.
794     */
795     k=i % MaxCixels;
796     symbol[0]=Cixel[k];
797     for (j=1; j < (ssize_t) characters_per_pixel; j++)
798     {
799       k=((i-k)/MaxCixels) % MaxCixels;
800       symbol[j]=Cixel[k];
801     }
802     symbol[j]='\0';
803     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",
804        symbol,name);
805     (void) WriteBlobString(image,buffer);
806   }
807   /*
808     Define XPM pixels.
809   */
810   (void) WriteBlobString(image,"/* pixels */\n");
811   for (y=0; y < (ssize_t) picon->rows; y++)
812   {
813     p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
814     if (p == (const Quantum *) NULL)
815       break;
816     (void) WriteBlobString(image,"\"");
817     for (x=0; x < (ssize_t) picon->columns; x++)
818     {
819       k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
820       symbol[0]=Cixel[k];
821       for (j=1; j < (ssize_t) characters_per_pixel; j++)
822       {
823         k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
824         symbol[j]=Cixel[k];
825       }
826       symbol[j]='\0';
827       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
828       (void) WriteBlobString(image,buffer);
829       p+=GetPixelChannels(image);
830     }
831     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
832       y == (ssize_t) (picon->rows-1) ? "" : ",");
833     (void) WriteBlobString(image,buffer);
834     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
835       picon->rows);
836     if (status == MagickFalse)
837       break;
838   }
839   picon=DestroyImage(picon);
840   (void) WriteBlobString(image,"};\n");
841   (void) CloseBlob(image);
842   return(MagickTrue);
843 }
844 \f
845 /*
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 %                                                                             %
848 %                                                                             %
849 %                                                                             %
850 %   W r i t e X P M I m a g e                                                 %
851 %                                                                             %
852 %                                                                             %
853 %                                                                             %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 %
856 %  WriteXPMImage() writes an image to a file in the X pixmap format.
857 %
858 %  The format of the WriteXPMImage method is:
859 %
860 %      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
861 %        Image *image,ExceptionInfo *exception)
862 %
863 %  A description of each parameter follows.
864 %
865 %    o image_info: the image info.
866 %
867 %    o image:  The image.
868 %
869 %    o exception: return any errors or warnings in this structure.
870 %
871 */
872 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
873   ExceptionInfo *exception)
874 {
875 #define MaxCixels  92
876
877   static const char
878     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
879                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
880
881   char
882     buffer[MagickPathExtent],
883     basename[MagickPathExtent],
884     name[MagickPathExtent],
885     symbol[MagickPathExtent];
886
887   MagickBooleanType
888     status;
889
890   PixelInfo
891     pixel;
892
893   register const Quantum
894     *p;
895
896   register ssize_t
897     i,
898     x;
899
900   size_t
901     characters_per_pixel;
902
903   ssize_t
904     j,
905     k,
906     opacity,
907     y;
908
909   /*
910     Open output image file.
911   */
912   assert(image_info != (const ImageInfo *) NULL);
913   assert(image_info->signature == MagickCoreSignature);
914   assert(image != (Image *) NULL);
915   assert(image->signature == MagickCoreSignature);
916   if (image->debug != MagickFalse)
917     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
918   assert(exception != (ExceptionInfo *) NULL);
919   assert(exception->signature == MagickCoreSignature);
920   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
921   if (status == MagickFalse)
922     return(status);
923   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
924     (void) TransformImageColorspace(image,sRGBColorspace,exception);
925   opacity=(-1);
926   if (image->alpha_trait == UndefinedPixelTrait)
927     {
928       if ((image->storage_class == DirectClass) || (image->colors > 256))
929         (void) SetImageType(image,PaletteType,exception);
930     }
931   else
932     {
933       double
934         alpha,
935         beta;
936
937       /*
938         Identify transparent colormap index.
939       */
940       if ((image->storage_class == DirectClass) || (image->colors > 256))
941         (void) SetImageType(image,PaletteBilevelAlphaType,exception);
942       for (i=0; i < (ssize_t) image->colors; i++)
943         if (image->colormap[i].alpha != OpaqueAlpha)
944           {
945             if (opacity < 0)
946               {
947                 opacity=i;
948                 continue;
949               }
950             alpha=(double) TransparentAlpha-(double)
951               image->colormap[i].alpha;
952             beta=(double) TransparentAlpha-(double)
953               image->colormap[opacity].alpha;
954             if (alpha < beta)
955               opacity=i;
956           }
957       if (opacity == -1)
958         {
959           (void) SetImageType(image,PaletteBilevelAlphaType,exception);
960           for (i=0; i < (ssize_t) image->colors; i++)
961             if (image->colormap[i].alpha != OpaqueAlpha)
962               {
963                 if (opacity < 0)
964                   {
965                     opacity=i;
966                     continue;
967                   }
968                 alpha=(Quantum) TransparentAlpha-(double)
969                   image->colormap[i].alpha;
970                 beta=(Quantum) TransparentAlpha-(double)
971                   image->colormap[opacity].alpha;
972                 if (alpha < beta)
973                   opacity=i;
974               }
975         }
976       if (opacity >= 0)
977         {
978           image->colormap[opacity].red=image->transparent_color.red;
979           image->colormap[opacity].green=image->transparent_color.green;
980           image->colormap[opacity].blue=image->transparent_color.blue;
981         }
982     }
983   /*
984     Compute the character per pixel.
985   */
986   characters_per_pixel=1;
987   for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
988     characters_per_pixel++;
989   /*
990     XPM header.
991   */
992   (void) WriteBlobString(image,"/* XPM */\n");
993   GetPathComponent(image->filename,BasePath,basename);
994   if (isalnum((int) ((unsigned char) *basename)) == 0)
995     {
996       (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename);
997       (void) CopyMagickString(basename,buffer,MagickPathExtent);
998     }
999   if (isalpha((int) ((unsigned char) basename[0])) == 0)
1000     basename[0]='_';
1001   for (i=1; basename[i] != '\0'; i++)
1002     if (isalnum((int) ((unsigned char) basename[i])) == 0)
1003       basename[i]='_';
1004   (void) FormatLocaleString(buffer,MagickPathExtent,
1005     "static char *%s[] = {\n",basename);
1006   (void) WriteBlobString(image,buffer);
1007   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
1008   (void) FormatLocaleString(buffer,MagickPathExtent,
1009     "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1010     image->rows,(double) image->colors,(double) characters_per_pixel);
1011   (void) WriteBlobString(image,buffer);
1012   GetPixelInfo(image,&pixel);
1013   for (i=0; i < (ssize_t) image->colors; i++)
1014   {
1015     /*
1016       Define XPM color.
1017     */
1018     pixel=image->colormap[i];
1019     pixel.colorspace=sRGBColorspace;
1020     pixel.depth=8;
1021     pixel.alpha=(double) OpaqueAlpha;
1022     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1023     if (i == opacity)
1024       (void) CopyMagickString(name,"None",MagickPathExtent);
1025     /*
1026       Write XPM color.
1027     */
1028     k=i % MaxCixels;
1029     symbol[0]=Cixel[k];
1030     for (j=1; j < (ssize_t) characters_per_pixel; j++)
1031     {
1032       k=((i-k)/MaxCixels) % MaxCixels;
1033       symbol[j]=Cixel[k];
1034     }
1035     symbol[j]='\0';
1036     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol,
1037       name);
1038     (void) WriteBlobString(image,buffer);
1039   }
1040   /*
1041     Define XPM pixels.
1042   */
1043   (void) WriteBlobString(image,"/* pixels */\n");
1044   for (y=0; y < (ssize_t) image->rows; y++)
1045   {
1046     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1047     if (p == (const Quantum *) NULL)
1048       break;
1049     (void) WriteBlobString(image,"\"");
1050     for (x=0; x < (ssize_t) image->columns; x++)
1051     {
1052       k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1053       symbol[0]=Cixel[k];
1054       for (j=1; j < (ssize_t) characters_per_pixel; j++)
1055       {
1056         k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1057         symbol[j]=Cixel[k];
1058       }
1059       symbol[j]='\0';
1060       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
1061       (void) WriteBlobString(image,buffer);
1062       p+=GetPixelChannels(image);
1063     }
1064     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
1065       (y == (ssize_t) (image->rows-1) ? "" : ","));
1066     (void) WriteBlobString(image,buffer);
1067     if (image->previous == (Image *) NULL)
1068       {
1069         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1070           image->rows);
1071         if (status == MagickFalse)
1072           break;
1073       }
1074   }
1075   (void) WriteBlobString(image,"};\n");
1076   (void) CloseBlob(image);
1077   return(MagickTrue);
1078 }