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