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