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