blob: b4fdbdc1f23ec6590ed1bb45798ae45aa2f01cc3 [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% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 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*/
cristy4c08aed2011-07-01 19:47:50 +000042#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"
cristy510d06a2011-07-06 23:43:54 +000051#include "MagickCore/colorspace-private.h"
cristy4c08aed2011-07-01 19:47:50 +000052#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"
cristy3ed852e2009-09-05 21:47:34 +000073
74/*
75 Forward declarations.
76*/
77static MagickBooleanType
cristy3a37efd2011-08-28 20:31:03 +000078 WritePICONImage(const ImageInfo *,Image *,ExceptionInfo *),
79 WriteXPMImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy3ed852e2009-09-05 21:47:34 +000080
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*/
107static 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
143static int CompareXPMColor(const void *target,const void *source)
cristy2f4741f2010-11-01 23:29:42 +0000144{
cristy3ed852e2009-09-05 21:47:34 +0000145 const char
146 *p,
147 *q;
cristy2f4741f2010-11-01 23:29:42 +0000148
cristy3ed852e2009-09-05 21:47:34 +0000149 p=(const char *) target;
150 q=(const char *) source;
151 return(strcmp(p,q));
152}
153
154static char *CopyXPMColor(char *destination,const char *source,size_t length)
155{
156 while (length-- && (*source != '\0'))
157 *destination++=(*source++);
158 *destination='\0';
159 return(destination-length);
160}
161
162static char *NextXPMLine(char *p)
163{
164 assert(p != (char*)NULL);
165 p=strchr(p,'\n');
166 if (p != (char *) NULL)
167 p++;
168 return(p);
169}
170
171static inline size_t MagickMin(const size_t x,const size_t y)
172{
173 if (x < y)
174 return(x);
175 return(y);
176}
177
178static char *ParseXPMColor(char *color)
179{
180#define NumberTargets 6
181
182 register char
183 *p,
184 *r;
185
186 register const char
187 *q;
188
cristybb503372010-05-27 20:51:26 +0000189 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000190 i;
191
192 static const char
193 *targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
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
220static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
221{
222 char
223 key[MaxTextExtent],
224 target[MaxTextExtent],
225 *xpm_buffer;
226
227 Image
228 *image;
229
cristy3ed852e2009-09-05 21:47:34 +0000230 MagickBooleanType
231 active,
232 status;
233
234 register char
235 *p,
236 *q,
237 *next;
238
cristybb503372010-05-27 20:51:26 +0000239 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000240 x;
241
cristy4c08aed2011-07-01 19:47:50 +0000242 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000243 *r;
244
245 size_t
246 length;
247
248 SplayTreeInfo
249 *xpm_colors;
250
251 ssize_t
cristyc6da28e2011-04-28 01:41:35 +0000252 count,
253 j,
254 y;
cristy3ed852e2009-09-05 21:47:34 +0000255
cristyd8af19d2010-05-29 16:59:36 +0000256 unsigned long
cristyf2faecf2010-05-28 19:19:36 +0000257 colors,
258 columns,
259 rows,
cristy3ed852e2009-09-05 21:47:34 +0000260 width;
261
262 /*
263 Open image file.
264 */
265 assert(image_info != (const ImageInfo *) NULL);
266 assert(image_info->signature == MagickSignature);
267 if (image_info->debug != MagickFalse)
268 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
269 image_info->filename);
270 assert(exception != (ExceptionInfo *) NULL);
271 assert(exception->signature == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000272 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000273 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
274 if (status == MagickFalse)
275 {
276 image=DestroyImageList(image);
277 return((Image *) NULL);
278 }
279 /*
280 Read XPM file.
281 */
282 length=MaxTextExtent;
283 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
284 p=xpm_buffer;
285 if (xpm_buffer != (char *) NULL)
286 while (ReadBlobString(image,p) != (char *) NULL)
287 {
288 if ((*p == '#') && ((p == xpm_buffer) || (*(p-1) == '\n')))
289 continue;
290 if ((*p == '}') && (*(p+1) == ';'))
291 break;
292 p+=strlen(p);
293 if ((size_t) (p-xpm_buffer+MaxTextExtent) < length)
294 continue;
295 length<<=1;
296 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
297 sizeof(*xpm_buffer));
298 if (xpm_buffer == (char *) NULL)
299 break;
300 p=xpm_buffer+strlen(xpm_buffer);
301 }
302 if (xpm_buffer == (char *) NULL)
303 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
304 /*
305 Remove comments.
306 */
307 count=0;
308 for (p=xpm_buffer; *p != '\0'; p++)
309 {
310 if (*p != '"')
311 continue;
cristyf2faecf2010-05-28 19:19:36 +0000312 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
cristyf2faecf2010-05-28 19:19:36 +0000313 image->columns=columns;
314 image->rows=rows;
315 image->colors=colors;
cristye6b6ece2010-05-29 13:25:01 +0000316 if (count == 4)
317 break;
cristy3ed852e2009-09-05 21:47:34 +0000318 }
319 if ((count != 4) || (width > 10) || (image->columns == 0) ||
320 (image->rows == 0) || (image->colors == 0))
321 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
322 image->depth=16;
323 /*
324 Remove unquoted characters.
325 */
cristy3ed852e2009-09-05 21:47:34 +0000326 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);
cristy018f07f2011-09-04 21:15:19 +0000345 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000346 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
347 /*
348 Read image colormap.
349 */
cristy3ed852e2009-09-05 21:47:34 +0000350 next=NextXPMLine(xpm_buffer);
cristybb503372010-05-27 20:51:26 +0000351 for (j=0; (j < (ssize_t) image->colors) && (next != (char*) NULL); j++)
cristy3ed852e2009-09-05 21:47:34 +0000352 {
353 p=next;
354 next=NextXPMLine(p);
355 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent));
356 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
357 /*
358 Parse color.
359 */
360 (void) CopyMagickString(target,"gray",MaxTextExtent);
361 q=ParseXPMColor(p+width);
362 if (q != (char *) NULL)
363 {
364 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
365 q++;
366 if (next != (char *) NULL)
367 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
368 MaxTextExtent));
369 else
370 (void) CopyMagickString(target,q,MaxTextExtent);
371 q=ParseXPMColor(target);
372 if (q != (char *) NULL)
373 *q='\0';
374 }
375 StripString(target);
376 if (LocaleCompare(target,"none") == 0)
377 {
378 image->storage_class=DirectClass;
379 image->matte=MagickTrue;
380 }
cristy2ea2cbf2010-08-19 22:05:54 +0000381 status=QueryColorCompliance(target,AllCompliance,&image->colormap[j],
cristya60c0202010-07-31 21:36:45 +0000382 exception);
383 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000384 break;
385 }
cristybb503372010-05-27 20:51:26 +0000386 if (j < (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +0000387 ThrowReaderException(CorruptImageError,"CorruptImage");
388 j=0;
389 if (image_info->ping == MagickFalse)
390 {
391 /*
392 Read image pixels.
393 */
cristybb503372010-05-27 20:51:26 +0000394 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000395 {
396 p=NextXPMLine(p);
397 if (p == (char *) NULL)
398 break;
399 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000400 if (r == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000401 break;
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)
cristy4c08aed2011-07-01 19:47:50 +0000407 SetPixelIndex(image,j,r);
cristy803640d2011-11-17 02:11:32 +0000408 SetPixelInfoPixel(image,image->colormap+j,r);
cristy3ed852e2009-09-05 21:47:34 +0000409 p+=width;
cristyed231572011-07-14 02:18:59 +0000410 r+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000411 }
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%
cristya60c0202010-07-31 21:36:45 +0000517% WritePICONImage() writes an image to a file in the Personal Icon format.
cristy3ed852e2009-09-05 21:47:34 +0000518%
519% The format of the WritePICONImage method is:
520%
521% MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000522% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000523%
524% A description of each parameter follows.
525%
526% o image_info: the image info.
527%
528% o image: The image.
529%
cristy3a37efd2011-08-28 20:31:03 +0000530% o exception: return any errors or warnings in this structure.
531%
cristy3ed852e2009-09-05 21:47:34 +0000532*/
533static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000534 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000535{
536#define ColormapExtent 155
537#define GraymapExtent 95
538#define PiconGeometry "48x48>"
539
540 static unsigned char
541 Colormap[]=
542 {
543 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
544 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
545 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
546 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
547 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
548 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
549 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
550 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
551 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
552 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
553 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
554 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
555 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
556 },
557 Graymap[]=
558 {
559 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
560 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
561 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
562 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
563 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
564 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
565 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
566 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
567 };
568
569#define MaxCixels 92
570
571 static const char
572 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
573 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
574
575 char
576 buffer[MaxTextExtent],
577 basename[MaxTextExtent],
578 name[MaxTextExtent],
579 symbol[MaxTextExtent];
580
cristy3ed852e2009-09-05 21:47:34 +0000581 Image
582 *affinity_image,
583 *picon;
584
585 ImageInfo
586 *blob_info;
587
cristy3ed852e2009-09-05 21:47:34 +0000588 MagickBooleanType
589 status,
590 transparent;
591
cristy4c08aed2011-07-01 19:47:50 +0000592 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000593 pixel;
594
595 QuantizeInfo
596 *quantize_info;
597
598 RectangleInfo
599 geometry;
600
cristy4c08aed2011-07-01 19:47:50 +0000601 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000602 *p;
603
cristybb503372010-05-27 20:51:26 +0000604 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000605 i,
606 x;
607
cristy4c08aed2011-07-01 19:47:50 +0000608 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000609 *q;
610
cristybb503372010-05-27 20:51:26 +0000611 size_t
cristy3ed852e2009-09-05 21:47:34 +0000612 characters_per_pixel,
613 colors;
614
cristyc6da28e2011-04-28 01:41:35 +0000615 ssize_t
616 j,
617 k,
618 y;
619
cristy3ed852e2009-09-05 21:47:34 +0000620 /*
621 Open output image file.
622 */
623 assert(image_info != (const ImageInfo *) NULL);
624 assert(image_info->signature == MagickSignature);
625 assert(image != (Image *) NULL);
626 assert(image->signature == MagickSignature);
627 if (image->debug != MagickFalse)
628 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000629 assert(exception != (ExceptionInfo *) NULL);
630 assert(exception->signature == MagickSignature);
631 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000632 if (status == MagickFalse)
633 return(status);
cristy510d06a2011-07-06 23:43:54 +0000634 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000635 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000636 SetGeometry(image,&geometry);
637 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
638 &geometry.width,&geometry.height);
639 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,1.0,
cristy3a37efd2011-08-28 20:31:03 +0000640 exception);
cristy3ed852e2009-09-05 21:47:34 +0000641 blob_info=CloneImageInfo(image_info);
642 (void) AcquireUniqueFilename(blob_info->filename);
643 if ((image_info->type != TrueColorType) &&
cristy3a37efd2011-08-28 20:31:03 +0000644 (IsImageGray(image,exception) != MagickFalse))
645 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000646 else
cristy3a37efd2011-08-28 20:31:03 +0000647 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000648 (void) RelinquishUniqueFileResource(blob_info->filename);
649 blob_info=DestroyImageInfo(blob_info);
650 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
651 return(MagickFalse);
652 quantize_info=AcquireQuantizeInfo(image_info);
cristy018f07f2011-09-04 21:15:19 +0000653 status=RemapImage(quantize_info,picon,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000654 quantize_info=DestroyQuantizeInfo(quantize_info);
655 affinity_image=DestroyImage(affinity_image);
656 transparent=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000657 if (picon->storage_class == PseudoClass)
658 {
cristy018f07f2011-09-04 21:15:19 +0000659 (void) CompressImageColormap(picon,exception);
cristy3ed852e2009-09-05 21:47:34 +0000660 if (picon->matte != MagickFalse)
661 transparent=MagickTrue;
662 }
663 else
664 {
665 /*
666 Convert DirectClass to PseudoClass picon.
667 */
668 if (picon->matte != MagickFalse)
669 {
670 /*
671 Map all the transparent pixels.
672 */
cristybb503372010-05-27 20:51:26 +0000673 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000674 {
675 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000676 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000677 break;
cristybb503372010-05-27 20:51:26 +0000678 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000679 {
cristy4c08aed2011-07-01 19:47:50 +0000680 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000681 transparent=MagickTrue;
682 else
cristy4c08aed2011-07-01 19:47:50 +0000683 SetPixelAlpha(picon,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +0000684 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000685 }
686 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
687 break;
688 }
689 }
cristy018f07f2011-09-04 21:15:19 +0000690 (void) SetImageType(picon,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000691 }
692 colors=picon->colors;
693 if (transparent != MagickFalse)
694 {
cristy3ed852e2009-09-05 21:47:34 +0000695 colors++;
cristy101ab702011-10-13 13:06:32 +0000696 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
cristy3ed852e2009-09-05 21:47:34 +0000697 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
cristy101ab702011-10-13 13:06:32 +0000698 if (picon->colormap == (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000699 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
cristybb503372010-05-27 20:51:26 +0000700 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000701 {
702 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000703 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000704 break;
cristybb503372010-05-27 20:51:26 +0000705 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000706 {
cristy4c08aed2011-07-01 19:47:50 +0000707 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
708 SetPixelIndex(picon,picon->colors,q);
cristyed231572011-07-14 02:18:59 +0000709 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000710 }
711 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
712 break;
713 }
714 }
715 /*
716 Compute the character per pixel.
717 */
718 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000719 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000720 characters_per_pixel++;
721 /*
722 XPM header.
723 */
724 (void) WriteBlobString(image,"/* XPM */\n");
725 GetPathComponent(picon->filename,BasePath,basename);
cristyb51dff52011-05-19 16:55:47 +0000726 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000727 "static char *%s[] = {\n",basename);
728 (void) WriteBlobString(image,buffer);
729 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +0000730 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000731 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
732 picon->rows,(double) colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000733 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +0000734 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000735 for (i=0; i < (ssize_t) colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000736 {
737 /*
738 Define XPM color.
739 */
cristy9d8c8ce2011-10-25 16:13:52 +0000740 pixel=picon->colormap[i];
cristy3ed852e2009-09-05 21:47:34 +0000741 pixel.colorspace=RGBColorspace;
742 pixel.depth=8;
cristy4c08aed2011-07-01 19:47:50 +0000743 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +0000744 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +0000745 if (transparent != MagickFalse)
746 {
cristybb503372010-05-27 20:51:26 +0000747 if (i == (ssize_t) (colors-1))
cristy3ed852e2009-09-05 21:47:34 +0000748 (void) CopyMagickString(name,"grey75",MaxTextExtent);
749 }
750 /*
751 Write XPM color.
752 */
753 k=i % MaxCixels;
754 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000755 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000756 {
757 k=((i-k)/MaxCixels) % MaxCixels;
758 symbol[j]=Cixel[k];
759 }
760 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +0000761 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
cristye6b6ece2010-05-29 13:25:01 +0000762 symbol,name);
cristy3ed852e2009-09-05 21:47:34 +0000763 (void) WriteBlobString(image,buffer);
764 }
765 /*
766 Define XPM pixels.
767 */
768 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +0000769 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000770 {
cristy3a37efd2011-08-28 20:31:03 +0000771 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000772 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000773 break;
cristy3ed852e2009-09-05 21:47:34 +0000774 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +0000775 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000776 {
cristy4c08aed2011-07-01 19:47:50 +0000777 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +0000778 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000779 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000780 {
cristy4c08aed2011-07-01 19:47:50 +0000781 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +0000782 symbol[j]=Cixel[k];
783 }
784 symbol[j]='\0';
785 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
786 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +0000787 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000788 }
cristyb51dff52011-05-19 16:55:47 +0000789 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +0000790 y == (ssize_t) (picon->rows-1) ? "" : ",");
cristy3ed852e2009-09-05 21:47:34 +0000791 (void) WriteBlobString(image,buffer);
cristy4cb162a2010-05-30 03:04:47 +0000792 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
793 picon->rows);
cristy3ed852e2009-09-05 21:47:34 +0000794 if (status == MagickFalse)
795 break;
796 }
797 picon=DestroyImage(picon);
798 (void) WriteBlobString(image,"};\n");
799 (void) CloseBlob(image);
800 return(MagickTrue);
801}
802
803/*
804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
805% %
806% %
807% %
808% W r i t e X P M I m a g e %
809% %
810% %
811% %
812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
813%
cristya60c0202010-07-31 21:36:45 +0000814% WriteXPMImage() writes an image to a file in the X pixmap format.
cristy3ed852e2009-09-05 21:47:34 +0000815%
816% The format of the WriteXPMImage method is:
817%
cristy3a37efd2011-08-28 20:31:03 +0000818% MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
819% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000820%
821% A description of each parameter follows.
822%
823% o image_info: the image info.
824%
825% o image: The image.
826%
cristy3a37efd2011-08-28 20:31:03 +0000827% o exception: return any errors or warnings in this structure.
828%
cristy3ed852e2009-09-05 21:47:34 +0000829*/
cristy3a37efd2011-08-28 20:31:03 +0000830static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
831 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000832{
833#define MaxCixels 92
834
835 static const char
836 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
837 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
838
839 char
840 buffer[MaxTextExtent],
841 basename[MaxTextExtent],
842 name[MaxTextExtent],
843 symbol[MaxTextExtent];
844
cristy3ed852e2009-09-05 21:47:34 +0000845 MagickBooleanType
846 status;
847
cristy4c08aed2011-07-01 19:47:50 +0000848 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000849 pixel;
850
cristy4c08aed2011-07-01 19:47:50 +0000851 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000852 *p;
853
cristybb503372010-05-27 20:51:26 +0000854 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000855 i,
856 x;
857
cristybb503372010-05-27 20:51:26 +0000858 size_t
cristy3ed852e2009-09-05 21:47:34 +0000859 characters_per_pixel;
860
cristyc6da28e2011-04-28 01:41:35 +0000861 ssize_t
862 j,
863 k,
864 opacity,
865 y;
866
cristy3ed852e2009-09-05 21:47:34 +0000867 /*
868 Open output image file.
869 */
870 assert(image_info != (const ImageInfo *) NULL);
871 assert(image_info->signature == MagickSignature);
872 assert(image != (Image *) NULL);
873 assert(image->signature == MagickSignature);
874 if (image->debug != MagickFalse)
875 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000876 assert(exception != (ExceptionInfo *) NULL);
877 assert(exception->signature == MagickSignature);
878 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000879 if (status == MagickFalse)
880 return(status);
cristy510d06a2011-07-06 23:43:54 +0000881 if (IsRGBColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000882 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000883 opacity=(-1);
884 if (image->matte == MagickFalse)
885 {
886 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000887 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000888 }
889 else
890 {
891 MagickRealType
892 alpha,
893 beta;
894
895 /*
896 Identify transparent colormap index.
897 */
898 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000899 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +0000900 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000901 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000902 {
903 if (opacity < 0)
904 {
905 opacity=i;
906 continue;
907 }
cristy4c08aed2011-07-01 19:47:50 +0000908 alpha=(MagickRealType) TransparentAlpha-(MagickRealType)
909 image->colormap[i].alpha;
910 beta=(MagickRealType) TransparentAlpha-(MagickRealType)
911 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000912 if (alpha < beta)
913 opacity=i;
914 }
915 if (opacity == -1)
916 {
cristy018f07f2011-09-04 21:15:19 +0000917 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +0000918 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000919 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000920 {
921 if (opacity < 0)
922 {
923 opacity=i;
924 continue;
925 }
cristy4c08aed2011-07-01 19:47:50 +0000926 alpha=(Quantum) TransparentAlpha-(MagickRealType)
927 image->colormap[i].alpha;
928 beta=(Quantum) TransparentAlpha-(MagickRealType)
929 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000930 if (alpha < beta)
931 opacity=i;
932 }
933 }
934 if (opacity >= 0)
935 {
936 image->colormap[opacity].red=image->transparent_color.red;
937 image->colormap[opacity].green=image->transparent_color.green;
938 image->colormap[opacity].blue=image->transparent_color.blue;
939 }
940 }
941 /*
942 Compute the character per pixel.
943 */
944 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000945 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000946 characters_per_pixel++;
947 /*
948 XPM header.
949 */
950 (void) WriteBlobString(image,"/* XPM */\n");
951 GetPathComponent(image->filename,BasePath,basename);
952 if (isalnum((int) ((unsigned char) *basename)) == 0)
953 {
cristyb51dff52011-05-19 16:55:47 +0000954 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
cristy3ed852e2009-09-05 21:47:34 +0000955 (void) CopyMagickString(basename,buffer,MaxTextExtent);
956 }
cristy2f4741f2010-11-01 23:29:42 +0000957 if (isalpha((int) ((unsigned char) basename[0])) == 0)
958 basename[0]='_';
959 for (i=1; basename[i] != '\0'; i++)
960 if (isalnum((int) ((unsigned char) basename[i])) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000961 basename[i]='_';
cristyb51dff52011-05-19 16:55:47 +0000962 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000963 "static char *%s[] = {\n",basename);
964 (void) WriteBlobString(image,buffer);
965 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +0000966 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000967 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
968 image->rows,(double) image->colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000969 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +0000970 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000971 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000972 {
973 /*
974 Define XPM color.
975 */
cristy9d8c8ce2011-10-25 16:13:52 +0000976 pixel=image->colormap[i];
cristy3ed852e2009-09-05 21:47:34 +0000977 pixel.colorspace=RGBColorspace;
978 pixel.depth=8;
cristy4c08aed2011-07-01 19:47:50 +0000979 pixel.alpha=(MagickRealType) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +0000980 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +0000981 if (i == opacity)
982 (void) CopyMagickString(name,"None",MaxTextExtent);
983 /*
984 Write XPM color.
985 */
986 k=i % MaxCixels;
987 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000988 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000989 {
990 k=((i-k)/MaxCixels) % MaxCixels;
991 symbol[j]=Cixel[k];
992 }
993 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +0000994 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
cristy3ed852e2009-09-05 21:47:34 +0000995 name);
996 (void) WriteBlobString(image,buffer);
997 }
998 /*
999 Define XPM pixels.
1000 */
1001 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +00001002 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001003 {
cristy3a37efd2011-08-28 20:31:03 +00001004 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001005 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001006 break;
cristy3ed852e2009-09-05 21:47:34 +00001007 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +00001008 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001009 {
cristy4c08aed2011-07-01 19:47:50 +00001010 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +00001011 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001012 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001013 {
cristy4c08aed2011-07-01 19:47:50 +00001014 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +00001015 symbol[j]=Cixel[k];
1016 }
1017 symbol[j]='\0';
1018 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1019 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +00001020 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001021 }
cristyb51dff52011-05-19 16:55:47 +00001022 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +00001023 (y == (ssize_t) (image->rows-1) ? "" : ","));
cristy3ed852e2009-09-05 21:47:34 +00001024 (void) WriteBlobString(image,buffer);
cristy8b27a6d2010-02-14 03:31:15 +00001025 if (image->previous == (Image *) NULL)
1026 {
cristy4cb162a2010-05-30 03:04:47 +00001027 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1028 image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00001029 if (status == MagickFalse)
1030 break;
1031 }
cristy3ed852e2009-09-05 21:47:34 +00001032 }
1033 (void) WriteBlobString(image,"};\n");
1034 (void) CloseBlob(image);
1035 return(MagickTrue);
1036}