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