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