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