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