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