blob: 8c3bb1cca4f790320bd2cb1ca02bdbc27f180d4f [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)
cristy12a5ddd2015-03-05 23:48:38 +0000365 {
366 xpm_buffer=DestroyString(xpm_buffer);
367 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
368 }
cristy3ed852e2009-09-05 21:47:34 +0000369 /*
370 Read image colormap.
371 */
cristy1d92f792013-09-09 13:04:57 +0000372 image->depth=1;
cristy3ed852e2009-09-05 21:47:34 +0000373 next=NextXPMLine(xpm_buffer);
cristy76d045a2014-12-02 02:12:04 +0000374 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
cristy3ed852e2009-09-05 21:47:34 +0000375 {
376 p=next;
377 next=NextXPMLine(p);
cristy6f1d4192014-12-28 13:59:39 +0000378 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MaxTextExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000379 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
380 /*
381 Parse color.
382 */
383 (void) CopyMagickString(target,"gray",MaxTextExtent);
dirk785a7232014-10-20 19:50:07 +0000384 q=ParseXPMColor(p+width,MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000385 if (q != (char *) NULL)
386 {
387 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
388 q++;
cristyf432c632014-12-07 15:11:28 +0000389 if ((next-q) < 0)
390 break;
cristy3ed852e2009-09-05 21:47:34 +0000391 if (next != (char *) NULL)
392 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
cristy6f1d4192014-12-28 13:59:39 +0000393 MaxTextExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000394 else
395 (void) CopyMagickString(target,q,MaxTextExtent);
dirk785a7232014-10-20 19:50:07 +0000396 q=ParseXPMColor(target,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000397 if (q != (char *) NULL)
398 *q='\0';
399 }
400 StripString(target);
dirk785a7232014-10-20 19:50:07 +0000401 grey=strstr(target,"grey");
402 if (grey != (char *) NULL)
dirk8695d952014-10-22 05:01:06 +0000403 grey[2]='a';
cristy3ed852e2009-09-05 21:47:34 +0000404 if (LocaleCompare(target,"none") == 0)
405 {
406 image->storage_class=DirectClass;
cristy8a46d822012-08-28 23:32:39 +0000407 image->alpha_trait=BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000408 }
cristyfc502a12013-04-09 16:52:51 +0000409 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
cristya60c0202010-07-31 21:36:45 +0000410 exception);
411 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000412 break;
cristy1d92f792013-09-09 13:04:57 +0000413 if (image->depth < image->colormap[j].depth)
414 image->depth=image->colormap[j].depth;
cristy3ed852e2009-09-05 21:47:34 +0000415 }
cristybb503372010-05-27 20:51:26 +0000416 if (j < (ssize_t) image->colors)
cristy12a5ddd2015-03-05 23:48:38 +0000417 {
418 xpm_colors=DestroySplayTree(xpm_colors);
419 xpm_buffer=DestroyString(xpm_buffer);
420 ThrowReaderException(CorruptImageError,"CorruptImage");
421 }
cristy3ed852e2009-09-05 21:47:34 +0000422 j=0;
423 if (image_info->ping == MagickFalse)
424 {
425 /*
426 Read image pixels.
427 */
cristyacabb842014-12-14 23:36:33 +0000428 status=SetImageExtent(image,image->columns,image->rows,exception);
429 if (status == MagickFalse)
430 return(DestroyImageList(image));
cristybb503372010-05-27 20:51:26 +0000431 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000432 {
433 p=NextXPMLine(p);
434 if (p == (char *) NULL)
435 break;
436 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000437 if (r == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000438 break;
cristybb503372010-05-27 20:51:26 +0000439 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000440 {
cristy5463e2e2014-12-31 12:06:30 +0000441 ssize_t count=CopyXPMColor(key,p,MagickMin(width,MaxTextExtent-1));
442 if (count != (ssize_t) width)
443 break;
cristybb503372010-05-27 20:51:26 +0000444 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
cristy3ed852e2009-09-05 21:47:34 +0000445 if (image->storage_class == PseudoClass)
cristydd12db52014-11-30 21:54:11 +0000446 SetPixelIndex(image,(Quantum) j,r);
cristy11a06d32015-01-04 12:03:27 +0000447 SetPixelViaPixelInfo(image,image->colormap+j,r);
cristy5463e2e2014-12-31 12:06:30 +0000448 p+=count;
cristyed231572011-07-14 02:18:59 +0000449 r+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000450 }
cristy5463e2e2014-12-31 12:06:30 +0000451 if (x < (ssize_t) image->columns)
452 break;
cristy3ed852e2009-09-05 21:47:34 +0000453 if (SyncAuthenticPixels(image,exception) == MagickFalse)
454 break;
455 }
cristybb503372010-05-27 20:51:26 +0000456 if (y < (ssize_t) image->rows)
cristy12a5ddd2015-03-05 23:48:38 +0000457 {
458 xpm_colors=DestroySplayTree(xpm_colors);
459 xpm_buffer=DestroyString(xpm_buffer);
460 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
461 }
cristy3ed852e2009-09-05 21:47:34 +0000462 }
463 /*
464 Relinquish resources.
465 */
cristy12a5ddd2015-03-05 23:48:38 +0000466 xpm_buffer=DestroyString(xpm_buffer);
cristy3ed852e2009-09-05 21:47:34 +0000467 xpm_colors=DestroySplayTree(xpm_colors);
468 (void) CloseBlob(image);
469 return(GetFirstImageInList(image));
470}
471
472/*
473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474% %
475% %
476% %
477% R e g i s t e r X P M I m a g e %
478% %
479% %
480% %
481%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482%
483% RegisterXPMImage() adds attributes for the XPM image format to
484% the list of supported formats. The attributes include the image format
485% tag, a method to read and/or write the format, whether the format
486% supports the saving of more than one frame to the same file or blob,
487% whether the format supports native in-memory I/O, and a brief
488% description of the format.
489%
490% The format of the RegisterXPMImage method is:
491%
cristybb503372010-05-27 20:51:26 +0000492% size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000493%
494*/
cristybb503372010-05-27 20:51:26 +0000495ModuleExport size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000496{
497 MagickInfo
498 *entry;
499
dirk06b627a2015-04-06 18:59:17 +0000500 entry=AcquireMagickInfo("XPM","PICON","Personal Icon");
cristy3ed852e2009-09-05 21:47:34 +0000501 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
502 entry->encoder=(EncodeImageHandler *) WritePICONImage;
dirk08e9a112015-02-22 01:51:41 +0000503 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000504 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000505 entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)");
cristy3ed852e2009-09-05 21:47:34 +0000506 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
507 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
dirk08e9a112015-02-22 01:51:41 +0000508 entry->flags^=CoderAdjoinFlag;
509 entry->flags|=CoderStealthFlag;
cristy3ed852e2009-09-05 21:47:34 +0000510 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000511 entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)");
cristy3ed852e2009-09-05 21:47:34 +0000512 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
513 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
514 entry->magick=(IsImageFormatHandler *) IsXPM;
dirk08e9a112015-02-22 01:51:41 +0000515 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000516 (void) RegisterMagickInfo(entry);
517 return(MagickImageCoderSignature);
518}
519
520/*
521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522% %
523% %
524% %
525% U n r e g i s t e r X P M I m a g e %
526% %
527% %
528% %
529%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530%
531% UnregisterXPMImage() removes format registrations made by the
532% XPM module from the list of supported formats.
533%
534% The format of the UnregisterXPMImage method is:
535%
536% UnregisterXPMImage(void)
537%
538*/
539ModuleExport void UnregisterXPMImage(void)
540{
541 (void) UnregisterMagickInfo("PICON");
542 (void) UnregisterMagickInfo("PM");
543 (void) UnregisterMagickInfo("XPM");
544}
545
546/*
547%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548% %
549% %
550% %
551% W r i t e P I C O N I m a g e %
552% %
553% %
554% %
555%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556%
cristya60c0202010-07-31 21:36:45 +0000557% WritePICONImage() writes an image to a file in the Personal Icon format.
cristy3ed852e2009-09-05 21:47:34 +0000558%
559% The format of the WritePICONImage method is:
560%
561% MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000562% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000563%
564% A description of each parameter follows.
565%
566% o image_info: the image info.
567%
568% o image: The image.
569%
cristy3a37efd2011-08-28 20:31:03 +0000570% o exception: return any errors or warnings in this structure.
571%
cristy3ed852e2009-09-05 21:47:34 +0000572*/
573static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000574 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000575{
576#define ColormapExtent 155
577#define GraymapExtent 95
578#define PiconGeometry "48x48>"
579
580 static unsigned char
581 Colormap[]=
582 {
583 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
584 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
585 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
586 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
587 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
588 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
589 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
590 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
591 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
592 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
593 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
594 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
595 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
596 },
597 Graymap[]=
598 {
599 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
600 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
601 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
602 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
603 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
604 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
605 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
606 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
607 };
608
609#define MaxCixels 92
610
611 static const char
612 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
613 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
614
615 char
616 buffer[MaxTextExtent],
617 basename[MaxTextExtent],
618 name[MaxTextExtent],
619 symbol[MaxTextExtent];
620
cristy3ed852e2009-09-05 21:47:34 +0000621 Image
622 *affinity_image,
623 *picon;
624
625 ImageInfo
626 *blob_info;
627
cristy3ed852e2009-09-05 21:47:34 +0000628 MagickBooleanType
629 status,
630 transparent;
631
cristy4c08aed2011-07-01 19:47:50 +0000632 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000633 pixel;
634
635 QuantizeInfo
636 *quantize_info;
637
638 RectangleInfo
639 geometry;
640
cristy4c08aed2011-07-01 19:47:50 +0000641 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000642 *p;
643
cristybb503372010-05-27 20:51:26 +0000644 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000645 i,
646 x;
647
cristy4c08aed2011-07-01 19:47:50 +0000648 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000649 *q;
650
cristybb503372010-05-27 20:51:26 +0000651 size_t
cristy3ed852e2009-09-05 21:47:34 +0000652 characters_per_pixel,
653 colors;
654
cristyc6da28e2011-04-28 01:41:35 +0000655 ssize_t
656 j,
657 k,
658 y;
659
cristy3ed852e2009-09-05 21:47:34 +0000660 /*
661 Open output image file.
662 */
663 assert(image_info != (const ImageInfo *) NULL);
664 assert(image_info->signature == MagickSignature);
665 assert(image != (Image *) NULL);
666 assert(image->signature == MagickSignature);
667 if (image->debug != MagickFalse)
668 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000669 assert(exception != (ExceptionInfo *) NULL);
670 assert(exception->signature == MagickSignature);
671 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000672 if (status == MagickFalse)
673 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000674 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000675 SetGeometry(image,&geometry);
676 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
677 &geometry.width,&geometry.height);
cristyaa2c16c2012-03-25 22:21:35 +0000678 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
cristy3a37efd2011-08-28 20:31:03 +0000679 exception);
cristy3ed852e2009-09-05 21:47:34 +0000680 blob_info=CloneImageInfo(image_info);
681 (void) AcquireUniqueFilename(blob_info->filename);
682 if ((image_info->type != TrueColorType) &&
dirkf1d85482015-04-06 00:36:00 +0000683 (SetImageGray(image,exception) != MagickFalse))
cristy3a37efd2011-08-28 20:31:03 +0000684 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000685 else
cristy3a37efd2011-08-28 20:31:03 +0000686 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000687 (void) RelinquishUniqueFileResource(blob_info->filename);
688 blob_info=DestroyImageInfo(blob_info);
689 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
690 return(MagickFalse);
691 quantize_info=AcquireQuantizeInfo(image_info);
cristy018f07f2011-09-04 21:15:19 +0000692 status=RemapImage(quantize_info,picon,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000693 quantize_info=DestroyQuantizeInfo(quantize_info);
694 affinity_image=DestroyImage(affinity_image);
695 transparent=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000696 if (picon->storage_class == PseudoClass)
697 {
cristy018f07f2011-09-04 21:15:19 +0000698 (void) CompressImageColormap(picon,exception);
cristy17f11b02014-12-20 19:37:04 +0000699 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000700 transparent=MagickTrue;
701 }
702 else
703 {
704 /*
705 Convert DirectClass to PseudoClass picon.
706 */
cristy17f11b02014-12-20 19:37:04 +0000707 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000708 {
709 /*
710 Map all the transparent pixels.
711 */
cristybb503372010-05-27 20:51:26 +0000712 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000713 {
714 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000715 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000716 break;
cristybb503372010-05-27 20:51:26 +0000717 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000718 {
cristy4c08aed2011-07-01 19:47:50 +0000719 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000720 transparent=MagickTrue;
721 else
cristy4c08aed2011-07-01 19:47:50 +0000722 SetPixelAlpha(picon,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +0000723 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000724 }
725 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
726 break;
727 }
728 }
cristy018f07f2011-09-04 21:15:19 +0000729 (void) SetImageType(picon,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000730 }
731 colors=picon->colors;
732 if (transparent != MagickFalse)
733 {
cristy3ed852e2009-09-05 21:47:34 +0000734 colors++;
cristy101ab702011-10-13 13:06:32 +0000735 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
cristy3ed852e2009-09-05 21:47:34 +0000736 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
cristy101ab702011-10-13 13:06:32 +0000737 if (picon->colormap == (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000738 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
cristybb503372010-05-27 20:51:26 +0000739 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000740 {
741 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000742 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000743 break;
cristybb503372010-05-27 20:51:26 +0000744 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000745 {
cristy4c08aed2011-07-01 19:47:50 +0000746 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristydd12db52014-11-30 21:54:11 +0000747 SetPixelIndex(picon,(Quantum) picon->colors,q);
cristyed231572011-07-14 02:18:59 +0000748 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000749 }
750 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
751 break;
752 }
753 }
754 /*
755 Compute the character per pixel.
756 */
757 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000758 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000759 characters_per_pixel++;
760 /*
761 XPM header.
762 */
763 (void) WriteBlobString(image,"/* XPM */\n");
764 GetPathComponent(picon->filename,BasePath,basename);
cristyb51dff52011-05-19 16:55:47 +0000765 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +0000766 "static char *%s[] = {\n",basename);
767 (void) WriteBlobString(image,buffer);
768 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +0000769 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +0000770 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
771 picon->rows,(double) colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000772 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +0000773 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000774 for (i=0; i < (ssize_t) colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000775 {
776 /*
777 Define XPM color.
778 */
cristy9d8c8ce2011-10-25 16:13:52 +0000779 pixel=picon->colormap[i];
cristy7020ae62012-04-18 12:58:34 +0000780 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000781 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +0000782 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +0000783 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +0000784 if (transparent != MagickFalse)
785 {
cristybb503372010-05-27 20:51:26 +0000786 if (i == (ssize_t) (colors-1))
cristy3ed852e2009-09-05 21:47:34 +0000787 (void) CopyMagickString(name,"grey75",MaxTextExtent);
788 }
789 /*
790 Write XPM color.
791 */
792 k=i % MaxCixels;
793 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000794 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000795 {
796 k=((i-k)/MaxCixels) % MaxCixels;
797 symbol[j]=Cixel[k];
798 }
799 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +0000800 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",
cristye6b6ece2010-05-29 13:25:01 +0000801 symbol,name);
cristy3ed852e2009-09-05 21:47:34 +0000802 (void) WriteBlobString(image,buffer);
803 }
804 /*
805 Define XPM pixels.
806 */
807 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +0000808 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000809 {
cristy3a37efd2011-08-28 20:31:03 +0000810 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000811 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000812 break;
cristy3ed852e2009-09-05 21:47:34 +0000813 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +0000814 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000815 {
cristy4c08aed2011-07-01 19:47:50 +0000816 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +0000817 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000818 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000819 {
cristy4c08aed2011-07-01 19:47:50 +0000820 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +0000821 symbol[j]=Cixel[k];
822 }
823 symbol[j]='\0';
824 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
825 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +0000826 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000827 }
cristyb51dff52011-05-19 16:55:47 +0000828 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +0000829 y == (ssize_t) (picon->rows-1) ? "" : ",");
cristy3ed852e2009-09-05 21:47:34 +0000830 (void) WriteBlobString(image,buffer);
cristy4cb162a2010-05-30 03:04:47 +0000831 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
832 picon->rows);
cristy3ed852e2009-09-05 21:47:34 +0000833 if (status == MagickFalse)
834 break;
835 }
836 picon=DestroyImage(picon);
837 (void) WriteBlobString(image,"};\n");
838 (void) CloseBlob(image);
839 return(MagickTrue);
840}
841
842/*
843%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
844% %
845% %
846% %
847% W r i t e X P M I m a g e %
848% %
849% %
850% %
851%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
852%
cristya60c0202010-07-31 21:36:45 +0000853% WriteXPMImage() writes an image to a file in the X pixmap format.
cristy3ed852e2009-09-05 21:47:34 +0000854%
855% The format of the WriteXPMImage method is:
856%
cristy3a37efd2011-08-28 20:31:03 +0000857% MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
858% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000859%
860% A description of each parameter follows.
861%
862% o image_info: the image info.
863%
864% o image: The image.
865%
cristy3a37efd2011-08-28 20:31:03 +0000866% o exception: return any errors or warnings in this structure.
867%
cristy3ed852e2009-09-05 21:47:34 +0000868*/
cristy3a37efd2011-08-28 20:31:03 +0000869static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
870 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000871{
872#define MaxCixels 92
873
874 static const char
875 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
876 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
877
878 char
879 buffer[MaxTextExtent],
880 basename[MaxTextExtent],
881 name[MaxTextExtent],
882 symbol[MaxTextExtent];
883
cristy3ed852e2009-09-05 21:47:34 +0000884 MagickBooleanType
885 status;
886
cristy4c08aed2011-07-01 19:47:50 +0000887 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000888 pixel;
889
cristy4c08aed2011-07-01 19:47:50 +0000890 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000891 *p;
892
cristybb503372010-05-27 20:51:26 +0000893 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000894 i,
895 x;
896
cristybb503372010-05-27 20:51:26 +0000897 size_t
cristy3ed852e2009-09-05 21:47:34 +0000898 characters_per_pixel;
899
cristyc6da28e2011-04-28 01:41:35 +0000900 ssize_t
901 j,
902 k,
903 opacity,
904 y;
905
cristy3ed852e2009-09-05 21:47:34 +0000906 /*
907 Open output image file.
908 */
909 assert(image_info != (const ImageInfo *) NULL);
910 assert(image_info->signature == MagickSignature);
911 assert(image != (Image *) NULL);
912 assert(image->signature == MagickSignature);
913 if (image->debug != MagickFalse)
914 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000915 assert(exception != (ExceptionInfo *) NULL);
916 assert(exception->signature == MagickSignature);
917 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000918 if (status == MagickFalse)
919 return(status);
cristy3d9f5ba2012-06-26 13:37:31 +0000920 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000921 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000922 opacity=(-1);
cristy17f11b02014-12-20 19:37:04 +0000923 if (image->alpha_trait == UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000924 {
925 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000926 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000927 }
928 else
929 {
cristya19f1d72012-08-07 18:24:38 +0000930 double
cristy3ed852e2009-09-05 21:47:34 +0000931 alpha,
932 beta;
933
934 /*
935 Identify transparent colormap index.
936 */
937 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristydef23e52015-01-22 11:52:01 +0000938 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +0000939 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000940 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000941 {
942 if (opacity < 0)
943 {
944 opacity=i;
945 continue;
946 }
cristya19f1d72012-08-07 18:24:38 +0000947 alpha=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000948 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000949 beta=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000950 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000951 if (alpha < beta)
952 opacity=i;
953 }
954 if (opacity == -1)
955 {
cristydef23e52015-01-22 11:52:01 +0000956 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +0000957 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000958 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000959 {
960 if (opacity < 0)
961 {
962 opacity=i;
963 continue;
964 }
cristya19f1d72012-08-07 18:24:38 +0000965 alpha=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000966 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000967 beta=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000968 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000969 if (alpha < beta)
970 opacity=i;
971 }
972 }
973 if (opacity >= 0)
974 {
975 image->colormap[opacity].red=image->transparent_color.red;
976 image->colormap[opacity].green=image->transparent_color.green;
977 image->colormap[opacity].blue=image->transparent_color.blue;
978 }
979 }
980 /*
981 Compute the character per pixel.
982 */
983 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000984 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000985 characters_per_pixel++;
986 /*
987 XPM header.
988 */
989 (void) WriteBlobString(image,"/* XPM */\n");
990 GetPathComponent(image->filename,BasePath,basename);
991 if (isalnum((int) ((unsigned char) *basename)) == 0)
992 {
cristyb51dff52011-05-19 16:55:47 +0000993 (void) FormatLocaleString(buffer,MaxTextExtent,"xpm_%s",basename);
cristy3ed852e2009-09-05 21:47:34 +0000994 (void) CopyMagickString(basename,buffer,MaxTextExtent);
995 }
cristy2f4741f2010-11-01 23:29:42 +0000996 if (isalpha((int) ((unsigned char) basename[0])) == 0)
997 basename[0]='_';
998 for (i=1; basename[i] != '\0'; i++)
999 if (isalnum((int) ((unsigned char) basename[i])) == 0)
cristy3ed852e2009-09-05 21:47:34 +00001000 basename[i]='_';
cristyb51dff52011-05-19 16:55:47 +00001001 (void) FormatLocaleString(buffer,MaxTextExtent,
cristy3ed852e2009-09-05 21:47:34 +00001002 "static char *%s[] = {\n",basename);
1003 (void) WriteBlobString(image,buffer);
1004 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristyb51dff52011-05-19 16:55:47 +00001005 (void) FormatLocaleString(buffer,MaxTextExtent,
cristye8c25f92010-06-03 00:53:06 +00001006 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1007 image->rows,(double) image->colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001008 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +00001009 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001010 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001011 {
1012 /*
1013 Define XPM color.
1014 */
cristy9d8c8ce2011-10-25 16:13:52 +00001015 pixel=image->colormap[i];
cristy7020ae62012-04-18 12:58:34 +00001016 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001017 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +00001018 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +00001019 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +00001020 if (i == opacity)
1021 (void) CopyMagickString(name,"None",MaxTextExtent);
1022 /*
1023 Write XPM color.
1024 */
1025 k=i % MaxCixels;
1026 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001027 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001028 {
1029 k=((i-k)/MaxCixels) % MaxCixels;
1030 symbol[j]=Cixel[k];
1031 }
1032 symbol[j]='\0';
cristyb51dff52011-05-19 16:55:47 +00001033 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s c %s\",\n",symbol,
cristy3ed852e2009-09-05 21:47:34 +00001034 name);
1035 (void) WriteBlobString(image,buffer);
1036 }
1037 /*
1038 Define XPM pixels.
1039 */
1040 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +00001041 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001042 {
cristy3a37efd2011-08-28 20:31:03 +00001043 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001044 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001045 break;
cristy3ed852e2009-09-05 21:47:34 +00001046 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +00001047 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001048 {
cristy4c08aed2011-07-01 19:47:50 +00001049 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +00001050 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001051 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001052 {
cristy4c08aed2011-07-01 19:47:50 +00001053 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +00001054 symbol[j]=Cixel[k];
1055 }
1056 symbol[j]='\0';
1057 (void) CopyMagickString(buffer,symbol,MaxTextExtent);
1058 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +00001059 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001060 }
cristyb51dff52011-05-19 16:55:47 +00001061 (void) FormatLocaleString(buffer,MaxTextExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +00001062 (y == (ssize_t) (image->rows-1) ? "" : ","));
cristy3ed852e2009-09-05 21:47:34 +00001063 (void) WriteBlobString(image,buffer);
cristy8b27a6d2010-02-14 03:31:15 +00001064 if (image->previous == (Image *) NULL)
1065 {
cristy4cb162a2010-05-30 03:04:47 +00001066 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1067 image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00001068 if (status == MagickFalse)
1069 break;
1070 }
cristy3ed852e2009-09-05 21:47:34 +00001071 }
1072 (void) WriteBlobString(image,"};\n");
1073 (void) CloseBlob(image);
1074 return(MagickTrue);
1075}