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