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