blob: 29eb8810577119736eef3934687f7d2d8e5de49f [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristy3ed852e2009-09-05 21:47:34 +000017% July 1992 %
18% %
19% %
cristyb56bb242014-11-25 17:12:48 +000020% Copyright 1999-2015 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
cristy5463e2e2014-12-31 12:06:30 +0000154static ssize_t CopyXPMColor(char *destination,const char *source,size_t length)
cristy3ed852e2009-09-05 21:47:34 +0000155{
cristybcbe6c82014-12-01 11:54:39 +0000156 register const char
cristydd12db52014-11-30 21:54:11 +0000157 *p;
158
159 p=source;
cristy26f64582014-12-27 20:04:16 +0000160 while (length-- && (*p != '\0'))
cristydd12db52014-11-30 21:54:11 +0000161 *destination++=(*p++);
cristy6f1d4192014-12-28 13:59:39 +0000162 if (length != 0)
163 *destination='\0';
cristy5463e2e2014-12-31 12:06:30 +0000164 return((ssize_t) (p-source));
cristy3ed852e2009-09-05 21:47:34 +0000165}
166
167static char *NextXPMLine(char *p)
168{
cristyf432c632014-12-07 15:11:28 +0000169 assert(p != (char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000170 p=strchr(p,'\n');
171 if (p != (char *) NULL)
172 p++;
173 return(p);
174}
175
dirk785a7232014-10-20 19:50:07 +0000176static char *ParseXPMColor(char *color,MagickBooleanType search_start)
cristy3ed852e2009-09-05 21:47:34 +0000177{
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
dirk785a7232014-10-20 19:50:07 +0000193 if (search_start != MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000194 {
dirk785a7232014-10-20 19:50:07 +0000195 for (i=0; i < NumberTargets; i++)
cristy3ed852e2009-09-05 21:47:34 +0000196 {
dirk785a7232014-10-20 19:50:07 +0000197 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 }
cristy3ed852e2009-09-05 21:47:34 +0000216 }
dirk785a7232014-10-20 19:50:07 +0000217 return((char *) NULL);
cristy3ed852e2009-09-05 21:47:34 +0000218 }
cristy76d045a2014-12-02 02:12:04 +0000219 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++)
dirk785a7232014-10-20 19:50:07 +0000228 {
cristy76d045a2014-12-02 02:12:04 +0000229 if ((*p == *targets[i]) && (*(p+1) == *(targets[i]+1)))
230 return(p);
dirk785a7232014-10-20 19:50:07 +0000231 }
cristy76d045a2014-12-02 02:12:04 +0000232 }
233 return(p);
cristy3ed852e2009-09-05 21:47:34 +0000234}
235
236static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception)
237{
238 char
dirk785a7232014-10-20 19:50:07 +0000239 *grey,
cristy3ed852e2009-09-05 21:47:34 +0000240 key[MaxTextExtent],
241 target[MaxTextExtent],
242 *xpm_buffer;
243
244 Image
245 *image;
246
cristy3ed852e2009-09-05 21:47:34 +0000247 MagickBooleanType
248 active,
249 status;
250
251 register char
dirk785a7232014-10-20 19:50:07 +0000252 *next,
cristy3ed852e2009-09-05 21:47:34 +0000253 *p,
dirk785a7232014-10-20 19:50:07 +0000254 *q;
cristy3ed852e2009-09-05 21:47:34 +0000255
cristybb503372010-05-27 20:51:26 +0000256 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000257 x;
258
cristy4c08aed2011-07-01 19:47:50 +0000259 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000260 *r;
261
262 size_t
263 length;
264
265 SplayTreeInfo
266 *xpm_colors;
267
268 ssize_t
cristyc6da28e2011-04-28 01:41:35 +0000269 count,
270 j,
271 y;
cristy3ed852e2009-09-05 21:47:34 +0000272
cristyd8af19d2010-05-29 16:59:36 +0000273 unsigned long
cristyf2faecf2010-05-28 19:19:36 +0000274 colors,
275 columns,
276 rows,
cristy3ed852e2009-09-05 21:47:34 +0000277 width;
278
279 /*
280 Open image file.
281 */
282 assert(image_info != (const ImageInfo *) NULL);
283 assert(image_info->signature == MagickSignature);
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 == MagickSignature);
cristy9950d572011-10-01 18:22:35 +0000289 image=AcquireImage(image_info,exception);
cristy3ed852e2009-09-05 21:47:34 +0000290 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=MaxTextExtent;
300 xpm_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*xpm_buffer));
cristydd12db52014-11-30 21:54:11 +0000301 if (xpm_buffer == (char *) NULL)
302 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
303 *xpm_buffer='\0';
cristy3ed852e2009-09-05 21:47:34 +0000304 p=xpm_buffer;
cristydd12db52014-11-30 21:54:11 +0000305 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+MaxTextExtent) < length)
313 continue;
314 length<<=1;
315 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MaxTextExtent,
316 sizeof(*xpm_buffer));
317 if (xpm_buffer == (char *) NULL)
318 break;
319 p=xpm_buffer+strlen(xpm_buffer);
320 }
cristy3ed852e2009-09-05 21:47:34 +0000321 if (xpm_buffer == (char *) NULL)
322 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
323 /*
324 Remove comments.
325 */
326 count=0;
dirk93b02b72013-11-16 16:03:36 +0000327 width=0;
cristy3ed852e2009-09-05 21:47:34 +0000328 for (p=xpm_buffer; *p != '\0'; p++)
329 {
330 if (*p != '"')
331 continue;
cristyf2faecf2010-05-28 19:19:36 +0000332 count=(ssize_t) sscanf(p+1,"%lu %lu %lu %lu",&columns,&rows,&colors,&width);
cristyf2faecf2010-05-28 19:19:36 +0000333 image->columns=columns;
334 image->rows=rows;
335 image->colors=colors;
cristye6b6ece2010-05-29 13:25:01 +0000336 if (count == 4)
337 break;
cristy3ed852e2009-09-05 21:47:34 +0000338 }
339 if ((count != 4) || (width > 10) || (image->columns == 0) ||
340 (image->rows == 0) || (image->colors == 0))
341 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
cristy3ed852e2009-09-05 21:47:34 +0000342 /*
343 Remove unquoted characters.
344 */
cristy3ed852e2009-09-05 21:47:34 +0000345 active=MagickFalse;
346 q=xpm_buffer;
347 while (*p != '\0')
348 {
349 if (*p++ == '"')
350 {
351 if (active != MagickFalse)
352 *q++='\n';
353 active=active != MagickFalse ? MagickFalse : MagickTrue;
354 }
355 if (active != MagickFalse)
356 *q++=(*p);
357 }
358 *q='\0';
359 /*
360 Initialize image structure.
361 */
362 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
363 (void *(*)(void *)) NULL);
cristy018f07f2011-09-04 21:15:19 +0000364 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000365 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
366 /*
367 Read image colormap.
368 */
cristy1d92f792013-09-09 13:04:57 +0000369 image->depth=1;
cristy3ed852e2009-09-05 21:47:34 +0000370 next=NextXPMLine(xpm_buffer);
cristy76d045a2014-12-02 02:12:04 +0000371 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
cristy3ed852e2009-09-05 21:47:34 +0000372 {
373 p=next;
374 next=NextXPMLine(p);
cristy6f1d4192014-12-28 13:59:39 +0000375 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000376 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
377 /*
378 Parse color.
379 */
380 (void) CopyMagickString(target,"gray",MaxTextExtent);
dirk785a7232014-10-20 19:50:07 +0000381 q=ParseXPMColor(p+width,MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000382 if (q != (char *) NULL)
383 {
384 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
385 q++;
cristyf432c632014-12-07 15:11:28 +0000386 if ((next-q) < 0)
387 break;
cristy3ed852e2009-09-05 21:47:34 +0000388 if (next != (char *) NULL)
389 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
cristy6f1d4192014-12-28 13:59:39 +0000390 MaxTextExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000391 else
392 (void) CopyMagickString(target,q,MaxTextExtent);
dirk785a7232014-10-20 19:50:07 +0000393 q=ParseXPMColor(target,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000394 if (q != (char *) NULL)
395 *q='\0';
396 }
397 StripString(target);
dirk785a7232014-10-20 19:50:07 +0000398 grey=strstr(target,"grey");
399 if (grey != (char *) NULL)
dirk8695d952014-10-22 05:01:06 +0000400 grey[2]='a';
cristy3ed852e2009-09-05 21:47:34 +0000401 if (LocaleCompare(target,"none") == 0)
402 {
403 image->storage_class=DirectClass;
cristy8a46d822012-08-28 23:32:39 +0000404 image->alpha_trait=BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000405 }
cristyfc502a12013-04-09 16:52:51 +0000406 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
cristya60c0202010-07-31 21:36:45 +0000407 exception);
408 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000409 break;
cristy1d92f792013-09-09 13:04:57 +0000410 if (image->depth < image->colormap[j].depth)
411 image->depth=image->colormap[j].depth;
cristy3ed852e2009-09-05 21:47:34 +0000412 }
cristybb503372010-05-27 20:51:26 +0000413 if (j < (ssize_t) image->colors)
cristy3ed852e2009-09-05 21:47:34 +0000414 ThrowReaderException(CorruptImageError,"CorruptImage");
415 j=0;
416 if (image_info->ping == MagickFalse)
417 {
418 /*
419 Read image pixels.
420 */
cristyacabb842014-12-14 23:36:33 +0000421 status=SetImageExtent(image,image->columns,image->rows,exception);
422 if (status == MagickFalse)
423 return(DestroyImageList(image));
cristybb503372010-05-27 20:51:26 +0000424 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000425 {
426 p=NextXPMLine(p);
427 if (p == (char *) NULL)
428 break;
429 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000430 if (r == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000431 break;
cristybb503372010-05-27 20:51:26 +0000432 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000433 {
cristy5463e2e2014-12-31 12:06:30 +0000434 ssize_t count=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent-1));
435 if (count != (ssize_t) width)
436 break;
cristybb503372010-05-27 20:51:26 +0000437 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
cristy3ed852e2009-09-05 21:47:34 +0000438 if (image->storage_class == PseudoClass)
cristydd12db52014-11-30 21:54:11 +0000439 SetPixelIndex(image,(Quantum) j,r);
cristy11a06d32015-01-04 12:03:27 +0000440 SetPixelViaPixelInfo(image,image->colormap+j,r);
cristy5463e2e2014-12-31 12:06:30 +0000441 p+=count;
cristyed231572011-07-14 02:18:59 +0000442 r+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000443 }
cristy5463e2e2014-12-31 12:06:30 +0000444 if (x < (ssize_t) image->columns)
445 break;
cristy3ed852e2009-09-05 21:47:34 +0000446 if (SyncAuthenticPixels(image,exception) == MagickFalse)
447 break;
448 }
cristybb503372010-05-27 20:51:26 +0000449 if (y < (ssize_t) image->rows)
cristy3ed852e2009-09-05 21:47:34 +0000450 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
451 }
452 /*
453 Relinquish resources.
454 */
455 xpm_colors=DestroySplayTree(xpm_colors);
456 (void) CloseBlob(image);
457 return(GetFirstImageInList(image));
458}
459
460/*
461%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462% %
463% %
464% %
465% R e g i s t e r X P M I m a g e %
466% %
467% %
468% %
469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470%
471% RegisterXPMImage() adds attributes for the XPM image format to
472% the list of supported formats. The attributes include the image format
473% tag, a method to read and/or write the format, whether the format
474% supports the saving of more than one frame to the same file or blob,
475% whether the format supports native in-memory I/O, and a brief
476% description of the format.
477%
478% The format of the RegisterXPMImage method is:
479%
cristybb503372010-05-27 20:51:26 +0000480% size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000481%
482*/
cristybb503372010-05-27 20:51:26 +0000483ModuleExport size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000484{
485 MagickInfo
486 *entry;
487
488 entry=SetMagickInfo("PICON");
489 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
490 entry->encoder=(EncodeImageHandler *) WritePICONImage;
491 entry->adjoin=MagickFalse;
492 entry->description=ConstantString("Personal Icon");
493 entry->module=ConstantString("XPM");
494 (void) RegisterMagickInfo(entry);
495 entry=SetMagickInfo("PM");
496 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
497 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
498 entry->adjoin=MagickFalse;
499 entry->stealth=MagickTrue;
500 entry->description=ConstantString("X Windows system pixmap (color)");
501 entry->module=ConstantString("XPM");
502 (void) RegisterMagickInfo(entry);
503 entry=SetMagickInfo("XPM");
504 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
505 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
506 entry->magick=(IsImageFormatHandler *) IsXPM;
507 entry->adjoin=MagickFalse;
508 entry->description=ConstantString("X Windows system pixmap (color)");
509 entry->module=ConstantString("XPM");
510 (void) RegisterMagickInfo(entry);
511 return(MagickImageCoderSignature);
512}
513
514/*
515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
516% %
517% %
518% %
519% U n r e g i s t e r X P M I m a g e %
520% %
521% %
522% %
523%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524%
525% UnregisterXPMImage() removes format registrations made by the
526% XPM module from the list of supported formats.
527%
528% The format of the UnregisterXPMImage method is:
529%
530% UnregisterXPMImage(void)
531%
532*/
533ModuleExport void UnregisterXPMImage(void)
534{
535 (void) UnregisterMagickInfo("PICON");
536 (void) UnregisterMagickInfo("PM");
537 (void) UnregisterMagickInfo("XPM");
538}
539
540/*
541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542% %
543% %
544% %
545% W r i t e P I C O N I m a g e %
546% %
547% %
548% %
549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550%
cristya60c0202010-07-31 21:36:45 +0000551% WritePICONImage() writes an image to a file in the Personal Icon format.
cristy3ed852e2009-09-05 21:47:34 +0000552%
553% The format of the WritePICONImage method is:
554%
555% MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000556% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000557%
558% A description of each parameter follows.
559%
560% o image_info: the image info.
561%
562% o image: The image.
563%
cristy3a37efd2011-08-28 20:31:03 +0000564% o exception: return any errors or warnings in this structure.
565%
cristy3ed852e2009-09-05 21:47:34 +0000566*/
567static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000568 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000569{
570#define ColormapExtent 155
571#define GraymapExtent 95
572#define PiconGeometry "48x48>"
573
574 static unsigned char
575 Colormap[]=
576 {
577 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
578 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
579 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
580 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
581 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
582 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
583 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
584 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
585 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
586 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
587 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
588 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
589 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
590 },
591 Graymap[]=
592 {
593 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
594 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
595 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
596 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
597 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
598 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
599 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
600 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
601 };
602
603#define MaxCixels 92
604
605 static const char
606 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
607 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
608
609 char
610 buffer[MaxTextExtent],
611 basename[MaxTextExtent],
612 name[MaxTextExtent],
613 symbol[MaxTextExtent];
614
cristy3ed852e2009-09-05 21:47:34 +0000615 Image
616 *affinity_image,
617 *picon;
618
619 ImageInfo
620 *blob_info;
621
cristy3ed852e2009-09-05 21:47:34 +0000622 MagickBooleanType
623 status,
624 transparent;
625
cristy4c08aed2011-07-01 19:47:50 +0000626 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000627 pixel;
628
629 QuantizeInfo
630 *quantize_info;
631
632 RectangleInfo
633 geometry;
634
cristy4c08aed2011-07-01 19:47:50 +0000635 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000636 *p;
637
cristybb503372010-05-27 20:51:26 +0000638 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000639 i,
640 x;
641
cristy4c08aed2011-07-01 19:47:50 +0000642 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000643 *q;
644
cristybb503372010-05-27 20:51:26 +0000645 size_t
cristy3ed852e2009-09-05 21:47:34 +0000646 characters_per_pixel,
647 colors;
648
cristyc6da28e2011-04-28 01:41:35 +0000649 ssize_t
650 j,
651 k,
652 y;
653
cristy3ed852e2009-09-05 21:47:34 +0000654 /*
655 Open output image file.
656 */
657 assert(image_info != (const ImageInfo *) NULL);
658 assert(image_info->signature == MagickSignature);
659 assert(image != (Image *) NULL);
660 assert(image->signature == MagickSignature);
661 if (image->debug != MagickFalse)
662 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000663 assert(exception != (ExceptionInfo *) NULL);
664 assert(exception->signature == MagickSignature);
665 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000666 if (status == MagickFalse)
667 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000668 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000669 SetGeometry(image,&geometry);
670 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
671 &geometry.width,&geometry.height);
cristyaa2c16c2012-03-25 22:21:35 +0000672 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
cristy3a37efd2011-08-28 20:31:03 +0000673 exception);
cristy3ed852e2009-09-05 21:47:34 +0000674 blob_info=CloneImageInfo(image_info);
675 (void) AcquireUniqueFilename(blob_info->filename);
676 if ((image_info->type != TrueColorType) &&
cristy3a37efd2011-08-28 20:31:03 +0000677 (IsImageGray(image,exception) != MagickFalse))
678 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000679 else
cristy3a37efd2011-08-28 20:31:03 +0000680 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000681 (void) RelinquishUniqueFileResource(blob_info->filename);
682 blob_info=DestroyImageInfo(blob_info);
683 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
684 return(MagickFalse);
685 quantize_info=AcquireQuantizeInfo(image_info);
cristy018f07f2011-09-04 21:15:19 +0000686 status=RemapImage(quantize_info,picon,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000687 quantize_info=DestroyQuantizeInfo(quantize_info);
688 affinity_image=DestroyImage(affinity_image);
689 transparent=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000690 if (picon->storage_class == PseudoClass)
691 {
cristy018f07f2011-09-04 21:15:19 +0000692 (void) CompressImageColormap(picon,exception);
cristy17f11b02014-12-20 19:37:04 +0000693 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000694 transparent=MagickTrue;
695 }
696 else
697 {
698 /*
699 Convert DirectClass to PseudoClass picon.
700 */
cristy17f11b02014-12-20 19:37:04 +0000701 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000702 {
703 /*
704 Map all the transparent pixels.
705 */
cristybb503372010-05-27 20:51:26 +0000706 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000707 {
708 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000709 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000710 break;
cristybb503372010-05-27 20:51:26 +0000711 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000712 {
cristy4c08aed2011-07-01 19:47:50 +0000713 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000714 transparent=MagickTrue;
715 else
cristy4c08aed2011-07-01 19:47:50 +0000716 SetPixelAlpha(picon,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +0000717 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000718 }
719 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
720 break;
721 }
722 }
cristy018f07f2011-09-04 21:15:19 +0000723 (void) SetImageType(picon,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000724 }
725 colors=picon->colors;
726 if (transparent != MagickFalse)
727 {
cristy3ed852e2009-09-05 21:47:34 +0000728 colors++;
cristy101ab702011-10-13 13:06:32 +0000729 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
cristy3ed852e2009-09-05 21:47:34 +0000730 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
cristy101ab702011-10-13 13:06:32 +0000731 if (picon->colormap == (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000732 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
cristybb503372010-05-27 20:51:26 +0000733 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000734 {
735 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000736 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000737 break;
cristybb503372010-05-27 20:51:26 +0000738 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000739 {
cristy4c08aed2011-07-01 19:47:50 +0000740 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristydd12db52014-11-30 21:54:11 +0000741 SetPixelIndex(picon,(Quantum) picon->colors,q);
cristyed231572011-07-14 02:18:59 +0000742 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000743 }
744 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
745 break;
746 }
747 }
748 /*
749 Compute the character per pixel.
750 */
751 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000752 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000753 characters_per_pixel++;
754 /*
755 XPM header.
756 */
757 (void) WriteBlobString(image,"/* XPM */\n");
758 GetPathComponent(picon->filename,BasePath,basename);
cristyb51dff52011-05-19 16:55:47 +0000759 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000760 "static char *%s[] = {\n",basename);
761 (void) WriteBlobString(image,buffer);
762 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +0000763 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000764 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
765 picon->rows,(double) colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000766 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +0000767 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000768 for (i=0; i < (ssize_t) colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000769 {
770 /*
771 Define XPM color.
772 */
cristy9d8c8ce2011-10-25 16:13:52 +0000773 pixel=picon->colormap[i];
cristy7020ae62012-04-18 12:58:34 +0000774 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000775 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +0000776 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +0000777 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +0000778 if (transparent != MagickFalse)
779 {
cristybb503372010-05-27 20:51:26 +0000780 if (i == (ssize_t) (colors-1))
cristy3ed852e2009-09-05 21:47:34 +0000781 (void) CopyMagickString(name,"grey75",MaxTextExtent);
782 }
783 /*
784 Write XPM color.
785 */
786 k=i % MaxCixels;
787 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000788 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000789 {
790 k=((i-k)/MaxCixels) % MaxCixels;
791 symbol[j]=Cixel[k];
792 }
793 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +0000794 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
cristye6b6ece2010-05-29 13:25:01 +0000795 symbol,name);
cristy3ed852e2009-09-05 21:47:34 +0000796 (void) WriteBlobString(image,buffer);
797 }
798 /*
799 Define XPM pixels.
800 */
801 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +0000802 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000803 {
cristy3a37efd2011-08-28 20:31:03 +0000804 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000805 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000806 break;
cristy3ed852e2009-09-05 21:47:34 +0000807 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +0000808 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000809 {
cristy4c08aed2011-07-01 19:47:50 +0000810 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +0000811 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000812 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000813 {
cristy4c08aed2011-07-01 19:47:50 +0000814 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +0000815 symbol[j]=Cixel[k];
816 }
817 symbol[j]='\0';
818 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
819 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +0000820 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000821 }
cristyb51dff52011-05-19 16:55:47 +0000822 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +0000823 y == (ssize_t) (picon->rows-1) ? "" : ",");
cristy3ed852e2009-09-05 21:47:34 +0000824 (void) WriteBlobString(image,buffer);
cristy4cb162a2010-05-30 03:04:47 +0000825 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
826 picon->rows);
cristy3ed852e2009-09-05 21:47:34 +0000827 if (status == MagickFalse)
828 break;
829 }
830 picon=DestroyImage(picon);
831 (void) WriteBlobString(image,"};\n");
832 (void) CloseBlob(image);
833 return(MagickTrue);
834}
835
836/*
837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838% %
839% %
840% %
841% W r i t e X P M I m a g e %
842% %
843% %
844% %
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846%
cristya60c0202010-07-31 21:36:45 +0000847% WriteXPMImage() writes an image to a file in the X pixmap format.
cristy3ed852e2009-09-05 21:47:34 +0000848%
849% The format of the WriteXPMImage method is:
850%
cristy3a37efd2011-08-28 20:31:03 +0000851% MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
852% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000853%
854% A description of each parameter follows.
855%
856% o image_info: the image info.
857%
858% o image: The image.
859%
cristy3a37efd2011-08-28 20:31:03 +0000860% o exception: return any errors or warnings in this structure.
861%
cristy3ed852e2009-09-05 21:47:34 +0000862*/
cristy3a37efd2011-08-28 20:31:03 +0000863static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
864 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000865{
866#define MaxCixels 92
867
868 static const char
869 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
870 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
871
872 char
873 buffer[MaxTextExtent],
874 basename[MaxTextExtent],
875 name[MaxTextExtent],
876 symbol[MaxTextExtent];
877
cristy3ed852e2009-09-05 21:47:34 +0000878 MagickBooleanType
879 status;
880
cristy4c08aed2011-07-01 19:47:50 +0000881 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000882 pixel;
883
cristy4c08aed2011-07-01 19:47:50 +0000884 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000885 *p;
886
cristybb503372010-05-27 20:51:26 +0000887 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000888 i,
889 x;
890
cristybb503372010-05-27 20:51:26 +0000891 size_t
cristy3ed852e2009-09-05 21:47:34 +0000892 characters_per_pixel;
893
cristyc6da28e2011-04-28 01:41:35 +0000894 ssize_t
895 j,
896 k,
897 opacity,
898 y;
899
cristy3ed852e2009-09-05 21:47:34 +0000900 /*
901 Open output image file.
902 */
903 assert(image_info != (const ImageInfo *) NULL);
904 assert(image_info->signature == MagickSignature);
905 assert(image != (Image *) NULL);
906 assert(image->signature == MagickSignature);
907 if (image->debug != MagickFalse)
908 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000909 assert(exception != (ExceptionInfo *) NULL);
910 assert(exception->signature == MagickSignature);
911 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000912 if (status == MagickFalse)
913 return(status);
cristy3d9f5ba2012-06-26 13:37:31 +0000914 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000915 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000916 opacity=(-1);
cristy17f11b02014-12-20 19:37:04 +0000917 if (image->alpha_trait == UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000918 {
919 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000920 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000921 }
922 else
923 {
cristya19f1d72012-08-07 18:24:38 +0000924 double
cristy3ed852e2009-09-05 21:47:34 +0000925 alpha,
926 beta;
927
928 /*
929 Identify transparent colormap index.
930 */
931 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000932 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +0000933 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000934 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000935 {
936 if (opacity < 0)
937 {
938 opacity=i;
939 continue;
940 }
cristya19f1d72012-08-07 18:24:38 +0000941 alpha=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000942 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000943 beta=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000944 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000945 if (alpha < beta)
946 opacity=i;
947 }
948 if (opacity == -1)
949 {
cristy018f07f2011-09-04 21:15:19 +0000950 (void) SetImageType(image,PaletteBilevelMatteType,exception);
cristybb503372010-05-27 20:51:26 +0000951 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000952 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000953 {
954 if (opacity < 0)
955 {
956 opacity=i;
957 continue;
958 }
cristya19f1d72012-08-07 18:24:38 +0000959 alpha=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000960 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000961 beta=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000962 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000963 if (alpha < beta)
964 opacity=i;
965 }
966 }
967 if (opacity >= 0)
968 {
969 image->colormap[opacity].red=image->transparent_color.red;
970 image->colormap[opacity].green=image->transparent_color.green;
971 image->colormap[opacity].blue=image->transparent_color.blue;
972 }
973 }
974 /*
975 Compute the character per pixel.
976 */
977 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000978 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000979 characters_per_pixel++;
980 /*
981 XPM header.
982 */
983 (void) WriteBlobString(image,"/* XPM */\n");
984 GetPathComponent(image->filename,BasePath,basename);
985 if (isalnum((int) ((unsigned char) *basename)) == 0)
986 {
cristyb51dff52011-05-19 16:55:47 +0000987 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
cristy3ed852e2009-09-05 21:47:34 +0000988 (void) CopyMagickString(basename,buffer,MaxTextExtent);
989 }
cristy2f4741f2010-11-01 23:29:42 +0000990 if (isalpha((int) ((unsigned char) basename[0])) == 0)
991 basename[0]='_';
992 for (i=1; basename[i] != '\0'; i++)
993 if (isalnum((int) ((unsigned char) basename[i])) == 0)
cristy3ed852e2009-09-05 21:47:34 +0000994 basename[i]='_';
cristyb51dff52011-05-19 16:55:47 +0000995 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000996 "static char *%s[] = {\n",basename);
997 (void) WriteBlobString(image,buffer);
998 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +0000999 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001000 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1001 image->rows,(double) image->colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001002 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +00001003 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001004 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001005 {
1006 /*
1007 Define XPM color.
1008 */
cristy9d8c8ce2011-10-25 16:13:52 +00001009 pixel=image->colormap[i];
cristy7020ae62012-04-18 12:58:34 +00001010 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001011 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +00001012 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +00001013 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +00001014 if (i == opacity)
1015 (void) CopyMagickString(name,"None",MaxTextExtent);
1016 /*
1017 Write XPM color.
1018 */
1019 k=i % MaxCixels;
1020 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001021 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001022 {
1023 k=((i-k)/MaxCixels) % MaxCixels;
1024 symbol[j]=Cixel[k];
1025 }
1026 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +00001027 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
cristy3ed852e2009-09-05 21:47:34 +00001028 name);
1029 (void) WriteBlobString(image,buffer);
1030 }
1031 /*
1032 Define XPM pixels.
1033 */
1034 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +00001035 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001036 {
cristy3a37efd2011-08-28 20:31:03 +00001037 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001038 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001039 break;
cristy3ed852e2009-09-05 21:47:34 +00001040 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +00001041 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001042 {
cristy4c08aed2011-07-01 19:47:50 +00001043 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +00001044 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001045 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001046 {
cristy4c08aed2011-07-01 19:47:50 +00001047 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +00001048 symbol[j]=Cixel[k];
1049 }
1050 symbol[j]='\0';
1051 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1052 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +00001053 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001054 }
cristyb51dff52011-05-19 16:55:47 +00001055 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +00001056 (y == (ssize_t) (image->rows-1) ? "" : ","));
cristy3ed852e2009-09-05 21:47:34 +00001057 (void) WriteBlobString(image,buffer);
cristy8b27a6d2010-02-14 03:31:15 +00001058 if (image->previous == (Image *) NULL)
1059 {
cristy4cb162a2010-05-30 03:04:47 +00001060 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1061 image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00001062 if (status == MagickFalse)
1063 break;
1064 }
cristy3ed852e2009-09-05 21:47:34 +00001065 }
1066 (void) WriteBlobString(image,"};\n");
1067 (void) CloseBlob(image);
1068 return(MagickTrue);
1069}