blob: 99dad055cec91d456ade22bfe61d5bc505daab5c [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% July 1992 %
18% %
19% %
cristy16af1cb2009-12-11 21:38:29 +000020% Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% 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 "magick/studio.h"
43#include "magick/blob.h"
44#include "magick/blob-private.h"
45#include "magick/cache.h"
46#include "magick/color.h"
47#include "magick/color-private.h"
cristye7e40552010-04-24 21:34:22 +000048#include "magick/colormap.h"
cristy3ed852e2009-09-05 21:47:34 +000049#include "magick/colorspace.h"
50#include "magick/exception.h"
51#include "magick/exception-private.h"
52#include "magick/geometry.h"
53#include "magick/image.h"
54#include "magick/image-private.h"
55#include "magick/list.h"
56#include "magick/magick.h"
57#include "magick/memory_.h"
58#include "magick/monitor.h"
59#include "magick/monitor-private.h"
60#include "magick/pixel-private.h"
61#include "magick/quantize.h"
62#include "magick/quantum-private.h"
63#include "magick/resize.h"
64#include "magick/resource_.h"
65#include "magick/splay-tree.h"
66#include "magick/static.h"
67#include "magick/string_.h"
68#include "magick/module.h"
69#include "magick/threshold.h"
70#include "magick/utility.h"
71
72/*
73 Forward declarations.
74*/
75static MagickBooleanType
76 WritePICONImage(const ImageInfo *,Image *),
77 WriteXPMImage(const ImageInfo *,Image *);
78
79/*
80%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81% %
82% %
83% %
84% I s X P M %
85% %
86% %
87% %
88%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
89%
90% IsXPM() returns MagickTrue if the image format type, identified by the
91% magick string, is XPM.
92%
93% The format of the IsXPM method is:
94%
95% MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
96%
97% A description of each parameter follows:
98%
99% o magick: compare image format pattern against these bytes. or
100% blob.
101%
102% o length: Specifies the length of the magick string.
103%
104*/
105static MagickBooleanType IsXPM(const unsigned char *magick,const size_t length)
106{
107 if (length < 9)
108 return(MagickFalse);
109 if (LocaleNCompare((char *) magick+1,"* XPM *",7) == 0)
110 return(MagickTrue);
111 return(MagickFalse);
112}
113
114/*
115%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
116% %
117% %
118% %
119% R e a d X P M I m a g e %
120% %
121% %
122% %
123%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124%
125% ReadXPMImage() reads an X11 pixmap image file and returns it. It
126% allocates the memory necessary for the new Image structure and returns a
127% pointer to the new image.
128%
129% The format of the ReadXPMImage method is:
130%
131% Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
132%
133% A description of each parameter follows:
134%
135% o image_info: the image info.
136%
137% o exception: return any errors or warnings in this structure.
138%
139*/
140
141static int CompareXPMColor(const void *target,const void *source)
142{
143 const char
144 *p,
145 *q;
146
147 p=(const char *) target;
148 q=(const char *) source;
149 return(strcmp(p,q));
150}
151
152static char *CopyXPMColor(char *destination,const char *source,size_t length)
153{
154 while (length-- && (*source != '\0'))
155 *destination++=(*source++);
156 *destination='\0';
157 return(destination-length);
158}
159
160static char *NextXPMLine(char *p)
161{
162 assert(p != (char*)NULL);
163 p=strchr(p,'\n');
164 if (p != (char *) NULL)
165 p++;
166 return(p);
167}
168
169static inline size_t MagickMin(const size_t x,const size_t y)
170{
171 if (x < y)
172 return(x);
173 return(y);
174}
175
176static char *ParseXPMColor(char *color)
177{
178#define NumberTargets 6
179
180 register char
181 *p,
182 *r;
183
184 register const char
185 *q;
186
cristybb503372010-05-27 20:51:26 +0000187 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000188 i;
189
190 static const char
191 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
192
193 for (i=0; i < NumberTargets; i++)
194 {
195 p=color;
196 for (q=targets[i]; *p != '\0'; p++)
197 {
198 if (*p == '\n')
199 break;
200 if (*p != *q)
201 continue;
202 if (isspace((int) ((unsigned char) (*(p-1)))) == 0)
203 continue;
204 r=p;
205 for ( ; ; )
206 {
207 if (*q == '\0')
208 return(p);
209 if (*r++ != *q++)
210 break;
211 }
212 q=targets[i];
213 }
214 }
215 return((char *) NULL);
216}
217
218static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
219{
220 char
221 key[MaxTextExtent],
222 target[MaxTextExtent],
223 *xpm_buffer;
224
225 Image
226 *image;
227
cristybb503372010-05-27 20:51:26 +0000228 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000229 j,
230 y;
231
232 MagickBooleanType
233 active,
234 status;
235
236 register char
237 *p,
238 *q,
239 *next;
240
241 register IndexPacket
242 *indexes;
243
cristybb503372010-05-27 20:51:26 +0000244 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000245 i,
246 x;
247
248 register PixelPacket
249 *r;
250
251 size_t
252 length;
253
254 SplayTreeInfo
255 *xpm_colors;
256
257 ssize_t
258 count;
259
cristybb503372010-05-27 20:51:26 +0000260 size_t
cristy3ed852e2009-09-05 21:47:34 +0000261 width;
262
263 /*
264 Open image file.
265 */
266 assert(image_info != (const ImageInfo *) NULL);
267 assert(image_info->signature == MagickSignature);
268 if (image_info->debug != MagickFalse)
269 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
270 image_info->filename);
271 assert(exception != (ExceptionInfo *) NULL);
272 assert(exception->signature == MagickSignature);
273 image=AcquireImage(image_info);
274 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
275 if (status == MagickFalse)
276 {
277 image=DestroyImageList(image);
278 return((Image *) NULL);
279 }
280 /*
281 Read XPM file.
282 */
283 length=MaxTextExtent;
284 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
285 p=xpm_buffer;
286 if (xpm_buffer != (char *) NULL)
287 while (ReadBlobString(image,p) != (char *) NULL)
288 {
289 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
290 continue;
291 if ((*p == '}') && (*(p+1) == ';'))
292 break;
293 p+=strlen(p);
294 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
295 continue;
296 length<<=1;
297 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
298 sizeof(*xpm_buffer));
299 if (xpm_buffer == (char *) NULL)
300 break;
301 p=xpm_buffer+strlen(xpm_buffer);
302 }
303 if (xpm_buffer == (char *) NULL)
304 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
305 /*
306 Remove comments.
307 */
308 count=0;
309 for (p=xpm_buffer; *p != '\0'; p++)
310 {
311 if (*p != '"')
312 continue;
313 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&image->columns,&image->rows,
314 &image->colors,&width);
315 if (count == 4)
316 break;
317 }
318 if ((count != 4) || (width > 10) || (image->columns == 0) ||
319 (image->rows == 0) || (image->colors == 0))
320 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
321 image->depth=16;
322 /*
323 Remove unquoted characters.
324 */
325 i=0;
326 active=MagickFalse;
327 q=xpm_buffer;
328 while (*p != '\0')
329 {
330 if (*p++ == '"')
331 {
332 if (active != MagickFalse)
333 *q++='\n';
334 active=active != MagickFalse ? MagickFalse : MagickTrue;
335 }
336 if (active != MagickFalse)
337 *q++=(*p);
338 }
339 *q='\0';
340 /*
341 Initialize image structure.
342 */
343 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
344 (void *(*)(void *)) NULL);
345 if (AcquireImageColormap(image,image->colors) == MagickFalse)
346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
347 /*
348 Read image colormap.
349 */
350 i=1;
351 next=NextXPMLine(xpm_buffer);
cristybb503372010-05-27 20:51:26 +0000352 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
cristy3ed852e2009-09-05 21:47:34 +0000353 {
354 p=next;
355 next=NextXPMLine(p);
356 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
357 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
358 /*
359 Parse color.
360 */
361 (void) CopyMagickString(target,"gray",MaxTextExtent);
362 q=ParseXPMColor(p+width);
363 if (q != (char *) NULL)
364 {
365 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
366 q++;
367 if (next != (char *) NULL)
368 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
369 MaxTextExtent));
370 else
371 (void) CopyMagickString(target,q,MaxTextExtent);
372 q=ParseXPMColor(target);
373 if (q != (char *) NULL)
374 *q='\0';
375 }
376 StripString(target);
377 if (LocaleCompare(target,"none") == 0)
378 {
379 image->storage_class=DirectClass;
380 image->matte=MagickTrue;
381 }
382 if (QueryColorDatabase(target,&image->colormap[j],exception) == MagickFalse)
383 break;
384 }
cristybb503372010-05-27 20:51:26 +0000385 if (j < (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +0000386 ThrowReaderException(CorruptImageError,"CorruptImage");
387 j=0;
388 if (image_info->ping == MagickFalse)
389 {
390 /*
391 Read image pixels.
392 */
cristybb503372010-05-27 20:51:26 +0000393 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000394 {
395 p=NextXPMLine(p);
396 if (p == (char *) NULL)
397 break;
398 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
399 if (r == (PixelPacket *) NULL)
400 break;
401 indexes=GetAuthenticIndexQueue(image);
cristybb503372010-05-27 20:51:26 +0000402 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000403 {
404 (void) CopyXPMColor(key,p,(size_t) width);
cristybb503372010-05-27 20:51:26 +0000405 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
cristy3ed852e2009-09-05 21:47:34 +0000406 if (image->storage_class == PseudoClass)
407 indexes[x]=(IndexPacket) j;
408 *r=image->colormap[j];
409 r++;
410 p+=width;
411 }
412 if (SyncAuthenticPixels(image,exception) == MagickFalse)
413 break;
414 }
cristybb503372010-05-27 20:51:26 +0000415 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000416 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
417 }
418 /*
419 Relinquish resources.
420 */
421 xpm_colors=DestroySplayTree(xpm_colors);
422 (void) CloseBlob(image);
423 return(GetFirstImageInList(image));
424}
425
426/*
427%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428% %
429% %
430% %
431% R e g i s t e r X P M I m a g e %
432% %
433% %
434% %
435%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
436%
437% RegisterXPMImage() adds attributes for the XPM image format to
438% the list of supported formats. The attributes include the image format
439% tag, a method to read and/or write the format, whether the format
440% supports the saving of more than one frame to the same file or blob,
441% whether the format supports native in-memory I/O, and a brief
442% description of the format.
443%
444% The format of the RegisterXPMImage method is:
445%
cristybb503372010-05-27 20:51:26 +0000446% size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000447%
448*/
cristybb503372010-05-27 20:51:26 +0000449ModuleExport size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000450{
451 MagickInfo
452 *entry;
453
454 entry=SetMagickInfo("PICON");
455 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
456 entry->encoder=(EncodeImageHandler *) WritePICONImage;
457 entry->adjoin=MagickFalse;
458 entry->description=ConstantString("Personal Icon");
459 entry->module=ConstantString("XPM");
460 (void) RegisterMagickInfo(entry);
461 entry=SetMagickInfo("PM");
462 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
463 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
464 entry->adjoin=MagickFalse;
465 entry->stealth=MagickTrue;
466 entry->description=ConstantString("X Windows system pixmap (color)");
467 entry->module=ConstantString("XPM");
468 (void) RegisterMagickInfo(entry);
469 entry=SetMagickInfo("XPM");
470 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
471 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
472 entry->magick=(IsImageFormatHandler *) IsXPM;
473 entry->adjoin=MagickFalse;
474 entry->description=ConstantString("X Windows system pixmap (color)");
475 entry->module=ConstantString("XPM");
476 (void) RegisterMagickInfo(entry);
477 return(MagickImageCoderSignature);
478}
479
480/*
481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482% %
483% %
484% %
485% U n r e g i s t e r X P M I m a g e %
486% %
487% %
488% %
489%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490%
491% UnregisterXPMImage() removes format registrations made by the
492% XPM module from the list of supported formats.
493%
494% The format of the UnregisterXPMImage method is:
495%
496% UnregisterXPMImage(void)
497%
498*/
499ModuleExport void UnregisterXPMImage(void)
500{
501 (void) UnregisterMagickInfo("PICON");
502 (void) UnregisterMagickInfo("PM");
503 (void) UnregisterMagickInfo("XPM");
504}
505
506/*
507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
508% %
509% %
510% %
511% W r i t e P I C O N I m a g e %
512% %
513% %
514% %
515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516%
517% Procedure WritePICONImage() writes an image to a file in the Personal Icon
518% format.
519%
520% The format of the WritePICONImage method is:
521%
522% MagickBooleanType WritePICONImage(const ImageInfo *image_info,
523% Image *image)
524%
525% A description of each parameter follows.
526%
527% o image_info: the image info.
528%
529% o image: The image.
530%
531*/
532static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
533 Image *image)
534{
535#define ColormapExtent 155
536#define GraymapExtent 95
537#define PiconGeometry "48x48>"
538
539 static unsigned char
540 Colormap[]=
541 {
542 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
543 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
544 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
545 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
546 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
547 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
548 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
549 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
550 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
551 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
552 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
553 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
554 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
555 },
556 Graymap[]=
557 {
558 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
559 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
560 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
561 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
562 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
563 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
564 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
565 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
566 };
567
568#define MaxCixels 92
569
570 static const char
571 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
572 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
573
574 char
575 buffer[MaxTextExtent],
576 basename[MaxTextExtent],
577 name[MaxTextExtent],
578 symbol[MaxTextExtent];
579
580 ExceptionInfo
581 *exception;
582
583 Image
584 *affinity_image,
585 *picon;
586
587 ImageInfo
588 *blob_info;
589
cristybb503372010-05-27 20:51:26 +0000590 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000591 j,
592 k,
593 y;
594
595 MagickBooleanType
596 status,
597 transparent;
598
599 MagickPixelPacket
600 pixel;
601
602 QuantizeInfo
603 *quantize_info;
604
605 RectangleInfo
606 geometry;
607
608 register const IndexPacket
609 *indexes;
610
611 register const PixelPacket
612 *p;
613
cristybb503372010-05-27 20:51:26 +0000614 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000615 i,
616 x;
617
618 register PixelPacket
619 *q;
620
cristybb503372010-05-27 20:51:26 +0000621 size_t
cristy3ed852e2009-09-05 21:47:34 +0000622 characters_per_pixel,
623 colors;
624
625 /*
626 Open output image file.
627 */
628 assert(image_info != (const ImageInfo *) NULL);
629 assert(image_info->signature == MagickSignature);
630 assert(image != (Image *) NULL);
631 assert(image->signature == MagickSignature);
632 if (image->debug != MagickFalse)
633 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
634 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
635 if (status == MagickFalse)
636 return(status);
637 if (image->colorspace != RGBColorspace)
638 (void) TransformImageColorspace(image,RGBColorspace);
639 SetGeometry(image,&geometry);
640 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
641 &geometry.width,&geometry.height);
642 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
643 &image->exception);
644 blob_info=CloneImageInfo(image_info);
645 (void) AcquireUniqueFilename(blob_info->filename);
646 if ((image_info->type != TrueColorType) &&
647 (IsGrayImage(image,&image->exception) != MagickFalse))
648 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,
649 &image->exception);
650 else
651 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,
652 &image->exception);
653 (void) RelinquishUniqueFileResource(blob_info->filename);
654 blob_info=DestroyImageInfo(blob_info);
655 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
656 return(MagickFalse);
657 quantize_info=AcquireQuantizeInfo(image_info);
658 status=RemapImage(quantize_info,picon,affinity_image);
659 quantize_info=DestroyQuantizeInfo(quantize_info);
660 affinity_image=DestroyImage(affinity_image);
661 transparent=MagickFalse;
662 exception=(&image->exception);
663 if (picon->storage_class == PseudoClass)
664 {
665 CompressImageColormap(picon);
666 if (picon->matte != MagickFalse)
667 transparent=MagickTrue;
668 }
669 else
670 {
671 /*
672 Convert DirectClass to PseudoClass picon.
673 */
674 if (picon->matte != MagickFalse)
675 {
676 /*
677 Map all the transparent pixels.
678 */
cristybb503372010-05-27 20:51:26 +0000679 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000680 {
681 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
682 if (q == (PixelPacket *) NULL)
683 break;
cristybb503372010-05-27 20:51:26 +0000684 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000685 {
686 if (q->opacity == (Quantum) TransparentOpacity)
687 transparent=MagickTrue;
688 else
cristyce70c172010-01-07 17:15:30 +0000689 SetOpacityPixelComponent(q,OpaqueOpacity);
cristy3ed852e2009-09-05 21:47:34 +0000690 q++;
691 }
692 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
693 break;
694 }
695 }
696 (void) SetImageType(picon,PaletteType);
697 }
698 colors=picon->colors;
699 if (transparent != MagickFalse)
700 {
701 register IndexPacket
702 *indexes;
703
704 colors++;
705 picon->colormap=(PixelPacket *) ResizeQuantumMemory((void **)
706 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
707 if (picon->colormap == (PixelPacket *) NULL)
708 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
cristybb503372010-05-27 20:51:26 +0000709 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000710 {
711 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
712 if (q == (PixelPacket *) NULL)
713 break;
714 indexes=GetAuthenticIndexQueue(picon);
cristybb503372010-05-27 20:51:26 +0000715 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000716 {
717 if (q->opacity == (Quantum) TransparentOpacity)
718 indexes[x]=(IndexPacket) picon->colors;
719 q++;
720 }
721 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
722 break;
723 }
724 }
725 /*
726 Compute the character per pixel.
727 */
728 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000729 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000730 characters_per_pixel++;
731 /*
732 XPM header.
733 */
734 (void) WriteBlobString(image,"/* XPM */\n");
735 GetPathComponent(picon->filename,BasePath,basename);
736 (void) FormatMagickString(buffer,MaxTextExtent,
737 "static char *%s[] = {\n",basename);
738 (void) WriteBlobString(image,buffer);
739 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
740 (void) FormatMagickString(buffer,MaxTextExtent,"\"%lu %lu %lu %ld\",\n",
741 picon->columns,picon->rows,colors,characters_per_pixel);
742 (void) WriteBlobString(image,buffer);
743 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000744 for (i=0; i < (ssize_t) colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000745 {
746 /*
747 Define XPM color.
748 */
749 SetMagickPixelPacket(image,picon->colormap+i,(IndexPacket *) NULL,&pixel);
750 pixel.colorspace=RGBColorspace;
751 pixel.depth=8;
752 pixel.opacity=(MagickRealType) OpaqueOpacity;
753 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
754 &image->exception);
755 if (transparent != MagickFalse)
756 {
cristybb503372010-05-27 20:51:26 +0000757 if (i == (ssize_t) (colors-1))
cristy3ed852e2009-09-05 21:47:34 +0000758 (void) CopyMagickString(name,"grey75",MaxTextExtent);
759 }
760 /*
761 Write XPM color.
762 */
763 k=i % MaxCixels;
764 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000765 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000766 {
767 k=((i-k)/MaxCixels) % MaxCixels;
768 symbol[j]=Cixel[k];
769 }
770 symbol[j]='\0';
771 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",
772 symbol,name);
773 (void) WriteBlobString(image,buffer);
774 }
775 /*
776 Define XPM pixels.
777 */
778 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +0000779 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000780 {
781 p=GetVirtualPixels(picon,0,y,picon->columns,1,&picon->exception);
782 if (p == (const PixelPacket *) NULL)
783 break;
784 indexes=GetVirtualIndexQueue(picon);
785 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +0000786 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000787 {
cristybb503372010-05-27 20:51:26 +0000788 k=((ssize_t) indexes[x] % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +0000789 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000790 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000791 {
792 k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
793 symbol[j]=Cixel[k];
794 }
795 symbol[j]='\0';
796 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
797 (void) WriteBlobString(image,buffer);
798 }
799 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +0000800 y == (ssize_t) (picon->rows-1) ? "" : ",");
cristy3ed852e2009-09-05 21:47:34 +0000801 (void) WriteBlobString(image,buffer);
802 status=SetImageProgress(image,SaveImageTag,y,picon->rows);
803 if (status == MagickFalse)
804 break;
805 }
806 picon=DestroyImage(picon);
807 (void) WriteBlobString(image,"};\n");
808 (void) CloseBlob(image);
809 return(MagickTrue);
810}
811
812/*
813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814% %
815% %
816% %
817% W r i t e X P M I m a g e %
818% %
819% %
820% %
821%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
822%
823% Procedure WriteXPMImage() writes an image to a file in the X pixmap format.
824%
825% The format of the WriteXPMImage method is:
826%
827% MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
828%
829% A description of each parameter follows.
830%
831% o image_info: the image info.
832%
833% o image: The image.
834%
835%
836*/
837static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image)
838{
839#define MaxCixels 92
840
841 static const char
842 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
843 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
844
845 char
846 buffer[MaxTextExtent],
847 basename[MaxTextExtent],
848 name[MaxTextExtent],
849 symbol[MaxTextExtent];
850
cristybb503372010-05-27 20:51:26 +0000851 ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000852 j,
853 k,
854 opacity,
855 y;
856
857 MagickBooleanType
858 status;
859
860 MagickPixelPacket
861 pixel;
862
863 register const IndexPacket
864 *indexes;
865
866 register const PixelPacket
867 *p;
868
cristybb503372010-05-27 20:51:26 +0000869 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000870 i,
871 x;
872
cristybb503372010-05-27 20:51:26 +0000873 size_t
cristy3ed852e2009-09-05 21:47:34 +0000874 characters_per_pixel;
875
876 /*
877 Open output image file.
878 */
879 assert(image_info != (const ImageInfo *) NULL);
880 assert(image_info->signature == MagickSignature);
881 assert(image != (Image *) NULL);
882 assert(image->signature == MagickSignature);
883 if (image->debug != MagickFalse)
884 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
885 status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception);
886 if (status == MagickFalse)
887 return(status);
888 if (image->colorspace != RGBColorspace)
889 (void) TransformImageColorspace(image,RGBColorspace);
890 opacity=(-1);
891 if (image->matte == MagickFalse)
892 {
893 if ((image->storage_class == DirectClass) || (image->colors > 256))
894 (void) SetImageType(image,PaletteType);
895 }
896 else
897 {
898 MagickRealType
899 alpha,
900 beta;
901
902 /*
903 Identify transparent colormap index.
904 */
905 if ((image->storage_class == DirectClass) || (image->colors > 256))
906 (void) SetImageType(image,PaletteBilevelMatteType);
cristybb503372010-05-27 20:51:26 +0000907 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000908 if (image->colormap[i].opacity != OpaqueOpacity)
909 {
910 if (opacity < 0)
911 {
912 opacity=i;
913 continue;
914 }
915 alpha=(Quantum) TransparentOpacity-(MagickRealType)
916 image->colormap[i].opacity;
917 beta=(Quantum) TransparentOpacity-(MagickRealType)
918 image->colormap[opacity].opacity;
919 if (alpha < beta)
920 opacity=i;
921 }
922 if (opacity == -1)
923 {
924 (void) SetImageType(image,PaletteBilevelMatteType);
cristybb503372010-05-27 20:51:26 +0000925 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000926 if (image->colormap[i].opacity != OpaqueOpacity)
927 {
928 if (opacity < 0)
929 {
930 opacity=i;
931 continue;
932 }
933 alpha=(Quantum) TransparentOpacity-(MagickRealType)
934 image->colormap[i].opacity;
935 beta=(Quantum) TransparentOpacity-(MagickRealType)
936 image->colormap[opacity].opacity;
937 if (alpha < beta)
938 opacity=i;
939 }
940 }
941 if (opacity >= 0)
942 {
943 image->colormap[opacity].red=image->transparent_color.red;
944 image->colormap[opacity].green=image->transparent_color.green;
945 image->colormap[opacity].blue=image->transparent_color.blue;
946 }
947 }
948 /*
949 Compute the character per pixel.
950 */
951 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000952 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000953 characters_per_pixel++;
954 /*
955 XPM header.
956 */
957 (void) WriteBlobString(image,"/* XPM */\n");
958 GetPathComponent(image->filename,BasePath,basename);
959 if (isalnum((int) ((unsigned char) *basename)) == 0)
960 {
961 (void) FormatMagickString(buffer,MaxTextExtent,"xpm_%s",basename);
962 (void) CopyMagickString(basename,buffer,MaxTextExtent);
963 }
964 for (i=0; basename[i] != '\0'; i++)
965 if (isalpha((int) ((unsigned char) basename[i])) == 0)
966 basename[i]='_';
967 (void) FormatMagickString(buffer,MaxTextExtent,
968 "static char *%s[] = {\n",basename);
969 (void) WriteBlobString(image,buffer);
970 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
971 (void) FormatMagickString(buffer,MaxTextExtent,"\"%lu %lu %lu %ld\",\n",
972 image->columns,image->rows,image->colors,characters_per_pixel);
973 (void) WriteBlobString(image,buffer);
974 GetMagickPixelPacket(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000975 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000976 {
977 /*
978 Define XPM color.
979 */
980 SetMagickPixelPacket(image,image->colormap+i,(IndexPacket *) NULL,&pixel);
981 pixel.colorspace=RGBColorspace;
982 pixel.depth=8;
983 pixel.opacity=(MagickRealType) OpaqueOpacity;
984 (void) QueryMagickColorname(image,&pixel,XPMCompliance,name,
985 &image->exception);
986 if (i == opacity)
987 (void) CopyMagickString(name,"None",MaxTextExtent);
988 /*
989 Write XPM color.
990 */
991 k=i % MaxCixels;
992 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000993 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000994 {
995 k=((i-k)/MaxCixels) % MaxCixels;
996 symbol[j]=Cixel[k];
997 }
998 symbol[j]='\0';
999 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
1000 name);
1001 (void) WriteBlobString(image,buffer);
1002 }
1003 /*
1004 Define XPM pixels.
1005 */
1006 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +00001007 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001008 {
1009 p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
1010 if (p == (const PixelPacket *) NULL)
1011 break;
1012 indexes=GetVirtualIndexQueue(image);
1013 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +00001014 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001015 {
cristybb503372010-05-27 20:51:26 +00001016 k=((ssize_t) indexes[x] % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +00001017 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001018 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001019 {
1020 k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels;
1021 symbol[j]=Cixel[k];
1022 }
1023 symbol[j]='\0';
1024 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1025 (void) WriteBlobString(image,buffer);
1026 }
1027 (void) FormatMagickString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +00001028 (y == (ssize_t) (image->rows-1) ? "" : ","));
cristy3ed852e2009-09-05 21:47:34 +00001029 (void) WriteBlobString(image,buffer);
cristy8b27a6d2010-02-14 03:31:15 +00001030 if (image->previous == (Image *) NULL)
1031 {
1032 status=SetImageProgress(image,SaveImageTag,y,image->rows);
1033 if (status == MagickFalse)
1034 break;
1035 }
cristy3ed852e2009-09-05 21:47:34 +00001036 }
1037 (void) WriteBlobString(image,"};\n");
1038 (void) CloseBlob(image);
1039 return(MagickTrue);
1040}