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