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