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