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