blob: 1469fe445dd3a35b01c4b813851c5e5ac74f5fef [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% %
Cristyf6ff9ea2016-12-05 09:53:35 -050020% Copyright 1999-2017 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% %
Cristyf19d4142017-04-24 11:34:30 -040026% https://www.imagemagick.org/script/license.php %
cristy3ed852e2009-09-05 21:47:34 +000027% %
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
Cristy629f8052015-09-14 07:48:08 -0400191 *const targets[NumberTargets] = { "c ", "g ", "g4 ", "m ", "b ", "s " };
cristy3ed852e2009-09-05 21:47:34 +0000192
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,
cristy151b66d2015-04-15 10:50:31 +0000240 key[MagickPathExtent],
241 target[MagickPathExtent],
cristy3ed852e2009-09-05 21:47:34 +0000242 *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);
cristye1c94d92015-06-28 12:16:33 +0000283 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000284 if (image_info->debug != MagickFalse)
285 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
286 image_info->filename);
287 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000288 assert(exception->signature == MagickCoreSignature);
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 */
cristy151b66d2015-04-15 10:50:31 +0000299 length=MagickPathExtent;
cristy3ed852e2009-09-05 21:47:34 +0000300 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);
cristy151b66d2015-04-15 10:50:31 +0000312 if ((size_t) (p-xpm_buffer+MagickPathExtent) < length)
cristydd12db52014-11-30 21:54:11 +0000313 continue;
314 length<<=1;
cristy151b66d2015-04-15 10:50:31 +0000315 xpm_buffer=(char *) ResizeQuantumMemory(xpm_buffer,length+MagickPathExtent,
cristydd12db52014-11-30 21:54:11 +0000316 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 }
Cristy5b348482016-10-30 20:30:48 -0400339 if ((count != 4) || (width == 0) || (width > 3) ||
Cristy76edbba2016-05-06 19:11:52 -0400340 (image->columns == 0) || (image->rows == 0) ||
341 (image->colors == 0) || (image->colors > MaxColormapSize))
342 {
343 xpm_buffer=DestroyString(xpm_buffer);
344 ThrowReaderException(CorruptImageError,"ImproperImageHeader");
345 }
cristy3ed852e2009-09-05 21:47:34 +0000346 /*
347 Remove unquoted characters.
348 */
cristy3ed852e2009-09-05 21:47:34 +0000349 active=MagickFalse;
Cristye231b1b2016-04-18 08:22:26 -0400350 for (q=xpm_buffer; *p != '\0'; )
cristy3ed852e2009-09-05 21:47:34 +0000351 {
352 if (*p++ == '"')
353 {
354 if (active != MagickFalse)
355 *q++='\n';
356 active=active != MagickFalse ? MagickFalse : MagickTrue;
357 }
358 if (active != MagickFalse)
359 *q++=(*p);
360 }
361 *q='\0';
362 /*
363 Initialize image structure.
364 */
365 xpm_colors=NewSplayTree(CompareXPMColor,RelinquishMagickMemory,
366 (void *(*)(void *)) NULL);
cristy018f07f2011-09-04 21:15:19 +0000367 if (AcquireImageColormap(image,image->colors,exception) == MagickFalse)
cristy12a5ddd2015-03-05 23:48:38 +0000368 {
369 xpm_buffer=DestroyString(xpm_buffer);
370 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
371 }
cristy3ed852e2009-09-05 21:47:34 +0000372 /*
373 Read image colormap.
374 */
cristy1d92f792013-09-09 13:04:57 +0000375 image->depth=1;
cristy3ed852e2009-09-05 21:47:34 +0000376 next=NextXPMLine(xpm_buffer);
cristy76d045a2014-12-02 02:12:04 +0000377 for (j=0; (j < (ssize_t) image->colors) && (next != (char *) NULL); j++)
cristy3ed852e2009-09-05 21:47:34 +0000378 {
379 p=next;
380 next=NextXPMLine(p);
cristy151b66d2015-04-15 10:50:31 +0000381 (void) CopyXPMColor(key,p,MagickMin((size_t) width,MagickPathExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000382 status=AddValueToSplayTree(xpm_colors,ConstantString(key),(void *) j);
383 /*
384 Parse color.
385 */
cristy151b66d2015-04-15 10:50:31 +0000386 (void) CopyMagickString(target,"gray",MagickPathExtent);
dirk785a7232014-10-20 19:50:07 +0000387 q=ParseXPMColor(p+width,MagickTrue);
cristy3ed852e2009-09-05 21:47:34 +0000388 if (q != (char *) NULL)
389 {
390 while ((isspace((int) ((unsigned char) *q)) == 0) && (*q != '\0'))
391 q++;
cristyf432c632014-12-07 15:11:28 +0000392 if ((next-q) < 0)
393 break;
cristy3ed852e2009-09-05 21:47:34 +0000394 if (next != (char *) NULL)
395 (void) CopyXPMColor(target,q,MagickMin((size_t) (next-q),
cristy151b66d2015-04-15 10:50:31 +0000396 MagickPathExtent-1));
cristy3ed852e2009-09-05 21:47:34 +0000397 else
cristy151b66d2015-04-15 10:50:31 +0000398 (void) CopyMagickString(target,q,MagickPathExtent);
dirk785a7232014-10-20 19:50:07 +0000399 q=ParseXPMColor(target,MagickFalse);
cristy3ed852e2009-09-05 21:47:34 +0000400 if (q != (char *) NULL)
401 *q='\0';
402 }
403 StripString(target);
dirk785a7232014-10-20 19:50:07 +0000404 grey=strstr(target,"grey");
405 if (grey != (char *) NULL)
dirk8695d952014-10-22 05:01:06 +0000406 grey[2]='a';
cristy3ed852e2009-09-05 21:47:34 +0000407 if (LocaleCompare(target,"none") == 0)
408 {
409 image->storage_class=DirectClass;
cristy8a46d822012-08-28 23:32:39 +0000410 image->alpha_trait=BlendPixelTrait;
cristy3ed852e2009-09-05 21:47:34 +0000411 }
cristyfc502a12013-04-09 16:52:51 +0000412 status=QueryColorCompliance(target,XPMCompliance,&image->colormap[j],
cristya60c0202010-07-31 21:36:45 +0000413 exception);
414 if (status == MagickFalse)
cristy3ed852e2009-09-05 21:47:34 +0000415 break;
cristy1d92f792013-09-09 13:04:57 +0000416 if (image->depth < image->colormap[j].depth)
417 image->depth=image->colormap[j].depth;
cristy3ed852e2009-09-05 21:47:34 +0000418 }
cristybb503372010-05-27 20:51:26 +0000419 if (j < (ssize_t) image->colors)
cristy12a5ddd2015-03-05 23:48:38 +0000420 {
421 xpm_colors=DestroySplayTree(xpm_colors);
422 xpm_buffer=DestroyString(xpm_buffer);
423 ThrowReaderException(CorruptImageError,"CorruptImage");
424 }
cristy3ed852e2009-09-05 21:47:34 +0000425 j=0;
426 if (image_info->ping == MagickFalse)
427 {
428 /*
429 Read image pixels.
430 */
cristyacabb842014-12-14 23:36:33 +0000431 status=SetImageExtent(image,image->columns,image->rows,exception);
432 if (status == MagickFalse)
433 return(DestroyImageList(image));
cristybb503372010-05-27 20:51:26 +0000434 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000435 {
436 p=NextXPMLine(p);
437 if (p == (char *) NULL)
438 break;
439 r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000440 if (r == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000441 break;
cristybb503372010-05-27 20:51:26 +0000442 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000443 {
cristy151b66d2015-04-15 10:50:31 +0000444 ssize_t count=CopyXPMColor(key,p,MagickMin(width,MagickPathExtent-1));
cristy5463e2e2014-12-31 12:06:30 +0000445 if (count != (ssize_t) width)
446 break;
cristybb503372010-05-27 20:51:26 +0000447 j=(ssize_t) GetValueFromSplayTree(xpm_colors,key);
cristy3ed852e2009-09-05 21:47:34 +0000448 if (image->storage_class == PseudoClass)
cristydd12db52014-11-30 21:54:11 +0000449 SetPixelIndex(image,(Quantum) j,r);
cristy11a06d32015-01-04 12:03:27 +0000450 SetPixelViaPixelInfo(image,image->colormap+j,r);
cristy5463e2e2014-12-31 12:06:30 +0000451 p+=count;
cristyed231572011-07-14 02:18:59 +0000452 r+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000453 }
cristy5463e2e2014-12-31 12:06:30 +0000454 if (x < (ssize_t) image->columns)
455 break;
cristy3ed852e2009-09-05 21:47:34 +0000456 if (SyncAuthenticPixels(image,exception) == MagickFalse)
457 break;
458 }
cristybb503372010-05-27 20:51:26 +0000459 if (y < (ssize_t) image->rows)
cristy12a5ddd2015-03-05 23:48:38 +0000460 {
461 xpm_colors=DestroySplayTree(xpm_colors);
462 xpm_buffer=DestroyString(xpm_buffer);
463 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
464 }
cristy3ed852e2009-09-05 21:47:34 +0000465 }
466 /*
467 Relinquish resources.
468 */
cristy12a5ddd2015-03-05 23:48:38 +0000469 xpm_buffer=DestroyString(xpm_buffer);
cristy3ed852e2009-09-05 21:47:34 +0000470 xpm_colors=DestroySplayTree(xpm_colors);
471 (void) CloseBlob(image);
472 return(GetFirstImageInList(image));
473}
474
475/*
476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
477% %
478% %
479% %
480% R e g i s t e r X P M I m a g e %
481% %
482% %
483% %
484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485%
486% RegisterXPMImage() adds attributes for the XPM image format to
487% the list of supported formats. The attributes include the image format
488% tag, a method to read and/or write the format, whether the format
489% supports the saving of more than one frame to the same file or blob,
490% whether the format supports native in-memory I/O, and a brief
491% description of the format.
492%
493% The format of the RegisterXPMImage method is:
494%
cristybb503372010-05-27 20:51:26 +0000495% size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000496%
497*/
cristybb503372010-05-27 20:51:26 +0000498ModuleExport size_t RegisterXPMImage(void)
cristy3ed852e2009-09-05 21:47:34 +0000499{
500 MagickInfo
501 *entry;
502
dirk06b627a2015-04-06 18:59:17 +0000503 entry=AcquireMagickInfo("XPM","PICON","Personal Icon");
cristy3ed852e2009-09-05 21:47:34 +0000504 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
505 entry->encoder=(EncodeImageHandler *) WritePICONImage;
dirk08e9a112015-02-22 01:51:41 +0000506 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000507 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000508 entry=AcquireMagickInfo("XPM","PM","X Windows system pixmap (color)");
cristy3ed852e2009-09-05 21:47:34 +0000509 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
510 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
dirk08e9a112015-02-22 01:51:41 +0000511 entry->flags^=CoderAdjoinFlag;
512 entry->flags|=CoderStealthFlag;
cristy3ed852e2009-09-05 21:47:34 +0000513 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +0000514 entry=AcquireMagickInfo("XPM","XPM","X Windows system pixmap (color)");
cristy3ed852e2009-09-05 21:47:34 +0000515 entry->decoder=(DecodeImageHandler *) ReadXPMImage;
516 entry->encoder=(EncodeImageHandler *) WriteXPMImage;
517 entry->magick=(IsImageFormatHandler *) IsXPM;
dirk08e9a112015-02-22 01:51:41 +0000518 entry->flags^=CoderAdjoinFlag;
cristy3ed852e2009-09-05 21:47:34 +0000519 (void) RegisterMagickInfo(entry);
520 return(MagickImageCoderSignature);
521}
522
523/*
524%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
525% %
526% %
527% %
528% U n r e g i s t e r X P M I m a g e %
529% %
530% %
531% %
532%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533%
534% UnregisterXPMImage() removes format registrations made by the
535% XPM module from the list of supported formats.
536%
537% The format of the UnregisterXPMImage method is:
538%
539% UnregisterXPMImage(void)
540%
541*/
542ModuleExport void UnregisterXPMImage(void)
543{
544 (void) UnregisterMagickInfo("PICON");
545 (void) UnregisterMagickInfo("PM");
546 (void) UnregisterMagickInfo("XPM");
547}
548
549/*
550%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
551% %
552% %
553% %
554% W r i t e P I C O N I m a g e %
555% %
556% %
557% %
558%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
559%
cristya60c0202010-07-31 21:36:45 +0000560% WritePICONImage() writes an image to a file in the Personal Icon format.
cristy3ed852e2009-09-05 21:47:34 +0000561%
562% The format of the WritePICONImage method is:
563%
564% MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000565% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000566%
567% A description of each parameter follows.
568%
569% o image_info: the image info.
570%
571% o image: The image.
572%
cristy3a37efd2011-08-28 20:31:03 +0000573% o exception: return any errors or warnings in this structure.
574%
cristy3ed852e2009-09-05 21:47:34 +0000575*/
576static MagickBooleanType WritePICONImage(const ImageInfo *image_info,
cristy3a37efd2011-08-28 20:31:03 +0000577 Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000578{
579#define ColormapExtent 155
580#define GraymapExtent 95
581#define PiconGeometry "48x48>"
582
583 static unsigned char
584 Colormap[]=
585 {
586 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05,
587 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e,
588 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00,
589 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff,
590 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd,
591 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00,
592 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff,
593 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4,
594 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00,
595 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
596 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08,
597 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49,
598 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b,
599 },
600 Graymap[]=
601 {
602 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f,
603 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33,
604 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78,
605 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba,
606 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff,
607 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00,
608 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31,
609 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b,
610 };
611
612#define MaxCixels 92
613
614 static const char
615 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
616 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
617
618 char
cristy151b66d2015-04-15 10:50:31 +0000619 buffer[MagickPathExtent],
620 basename[MagickPathExtent],
621 name[MagickPathExtent],
622 symbol[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000623
cristy3ed852e2009-09-05 21:47:34 +0000624 Image
625 *affinity_image,
626 *picon;
627
628 ImageInfo
629 *blob_info;
630
cristy3ed852e2009-09-05 21:47:34 +0000631 MagickBooleanType
632 status,
633 transparent;
634
cristy4c08aed2011-07-01 19:47:50 +0000635 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000636 pixel;
637
638 QuantizeInfo
639 *quantize_info;
640
641 RectangleInfo
642 geometry;
643
cristy4c08aed2011-07-01 19:47:50 +0000644 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000645 *p;
646
cristybb503372010-05-27 20:51:26 +0000647 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000648 i,
649 x;
650
cristy4c08aed2011-07-01 19:47:50 +0000651 register Quantum
cristy3ed852e2009-09-05 21:47:34 +0000652 *q;
653
cristybb503372010-05-27 20:51:26 +0000654 size_t
cristy3ed852e2009-09-05 21:47:34 +0000655 characters_per_pixel,
656 colors;
657
cristyc6da28e2011-04-28 01:41:35 +0000658 ssize_t
659 j,
660 k,
661 y;
662
cristy3ed852e2009-09-05 21:47:34 +0000663 /*
664 Open output image file.
665 */
666 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000667 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000668 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000669 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000670 if (image->debug != MagickFalse)
671 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000672 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000673 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +0000674 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000675 if (status == MagickFalse)
676 return(status);
cristyaf8d3912014-02-21 14:50:33 +0000677 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000678 SetGeometry(image,&geometry);
679 (void) ParseMetaGeometry(PiconGeometry,&geometry.x,&geometry.y,
680 &geometry.width,&geometry.height);
cristyaa2c16c2012-03-25 22:21:35 +0000681 picon=ResizeImage(image,geometry.width,geometry.height,TriangleFilter,
cristy3a37efd2011-08-28 20:31:03 +0000682 exception);
cristy3ed852e2009-09-05 21:47:34 +0000683 blob_info=CloneImageInfo(image_info);
684 (void) AcquireUniqueFilename(blob_info->filename);
685 if ((image_info->type != TrueColorType) &&
dirkf1d85482015-04-06 00:36:00 +0000686 (SetImageGray(image,exception) != MagickFalse))
cristy3a37efd2011-08-28 20:31:03 +0000687 affinity_image=BlobToImage(blob_info,Graymap,GraymapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000688 else
cristy3a37efd2011-08-28 20:31:03 +0000689 affinity_image=BlobToImage(blob_info,Colormap,ColormapExtent,exception);
cristy3ed852e2009-09-05 21:47:34 +0000690 (void) RelinquishUniqueFileResource(blob_info->filename);
691 blob_info=DestroyImageInfo(blob_info);
692 if ((picon == (Image *) NULL) || (affinity_image == (Image *) NULL))
693 return(MagickFalse);
694 quantize_info=AcquireQuantizeInfo(image_info);
cristy018f07f2011-09-04 21:15:19 +0000695 status=RemapImage(quantize_info,picon,affinity_image,exception);
cristy3ed852e2009-09-05 21:47:34 +0000696 quantize_info=DestroyQuantizeInfo(quantize_info);
697 affinity_image=DestroyImage(affinity_image);
698 transparent=MagickFalse;
cristy3ed852e2009-09-05 21:47:34 +0000699 if (picon->storage_class == PseudoClass)
700 {
cristy018f07f2011-09-04 21:15:19 +0000701 (void) CompressImageColormap(picon,exception);
cristy17f11b02014-12-20 19:37:04 +0000702 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000703 transparent=MagickTrue;
704 }
705 else
706 {
707 /*
708 Convert DirectClass to PseudoClass picon.
709 */
cristy17f11b02014-12-20 19:37:04 +0000710 if (picon->alpha_trait != UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000711 {
712 /*
713 Map all the transparent pixels.
714 */
cristybb503372010-05-27 20:51:26 +0000715 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000716 {
717 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000718 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000719 break;
cristybb503372010-05-27 20:51:26 +0000720 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000721 {
cristy4c08aed2011-07-01 19:47:50 +0000722 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000723 transparent=MagickTrue;
724 else
cristy4c08aed2011-07-01 19:47:50 +0000725 SetPixelAlpha(picon,OpaqueAlpha,q);
cristyed231572011-07-14 02:18:59 +0000726 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000727 }
728 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
729 break;
730 }
731 }
cristy018f07f2011-09-04 21:15:19 +0000732 (void) SetImageType(picon,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000733 }
734 colors=picon->colors;
735 if (transparent != MagickFalse)
736 {
cristy3ed852e2009-09-05 21:47:34 +0000737 colors++;
cristy101ab702011-10-13 13:06:32 +0000738 picon->colormap=(PixelInfo *) ResizeQuantumMemory((void **)
cristy3ed852e2009-09-05 21:47:34 +0000739 picon->colormap,(size_t) colors,sizeof(*picon->colormap));
cristy101ab702011-10-13 13:06:32 +0000740 if (picon->colormap == (PixelInfo *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000741 ThrowWriterException(ResourceLimitError,"MemoryAllocationError");
cristybb503372010-05-27 20:51:26 +0000742 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000743 {
744 q=GetAuthenticPixels(picon,0,y,picon->columns,1,exception);
cristyacd2ed22011-08-30 01:44:23 +0000745 if (q == (Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000746 break;
cristybb503372010-05-27 20:51:26 +0000747 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000748 {
cristy4c08aed2011-07-01 19:47:50 +0000749 if (GetPixelAlpha(image,q) == (Quantum) TransparentAlpha)
cristydd12db52014-11-30 21:54:11 +0000750 SetPixelIndex(picon,(Quantum) picon->colors,q);
cristyed231572011-07-14 02:18:59 +0000751 q+=GetPixelChannels(picon);
cristy3ed852e2009-09-05 21:47:34 +0000752 }
753 if (SyncAuthenticPixels(picon,exception) == MagickFalse)
754 break;
755 }
756 }
757 /*
758 Compute the character per pixel.
759 */
760 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000761 for (k=MaxCixels; (ssize_t) colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000762 characters_per_pixel++;
763 /*
764 XPM header.
765 */
766 (void) WriteBlobString(image,"/* XPM */\n");
767 GetPathComponent(picon->filename,BasePath,basename);
cristy151b66d2015-04-15 10:50:31 +0000768 (void) FormatLocaleString(buffer,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +0000769 "static char *%s[] = {\n",basename);
770 (void) WriteBlobString(image,buffer);
771 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristy151b66d2015-04-15 10:50:31 +0000772 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +0000773 "\"%.20g %.20g %.20g %.20g\",\n",(double) picon->columns,(double)
774 picon->rows,(double) colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +0000775 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +0000776 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +0000777 for (i=0; i < (ssize_t) colors; i++)
cristy3ed852e2009-09-05 21:47:34 +0000778 {
779 /*
780 Define XPM color.
781 */
cristy9d8c8ce2011-10-25 16:13:52 +0000782 pixel=picon->colormap[i];
cristy7020ae62012-04-18 12:58:34 +0000783 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +0000784 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +0000785 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +0000786 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +0000787 if (transparent != MagickFalse)
788 {
cristybb503372010-05-27 20:51:26 +0000789 if (i == (ssize_t) (colors-1))
cristy151b66d2015-04-15 10:50:31 +0000790 (void) CopyMagickString(name,"grey75",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000791 }
792 /*
793 Write XPM color.
794 */
795 k=i % MaxCixels;
796 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000797 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000798 {
799 k=((i-k)/MaxCixels) % MaxCixels;
800 symbol[j]=Cixel[k];
801 }
802 symbol[j]='\0';
cristy151b66d2015-04-15 10:50:31 +0000803 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",
cristye6b6ece2010-05-29 13:25:01 +0000804 symbol,name);
cristy3ed852e2009-09-05 21:47:34 +0000805 (void) WriteBlobString(image,buffer);
806 }
807 /*
808 Define XPM pixels.
809 */
810 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +0000811 for (y=0; y < (ssize_t) picon->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +0000812 {
cristy3a37efd2011-08-28 20:31:03 +0000813 p=GetVirtualPixels(picon,0,y,picon->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +0000814 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +0000815 break;
cristy3ed852e2009-09-05 21:47:34 +0000816 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +0000817 for (x=0; x < (ssize_t) picon->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +0000818 {
cristy4c08aed2011-07-01 19:47:50 +0000819 k=((ssize_t) GetPixelIndex(picon,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +0000820 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +0000821 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +0000822 {
cristy4c08aed2011-07-01 19:47:50 +0000823 k=(((int) GetPixelIndex(picon,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +0000824 symbol[j]=Cixel[k];
825 }
826 symbol[j]='\0';
cristy151b66d2015-04-15 10:50:31 +0000827 (void) CopyMagickString(buffer,symbol,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000828 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +0000829 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +0000830 }
cristy151b66d2015-04-15 10:50:31 +0000831 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +0000832 y == (ssize_t) (picon->rows-1) ? "" : ",");
cristy3ed852e2009-09-05 21:47:34 +0000833 (void) WriteBlobString(image,buffer);
cristy4cb162a2010-05-30 03:04:47 +0000834 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
835 picon->rows);
cristy3ed852e2009-09-05 21:47:34 +0000836 if (status == MagickFalse)
837 break;
838 }
839 picon=DestroyImage(picon);
840 (void) WriteBlobString(image,"};\n");
841 (void) CloseBlob(image);
842 return(MagickTrue);
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
850% W r i t e X P M I m a g e %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
cristya60c0202010-07-31 21:36:45 +0000856% WriteXPMImage() writes an image to a file in the X pixmap format.
cristy3ed852e2009-09-05 21:47:34 +0000857%
858% The format of the WriteXPMImage method is:
859%
cristy3a37efd2011-08-28 20:31:03 +0000860% MagickBooleanType WriteXPMImage(const ImageInfo *image_info,
861% Image *image,ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000862%
863% A description of each parameter follows.
864%
865% o image_info: the image info.
866%
867% o image: The image.
868%
cristy3a37efd2011-08-28 20:31:03 +0000869% o exception: return any errors or warnings in this structure.
870%
cristy3ed852e2009-09-05 21:47:34 +0000871*/
cristy3a37efd2011-08-28 20:31:03 +0000872static MagickBooleanType WriteXPMImage(const ImageInfo *image_info,Image *image,
873 ExceptionInfo *exception)
cristy3ed852e2009-09-05 21:47:34 +0000874{
875#define MaxCixels 92
876
877 static const char
878 Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk"
879 "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|";
880
881 char
cristy151b66d2015-04-15 10:50:31 +0000882 buffer[MagickPathExtent],
883 basename[MagickPathExtent],
884 name[MagickPathExtent],
885 symbol[MagickPathExtent];
cristy3ed852e2009-09-05 21:47:34 +0000886
cristy3ed852e2009-09-05 21:47:34 +0000887 MagickBooleanType
888 status;
889
cristy4c08aed2011-07-01 19:47:50 +0000890 PixelInfo
cristy3ed852e2009-09-05 21:47:34 +0000891 pixel;
892
cristy4c08aed2011-07-01 19:47:50 +0000893 register const Quantum
cristy3ed852e2009-09-05 21:47:34 +0000894 *p;
895
cristybb503372010-05-27 20:51:26 +0000896 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000897 i,
898 x;
899
cristybb503372010-05-27 20:51:26 +0000900 size_t
cristy3ed852e2009-09-05 21:47:34 +0000901 characters_per_pixel;
902
cristyc6da28e2011-04-28 01:41:35 +0000903 ssize_t
904 j,
905 k,
906 opacity,
907 y;
908
cristy3ed852e2009-09-05 21:47:34 +0000909 /*
910 Open output image file.
911 */
912 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000913 assert(image_info->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000914 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000915 assert(image->signature == MagickCoreSignature);
cristy3ed852e2009-09-05 21:47:34 +0000916 if (image->debug != MagickFalse)
917 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy3a37efd2011-08-28 20:31:03 +0000918 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000919 assert(exception->signature == MagickCoreSignature);
cristy3a37efd2011-08-28 20:31:03 +0000920 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
cristy3ed852e2009-09-05 21:47:34 +0000921 if (status == MagickFalse)
922 return(status);
cristy3d9f5ba2012-06-26 13:37:31 +0000923 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
cristy8d951092012-02-08 18:54:56 +0000924 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy3ed852e2009-09-05 21:47:34 +0000925 opacity=(-1);
cristy17f11b02014-12-20 19:37:04 +0000926 if (image->alpha_trait == UndefinedPixelTrait)
cristy3ed852e2009-09-05 21:47:34 +0000927 {
928 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristy018f07f2011-09-04 21:15:19 +0000929 (void) SetImageType(image,PaletteType,exception);
cristy3ed852e2009-09-05 21:47:34 +0000930 }
931 else
932 {
cristya19f1d72012-08-07 18:24:38 +0000933 double
cristy3ed852e2009-09-05 21:47:34 +0000934 alpha,
935 beta;
936
937 /*
938 Identify transparent colormap index.
939 */
940 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristydef23e52015-01-22 11:52:01 +0000941 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +0000942 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000943 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000944 {
945 if (opacity < 0)
946 {
947 opacity=i;
948 continue;
949 }
cristya19f1d72012-08-07 18:24:38 +0000950 alpha=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000951 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000952 beta=(double) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000953 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000954 if (alpha < beta)
955 opacity=i;
956 }
957 if (opacity == -1)
958 {
cristydef23e52015-01-22 11:52:01 +0000959 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristybb503372010-05-27 20:51:26 +0000960 for (i=0; i < (ssize_t) image->colors; i++)
cristy4c08aed2011-07-01 19:47:50 +0000961 if (image->colormap[i].alpha != OpaqueAlpha)
cristy3ed852e2009-09-05 21:47:34 +0000962 {
963 if (opacity < 0)
964 {
965 opacity=i;
966 continue;
967 }
cristya19f1d72012-08-07 18:24:38 +0000968 alpha=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000969 image->colormap[i].alpha;
cristya19f1d72012-08-07 18:24:38 +0000970 beta=(Quantum) TransparentAlpha-(double)
cristy4c08aed2011-07-01 19:47:50 +0000971 image->colormap[opacity].alpha;
cristy3ed852e2009-09-05 21:47:34 +0000972 if (alpha < beta)
973 opacity=i;
974 }
975 }
976 if (opacity >= 0)
977 {
978 image->colormap[opacity].red=image->transparent_color.red;
979 image->colormap[opacity].green=image->transparent_color.green;
980 image->colormap[opacity].blue=image->transparent_color.blue;
981 }
982 }
983 /*
984 Compute the character per pixel.
985 */
986 characters_per_pixel=1;
cristybb503372010-05-27 20:51:26 +0000987 for (k=MaxCixels; (ssize_t) image->colors > k; k*=MaxCixels)
cristy3ed852e2009-09-05 21:47:34 +0000988 characters_per_pixel++;
989 /*
990 XPM header.
991 */
992 (void) WriteBlobString(image,"/* XPM */\n");
993 GetPathComponent(image->filename,BasePath,basename);
994 if (isalnum((int) ((unsigned char) *basename)) == 0)
995 {
cristy151b66d2015-04-15 10:50:31 +0000996 (void) FormatLocaleString(buffer,MagickPathExtent,"xpm_%s",basename);
997 (void) CopyMagickString(basename,buffer,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +0000998 }
cristy2f4741f2010-11-01 23:29:42 +0000999 if (isalpha((int) ((unsigned char) basename[0])) == 0)
1000 basename[0]='_';
1001 for (i=1; basename[i] != '\0'; i++)
1002 if (isalnum((int) ((unsigned char) basename[i])) == 0)
cristy3ed852e2009-09-05 21:47:34 +00001003 basename[i]='_';
cristy151b66d2015-04-15 10:50:31 +00001004 (void) FormatLocaleString(buffer,MagickPathExtent,
cristy3ed852e2009-09-05 21:47:34 +00001005 "static char *%s[] = {\n",basename);
1006 (void) WriteBlobString(image,buffer);
1007 (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n");
cristy151b66d2015-04-15 10:50:31 +00001008 (void) FormatLocaleString(buffer,MagickPathExtent,
cristye8c25f92010-06-03 00:53:06 +00001009 "\"%.20g %.20g %.20g %.20g \",\n",(double) image->columns,(double)
1010 image->rows,(double) image->colors,(double) characters_per_pixel);
cristy3ed852e2009-09-05 21:47:34 +00001011 (void) WriteBlobString(image,buffer);
cristy4c08aed2011-07-01 19:47:50 +00001012 GetPixelInfo(image,&pixel);
cristybb503372010-05-27 20:51:26 +00001013 for (i=0; i < (ssize_t) image->colors; i++)
cristy3ed852e2009-09-05 21:47:34 +00001014 {
1015 /*
1016 Define XPM color.
1017 */
cristy9d8c8ce2011-10-25 16:13:52 +00001018 pixel=image->colormap[i];
cristy7020ae62012-04-18 12:58:34 +00001019 pixel.colorspace=sRGBColorspace;
cristy3ed852e2009-09-05 21:47:34 +00001020 pixel.depth=8;
cristya19f1d72012-08-07 18:24:38 +00001021 pixel.alpha=(double) OpaqueAlpha;
cristy269c9412011-10-13 23:41:15 +00001022 (void) QueryColorname(image,&pixel,XPMCompliance,name,exception);
cristy3ed852e2009-09-05 21:47:34 +00001023 if (i == opacity)
cristy151b66d2015-04-15 10:50:31 +00001024 (void) CopyMagickString(name,"None",MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001025 /*
1026 Write XPM color.
1027 */
1028 k=i % MaxCixels;
1029 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001030 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001031 {
1032 k=((i-k)/MaxCixels) % MaxCixels;
1033 symbol[j]=Cixel[k];
1034 }
1035 symbol[j]='\0';
cristy151b66d2015-04-15 10:50:31 +00001036 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s c %s\",\n",symbol,
cristy3ed852e2009-09-05 21:47:34 +00001037 name);
1038 (void) WriteBlobString(image,buffer);
1039 }
1040 /*
1041 Define XPM pixels.
1042 */
1043 (void) WriteBlobString(image,"/* pixels */\n");
cristybb503372010-05-27 20:51:26 +00001044 for (y=0; y < (ssize_t) image->rows; y++)
cristy3ed852e2009-09-05 21:47:34 +00001045 {
cristy3a37efd2011-08-28 20:31:03 +00001046 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
cristy4c08aed2011-07-01 19:47:50 +00001047 if (p == (const Quantum *) NULL)
cristy3ed852e2009-09-05 21:47:34 +00001048 break;
cristy3ed852e2009-09-05 21:47:34 +00001049 (void) WriteBlobString(image,"\"");
cristybb503372010-05-27 20:51:26 +00001050 for (x=0; x < (ssize_t) image->columns; x++)
cristy3ed852e2009-09-05 21:47:34 +00001051 {
cristy4c08aed2011-07-01 19:47:50 +00001052 k=((ssize_t) GetPixelIndex(image,p) % MaxCixels);
cristy3ed852e2009-09-05 21:47:34 +00001053 symbol[0]=Cixel[k];
cristybb503372010-05-27 20:51:26 +00001054 for (j=1; j < (ssize_t) characters_per_pixel; j++)
cristy3ed852e2009-09-05 21:47:34 +00001055 {
cristy4c08aed2011-07-01 19:47:50 +00001056 k=(((int) GetPixelIndex(image,p)-k)/MaxCixels) % MaxCixels;
cristy3ed852e2009-09-05 21:47:34 +00001057 symbol[j]=Cixel[k];
1058 }
1059 symbol[j]='\0';
cristy151b66d2015-04-15 10:50:31 +00001060 (void) CopyMagickString(buffer,symbol,MagickPathExtent);
cristy3ed852e2009-09-05 21:47:34 +00001061 (void) WriteBlobString(image,buffer);
cristyed231572011-07-14 02:18:59 +00001062 p+=GetPixelChannels(image);
cristy3ed852e2009-09-05 21:47:34 +00001063 }
cristy151b66d2015-04-15 10:50:31 +00001064 (void) FormatLocaleString(buffer,MagickPathExtent,"\"%s\n",
cristybb503372010-05-27 20:51:26 +00001065 (y == (ssize_t) (image->rows-1) ? "" : ","));
cristy3ed852e2009-09-05 21:47:34 +00001066 (void) WriteBlobString(image,buffer);
cristy8b27a6d2010-02-14 03:31:15 +00001067 if (image->previous == (Image *) NULL)
1068 {
cristy4cb162a2010-05-30 03:04:47 +00001069 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
1070 image->rows);
cristy8b27a6d2010-02-14 03:31:15 +00001071 if (status == MagickFalse)
1072 break;
1073 }
cristy3ed852e2009-09-05 21:47:34 +00001074 }
1075 (void) WriteBlobString(image,"};\n");
1076 (void) CloseBlob(image);
1077 return(MagickTrue);
1078}