]> granicus.if.org Git - imagemagick/blob - coders/xpm.c
https://github.com/ImageMagick/ImageMagick/issues/1286
[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-2018 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_colors=DestroySplayTree(xpm_colors);
370       xpm_buffer=DestroyString(xpm_buffer);
371       ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
372     }
373   /*
374     Read image colormap.
375   */
376   image->depth=1;
377   next=NextXPMLine(xpm_buffer);
378   for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
379   {
380     p=next;
381     next=NextXPMLine(p);
382     if (next == (char *) NULL)
383       break;
384     length=MagickMin((size_t) width,MagickPathExtent-1);
385     if (CopyXPMColor(key,p,length) != length)
386       break;
387     status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
388     /*
389       Parse color.
390     */
391     (void) CopyMagickString(target,"gray",MagickPathExtent);
392     q=(char *) NULL;
393     if (strlen(p) > width)
394       q=ParseXPMColor(p+width,MagickTrue);
395     if (q != (char *) NULL)
396       {
397         while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
398           q++;
399         if ((next-q) < 0)
400           break;
401         if (next != (char *) NULL)
402           (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
403             MagickPathExtent-1));
404         else
405           (void) CopyMagickString(target,q,MagickPathExtent);
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 \f
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 */
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 \f
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 */
553 ModuleExport void UnregisterXPMImage(void)
554 {
555   (void) UnregisterMagickInfo("PICON");
556   (void) UnregisterMagickInfo("PM");
557   (void) UnregisterMagickInfo("XPM");
558 }
559 \f
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 */
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       for (y=0; y < (ssize_t) picon->rows; y++)
760       {
761         q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
762         if (q == (Quantum *) NULL)
763           break;
764         for (x=0; x < (ssize_t) picon->columns; x++)
765         {
766           if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
767             SetPixelIndex(picon,(Quantum) picon->colors,q);
768           q+=GetPixelChannels(picon);
769         }
770         if (SyncAuthenticPixels(picon,exception) == MagickFalse)
771           break;
772       }
773     }
774   /*
775     Compute the character per pixel.
776   */
777   characters_per_pixel=1;
778   for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
779     characters_per_pixel++;
780   /*
781     XPM header.
782   */
783   (void) WriteBlobString(image,"/* XPM */\n");
784   GetPathComponent(picon->filename,BasePath,basename);
785   (void) FormatLocaleString(buffer,MagickPathExtent,
786     "static char *%.1024s[] = {\n",basename);
787   (void) WriteBlobString(image,buffer);
788   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
789   (void) FormatLocaleString(buffer,MagickPathExtent,
790     "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
791     picon->rows,(double) colors,(double) characters_per_pixel);
792   (void) WriteBlobString(image,buffer);
793   GetPixelInfo(image,&pixel);
794   for (i=0; i < (ssize_t) colors; i++)
795   {
796     /*
797       Define XPM color.
798     */
799     pixel=picon->colormap[i];
800     pixel.colorspace=sRGBColorspace;
801     pixel.depth=8;
802     pixel.alpha=(double) OpaqueAlpha;
803     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
804     if (transparent != MagickFalse)
805       {
806         if (i == (ssize_t) (colors-1))
807           (void) CopyMagickString(name,"grey75",MagickPathExtent);
808       }
809     /*
810       Write XPM color.
811     */
812     k=i % MaxCixels;
813     symbol[0]=Cixel[k];
814     for (j=1; j < (ssize_t) characters_per_pixel; j++)
815     {
816       k=((i-k)/MaxCixels) % MaxCixels;
817       symbol[j]=Cixel[k];
818     }
819     symbol[j]='\0';
820     (void) FormatLocaleString(buffer,MagickPathExtent,
821       "\"%.1024s c %.1024s\",\n",symbol,name);
822     (void) WriteBlobString(image,buffer);
823   }
824   /*
825     Define XPM pixels.
826   */
827   (void) WriteBlobString(image,"/* pixels */\n");
828   for (y=0; y < (ssize_t) picon->rows; y++)
829   {
830     p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
831     if (p == (const Quantum *) NULL)
832       break;
833     (void) WriteBlobString(image,"\"");
834     for (x=0; x < (ssize_t) picon->columns; x++)
835     {
836       k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
837       symbol[0]=Cixel[k];
838       for (j=1; j < (ssize_t) characters_per_pixel; j++)
839       {
840         k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
841         symbol[j]=Cixel[k];
842       }
843       symbol[j]='\0';
844       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
845       (void) WriteBlobString(image,buffer);
846       p+=GetPixelChannels(picon);
847     }
848     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%.1024s\n",
849       y == (ssize_t) (picon->rows-1) ? "" : ",");
850     (void) WriteBlobString(image,buffer);
851     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
852       picon->rows);
853     if (status == MagickFalse)
854       break;
855   }
856   picon=DestroyImage(picon);
857   (void) WriteBlobString(image,"};\n");
858   (void) CloseBlob(image);
859   return(MagickTrue);
860 }
861 \f
862 /*
863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
864 %                                                                             %
865 %                                                                             %
866 %                                                                             %
867 %   W r i t e X P M I m a g e                                                 %
868 %                                                                             %
869 %                                                                             %
870 %                                                                             %
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %
873 %  WriteXPMImage() writes an image to a file in the X pixmap format.
874 %
875 %  The format of the WriteXPMImage method is:
876 %
877 %      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
878 %        Image *image,ExceptionInfo *exception)
879 %
880 %  A description of each parameter follows.
881 %
882 %    o image_info: the image info.
883 %
884 %    o image:  The image.
885 %
886 %    o exception: return any errors or warnings in this structure.
887 %
888 */
889 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
890   ExceptionInfo *exception)
891 {
892 #define MaxCixels  92
893
894   static const char
895     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
896                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
897
898   char
899     buffer[MagickPathExtent],
900     basename[MagickPathExtent],
901     name[MagickPathExtent],
902     symbol[MagickPathExtent];
903
904   MagickBooleanType
905     status;
906
907   PixelInfo
908     pixel;
909
910   register const Quantum
911     *p;
912
913   register ssize_t
914     i,
915     x;
916
917   size_t
918     characters_per_pixel;
919
920   ssize_t
921     j,
922     k,
923     opacity,
924     y;
925
926   /*
927     Open output image file.
928   */
929   assert(image_info != (const ImageInfo *) NULL);
930   assert(image_info->signature == MagickCoreSignature);
931   assert(image != (Image *) NULL);
932   assert(image->signature == MagickCoreSignature);
933   if (image->debug != MagickFalse)
934     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
935   assert(exception != (ExceptionInfo *) NULL);
936   assert(exception->signature == MagickCoreSignature);
937   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
938   if (status == MagickFalse)
939     return(status);
940   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
941     (void) TransformImageColorspace(image,sRGBColorspace,exception);
942   opacity=(-1);
943   if (image->alpha_trait == UndefinedPixelTrait)
944     {
945       if ((image->storage_class == DirectClass) || (image->colors > 256))
946         (void) SetImageType(image,PaletteType,exception);
947     }
948   else
949     {
950       double
951         alpha,
952         beta;
953
954       /*
955         Identify transparent colormap index.
956       */
957       if ((image->storage_class == DirectClass) || (image->colors > 256))
958         (void) SetImageType(image,PaletteBilevelAlphaType,exception);
959       for (i=0; i < (ssize_t) image->colors; i++)
960         if (image->colormap[i].alpha != OpaqueAlpha)
961           {
962             if (opacity < 0)
963               {
964                 opacity=i;
965                 continue;
966               }
967             alpha=(double) TransparentAlpha-(double)
968               image->colormap[i].alpha;
969             beta=(double) TransparentAlpha-(double)
970               image->colormap[opacity].alpha;
971             if (alpha < beta)
972               opacity=i;
973           }
974       if (opacity == -1)
975         {
976           (void) SetImageType(image,PaletteBilevelAlphaType,exception);
977           for (i=0; i < (ssize_t) image->colors; i++)
978             if (image->colormap[i].alpha != OpaqueAlpha)
979               {
980                 if (opacity < 0)
981                   {
982                     opacity=i;
983                     continue;
984                   }
985                 alpha=(Quantum) TransparentAlpha-(double)
986                   image->colormap[i].alpha;
987                 beta=(Quantum) TransparentAlpha-(double)
988                   image->colormap[opacity].alpha;
989                 if (alpha < beta)
990                   opacity=i;
991               }
992         }
993       if (opacity >= 0)
994         {
995           image->colormap[opacity].red=image->transparent_color.red;
996           image->colormap[opacity].green=image->transparent_color.green;
997           image->colormap[opacity].blue=image->transparent_color.blue;
998         }
999     }
1000   /*
1001     Compute the character per pixel.
1002   */
1003   characters_per_pixel=1;
1004   for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
1005     characters_per_pixel++;
1006   /*
1007     XPM header.
1008   */
1009   (void) WriteBlobString(image,"/* XPM */\n");
1010   GetPathComponent(image->filename,BasePath,basename);
1011   if (isalnum((int) ((unsigned char) *basename)) == 0)
1012     {
1013       (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%.1024s",basename);
1014       (void) CopyMagickString(basename,buffer,MagickPathExtent);
1015     }
1016   if (isalpha((int) ((unsigned char) basename[0])) == 0)
1017     basename[0]='_';
1018   for (i=1; basename[i] != '\0'; i++)
1019     if (isalnum((int) ((unsigned char) basename[i])) == 0)
1020       basename[i]='_';
1021   (void) FormatLocaleString(buffer,MagickPathExtent,
1022     "static char *%.1024s[] = {\n",basename);
1023   (void) WriteBlobString(image,buffer);
1024   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
1025   (void) FormatLocaleString(buffer,MagickPathExtent,
1026     "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1027     image->rows,(double) image->colors,(double) characters_per_pixel);
1028   (void) WriteBlobString(image,buffer);
1029   GetPixelInfo(image,&pixel);
1030   for (i=0; i < (ssize_t) image->colors; i++)
1031   {
1032     /*
1033       Define XPM color.
1034     */
1035     pixel=image->colormap[i];
1036     pixel.colorspace=sRGBColorspace;
1037     pixel.depth=8;
1038     pixel.alpha=(double) OpaqueAlpha;
1039     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
1040     if (i == opacity)
1041       (void) CopyMagickString(name,"None",MagickPathExtent);
1042     /*
1043       Write XPM color.
1044     */
1045     k=i % MaxCixels;
1046     symbol[0]=Cixel[k];
1047     for (j=1; j < (ssize_t) characters_per_pixel; j++)
1048     {
1049       k=((i-k)/MaxCixels) % MaxCixels;
1050       symbol[j]=Cixel[k];
1051     }
1052     symbol[j]='\0';
1053     (void) FormatLocaleString(buffer,MagickPathExtent,
1054       "\"%.1024s c %.1024s\",\n",symbol,name);
1055     (void) WriteBlobString(image,buffer);
1056   }
1057   /*
1058     Define XPM pixels.
1059   */
1060   (void) WriteBlobString(image,"/* pixels */\n");
1061   for (y=0; y < (ssize_t) image->rows; y++)
1062   {
1063     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1064     if (p == (const Quantum *) NULL)
1065       break;
1066     (void) WriteBlobString(image,"\"");
1067     for (x=0; x < (ssize_t) image->columns; x++)
1068     {
1069       k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1070       symbol[0]=Cixel[k];
1071       for (j=1; j < (ssize_t) characters_per_pixel; j++)
1072       {
1073         k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1074         symbol[j]=Cixel[k];
1075       }
1076       symbol[j]='\0';
1077       (void) CopyMagickString(buffer,symbol,MagickPathExtent);
1078       (void) WriteBlobString(image,buffer);
1079       p+=GetPixelChannels(image);
1080     }
1081     (void) FormatLocaleString(buffer,MagickPathExtent,"\"%.1024s\n",
1082       (y == (ssize_t) (image->rows-1) ? "" : ","));
1083     (void) WriteBlobString(image,buffer);
1084     if (image->previous == (Image *) NULL)
1085       {
1086         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1087           image->rows);
1088         if (status == MagickFalse)
1089           break;
1090       }
1091   }
1092   (void) WriteBlobString(image,"};\n");
1093   (void) CloseBlob(image);
1094   return(MagickTrue);
1095 }