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