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