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