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