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