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