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