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