]> 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-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38 \f
39 /*
40   Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/attribute.h"
44 #include "MagickCore/blob.h"
45 #include "MagickCore/blob-private.h"
46 #include "MagickCore/cache.h"
47 #include "MagickCore/color.h"
48 #include "MagickCore/color-private.h"
49 #include "MagickCore/colormap.h"
50 #include "MagickCore/colorspace.h"
51 #include "MagickCore/colorspace-private.h"
52 #include "MagickCore/exception.h"
53 #include "MagickCore/exception-private.h"
54 #include "MagickCore/geometry.h"
55 #include "MagickCore/image.h"
56 #include "MagickCore/image-private.h"
57 #include "MagickCore/list.h"
58 #include "MagickCore/magick.h"
59 #include "MagickCore/memory_.h"
60 #include "MagickCore/monitor.h"
61 #include "MagickCore/monitor-private.h"
62 #include "MagickCore/pixel-accessor.h"
63 #include "MagickCore/quantize.h"
64 #include "MagickCore/quantum-private.h"
65 #include "MagickCore/resize.h"
66 #include "MagickCore/resource_.h"
67 #include "MagickCore/splay-tree.h"
68 #include "MagickCore/static.h"
69 #include "MagickCore/string_.h"
70 #include "MagickCore/module.h"
71 #include "MagickCore/threshold.h"
72 #include "MagickCore/utility.h"
73 \f
74 /*
75   Forward declarations.
76 */
77 static MagickBooleanType
78   WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79   WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
80 \f
81 /*
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83 %                                                                             %
84 %                                                                             %
85 %                                                                             %
86 %   I s X P M                                                                 %
87 %                                                                             %
88 %                                                                             %
89 %                                                                             %
90 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91 %
92 %  IsXPM() returns MagickTrue if the image format type, identified by the
93 %  magick string, is XPM.
94 %
95 %  The format of the IsXPM method is:
96 %
97 %      MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
98 %
99 %  A description of each parameter follows:
100 %
101 %    o magick: compare image format pattern against these bytes. or
102 %      blob.
103 %
104 %    o length: Specifies the length of the magick string.
105 %
106 */
107 static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
108 {
109   if (length < 9)
110     return(MagickFalse);
111   if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
112     return(MagickTrue);
113   return(MagickFalse);
114 }
115 \f
116 /*
117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
118 %                                                                             %
119 %                                                                             %
120 %                                                                             %
121 %   R e a d X P M I m a g e                                                   %
122 %                                                                             %
123 %                                                                             %
124 %                                                                             %
125 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
126 %
127 %  ReadXPMImage() reads an X11 pixmap image file and returns it.  It
128 %  allocates the memory necessary for the new Image structure and returns a
129 %  pointer to the new image.
130 %
131 %  The format of the ReadXPMImage method is:
132 %
133 %      Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
134 %
135 %  A description of each parameter follows:
136 %
137 %    o image_info: the image info.
138 %
139 %    o exception: return any errors or warnings in this structure.
140 %
141 */
142
143 static int CompareXPMColor(const void *target,const void *source)
144 {
145   const char
146     *p,
147     *q;
148  
149   p=(const char *) target;
150   q=(const char *) source;
151   return(strcmp(p,q));
152 }
153
154 static char *CopyXPMColor(char *destination,const char *source,size_t length)
155 {
156   while (length-- && (*source != '\0'))
157     *destination++=(*source++);
158   *destination='\0';
159   return(destination-length);
160 }
161
162 static char *NextXPMLine(char *p)
163 {
164   assert(p != (char*)NULL);
165   p=strchr(p,'\n');
166   if (p != (char *) NULL)
167     p++;
168   return(p);
169 }
170
171 static inline size_t MagickMin(const size_t x,const size_t y)
172 {
173   if (x < y)
174     return(x);
175   return(y);
176 }
177
178 static char *ParseXPMColor(char *color)
179 {
180 #define NumberTargets  6
181
182   register char
183     *p,
184     *r;
185
186   register const char
187     *q;
188
189   register ssize_t
190     i;
191
192   static const char
193     *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
194
195   for (i=0; i < NumberTargets; i++)
196   {
197     p=color;
198     for (q=targets[i]; *p != '\0'; p++)
199     {
200       if (*p == '\n')
201         break;
202       if (*p != *q)
203         continue;
204       if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
205         continue;
206       r=p;
207       for ( ; ; )
208       {
209         if (*q == '\0')
210           return(p);
211         if (*r++ != *q++)
212           break;
213       }
214       q=targets[i];
215     }
216   }
217   return((char *) NULL);
218 }
219
220 static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
221 {
222   char
223     key[MaxTextExtent],
224     target[MaxTextExtent],
225     *xpm_buffer;
226
227   Image
228     *image;
229
230   MagickBooleanType
231     active,
232     status;
233
234   register char
235     *p,
236     *q,
237     *next;
238
239   register ssize_t
240     x;
241
242   register Quantum
243     *r;
244
245   size_t
246     length;
247
248   SplayTreeInfo
249     *xpm_colors;
250
251   ssize_t
252     count,
253     j,
254     y;
255
256   unsigned long
257     colors,
258     columns,
259     rows,
260     width;
261
262   /*
263     Open image file.
264   */
265   assert(image_info != (const ImageInfo *) NULL);
266   assert(image_info->signature == MagickSignature);
267   if (image_info->debug != MagickFalse)
268     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
269       image_info->filename);
270   assert(exception != (ExceptionInfo *) NULL);
271   assert(exception->signature == MagickSignature);
272   image=AcquireImage(image_info,exception);
273   status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
274   if (status == MagickFalse)
275     {
276       image=DestroyImageList(image);
277       return((Image *) NULL);
278     }
279   /*
280     Read XPM file.
281   */
282   length=MaxTextExtent;
283   xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
284   p=xpm_buffer;
285   if (xpm_buffer != (char *) NULL)
286     while (ReadBlobString(image,p) != (char *) NULL)
287     {
288       if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
289         continue;
290       if ((*p == '}') && (*(p+1) == ';'))
291         break;
292       p+=strlen(p);
293       if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
294         continue;
295       length<<=1;
296       xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
297         sizeof(*xpm_buffer));
298       if (xpm_buffer == (char *) NULL)
299         break;
300       p=xpm_buffer+strlen(xpm_buffer);
301     }
302   if (xpm_buffer == (char *) NULL)
303     ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
304   /*
305     Remove comments.
306   */
307   count=0;
308   for (p=xpm_buffer; *p != '\0'; p++)
309   {
310     if (*p != '"')
311       continue;
312     count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
313     image->columns=columns;
314     image->rows=rows;
315     image->colors=colors;
316     if (count == 4)
317       break;
318   }
319   if ((count != 4) || (width > 10) || (image->columns == 0) ||
320       (image->rows == 0) || (image->colors == 0))
321     ThrowReaderException(CorruptImageError,"ImproperImageHeader");
322   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,exception) == 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           SetPixelInfoPixel(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,ExceptionInfo *exception)
523 %
524 %  A description of each parameter follows.
525 %
526 %    o image_info: the image info.
527 %
528 %    o image:  The image.
529 %
530 %    o exception: return any errors or warnings in this structure.
531 %
532 */
533 static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
534   Image *image,ExceptionInfo *exception)
535 {
536 #define ColormapExtent  155
537 #define GraymapExtent  95
538 #define PiconGeometry  "48x48>"
539
540   static unsigned char
541     Colormap[]=
542     {
543       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
544       0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
545       0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
546       0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
547       0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
548       0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
549       0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
550       0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
551       0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
552       0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
553       0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
554       0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
555       0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
556     },
557     Graymap[]=
558     {
559       0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
560       0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
561       0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
562       0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
563       0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
564       0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
565       0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
566       0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
567     };
568
569 #define MaxCixels  92
570
571   static const char
572     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
573                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
574
575   char
576     buffer[MaxTextExtent],
577     basename[MaxTextExtent],
578     name[MaxTextExtent],
579     symbol[MaxTextExtent];
580
581   Image
582     *affinity_image,
583     *picon;
584
585   ImageInfo
586     *blob_info;
587
588   MagickBooleanType
589     status,
590     transparent;
591
592   PixelInfo
593     pixel;
594
595   QuantizeInfo
596     *quantize_info;
597
598   RectangleInfo
599     geometry;
600
601   register const Quantum
602     *p;
603
604   register ssize_t
605     i,
606     x;
607
608   register Quantum
609     *q;
610
611   size_t
612     characters_per_pixel,
613     colors;
614
615   ssize_t
616     j,
617     k,
618     y;
619
620   /*
621     Open output image file.
622   */
623   assert(image_info != (const ImageInfo *) NULL);
624   assert(image_info->signature == MagickSignature);
625   assert(image != (Image *) NULL);
626   assert(image->signature == MagickSignature);
627   if (image->debug != MagickFalse)
628     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
629   assert(exception != (ExceptionInfo *) NULL);
630   assert(exception->signature == MagickSignature);
631   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
632   if (status == MagickFalse)
633     return(status);
634   if (IssRGBColorspace(image->colorspace) == MagickFalse)
635     (void) TransformImageColorspace(image,sRGBColorspace,exception);
636   SetGeometry(image,&geometry);
637   (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
638     &geometry.width,&geometry.height);
639   picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
640     exception);
641   blob_info=CloneImageInfo(image_info);
642   (void) AcquireUniqueFilename(blob_info->filename);
643   if ((image_info->type != TrueColorType) &&
644       (IsImageGray(image,exception) != MagickFalse))
645     affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
646   else
647     affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
648   (void) RelinquishUniqueFileResource(blob_info->filename);
649   blob_info=DestroyImageInfo(blob_info);
650   if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
651     return(MagickFalse);
652   quantize_info=AcquireQuantizeInfo(image_info);
653   status=RemapImage(quantize_info,picon,affinity_image,exception);
654   quantize_info=DestroyQuantizeInfo(quantize_info);
655   affinity_image=DestroyImage(affinity_image);
656   transparent=MagickFalse;
657   if (picon->storage_class == PseudoClass)
658     {
659       (void) CompressImageColormap(picon,exception);
660       if (picon->matte != MagickFalse)
661         transparent=MagickTrue;
662     }
663   else
664     {
665       /*
666         Convert DirectClass to PseudoClass picon.
667       */
668       if (picon->matte != MagickFalse)
669         {
670           /*
671             Map all the transparent pixels.
672           */
673           for (y=0; y < (ssize_t) picon->rows; y++)
674           {
675             q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
676             if (q == (Quantum *) NULL)
677               break;
678             for (x=0; x < (ssize_t) picon->columns; x++)
679             {
680               if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
681                 transparent=MagickTrue;
682               else
683                 SetPixelAlpha(picon,OpaqueAlpha,q);
684               q+=GetPixelChannels(picon);
685             }
686             if (SyncAuthenticPixels(picon,exception) == MagickFalse)
687               break;
688           }
689         }
690       (void) SetImageType(picon,PaletteType,exception);
691     }
692   colors=picon->colors;
693   if (transparent != MagickFalse)
694     {
695       colors++;
696       picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
697         picon->colormap,(size_t) colors,sizeof(*picon->colormap));
698       if (picon->colormap == (PixelInfo *) NULL)
699         ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
700       for (y=0; y < (ssize_t) picon->rows; y++)
701       {
702         q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
703         if (q == (Quantum *) NULL)
704           break;
705         for (x=0; x < (ssize_t) picon->columns; x++)
706         {
707           if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
708             SetPixelIndex(picon,picon->colors,q);
709           q+=GetPixelChannels(picon);
710         }
711         if (SyncAuthenticPixels(picon,exception) == MagickFalse)
712           break;
713       }
714     }
715   /*
716     Compute the character per pixel.
717   */
718   characters_per_pixel=1;
719   for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
720     characters_per_pixel++;
721   /*
722     XPM header.
723   */
724   (void) WriteBlobString(image,"/* XPM */\n");
725   GetPathComponent(picon->filename,BasePath,basename);
726   (void) FormatLocaleString(buffer,MaxTextExtent,
727     "static char *%s[] = {\n",basename);
728   (void) WriteBlobString(image,buffer);
729   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
730   (void) FormatLocaleString(buffer,MaxTextExtent,
731     "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
732     picon->rows,(double) colors,(double) characters_per_pixel);
733   (void) WriteBlobString(image,buffer);
734   GetPixelInfo(image,&pixel);
735   for (i=0; i < (ssize_t) colors; i++)
736   {
737     /*
738       Define XPM color.
739     */
740     pixel=picon->colormap[i];
741     pixel.colorspace=sRGBColorspace;
742     pixel.depth=8;
743     pixel.alpha=(MagickRealType) OpaqueAlpha;
744     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
745     if (transparent != MagickFalse)
746       {
747         if (i == (ssize_t) (colors-1))
748           (void) CopyMagickString(name,"grey75",MaxTextExtent);
749       }
750     /*
751       Write XPM color.
752     */
753     k=i % MaxCixels;
754     symbol[0]=Cixel[k];
755     for (j=1; j < (ssize_t) characters_per_pixel; j++)
756     {
757       k=((i-k)/MaxCixels) % MaxCixels;
758       symbol[j]=Cixel[k];
759     }
760     symbol[j]='\0';
761     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
762        symbol,name);
763     (void) WriteBlobString(image,buffer);
764   }
765   /*
766     Define XPM pixels.
767   */
768   (void) WriteBlobString(image,"/* pixels */\n");
769   for (y=0; y < (ssize_t) picon->rows; y++)
770   {
771     p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
772     if (p == (const Quantum *) NULL)
773       break;
774     (void) WriteBlobString(image,"\"");
775     for (x=0; x < (ssize_t) picon->columns; x++)
776     {
777       k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
778       symbol[0]=Cixel[k];
779       for (j=1; j < (ssize_t) characters_per_pixel; j++)
780       {
781         k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
782         symbol[j]=Cixel[k];
783       }
784       symbol[j]='\0';
785       (void) CopyMagickString(buffer,symbol,MaxTextExtent);
786       (void) WriteBlobString(image,buffer);
787       p+=GetPixelChannels(image);
788     }
789     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
790       y == (ssize_t) (picon->rows-1) ? "" : ",");
791     (void) WriteBlobString(image,buffer);
792     status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
793       picon->rows);
794     if (status == MagickFalse)
795       break;
796   }
797   picon=DestroyImage(picon);
798   (void) WriteBlobString(image,"};\n");
799   (void) CloseBlob(image);
800   return(MagickTrue);
801 }
802 \f
803 /*
804 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805 %                                                                             %
806 %                                                                             %
807 %                                                                             %
808 %   W r i t e X P M I m a g e                                                 %
809 %                                                                             %
810 %                                                                             %
811 %                                                                             %
812 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813 %
814 %  WriteXPMImage() writes an image to a file in the X pixmap format.
815 %
816 %  The format of the WriteXPMImage method is:
817 %
818 %      MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
819 %        Image *image,ExceptionInfo *exception)
820 %
821 %  A description of each parameter follows.
822 %
823 %    o image_info: the image info.
824 %
825 %    o image:  The image.
826 %
827 %    o exception: return any errors or warnings in this structure.
828 %
829 */
830 static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
831   ExceptionInfo *exception)
832 {
833 #define MaxCixels  92
834
835   static const char
836     Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
837                          "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
838
839   char
840     buffer[MaxTextExtent],
841     basename[MaxTextExtent],
842     name[MaxTextExtent],
843     symbol[MaxTextExtent];
844
845   MagickBooleanType
846     status;
847
848   PixelInfo
849     pixel;
850
851   register const Quantum
852     *p;
853
854   register ssize_t
855     i,
856     x;
857
858   size_t
859     characters_per_pixel;
860
861   ssize_t
862     j,
863     k,
864     opacity,
865     y;
866
867   /*
868     Open output image file.
869   */
870   assert(image_info != (const ImageInfo *) NULL);
871   assert(image_info->signature == MagickSignature);
872   assert(image != (Image *) NULL);
873   assert(image->signature == MagickSignature);
874   if (image->debug != MagickFalse)
875     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
876   assert(exception != (ExceptionInfo *) NULL);
877   assert(exception->signature == MagickSignature);
878   status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
879   if (status == MagickFalse)
880     return(status);
881   if (IssRGBColorspace(image->colorspace) == MagickFalse)
882     (void) TransformImageColorspace(image,sRGBColorspace,exception);
883   opacity=(-1);
884   if (image->matte == MagickFalse)
885     {
886       if ((image->storage_class == DirectClass) || (image->colors > 256))
887         (void) SetImageType(image,PaletteType,exception);
888     }
889   else
890     {
891       MagickRealType
892         alpha,
893         beta;
894
895       /*
896         Identify transparent colormap index.
897       */
898       if ((image->storage_class == DirectClass) || (image->colors > 256))
899         (void) SetImageType(image,PaletteBilevelMatteType,exception);
900       for (i=0; i < (ssize_t) image->colors; i++)
901         if (image->colormap[i].alpha != OpaqueAlpha)
902           {
903             if (opacity < 0)
904               {
905                 opacity=i;
906                 continue;
907               }
908             alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
909               image->colormap[i].alpha;
910             beta=(MagickRealType) TransparentAlpha-(MagickRealType)
911               image->colormap[opacity].alpha;
912             if (alpha < beta)
913               opacity=i;
914           }
915       if (opacity == -1)
916         {
917           (void) SetImageType(image,PaletteBilevelMatteType,exception);
918           for (i=0; i < (ssize_t) image->colors; i++)
919             if (image->colormap[i].alpha != OpaqueAlpha)
920               {
921                 if (opacity < 0)
922                   {
923                     opacity=i;
924                     continue;
925                   }
926                 alpha=(Quantum) TransparentAlpha-(MagickRealType)
927                   image->colormap[i].alpha;
928                 beta=(Quantum) TransparentAlpha-(MagickRealType)
929                   image->colormap[opacity].alpha;
930                 if (alpha < beta)
931                   opacity=i;
932               }
933         }
934       if (opacity >= 0)
935         {
936           image->colormap[opacity].red=image->transparent_color.red;
937           image->colormap[opacity].green=image->transparent_color.green;
938           image->colormap[opacity].blue=image->transparent_color.blue;
939         }
940     }
941   /*
942     Compute the character per pixel.
943   */
944   characters_per_pixel=1;
945   for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
946     characters_per_pixel++;
947   /*
948     XPM header.
949   */
950   (void) WriteBlobString(image,"/* XPM */\n");
951   GetPathComponent(image->filename,BasePath,basename);
952   if (isalnum((int) ((unsigned char) *basename)) == 0)
953     {
954       (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
955       (void) CopyMagickString(basename,buffer,MaxTextExtent);
956     }
957   if (isalpha((int) ((unsigned char) basename[0])) == 0)
958     basename[0]='_';
959   for (i=1; basename[i] != '\0'; i++)
960     if (isalnum((int) ((unsigned char) basename[i])) == 0)
961       basename[i]='_';
962   (void) FormatLocaleString(buffer,MaxTextExtent,
963     "static char *%s[] = {\n",basename);
964   (void) WriteBlobString(image,buffer);
965   (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
966   (void) FormatLocaleString(buffer,MaxTextExtent,
967     "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
968     image->rows,(double) image->colors,(double) characters_per_pixel);
969   (void) WriteBlobString(image,buffer);
970   GetPixelInfo(image,&pixel);
971   for (i=0; i < (ssize_t) image->colors; i++)
972   {
973     /*
974       Define XPM color.
975     */
976     pixel=image->colormap[i];
977     pixel.colorspace=sRGBColorspace;
978     pixel.depth=8;
979     pixel.alpha=(MagickRealType) OpaqueAlpha;
980     (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
981     if (i == opacity)
982       (void) CopyMagickString(name,"None",MaxTextExtent);
983     /*
984       Write XPM color.
985     */
986     k=i % MaxCixels;
987     symbol[0]=Cixel[k];
988     for (j=1; j < (ssize_t) characters_per_pixel; j++)
989     {
990       k=((i-k)/MaxCixels) % MaxCixels;
991       symbol[j]=Cixel[k];
992     }
993     symbol[j]='\0';
994     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
995       name);
996     (void) WriteBlobString(image,buffer);
997   }
998   /*
999     Define XPM pixels.
1000   */
1001   (void) WriteBlobString(image,"/* pixels */\n");
1002   for (y=0; y < (ssize_t) image->rows; y++)
1003   {
1004     p=GetVirtualPixels(image,0,y,image->columns,1,exception);
1005     if (p == (const Quantum *) NULL)
1006       break;
1007     (void) WriteBlobString(image,"\"");
1008     for (x=0; x < (ssize_t) image->columns; x++)
1009     {
1010       k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
1011       symbol[0]=Cixel[k];
1012       for (j=1; j < (ssize_t) characters_per_pixel; j++)
1013       {
1014         k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
1015         symbol[j]=Cixel[k];
1016       }
1017       symbol[j]='\0';
1018       (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1019       (void) WriteBlobString(image,buffer);
1020       p+=GetPixelChannels(image);
1021     }
1022     (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
1023       (y == (ssize_t) (image->rows-1) ? "" : ","));
1024     (void) WriteBlobString(image,buffer);
1025     if (image->previous == (Image *) NULL)
1026       {
1027         status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1028           image->rows);
1029         if (status == MagickFalse)
1030           break;
1031       }
1032   }
1033   (void) WriteBlobString(image,"};\n");
1034   (void) CloseBlob(image);
1035   return(MagickTrue);
1036 }