]> 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-2011 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 *),
79   WriteXPMImage(const ImageInfo *,Image *);
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);
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   image->depth=16;
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) == MagickFalse)
346     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
347   /*
348     Read image colormap.
349   */
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->matte=MagickTrue;
380       }
381     status=QueryColorCompliance(target,AllCompliance,&image->colormap[j],
382       exception);
383     if (status == MagickFalse)
384       break;
385   }
386   if (j < (ssize_t) image->colors)
387     ThrowReaderException(CorruptImageError,"CorruptImage");
388   j=0;
389   if (image_info->ping == MagickFalse)
390     {
391       /*
392         Read image pixels.
393       */
394       for (y=0; y < (ssize_t) image->rows; y++)
395       {
396         p=NextXPMLine(p);
397         if (p == (char *) NULL)
398           break;
399         r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
400         if (r == (Quantum *) NULL)
401           break;
402         for (x=0; x < (ssize_t) image->columns; x++)
403         {
404           (void) CopyXPMColor(key,p,(size_t) width);
405           j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
406           if (image->storage_class == PseudoClass)
407             SetPixelIndex(image,j,r);
408           SetPixelPacket(image,image->colormap+j,r);
409           p+=width;
410           r+=GetPixelChannels(image);
411         }
412         if (SyncAuthenticPixels(image,exception) == MagickFalse)
413           break;
414       }
415       if (y < (ssize_t) image->rows)
416         ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
417     }
418   /*
419     Relinquish resources.
420   */
421   xpm_colors=DestroySplayTree(xpm_colors);
422   (void) CloseBlob(image);
423   return(GetFirstImageInList(image));
424 }
425 \f
426 /*
427 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 %                                                                             %
429 %                                                                             %
430 %                                                                             %
431 %   R e g i s t e r X P M I m a g e                                           %
432 %                                                                             %
433 %                                                                             %
434 %                                                                             %
435 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436 %
437 %  RegisterXPMImage() adds attributes for the XPM image format to
438 %  the list of supported formats.  The attributes include the image format
439 %  tag, a method to read and/or write the format, whether the format
440 %  supports the saving of more than one frame to the same file or blob,
441 %  whether the format supports native in-memory I/O, and a brief
442 %  description of the format.
443 %
444 %  The format of the RegisterXPMImage method is:
445 %
446 %      size_t RegisterXPMImage(void)
447 %
448 */
449 ModuleExport size_t RegisterXPMImage(void)
450 {
451   MagickInfo
452     *entry;
453
454   entry=SetMagickInfo("PICON");
455   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
456   entry->encoder=(EncodeImageHandler *) WritePICONImage;
457   entry->adjoin=MagickFalse;
458   entry->description=ConstantString("Personal Icon");
459   entry->module=ConstantString("XPM");
460   (void) RegisterMagickInfo(entry);
461   entry=SetMagickInfo("PM");
462   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
463   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
464   entry->adjoin=MagickFalse;
465   entry->stealth=MagickTrue;
466   entry->description=ConstantString("X Windows system pixmap (color)");
467   entry->module=ConstantString("XPM");
468   (void) RegisterMagickInfo(entry);
469   entry=SetMagickInfo("XPM");
470   entry->decoder=(DecodeImageHandler *) ReadXPMImage;
471   entry->encoder=(EncodeImageHandler *) WriteXPMImage;
472   entry->magick=(IsImageFormatHandler *) IsXPM;
473   entry->adjoin=MagickFalse;
474   entry->description=ConstantString("X Windows system pixmap (color)");
475   entry->module=ConstantString("XPM");
476   (void) RegisterMagickInfo(entry);
477   return(MagickImageCoderSignature);
478 }
479 \f
480 /*
481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 %                                                                             %
483 %                                                                             %
484 %                                                                             %
485 %   U n r e g i s t e r X P M I m a g e                                       %
486 %                                                                             %
487 %                                                                             %
488 %                                                                             %
489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490 %
491 %  UnregisterXPMImage() removes format registrations made by the
492 %  XPM module from the list of supported formats.
493 %
494 %  The format of the UnregisterXPMImage method is:
495 %
496 %      UnregisterXPMImage(void)
497 %
498 */
499 ModuleExport void UnregisterXPMImage(void)
500 {
501   (void) UnregisterMagickInfo("PICON");
502   (void) UnregisterMagickInfo("PM");
503   (void) UnregisterMagickInfo("XPM");
504 }
505 \f
506 /*
507 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508 %                                                                             %
509 %                                                                             %
510 %                                                                             %
511 %   W r i t e P I C O N I m a g e                                             %
512 %                                                                             %
513 %                                                                             %
514 %                                                                             %
515 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516 %
517 %  WritePICONImage() writes an image to a file in the Personal Icon format.
518 %
519 %  The format of the WritePICONImage method is:
520 %
521 %      MagickBooleanType WritePICONImage(const ImageInfo *image_info,
522 %        Image *image)
523 %
524 %  A description of each parameter follows.
525 %
526 %    o image_info: the image info.
527 %
528 %    o image:  The image.
529 %
530 */
531 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
532   Image *image)
533 {
534 #define ColormapExtent  155
535 #define GraymapExtent  95
536 #define PiconGeometry  "48x48>"
537
538   static unsigned char
539     Colormap[]=
540     {
541       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
542       0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
543       0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
544       0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
545       0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
546       0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
547       0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
548       0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
549       0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
550       0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
551       0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
552       0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
553       0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
554     },
555     Graymap[]=
556     {
557       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
558       0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
559       0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
560       0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
561       0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
562       0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
563       0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
564       0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
565     };
566
567 #define MaxCixels  92
568
569   static const char
570     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
571                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
572
573   char
574     buffer[MaxTextExtent],
575     basename[MaxTextExtent],
576     name[MaxTextExtent],
577     symbol[MaxTextExtent];
578
579   ExceptionInfo
580     *exception;
581
582   Image
583     *affinity_image,
584     *picon;
585
586   ImageInfo
587     *blob_info;
588
589   MagickBooleanType
590     status,
591     transparent;
592
593   PixelInfo
594     pixel;
595
596   QuantizeInfo
597     *quantize_info;
598
599   RectangleInfo
600     geometry;
601
602   register const Quantum
603     *p;
604
605   register ssize_t
606     i,
607     x;
608
609   register Quantum
610     *q;
611
612   size_t
613     characters_per_pixel,
614     colors;
615
616   ssize_t
617     j,
618     k,
619     y;
620
621   /*
622     Open output image file.
623   */
624   assert(image_info != (const ImageInfo *) NULL);
625   assert(image_info->signature == MagickSignature);
626   assert(image != (Image *) NULL);
627   assert(image->signature == MagickSignature);
628   if (image->debug != MagickFalse)
629     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
630   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
631   if (status == MagickFalse)
632     return(status);
633   if (IsRGBColorspace(image->colorspace) == MagickFalse)
634     (void) TransformImageColorspace(image,RGBColorspace);
635   SetGeometry(image,&geometry);
636   (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
637     &geometry.width,&geometry.height);
638   picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
639     &image->exception);
640   blob_info=CloneImageInfo(image_info);
641   (void) AcquireUniqueFilename(blob_info->filename);
642   if ((image_info->type != TrueColorType) &&
643       (IsImageGray(image,&image->exception) != MagickFalse))
644     affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,
645       &image->exception);
646   else
647     affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
648       &image->exception);
649   (void) RelinquishUniqueFileResource(blob_info->filename);
650   blob_info=DestroyImageInfo(blob_info);
651   if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
652     return(MagickFalse);
653   quantize_info=AcquireQuantizeInfo(image_info);
654   status=RemapImage(quantize_info,picon,affinity_image);
655   quantize_info=DestroyQuantizeInfo(quantize_info);
656   affinity_image=DestroyImage(affinity_image);
657   transparent=MagickFalse;
658   exception=(&image->exception);
659   if (picon->storage_class == PseudoClass)
660     {
661       (void) CompressImageColormap(picon);
662       if (picon->matte != MagickFalse)
663         transparent=MagickTrue;
664     }
665   else
666     {
667       /*
668         Convert DirectClass to PseudoClass picon.
669       */
670       if (picon->matte != MagickFalse)
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 == (const 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);
693     }
694   colors=picon->colors;
695   if (transparent != MagickFalse)
696     {
697       colors++;
698       picon->colormap=(PixelPacket *) ResizeQuantumMemory((void **)
699         picon->colormap,(size_t) colors,sizeof(*picon->colormap));
700       if (picon->colormap == (PixelPacket *) 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 == (const 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     SetPixelInfoPacket(image,picon->colormap+i,&pixel);
743     pixel.colorspace=RGBColorspace;
744     pixel.depth=8;
745     pixel.alpha=(MagickRealType) OpaqueAlpha;
746     (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
747       &image->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,&picon->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,Image *image)
822 %
823 %  A description of each parameter follows.
824 %
825 %    o image_info: the image info.
826 %
827 %    o image:  The image.
828 %
829 */
830 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
831 {
832 #define MaxCixels  92
833
834   static const char
835     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
836                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
837
838   char
839     buffer[MaxTextExtent],
840     basename[MaxTextExtent],
841     name[MaxTextExtent],
842     symbol[MaxTextExtent];
843
844   MagickBooleanType
845     status;
846
847   PixelInfo
848     pixel;
849
850   register const Quantum
851     *p;
852
853   register ssize_t
854     i,
855     x;
856
857   size_t
858     characters_per_pixel;
859
860   ssize_t
861     j,
862     k,
863     opacity,
864     y;
865
866   /*
867     Open output image file.
868   */
869   assert(image_info != (const ImageInfo *) NULL);
870   assert(image_info->signature == MagickSignature);
871   assert(image != (Image *) NULL);
872   assert(image->signature == MagickSignature);
873   if (image->debug != MagickFalse)
874     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
875   status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
876   if (status == MagickFalse)
877     return(status);
878   if (IsRGBColorspace(image->colorspace) == MagickFalse)
879     (void) TransformImageColorspace(image,RGBColorspace);
880   opacity=(-1);
881   if (image->matte == MagickFalse)
882     {
883       if ((image->storage_class == DirectClass) || (image->colors > 256))
884         (void) SetImageType(image,PaletteType);
885     }
886   else
887     {
888       MagickRealType
889         alpha,
890         beta;
891
892       /*
893         Identify transparent colormap index.
894       */
895       if ((image->storage_class == DirectClass) || (image->colors > 256))
896         (void) SetImageType(image,PaletteBilevelMatteType);
897       for (i=0; i < (ssize_t) image->colors; i++)
898         if (image->colormap[i].alpha != OpaqueAlpha)
899           {
900             if (opacity < 0)
901               {
902                 opacity=i;
903                 continue;
904               }
905             alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
906               image->colormap[i].alpha;
907             beta=(MagickRealType) TransparentAlpha-(MagickRealType)
908               image->colormap[opacity].alpha;
909             if (alpha < beta)
910               opacity=i;
911           }
912       if (opacity == -1)
913         {
914           (void) SetImageType(image,PaletteBilevelMatteType);
915           for (i=0; i < (ssize_t) image->colors; i++)
916             if (image->colormap[i].alpha != OpaqueAlpha)
917               {
918                 if (opacity < 0)
919                   {
920                     opacity=i;
921                     continue;
922                   }
923                 alpha=(Quantum) TransparentAlpha-(MagickRealType)
924                   image->colormap[i].alpha;
925                 beta=(Quantum) TransparentAlpha-(MagickRealType)
926                   image->colormap[opacity].alpha;
927                 if (alpha < beta)
928                   opacity=i;
929               }
930         }
931       if (opacity >= 0)
932         {
933           image->colormap[opacity].red=image->transparent_color.red;
934           image->colormap[opacity].green=image->transparent_color.green;
935           image->colormap[opacity].blue=image->transparent_color.blue;
936         }
937     }
938   /*
939     Compute the character per pixel.
940   */
941   characters_per_pixel=1;
942   for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
943     characters_per_pixel++;
944   /*
945     XPM header.
946   */
947   (void) WriteBlobString(image,"/* XPM */\n");
948   GetPathComponent(image->filename,BasePath,basename);
949   if (isalnum((int) ((unsigned char) *basename)) == 0)
950     {
951       (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
952       (void) CopyMagickString(basename,buffer,MaxTextExtent);
953     }
954   if (isalpha((int) ((unsigned char) basename[0])) == 0)
955     basename[0]='_';
956   for (i=1; basename[i] != '\0'; i++)
957     if (isalnum((int) ((unsigned char) basename[i])) == 0)
958       basename[i]='_';
959   (void) FormatLocaleString(buffer,MaxTextExtent,
960     "static char *%s[] = {\n",basename);
961   (void) WriteBlobString(image,buffer);
962   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
963   (void) FormatLocaleString(buffer,MaxTextExtent,
964     "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
965     image->rows,(double) image->colors,(double) characters_per_pixel);
966   (void) WriteBlobString(image,buffer);
967   GetPixelInfo(image,&pixel);
968   for (i=0; i < (ssize_t) image->colors; i++)
969   {
970     /*
971       Define XPM color.
972     */
973     SetPixelInfoPacket(image,image->colormap+i,&pixel);
974     pixel.colorspace=RGBColorspace;
975     pixel.depth=8;
976     pixel.alpha=(MagickRealType) OpaqueAlpha;
977     (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
978       &image->exception);
979     if (i == opacity)
980       (void) CopyMagickString(name,"None",MaxTextExtent);
981     /*
982       Write XPM color.
983     */
984     k=i % MaxCixels;
985     symbol[0]=Cixel[k];
986     for (j=1; j < (ssize_t) characters_per_pixel; j++)
987     {
988       k=((i-k)/MaxCixels) % MaxCixels;
989       symbol[j]=Cixel[k];
990     }
991     symbol[j]='\0';
992     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
993       name);
994     (void) WriteBlobString(image,buffer);
995   }
996   /*
997     Define XPM pixels.
998   */
999   (void) WriteBlobString(image,"/* pixels */\n");
1000   for (y=0; y < (ssize_t) image->rows; y++)
1001   {
1002     p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1003     if (p == (const Quantum *) NULL)
1004       break;
1005     (void) WriteBlobString(image,"\"");
1006     for (x=0; x < (ssize_t) image->columns; x++)
1007     {
1008       k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1009       symbol[0]=Cixel[k];
1010       for (j=1; j < (ssize_t) characters_per_pixel; j++)
1011       {
1012         k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1013         symbol[j]=Cixel[k];
1014       }
1015       symbol[j]='\0';
1016       (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1017       (void) WriteBlobString(image,buffer);
1018       p+=GetPixelChannels(image);
1019     }
1020     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1021       (y == (ssize_t) (image->rows-1) ? "" : ","));
1022     (void) WriteBlobString(image,buffer);
1023     if (image->previous == (Image *) NULL)
1024       {
1025         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1026           image->rows);
1027         if (status == MagickFalse)
1028           break;
1029       }
1030   }
1031   (void) WriteBlobString(image,"};\n");
1032   (void) CloseBlob(image);
1033   return(MagickTrue);
1034 }