/src/graphicsmagick/coders/xpm.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2026 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % SPDX-License-Identifier: MIT |
6 | | % |
7 | | % This program is covered by multiple licenses, which are described in |
8 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
9 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
10 | | % |
11 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
12 | | % % |
13 | | % % |
14 | | % % |
15 | | % X X PPPP M M % |
16 | | % X X P P MM MM % |
17 | | % X PPPP M M M % |
18 | | % X X P M M % |
19 | | % X X P M M % |
20 | | % % |
21 | | % % |
22 | | % Read/Write X Windows system Pixmap Format. % |
23 | | % % |
24 | | % % |
25 | | % Software Design % |
26 | | % John Cristy % |
27 | | % July 1992 % |
28 | | % % |
29 | | % % |
30 | | % % |
31 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
32 | | % |
33 | | % |
34 | | */ |
35 | | |
36 | | /* |
37 | | Include declarations. |
38 | | */ |
39 | | #include "magick/studio.h" |
40 | | #include "magick/analyze.h" |
41 | | #include "magick/blob.h" |
42 | | #include "magick/color.h" |
43 | | #include "magick/color_lookup.h" |
44 | | #include "magick/colormap.h" |
45 | | #include "magick/log.h" |
46 | | #include "magick/magick.h" |
47 | | #include "magick/monitor.h" |
48 | | #include "magick/pixel_cache.h" |
49 | | #include "magick/quantize.h" |
50 | | #include "magick/resize.h" |
51 | | #include "magick/utility.h" |
52 | | #include "magick/static.h" |
53 | | |
54 | | |
55 | 19.2k | #define MAX_XPM_SUPPORTED_COLORS Max(0x20000,MaxColormapSize) |
56 | | |
57 | | /* |
58 | | Forward declarations. |
59 | | */ |
60 | | static unsigned int |
61 | | WritePICONImage(const ImageInfo *,Image *), |
62 | | WriteXPMImage(const ImageInfo *,Image *); |
63 | | |
64 | | typedef magick_uint32_t xpmkeyval_t; |
65 | | |
66 | | typedef struct xpmkey |
67 | | { |
68 | | magick_uint32_t index; /* Colormap index */ |
69 | | xpmkeyval_t keyval; /* Encoded XPM key value */ |
70 | | } xpmkey_t; |
71 | | |
72 | | |
73 | | static int XPMKeyCompare(const void *l, const void *r) |
74 | 70.7M | { |
75 | 70.7M | const xpmkey_t * restrict lp = (const xpmkey_t *) l; |
76 | 70.7M | const xpmkey_t * restrict rp = (const xpmkey_t *) r; |
77 | 70.7M | int sense; |
78 | | |
79 | 70.7M | if (lp->keyval > rp->keyval) |
80 | 25.0M | sense = 1; |
81 | 45.6M | else if (lp->keyval < rp->keyval) |
82 | 18.4M | sense = -1; |
83 | 27.1M | else |
84 | 27.1M | sense = 0; |
85 | | |
86 | 70.7M | return sense; |
87 | 70.7M | } |
88 | | |
89 | | /* |
90 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
91 | | % % |
92 | | % % |
93 | | % % |
94 | | % I s X P M % |
95 | | % % |
96 | | % % |
97 | | % % |
98 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
99 | | % |
100 | | % Method IsXPM returns True if the image format type, identified by the |
101 | | % magick string, is XPM. |
102 | | % |
103 | | % The format of the IsXPM method is: |
104 | | % |
105 | | % unsigned int IsXPM(const unsigned char *magick,const size_t length) |
106 | | % |
107 | | % A description of each parameter follows: |
108 | | % |
109 | | % o status: Method IsXPM returns True if the image format type is XPM. |
110 | | % |
111 | | % o magick: This string is generally the first few bytes of an image file |
112 | | % or blob. |
113 | | % |
114 | | % o length: Specifies the length of the magick string. |
115 | | % |
116 | | % |
117 | | */ |
118 | | static unsigned int IsXPM(const unsigned char *magick,const size_t length) |
119 | 0 | { |
120 | 0 | if (length < 9) |
121 | 0 | return(False); |
122 | 0 | if (LocaleNCompare((char *) magick,"/* XPM */",9) == 0) |
123 | 0 | return(True); |
124 | 0 | return(False); |
125 | 0 | } |
126 | | |
127 | | /* |
128 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
129 | | % % |
130 | | % % |
131 | | % % |
132 | | % R e a d X P M I m a g e % |
133 | | % % |
134 | | % % |
135 | | % % |
136 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
137 | | % |
138 | | % Method ReadXPMImage reads an X11 pixmap image file and returns it. It |
139 | | % allocates the memory necessary for the new Image structure and returns a |
140 | | % pointer to the new image. |
141 | | % |
142 | | % The format of the ReadXPMImage method is: |
143 | | % |
144 | | % Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
145 | | % |
146 | | % A description of each parameter follows: |
147 | | % |
148 | | % o image: Method ReadXPMImage returns a pointer to the image after |
149 | | % creating it. A null image is returned if there is a memory shortage |
150 | | % or if the image cannot be read. |
151 | | % |
152 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
153 | | % |
154 | | % o exception: return any errors or warnings in this structure. |
155 | | % |
156 | | % |
157 | | */ |
158 | | |
159 | | static char **StringToListMod(char *text) |
160 | 13.9k | { |
161 | 13.9k | char |
162 | 13.9k | **textlist; |
163 | | |
164 | 13.9k | register char |
165 | 13.9k | *p; |
166 | | |
167 | 13.9k | register size_t |
168 | 13.9k | i; |
169 | | |
170 | 13.9k | size_t |
171 | 13.9k | lines; |
172 | | |
173 | 13.9k | if (text == (char *) NULL) |
174 | 0 | return((char **) NULL); |
175 | | |
176 | | /* |
177 | | Convert string to an ASCII list, modifying the input string. |
178 | | */ |
179 | 13.9k | lines=1; |
180 | 62.0M | for (p=text; *p != '\0'; p++) |
181 | 62.0M | if (*p == '\n') |
182 | 7.21M | lines++; |
183 | 13.9k | textlist=MagickAllocateResourceLimitedMemory(char **,(lines+1)*sizeof(char *)); |
184 | 13.9k | if (textlist == (char **) NULL) |
185 | 0 | MagickFatalError3(ResourceLimitFatalError,MemoryAllocationFailed, |
186 | 13.9k | UnableToConvertText); |
187 | 13.9k | i=0; |
188 | 13.9k | textlist[i]=text; |
189 | 62.0M | for (p=text; *p != '\0'; p++) |
190 | 62.0M | { |
191 | 62.0M | if (*p == '\r') |
192 | 10.3k | { |
193 | 10.3k | *p='\0'; |
194 | 10.3k | continue; |
195 | 10.3k | } |
196 | 62.0M | if (*p == '\n') |
197 | 7.21M | { |
198 | 7.21M | *p='\0'; |
199 | 7.21M | if (*(p+1) != '\0') |
200 | 7.20M | { |
201 | 7.20M | i++; |
202 | 7.20M | textlist[i]=p+1; |
203 | 7.20M | } |
204 | 7.21M | } |
205 | 62.0M | } |
206 | 13.9k | i++; |
207 | 13.9k | textlist[i]=(char *) NULL; |
208 | 13.9k | return(textlist); |
209 | 13.9k | } |
210 | | |
211 | | static char *ParseColor(char * restrict data) MAGICK_FUNC_PURE; |
212 | | |
213 | | static char *ParseColor(char * restrict data) |
214 | 7.00M | { |
215 | 38.9M | #define NumberTargets 6U |
216 | 7.00M | #define TargetsDimension 4U |
217 | | |
218 | 7.00M | static const char |
219 | 7.00M | targets[NumberTargets][TargetsDimension] = { "c ", "g ", "g4 ", "m ", "b ", "s " }; |
220 | | |
221 | 7.00M | register char |
222 | 7.00M | * restrict p, /* points into 'data' */ |
223 | 7.00M | * restrict r; /* points into 'data' */ |
224 | | |
225 | 7.00M | register const char |
226 | 7.00M | *q; /* points into targets */ |
227 | | |
228 | 7.00M | register unsigned long |
229 | 7.00M | i; |
230 | | |
231 | 38.9M | for (i=0; i < NumberTargets; i++) |
232 | 33.6M | { |
233 | 33.6M | r=data; |
234 | 177M | for (q=targets[i]; *r != '\0'; r++) |
235 | 146M | { |
236 | 146M | if (*r != *q) |
237 | 144M | continue; |
238 | 1.72M | if (!isspace((int) (*(r-1)))) |
239 | 20.4k | continue; |
240 | 1.70M | p=r; |
241 | 1.70M | for ( ; ; ) |
242 | 5.10M | { |
243 | 5.10M | if ((size_t) (q-targets[i]) >= TargetsDimension) |
244 | 0 | return((char *) NULL); |
245 | 5.10M | if (*q == '\0') |
246 | 1.69M | return(r); |
247 | 3.40M | if (*p++ != *q++) |
248 | 10.4k | break; |
249 | 3.40M | } |
250 | 10.4k | q=targets[i]; |
251 | 10.4k | } |
252 | 33.6M | } |
253 | 5.31M | return((char *) NULL); |
254 | 7.00M | } |
255 | | #define ThrowXPMReaderException(code_,reason_,image_) \ |
256 | 33.9k | do { \ |
257 | 33.9k | MagickFreeResourceLimitedMemory(xpmkey_t *,keys); \ |
258 | 33.9k | MagickFreeResourceLimitedMemory(char **,textlist); \ |
259 | 33.9k | MagickFreeResourceLimitedMemory(char *,xpm_buffer); \ |
260 | 33.9k | ThrowReaderException(code_,reason_,image_); \ |
261 | 0 | } while (0); |
262 | | |
263 | | static Image *ReadXPMImage(const ImageInfo *image_info,ExceptionInfo *exception) |
264 | 36.2k | { |
265 | 36.2k | xpmkey_t |
266 | 36.2k | *keys = (xpmkey_t *) NULL; |
267 | | |
268 | 36.2k | char |
269 | 36.2k | target[MaxTextExtent], |
270 | 36.2k | **textlist = (char **) NULL, |
271 | 36.2k | *xpm_buffer = (char *) NULL; |
272 | | |
273 | 36.2k | Image |
274 | 36.2k | *image; |
275 | | |
276 | 36.2k | int |
277 | 36.2k | count; |
278 | | |
279 | 36.2k | unsigned int |
280 | 36.2k | i, |
281 | 36.2k | j, |
282 | 36.2k | k, |
283 | 36.2k | none; |
284 | | |
285 | 36.2k | long |
286 | 36.2k | y; |
287 | | |
288 | 36.2k | register char |
289 | 36.2k | *p, |
290 | 36.2k | *q; |
291 | | |
292 | 36.2k | register IndexPacket |
293 | 36.2k | *indexes; |
294 | | |
295 | 36.2k | register long |
296 | 36.2k | x; |
297 | | |
298 | 36.2k | register PixelPacket |
299 | 36.2k | *r; |
300 | | |
301 | 36.2k | MagickPassFail |
302 | 36.2k | status; |
303 | | |
304 | 36.2k | MagickBool |
305 | 36.2k | colormap_initialized; |
306 | | |
307 | 36.2k | size_t |
308 | 36.2k | length; |
309 | | |
310 | 36.2k | unsigned int |
311 | 36.2k | width; /* characters per pixel */ |
312 | | |
313 | | /* |
314 | | Open image file. |
315 | | */ |
316 | 36.2k | assert(image_info != (const ImageInfo *) NULL); |
317 | 36.2k | assert(image_info->signature == MagickSignature); |
318 | 36.2k | assert(exception != (ExceptionInfo *) NULL); |
319 | 36.2k | assert(exception->signature == MagickSignature); |
320 | 36.2k | image=AllocateImage(image_info); |
321 | 36.2k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
322 | 36.2k | if (status == MagickFail) |
323 | 36.2k | ThrowXPMReaderException(FileOpenError,UnableToOpenFile,image); |
324 | | /* |
325 | | Read XPM file. |
326 | | */ |
327 | 36.2k | length=MaxTextExtent*4; |
328 | 36.2k | xpm_buffer=MagickAllocateResourceLimitedMemory(char *,length); |
329 | 36.2k | if (xpm_buffer != (char *) NULL) |
330 | 36.2k | { |
331 | 36.2k | xpm_buffer[0]='\0'; |
332 | 36.2k | p=xpm_buffer; |
333 | 2.42M | while (ReadBlobString(image,p) != (char *) NULL) |
334 | 2.39M | { |
335 | 2.39M | char *new_xpm_buffer; |
336 | 2.39M | if (*p == '#') |
337 | 9.66k | if ((p == xpm_buffer) || (*(p-1) == '\n')) |
338 | 7.27k | continue; |
339 | 2.38M | if ((*p == '}') && (*(p+1) == ';')) |
340 | 4 | break; |
341 | 2.38M | p+=strlen(p); |
342 | 2.38M | if (((size_t) (p-xpm_buffer)+MaxTextExtent+1) < length) |
343 | 2.37M | continue; |
344 | 10.8k | length += MaxTextExtent*4; |
345 | 10.8k | new_xpm_buffer=MagickReallocateResourceLimitedMemory(char *,xpm_buffer,length); |
346 | 10.8k | if (new_xpm_buffer == (char *) NULL) |
347 | 0 | { |
348 | 0 | MagickFreeResourceLimitedMemory(char *,xpm_buffer); |
349 | 0 | break; |
350 | 0 | } |
351 | 10.8k | if (xpm_buffer != new_xpm_buffer) |
352 | 1.43k | { |
353 | 1.43k | p=(p-xpm_buffer)+new_xpm_buffer; |
354 | 1.43k | xpm_buffer=new_xpm_buffer; |
355 | 1.43k | } |
356 | 10.8k | } |
357 | 36.2k | } |
358 | 36.2k | if (xpm_buffer == (char *) NULL) |
359 | 36.2k | ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
360 | | |
361 | | /* |
362 | | Parse image properties from file header while skipping over comment markers. |
363 | | */ |
364 | 36.2k | count=0; |
365 | 709k | for (p=xpm_buffer; ((p - xpm_buffer) < 512) && (*p != '\0'); p++) |
366 | 696k | { |
367 | 696k | if ((*p != '"') || !isdigit((int) *(p+1))) |
368 | 668k | continue; |
369 | 28.0k | count=sscanf(p+1,"%lu %lu %u %u",&image->columns,&image->rows, |
370 | 28.0k | &image->colors,&width); |
371 | 28.0k | if (count == 4) |
372 | 23.0k | { |
373 | 23.0k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
374 | 23.0k | "Columns: %lu, Rows: %lu, Colors: %u, Char Per Pixel: %u", |
375 | 23.0k | image->columns, image->rows, image->colors, width); |
376 | 23.0k | break; |
377 | 23.0k | } |
378 | 28.0k | } |
379 | 36.2k | if ((count != 4) || (width == 0) || (width > 3) || |
380 | 22.1k | (image->columns == 0) || (image->rows == 0) || |
381 | 19.4k | (image->colors == 0)) |
382 | 19.2k | ThrowXPMReaderException(CorruptImageError,ImproperImageHeader,image); |
383 | 19.2k | if (image->colors > MAX_XPM_SUPPORTED_COLORS) |
384 | 18.2k | ThrowXPMReaderException(CoderError,ColormapTooLarge,image); |
385 | 18.2k | image->depth=16; |
386 | | |
387 | | /* |
388 | | Remove unquoted characters. |
389 | | */ |
390 | 18.2k | { |
391 | 18.2k | MagickBool inquote = MagickFalse; |
392 | 18.2k | q=xpm_buffer; |
393 | 79.8M | while (*p != '\0') |
394 | 79.8M | { |
395 | 79.8M | if (*p++ == '"') |
396 | 15.6M | { |
397 | 15.6M | if (inquote) |
398 | 7.83M | *q++='\n'; |
399 | 15.6M | inquote = !inquote; |
400 | 15.6M | } |
401 | 79.8M | if (inquote) |
402 | 58.7M | *q++=*p; |
403 | 79.8M | } |
404 | 18.2k | *q='\0'; |
405 | 18.2k | if (inquote) |
406 | 3.28k | { |
407 | 3.28k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
408 | 3.28k | "Imbalanced quotes error"); |
409 | 3.28k | ThrowXPMReaderException(CorruptImageError,CorruptImage,image); |
410 | 0 | } |
411 | 18.2k | } |
412 | | |
413 | | /* |
414 | | Scan for non-white space binary control codes and reject file if |
415 | | they are present. |
416 | | */ |
417 | 62.4M | for (p=xpm_buffer; *p != '\0'; p++) |
418 | 62.4M | if (((unsigned char) *p < 32) && !isspace((int)(unsigned char) (*p))) |
419 | 1.00k | break; |
420 | 14.9k | if (*p != '\0') |
421 | 1.00k | { |
422 | 1.00k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
423 | 1.00k | "Binary control codes error"); |
424 | 1.00k | ThrowXPMReaderException(CorruptImageError,CorruptImage,image); |
425 | 0 | } |
426 | 13.9k | textlist=StringToListMod(xpm_buffer); |
427 | 13.9k | if (textlist == (char **) NULL) |
428 | 13.9k | ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
429 | | #if 0 |
430 | | if (image->logging) |
431 | | { |
432 | | for (i=0; textlist[i] != (char *) NULL; i++) |
433 | | { }; |
434 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
435 | | "TextList has %u entries", i); |
436 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
437 | | "TextList"); |
438 | | for (i=0; textlist[i] != (char *) NULL; i++) |
439 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
440 | | " %u: \"%s\"", i, textlist[i]); |
441 | | } |
442 | | #endif |
443 | | |
444 | | /* |
445 | | Initialize image structure. |
446 | | */ |
447 | 13.9k | keys=MagickAllocateResourceLimitedArray(xpmkey_t *,image->colors,sizeof(xpmkey_t)); |
448 | 13.9k | if (keys == (xpmkey_t *) NULL) |
449 | 13.9k | ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
450 | 114M | for (i=0; i < image->colors; i++) |
451 | 114M | { |
452 | 114M | keys[i].index=0; |
453 | 114M | keys[i].keyval=0; |
454 | 114M | } |
455 | 13.9k | if (image->colors <= MaxColormapSize) |
456 | 13.8k | { |
457 | 13.8k | if (!AllocateImageColormap(image,image->colors)) |
458 | 13.8k | ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
459 | 13.8k | } |
460 | 109 | else |
461 | 109 | { |
462 | | /* Allocate temporary palette. */ |
463 | 109 | if (image->colormap != (PixelPacket *)NULL) |
464 | 0 | MagickFreeMemory(image->colormap); |
465 | 109 | image->colormap=MagickAllocateMemory(PixelPacket *, |
466 | 109 | MagickArraySize((size_t) image->colors, |
467 | 109 | sizeof(PixelPacket))); |
468 | 109 | if (image->colormap==NULL) |
469 | 109 | ThrowXPMReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
470 | 109 | } |
471 | | |
472 | | /* |
473 | | Read image colormap. |
474 | | */ |
475 | 13.9k | i=1; |
476 | 13.9k | none=(~0U); |
477 | 13.9k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
478 | 13.9k | "Parsing colormap..."); |
479 | 13.9k | colormap_initialized=MagickFalse; |
480 | 5.33M | for (j=0; j < image->colors; j++) |
481 | 5.32M | { |
482 | 5.32M | p=textlist[i++]; |
483 | 5.32M | if ((p == (char *) NULL) || (p[0] == '\0')) |
484 | 2.41k | break; |
485 | 5.31M | if (image->logging) |
486 | 5.31M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
487 | 5.31M | " %u: %s", i-1, textlist[i-1]); |
488 | 5.31M | if (strlen(p) < width) |
489 | 2.11k | break; |
490 | | |
491 | 5.31M | keys[j].index = j; |
492 | 5.31M | keys[j].keyval = 0; |
493 | 13.2M | for (k=0; k < width; k++) |
494 | 7.92M | keys[j].keyval |= ((xpmkeyval_t) p[k]) << (k * 8); |
495 | | |
496 | | /* printf("Key[%03u] =\"%s\" (0x%04X)\n", keys[j].index, p, (unsigned int) keys[j].keyval); */ |
497 | | /* |
498 | | Parse color. |
499 | | */ |
500 | 5.31M | (void) strlcpy(target,"gray",sizeof(target)); |
501 | 5.31M | q=ParseColor(p+width); |
502 | 5.31M | if (q != (char *) NULL) |
503 | 1.69M | { |
504 | 3.38M | while (!isspace((int) (*q)) && (*q != '\0')) |
505 | 1.69M | q++; |
506 | 1.69M | (void) strlcpy(target,q,MaxTextExtent); |
507 | 1.69M | q=ParseColor(target); |
508 | 1.69M | if (q != (char *) NULL) |
509 | 1.38k | *q='\0'; |
510 | 1.69M | } |
511 | 5.31M | (void) MagickStripString(target); |
512 | 5.31M | if (LocaleCompare(target,"none") == 0) |
513 | 4.32k | { |
514 | 4.32k | image->storage_class=DirectClass; |
515 | 4.32k | image->matte=True; |
516 | 4.32k | none=j; |
517 | 4.32k | (void) strlcpy(target,"black",sizeof(target)); |
518 | 4.32k | } |
519 | 5.31M | if (!QueryColorDatabase(target,&image->colormap[j],exception)) |
520 | 720 | { |
521 | | /* Promote warning to error */ |
522 | 720 | exception->severity = CorruptImageError; |
523 | 720 | break; |
524 | 720 | } |
525 | | /* We are going to be done now */ |
526 | 5.31M | if (j+1 == image->colors) |
527 | 8.74k | colormap_initialized=MagickTrue; |
528 | 5.31M | } |
529 | 13.9k | if (!colormap_initialized) |
530 | 8.74k | ThrowXPMReaderException(CorruptImageError,CorruptImage,image); |
531 | | |
532 | | /* |
533 | | Log the XPM Colormap if logging is enabled. |
534 | | */ |
535 | 8.74k | if (image->logging) |
536 | 8.74k | { |
537 | 8.74k | char |
538 | 8.74k | name[MaxTextExtent]; |
539 | | |
540 | 8.74k | unsigned int |
541 | 8.74k | ii; |
542 | | |
543 | 8.74k | register PixelPacket |
544 | 8.74k | *p; |
545 | | |
546 | | /* |
547 | | Display image colormap. |
548 | | */ |
549 | 8.74k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
550 | 8.74k | "XPM Colormap (Depth %u):", image->depth); |
551 | 8.74k | p=image->colormap; |
552 | 5.08M | for (ii=0; ii < image->colors; ii++) |
553 | 5.07M | { |
554 | 5.07M | char |
555 | 5.07M | tuple[MaxTextExtent]; |
556 | | |
557 | 5.07M | GetColorTuple(p,image->depth,image->matte,False,tuple); |
558 | 5.07M | (void) QueryColorname(image,p,X11Compliance,name,&image->exception); |
559 | 5.07M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
560 | 5.07M | " %" MAGICK_SIZE_T_F "u: %.1024s\t %.1024s", |
561 | 5.07M | (MAGICK_SIZE_T) ii,tuple,name); |
562 | 5.07M | p++; |
563 | 5.07M | } |
564 | 8.74k | } |
565 | | |
566 | 8.74k | image->depth=GetImageDepth(image,&image->exception); |
567 | 8.74k | image->depth=NormalizeDepthToOctet(image->depth); |
568 | | |
569 | 8.74k | if (!image_info->ping) |
570 | 8.74k | { |
571 | 8.74k | size_t |
572 | 8.74k | expected_pixels=(size_t) image->rows * (size_t) image->columns, |
573 | 8.74k | pixels=0; |
574 | | |
575 | 8.74k | xpmkey_t |
576 | 8.74k | key = { 0, 0 }, |
577 | 8.74k | *keyp; |
578 | | |
579 | 8.74k | xpmkeyval_t |
580 | 8.74k | keyval; |
581 | | |
582 | 8.74k | j = 0; |
583 | | |
584 | | /* |
585 | | Sort keys by kval |
586 | | */ |
587 | 8.74k | qsort((void *) keys,image->colors, sizeof(keys[0]), XPMKeyCompare); |
588 | | |
589 | | #if 0 |
590 | | for (j=0; j < image->colors; j++) |
591 | | printf("Key[%03u] = (0x%04X)\n", keys[j].index, (unsigned int) keys[j].keyval); |
592 | | #endif |
593 | | |
594 | | /* |
595 | | Read image pixels. |
596 | | */ |
597 | 8.74k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
598 | 8.74k | "Parsing pixels..."); |
599 | 102k | for (y=0; y < (long) image->rows; y++) |
600 | 100k | { |
601 | 100k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
602 | 100k | " y=%ld", y); |
603 | 100k | p=textlist[i++]; |
604 | 100k | if ((p == (char *) NULL) || (p[0] == '\0')) |
605 | 2.64k | break; |
606 | 97.4k | if (image->logging) |
607 | 97.4k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
608 | 97.4k | " %u: %s", i-1, textlist[i-1]); |
609 | 97.4k | r=SetImagePixels(image,0,y,image->columns,1); |
610 | 97.4k | if (r == (PixelPacket *) NULL) |
611 | 3.30k | break; |
612 | 94.1k | indexes=AccessMutableIndexes(image); |
613 | 2.22M | for (x=0; x < (long) image->columns; x++) |
614 | 2.13M | { |
615 | 2.13M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
616 | 2.13M | " x=%ld", y); |
617 | 2.13M | keyval=0; |
618 | 7.45M | for (k=0; k < width; k++) |
619 | 5.31M | { |
620 | 5.31M | if (p[k] == '\0') |
621 | 130 | { |
622 | 130 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
623 | 130 | "Unexpected end of row %ld! (k=%u)", y, k); |
624 | 130 | ThrowXPMReaderException(CorruptImageError,UnexpectedEndOfFile, |
625 | 130 | image); |
626 | 0 | break; |
627 | 130 | } |
628 | 5.31M | keyval |= ((xpmkeyval_t) p[k]) << (k * 8); |
629 | 5.31M | } |
630 | 2.13M | if (keyval != key.keyval) |
631 | 1.26M | { |
632 | 1.26M | key.keyval = keyval; |
633 | 1.26M | keyp=(xpmkey_t *) bsearch((const void *) &key,(void *) keys,image->colors, |
634 | 1.26M | sizeof(keys[0]),XPMKeyCompare); |
635 | 1.26M | if (keyp != (xpmkey_t *) NULL) |
636 | 1.26M | { |
637 | 1.26M | key.index = keyp->index; |
638 | 1.26M | } |
639 | 219 | else |
640 | 219 | { |
641 | 219 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
642 | 219 | "Failed to find key! (0x%04X)", |
643 | 219 | key.keyval); |
644 | 219 | ThrowXPMReaderException(CorruptImageError,CorruptImage, |
645 | 219 | image); |
646 | 0 | break; |
647 | 219 | } |
648 | 1.26M | } |
649 | | |
650 | 2.13M | VerifyColormapIndex(status,image,key.index); |
651 | 2.13M | if (status == MagickFail) |
652 | 0 | break; |
653 | 2.13M | if (image->storage_class == PseudoClass) |
654 | 137k | indexes[x]=(IndexPacket) key.index; |
655 | 2.13M | *r=image->colormap[key.index]; |
656 | 2.13M | r->opacity=(Quantum) |
657 | 2.13M | (key.index == none ? TransparentOpacity : OpaqueOpacity); |
658 | 2.13M | pixels++; |
659 | 2.13M | r++; |
660 | 2.13M | p+=width; |
661 | 2.13M | } /* for (x= */ |
662 | 93.8k | if (status == MagickFail) |
663 | 0 | break; |
664 | 93.8k | if (!SyncImagePixels(image)) |
665 | 0 | break; |
666 | 93.8k | } /* for (y= */ |
667 | 8.39k | if (pixels != expected_pixels) |
668 | 6.12k | { |
669 | 6.12k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
670 | 6.12k | "Failed to transfer required" |
671 | 6.12k | " number of pixels (%zu vs %zu)",pixels,expected_pixels); |
672 | 6.12k | ThrowXPMReaderException(CorruptImageError,CorruptImage, |
673 | 6.12k | image); |
674 | 0 | } |
675 | 8.39k | } /* if (!image_info->ping) */ |
676 | 2.26k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
677 | 2.26k | "status=%u", status); |
678 | | /* |
679 | | Free resources. |
680 | | */ |
681 | 2.26k | MagickFreeResourceLimitedMemory(xpmkey_t *,keys); |
682 | 2.26k | MagickFreeResourceLimitedMemory(char **,textlist); |
683 | 2.26k | MagickFreeResourceLimitedMemory(char *,xpm_buffer); |
684 | 2.26k | if (image->colors > MaxColormapSize) |
685 | 15 | { |
686 | | /* Release temporary palette. */ |
687 | 15 | MagickFreeMemory(image->colormap); |
688 | 15 | image->colors = 0; |
689 | 15 | image->storage_class = DirectClass; |
690 | 15 | } |
691 | 2.26k | CloseBlob(image); |
692 | 2.26k | StopTimer(&image->timer); |
693 | | |
694 | 2.26k | return(image); |
695 | 8.74k | } |
696 | | |
697 | | /* |
698 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
699 | | % % |
700 | | % % |
701 | | % % |
702 | | % R e g i s t e r X P M I m a g e % |
703 | | % % |
704 | | % % |
705 | | % % |
706 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
707 | | % |
708 | | % Method RegisterXPMImage adds attributes for the XPM image format to |
709 | | % the list of supported formats. The attributes include the image format |
710 | | % tag, a method to read and/or write the format, whether the format |
711 | | % supports the saving of more than one frame to the same file or blob, |
712 | | % whether the format supports native in-memory I/O, and a brief |
713 | | % description of the format. |
714 | | % |
715 | | % The format of the RegisterXPMImage method is: |
716 | | % |
717 | | % RegisterXPMImage(void) |
718 | | % |
719 | | */ |
720 | | ModuleExport void RegisterXPMImage(void) |
721 | 6 | { |
722 | 6 | MagickInfo |
723 | 6 | *entry; |
724 | | |
725 | 6 | entry=SetMagickInfo("PICON"); |
726 | 6 | entry->decoder=(DecoderHandler) ReadXPMImage; |
727 | 6 | entry->encoder=(EncoderHandler) WritePICONImage; |
728 | 6 | entry->adjoin=False; |
729 | 6 | entry->description="Personal Icon"; |
730 | 6 | entry->module="XPM"; |
731 | 6 | (void) RegisterMagickInfo(entry); |
732 | | |
733 | 6 | entry=SetMagickInfo("PM"); |
734 | 6 | entry->decoder=(DecoderHandler) ReadXPMImage; |
735 | 6 | entry->encoder=(EncoderHandler) WriteXPMImage; |
736 | 6 | entry->adjoin=False; |
737 | 6 | entry->stealth=True; /* Don't list in '-list format' output */ |
738 | 6 | entry->description="X Windows system pixmap (color)"; |
739 | 6 | entry->module="XPM"; |
740 | 6 | (void) RegisterMagickInfo(entry); |
741 | | |
742 | 6 | entry=SetMagickInfo("XPM"); |
743 | 6 | entry->decoder=(DecoderHandler) ReadXPMImage; |
744 | 6 | entry->encoder=(EncoderHandler) WriteXPMImage; |
745 | 6 | entry->magick=(MagickHandler) IsXPM; |
746 | 6 | entry->adjoin=False; |
747 | 6 | entry->description="X Windows system pixmap (color)"; |
748 | 6 | entry->module="XPM"; |
749 | 6 | (void) RegisterMagickInfo(entry); |
750 | 6 | } |
751 | | |
752 | | /* |
753 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
754 | | % % |
755 | | % % |
756 | | % % |
757 | | % U n r e g i s t e r X P M I m a g e % |
758 | | % % |
759 | | % % |
760 | | % % |
761 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
762 | | % |
763 | | % Method UnregisterXPMImage removes format registrations made by the |
764 | | % XPM module from the list of supported formats. |
765 | | % |
766 | | % The format of the UnregisterXPMImage method is: |
767 | | % |
768 | | % UnregisterXPMImage(void) |
769 | | % |
770 | | */ |
771 | | ModuleExport void UnregisterXPMImage(void) |
772 | 0 | { |
773 | 0 | (void) UnregisterMagickInfo("PICON"); |
774 | 0 | (void) UnregisterMagickInfo("PM"); |
775 | 0 | (void) UnregisterMagickInfo("XPM"); |
776 | 0 | } |
777 | | |
778 | | /* |
779 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
780 | | % % |
781 | | % % |
782 | | % % |
783 | | % W r i t e P I C O N I m a g e % |
784 | | % % |
785 | | % % |
786 | | % % |
787 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
788 | | % |
789 | | % Procedure WritePICONImage writes an image to a file in the Personal Icon |
790 | | % format. |
791 | | % |
792 | | % The format of the WritePICONImage method is: |
793 | | % |
794 | | % MagickPassFail WritePICONImage(const ImageInfo *image_info,Image *image) |
795 | | % |
796 | | % A description of each parameter follows. |
797 | | % |
798 | | % o status: Method WritePICONImage return MagickPass if the image is written. |
799 | | % MagickFail is returned is there is a memory shortage or if the image file |
800 | | % fails to write. |
801 | | % |
802 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
803 | | % |
804 | | % o image: A pointer to an Image structure. |
805 | | % |
806 | | % |
807 | | */ |
808 | | static MagickPassFail WritePICONImage(const ImageInfo *image_info,Image *image) |
809 | 1.06k | { |
810 | 1.06k | #define ColormapExtent 155 |
811 | 1.06k | #define GraymapExtent 95 |
812 | 1.06k | #define PiconGeometry "48x48>" |
813 | | |
814 | 1.06k | static const unsigned char |
815 | 1.06k | Colormap[]= |
816 | 1.06k | { |
817 | 1.06k | 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x06, 0x00, 0x05, 0x00, 0xf4, 0x05, |
818 | 1.06k | 0x00, 0x00, 0x00, 0x00, 0x2f, 0x4f, 0x4f, 0x70, 0x80, 0x90, 0x7e, 0x7e, |
819 | 1.06k | 0x7e, 0xdc, 0xdc, 0xdc, 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, |
820 | 1.06k | 0xff, 0x1e, 0x90, 0xff, 0x87, 0xce, 0xeb, 0xe6, 0xe6, 0xfa, 0x00, 0xff, |
821 | 1.06k | 0xff, 0x80, 0x00, 0x80, 0xb2, 0x22, 0x22, 0x2e, 0x8b, 0x57, 0x32, 0xcd, |
822 | 1.06k | 0x32, 0x00, 0xff, 0x00, 0x98, 0xfb, 0x98, 0xff, 0x00, 0xff, 0xff, 0x00, |
823 | 1.06k | 0x00, 0xff, 0x63, 0x47, 0xff, 0xa5, 0x00, 0xff, 0xd7, 0x00, 0xff, 0xff, |
824 | 1.06k | 0x00, 0xee, 0x82, 0xee, 0xa0, 0x52, 0x2d, 0xcd, 0x85, 0x3f, 0xd2, 0xb4, |
825 | 1.06k | 0x8c, 0xf5, 0xde, 0xb3, 0xff, 0xfa, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, |
826 | 1.06k | 0x00, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, |
827 | 1.06k | 0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x00, 0x05, 0x18, 0x20, 0x10, 0x08, |
828 | 1.06k | 0x03, 0x51, 0x18, 0x07, 0x92, 0x28, 0x0b, 0xd3, 0x38, 0x0f, 0x14, 0x49, |
829 | 1.06k | 0x13, 0x55, 0x59, 0x17, 0x96, 0x69, 0x1b, 0xd7, 0x85, 0x00, 0x3b, |
830 | 1.06k | }, |
831 | 1.06k | Graymap[]= |
832 | 1.06k | { |
833 | 1.06k | 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x04, 0x00, 0x04, 0x00, 0xf3, 0x0f, |
834 | 1.06k | 0x00, 0x00, 0x00, 0x00, 0x12, 0x12, 0x12, 0x21, 0x21, 0x21, 0x33, 0x33, |
835 | 1.06k | 0x33, 0x45, 0x45, 0x45, 0x54, 0x54, 0x54, 0x66, 0x66, 0x66, 0x78, 0x78, |
836 | 1.06k | 0x78, 0x87, 0x87, 0x87, 0x99, 0x99, 0x99, 0xab, 0xab, 0xab, 0xba, 0xba, |
837 | 1.06k | 0xba, 0xcc, 0xcc, 0xcc, 0xde, 0xde, 0xde, 0xed, 0xed, 0xed, 0xff, 0xff, |
838 | 1.06k | 0xff, 0x21, 0xf9, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, |
839 | 1.06k | 0x00, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x04, 0x0c, 0x10, 0x04, 0x31, |
840 | 1.06k | 0x48, 0x31, 0x07, 0x25, 0xb5, 0x58, 0x73, 0x4f, 0x04, 0x00, 0x3b, |
841 | 1.06k | }; |
842 | | |
843 | 59.6k | #define MaxCixels 92 |
844 | | |
845 | 1.06k | static const char |
846 | 1.06k | Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" |
847 | 1.06k | "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; |
848 | | |
849 | 1.06k | char |
850 | 1.06k | buffer[MaxTextExtent], |
851 | 1.06k | basename[MaxTextExtent], |
852 | 1.06k | name[MaxTextExtent], |
853 | 1.06k | symbol[MaxTextExtent]; |
854 | | |
855 | 1.06k | Image |
856 | 1.06k | *picon = (Image *) NULL, |
857 | 1.06k | *map = (Image *) NULL; |
858 | | |
859 | 1.06k | int |
860 | 1.06k | j; |
861 | | |
862 | 1.06k | long |
863 | 1.06k | k, |
864 | 1.06k | y; |
865 | | |
866 | 1.06k | RectangleInfo |
867 | 1.06k | geometry; |
868 | | |
869 | 1.06k | register const PixelPacket |
870 | 1.06k | *p; |
871 | | |
872 | 1.06k | register long |
873 | 1.06k | i, |
874 | 1.06k | x; |
875 | | |
876 | 1.06k | register PixelPacket |
877 | 1.06k | *q; |
878 | | |
879 | 1.06k | unsigned int |
880 | 1.06k | status, |
881 | 1.06k | transparent; |
882 | | |
883 | 1.06k | unsigned long |
884 | 1.06k | characters_per_pixel, |
885 | 1.06k | colors; |
886 | | |
887 | 1.06k | ImageCharacteristics |
888 | 1.06k | characteristics; |
889 | | |
890 | 1.06k | assert(image_info != (const ImageInfo *) NULL); |
891 | 1.06k | assert(image_info->signature == MagickSignature); |
892 | 1.06k | assert(image != (Image *) NULL); |
893 | 1.06k | assert(image->signature == MagickSignature); |
894 | | /* |
895 | | Open output image file. |
896 | | */ |
897 | 1.06k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
898 | 1.06k | if (status == MagickFail) |
899 | 1.06k | ThrowWriterException(CoderError,UnableToOpenFile,image); |
900 | | /* |
901 | | Ensure that image is in an RGB space. |
902 | | */ |
903 | 1.06k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
904 | 1.06k | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
905 | | /* |
906 | | Analyze image to be written. |
907 | | */ |
908 | 1.06k | if (!GetImageCharacteristics(image,&characteristics, |
909 | 1.06k | (OptimizeType == image_info->type), |
910 | 1.06k | &image->exception)) |
911 | 1.06k | ThrowWriterException(CoderError,UnableToGetImageCharacteristics,image); |
912 | | |
913 | 1.06k | SetGeometry(image,&geometry); |
914 | 1.06k | (void) GetMagickGeometry(PiconGeometry,&geometry.x,&geometry.y, |
915 | 1.06k | &geometry.width,&geometry.height); |
916 | 1.06k | picon=ThumbnailImage(image,geometry.width,geometry.height,&image->exception); |
917 | 1.06k | if (picon == (Image *) NULL) |
918 | 737 | ThrowWriterException(CoderError,UnableToResizeImage,image); |
919 | 737 | if ((image_info->type != TrueColorType) && |
920 | 737 | (characteristics.grayscale)) |
921 | 264 | map=BlobToImage(image_info,Graymap,GraymapExtent,&image->exception); |
922 | 473 | else |
923 | 473 | map=BlobToImage(image_info,Colormap,ColormapExtent,&image->exception); |
924 | 737 | if (map == (Image *) NULL) |
925 | 0 | { |
926 | 0 | DestroyImage(picon); |
927 | 0 | picon=(Image *) NULL; |
928 | 0 | ThrowWriterException(CoderError,UnableToDecodeImage,image); |
929 | 0 | return MagickFail; |
930 | 0 | } |
931 | 737 | status=MapImage(picon,map,image_info->dither); |
932 | 737 | DestroyImage(map); |
933 | 737 | map=(Image *) NULL; |
934 | 737 | transparent=MagickFalse; |
935 | 737 | if (picon->storage_class == PseudoClass) |
936 | 391 | { |
937 | 391 | CompressImageColormap(picon); |
938 | 391 | if (picon->matte) |
939 | 0 | transparent=MagickTrue; |
940 | 391 | } |
941 | 346 | else |
942 | 346 | { |
943 | | /* |
944 | | Convert DirectClass to PseudoClass picon. |
945 | | */ |
946 | 346 | if (picon->matte) |
947 | 346 | { |
948 | | /* |
949 | | Map all the transparent pixels. |
950 | | */ |
951 | 4.04k | for (y=0; y < (long) picon->rows; y++) |
952 | 3.70k | { |
953 | 3.70k | q=GetImagePixels(picon,0,y,picon->columns,1); |
954 | 3.70k | if (q == (PixelPacket *) NULL) |
955 | 0 | break; |
956 | 24.3k | for (x=0; x < (long) picon->columns; x++) |
957 | 20.6k | { |
958 | 20.6k | if (q->opacity == TransparentOpacity) |
959 | 4.59k | transparent=True; |
960 | 16.0k | else |
961 | 16.0k | q->opacity=OpaqueOpacity; |
962 | 20.6k | q++; |
963 | 20.6k | } |
964 | 3.70k | if (!SyncImagePixels(picon)) |
965 | 0 | break; |
966 | 3.70k | } |
967 | 346 | if (picon->exception.severity >= ErrorException) |
968 | 0 | { |
969 | 0 | CopyException(&picon->exception,&image->exception); |
970 | 0 | DestroyImage(picon); |
971 | 0 | picon=(Image *) NULL; |
972 | 0 | ThrowWriterException(CoderError,UnableToSetImageOpacity,image); |
973 | 0 | } |
974 | 346 | } |
975 | 346 | if (SetImageType(picon,PaletteType) == MagickFail) |
976 | 0 | { |
977 | 0 | CopyException(&picon->exception,&image->exception); |
978 | 0 | DestroyImage(picon); |
979 | 0 | picon=(Image *) NULL; |
980 | 0 | ThrowWriterException(CoderError,UnableToSetImageType,image); |
981 | 0 | } |
982 | 346 | } |
983 | 737 | colors=picon->colors; |
984 | 737 | if (transparent) |
985 | 168 | { |
986 | 168 | colors++; |
987 | 168 | MagickReallocMemory(PixelPacket *,picon->colormap,colors*sizeof(PixelPacket)); |
988 | 168 | if (picon->colormap == (PixelPacket *) NULL) |
989 | 0 | { |
990 | 0 | DestroyImage(picon); |
991 | 0 | picon=(Image *) NULL; |
992 | 0 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
993 | 0 | } |
994 | 168 | picon->colormap[colors-1].red=picon->colormap[colors-1].green=picon->colormap[colors-1].blue=0; |
995 | 168 | picon->colormap[colors-1].opacity=TransparentOpacity; |
996 | 1.40k | for (y=0; y < (long) picon->rows; y++) |
997 | 1.23k | { |
998 | 1.23k | register IndexPacket |
999 | 1.23k | *indexes; |
1000 | | |
1001 | 1.23k | q=GetImagePixels(picon,0,y,picon->columns,1); |
1002 | 1.23k | if (q == (PixelPacket *) NULL) |
1003 | 0 | break; |
1004 | 1.23k | indexes=AccessMutableIndexes(picon); |
1005 | 17.4k | for (x=0; x < (long) picon->columns; x++) |
1006 | 16.1k | { |
1007 | 16.1k | if (q->opacity == TransparentOpacity) |
1008 | 4.59k | indexes[x]=(IndexPacket) picon->colors; |
1009 | 16.1k | q++; |
1010 | 16.1k | } |
1011 | 1.23k | if (!SyncImagePixels(picon)) |
1012 | 0 | break; |
1013 | 1.23k | } |
1014 | 168 | if (picon->exception.severity >= ErrorException) |
1015 | 0 | { |
1016 | 0 | CopyException(&picon->exception,&image->exception); |
1017 | 0 | DestroyImage(picon); |
1018 | 0 | picon=(Image *) NULL; |
1019 | 0 | ThrowWriterException(CoderError,UnableToSetImageOpacity,image); |
1020 | 0 | } |
1021 | 168 | } |
1022 | | /* |
1023 | | Compute the character per pixel. |
1024 | | */ |
1025 | 737 | characters_per_pixel=1; |
1026 | 737 | for (k=MaxCixels; (long) colors > k; k*=MaxCixels) |
1027 | 0 | characters_per_pixel++; |
1028 | | /* |
1029 | | XPM header. |
1030 | | */ |
1031 | 737 | (void) WriteBlobString(image,"/* XPM */\n"); |
1032 | 737 | GetPathComponent(picon->filename,BasePath,basename); |
1033 | 737 | (void) MagickFormatString(buffer,sizeof(buffer),"static char *%.1024s[] = {\n",basename); |
1034 | 737 | (void) WriteBlobString(image,buffer); |
1035 | 737 | (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); |
1036 | 737 | MagickFormatString(buffer,sizeof(buffer),"\"%lu %lu %lu %ld\",\n",picon->columns,picon->rows, |
1037 | 737 | colors,characters_per_pixel); |
1038 | 737 | (void) WriteBlobString(image,buffer); |
1039 | 10.0k | for (i=0; i < (long) colors; i++) |
1040 | 9.31k | { |
1041 | | /* |
1042 | | Define XPM color. |
1043 | | */ |
1044 | 9.31k | picon->colormap[i].opacity=OpaqueOpacity; |
1045 | 9.31k | (void) QueryColorname(picon,picon->colormap+i,XPMCompliance,name, |
1046 | 9.31k | &picon->exception); |
1047 | 9.31k | if (transparent) |
1048 | 805 | { |
1049 | 805 | if (i == (long) (colors-1)) |
1050 | 168 | (void) strlcpy(name,"grey75",sizeof(name)); |
1051 | 805 | } |
1052 | | /* |
1053 | | Write XPM color. |
1054 | | */ |
1055 | 9.31k | k=i % MaxCixels; |
1056 | 9.31k | symbol[0]=Cixel[k]; |
1057 | 9.31k | for (j=1; j < (long) characters_per_pixel; j++) |
1058 | 0 | { |
1059 | 0 | k=((i-k)/MaxCixels) % MaxCixels; |
1060 | 0 | symbol[j]=Cixel[k]; |
1061 | 0 | } |
1062 | 9.31k | symbol[j]='\0'; |
1063 | 9.31k | MagickFormatString(buffer,sizeof(buffer),"\"%.1024s c %.1024s\",\n",symbol,name); |
1064 | 9.31k | (void) WriteBlobString(image,buffer); |
1065 | 9.31k | } |
1066 | | /* |
1067 | | Define XPM pixels. |
1068 | | */ |
1069 | 737 | (void) WriteBlobString(image,"/* pixels */\n"); |
1070 | 9.52k | for (y=0; y < (long) picon->rows; y++) |
1071 | 8.78k | { |
1072 | 8.78k | register const IndexPacket |
1073 | 8.78k | *indexes; |
1074 | | |
1075 | 8.78k | p=AcquireImagePixels(picon,0,y,picon->columns,1,&picon->exception); |
1076 | 8.78k | if (p == (const PixelPacket *) NULL) |
1077 | 0 | break; |
1078 | 8.78k | indexes=AccessImmutableIndexes(picon); |
1079 | 8.78k | (void) WriteBlobString(image,"\""); |
1080 | 58.3k | for (x=0; x < (long) picon->columns; x++) |
1081 | 49.5k | { |
1082 | 49.5k | k=indexes[x] % MaxCixels; |
1083 | 49.5k | symbol[0]=Cixel[k]; |
1084 | 49.5k | for (j=1; j < (long) characters_per_pixel; j++) |
1085 | 0 | { |
1086 | 0 | k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels; |
1087 | 0 | symbol[j]=Cixel[k]; |
1088 | 0 | } |
1089 | 49.5k | symbol[j]='\0'; |
1090 | 49.5k | (void) strlcpy(buffer,symbol,MaxTextExtent); |
1091 | 49.5k | (void) WriteBlobString(image,buffer); |
1092 | 49.5k | } |
1093 | 8.78k | MagickFormatString(buffer,sizeof(buffer),"\"%.1024s\n", |
1094 | 8.78k | (y == (long) (picon->rows-1) ? "" : ",")); |
1095 | 8.78k | (void) WriteBlobString(image,buffer); |
1096 | 8.78k | if (QuantumTick(y,picon->rows)) |
1097 | 8.78k | if (!MagickMonitorFormatted(y,picon->rows,&image->exception, |
1098 | 8.78k | SaveImageText,image->filename, |
1099 | 8.78k | image->columns,image->rows)) |
1100 | 0 | break; |
1101 | 8.78k | } |
1102 | 737 | if (picon->exception.severity >= ErrorException) |
1103 | 0 | { |
1104 | 0 | GetImageException(picon,&image->exception); |
1105 | 0 | DestroyImage(picon); |
1106 | 0 | picon=(Image *) NULL; |
1107 | 0 | CloseBlob(image); |
1108 | 0 | return MagickFail; |
1109 | 0 | } |
1110 | 737 | DestroyImage(picon); |
1111 | 737 | picon=(Image *) NULL; |
1112 | 737 | (void) WriteBlobString(image,"};\n"); |
1113 | 737 | status &= CloseBlob(image); |
1114 | 737 | return(status); |
1115 | 737 | } |
1116 | | |
1117 | | /* |
1118 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1119 | | % % |
1120 | | % % |
1121 | | % % |
1122 | | % W r i t e X P M I m a g e % |
1123 | | % % |
1124 | | % % |
1125 | | % % |
1126 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1127 | | % |
1128 | | % Procedure WriteXPMImage writes an image to a file in the X pixmap format. |
1129 | | % |
1130 | | % The format of the WriteXPMImage method is: |
1131 | | % |
1132 | | % unsigned int WriteXPMImage(const ImageInfo *image_info,Image *image) |
1133 | | % |
1134 | | % A description of each parameter follows. |
1135 | | % |
1136 | | % o status: Method WriteXPMImage return True if the image is written. |
1137 | | % False is returned is there is a memory shortage or if the image file |
1138 | | % fails to write. |
1139 | | % |
1140 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
1141 | | % |
1142 | | % o image: A pointer to an Image structure. |
1143 | | % |
1144 | | % |
1145 | | */ |
1146 | | static unsigned int WriteXPMImage(const ImageInfo *image_info,Image *image) |
1147 | 1.19k | { |
1148 | 11.3M | #define MaxCixels 92 |
1149 | | |
1150 | 1.19k | static const char |
1151 | 1.19k | Cixel[MaxCixels+1] = " .XoO+@#$%&*=-;:>,<1234567890qwertyuipasdfghjk" |
1152 | 1.19k | "lzxcvbnmMNBVCZASDFGHJKLPIUYTREWQ!~^/()_`'][{}|"; |
1153 | | |
1154 | 1.19k | char |
1155 | 1.19k | buffer[MaxTextExtent], |
1156 | 1.19k | basename[MaxTextExtent], |
1157 | 1.19k | name[MaxTextExtent], |
1158 | 1.19k | symbol[MaxTextExtent]; |
1159 | | |
1160 | 1.19k | int |
1161 | 1.19k | j; |
1162 | | |
1163 | 1.19k | long |
1164 | 1.19k | k, |
1165 | 1.19k | y; |
1166 | | |
1167 | 1.19k | register const PixelPacket |
1168 | 1.19k | *p; |
1169 | | |
1170 | 1.19k | register long |
1171 | 1.19k | i, |
1172 | 1.19k | x; |
1173 | | |
1174 | 1.19k | register PixelPacket |
1175 | 1.19k | *q; |
1176 | | |
1177 | 1.19k | unsigned int |
1178 | 1.19k | status, |
1179 | 1.19k | transparent; |
1180 | | |
1181 | 1.19k | unsigned long |
1182 | 1.19k | characters_per_pixel, |
1183 | 1.19k | colors; |
1184 | | |
1185 | | /* |
1186 | | Open output image file. |
1187 | | */ |
1188 | 1.19k | assert(image_info != (const ImageInfo *) NULL); |
1189 | 1.19k | assert(image_info->signature == MagickSignature); |
1190 | 1.19k | assert(image != (Image *) NULL); |
1191 | 1.19k | assert(image->signature == MagickSignature); |
1192 | 1.19k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
1193 | 1.19k | if (status == False) |
1194 | 1.19k | ThrowWriterException(FileOpenError,UnableToOpenFile,image); |
1195 | 1.19k | if (TransformColorspace(image,RGBColorspace) == MagickFail) |
1196 | 1.19k | ThrowWriterException(CoderError,UnableToTransformColorspace,image); |
1197 | | /* |
1198 | | Limit color resolution to what XPM can traditionally handle |
1199 | | */ |
1200 | 1.19k | if (image->depth > 16) |
1201 | 0 | image->depth=16; |
1202 | 1.19k | transparent=False; |
1203 | 1.19k | if (image->storage_class == PseudoClass) |
1204 | 456 | { |
1205 | 456 | CompressImageColormap(image); |
1206 | 456 | if (image->matte) |
1207 | 0 | transparent=True; |
1208 | 456 | } |
1209 | 741 | else |
1210 | 741 | { |
1211 | | /* |
1212 | | Convert DirectClass to PseudoClass image. |
1213 | | */ |
1214 | 741 | if (image->matte) |
1215 | 733 | { |
1216 | | /* |
1217 | | Map all the transparent pixels. |
1218 | | */ |
1219 | 37.4k | for (y=0; y < (long) image->rows; y++) |
1220 | 36.7k | { |
1221 | 36.7k | q=GetImagePixels(image,0,y,image->columns,1); |
1222 | 36.7k | if (q == (PixelPacket *) NULL) |
1223 | 60 | break; |
1224 | 1.98M | for (x=0; x < (long) image->columns; x++) |
1225 | 1.95M | { |
1226 | 1.95M | if (q->opacity == TransparentOpacity) |
1227 | 11.8k | transparent=True; |
1228 | 1.94M | else |
1229 | 1.94M | q->opacity=OpaqueOpacity; |
1230 | 1.95M | q++; |
1231 | 1.95M | } |
1232 | 36.6k | if (!SyncImagePixels(image)) |
1233 | 0 | break; |
1234 | 36.6k | } |
1235 | 733 | } |
1236 | 741 | if (SetImageType(image,PaletteType) == MagickFail) |
1237 | 681 | ThrowWriterException(CoderError,UnableToSetImageType,image); |
1238 | 681 | } |
1239 | 1.13k | colors=image->colors; |
1240 | 1.13k | if (transparent) |
1241 | 342 | { |
1242 | 342 | colors++; |
1243 | 342 | MagickReallocMemory(PixelPacket *,image->colormap,colors*sizeof(PixelPacket)); |
1244 | 342 | if (image->colormap == (PixelPacket *) NULL) |
1245 | 342 | ThrowWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
1246 | 342 | image->colormap[colors-1].red=image->colormap[colors-1].green=image->colormap[colors-1].blue=0; |
1247 | 342 | image->colormap[colors-1].opacity=TransparentOpacity; |
1248 | 13.1k | for (y=0; y < (long) image->rows; y++) |
1249 | 12.7k | { |
1250 | 12.7k | register IndexPacket |
1251 | 12.7k | *indexes; |
1252 | | |
1253 | 12.7k | q=GetImagePixels(image,0,y,image->columns,1); |
1254 | 12.7k | if (q == (PixelPacket *) NULL) |
1255 | 0 | break; |
1256 | 12.7k | indexes=AccessMutableIndexes(image); |
1257 | 1.20M | for (x=0; x < (long) image->columns; x++) |
1258 | 1.19M | { |
1259 | 1.19M | if (q->opacity == TransparentOpacity) |
1260 | 11.8k | indexes[x]=(IndexPacket) image->colors; |
1261 | 1.19M | q++; |
1262 | 1.19M | } |
1263 | 12.7k | if (!SyncImagePixels(image)) |
1264 | 0 | break; |
1265 | 12.7k | } |
1266 | 342 | } |
1267 | | /* |
1268 | | Compute the character per pixel. |
1269 | | */ |
1270 | 1.13k | characters_per_pixel=1; |
1271 | 1.44k | for (k=MaxCixels; (long) colors > k; k*=MaxCixels) |
1272 | 309 | characters_per_pixel++; |
1273 | | /* |
1274 | | XPM header. |
1275 | | */ |
1276 | 1.13k | (void) WriteBlobString(image,"/* XPM */\n"); |
1277 | 1.13k | GetPathComponent(image->filename,BasePath,basename); |
1278 | 1.13k | (void) MagickFormatString(buffer,sizeof(buffer),"static char *%.1024s[] = {\n",basename); |
1279 | 1.13k | (void) WriteBlobString(image,buffer); |
1280 | 1.13k | (void) WriteBlobString(image,"/* columns rows colors chars-per-pixel */\n"); |
1281 | 1.13k | MagickFormatString(buffer,sizeof(buffer),"\"%lu %lu %lu %ld\",\n",image->columns, |
1282 | 1.13k | image->rows,colors,characters_per_pixel); |
1283 | 1.13k | (void) WriteBlobString(image,buffer); |
1284 | 1.15M | for (i=0; i < (long) colors; i++) |
1285 | 1.15M | { |
1286 | | /* |
1287 | | Define XPM color. |
1288 | | */ |
1289 | 1.15M | image->colormap[i].opacity=OpaqueOpacity; |
1290 | 1.15M | (void) QueryColorname(image,image->colormap+i,XPMCompliance,name, |
1291 | 1.15M | &image->exception); |
1292 | 1.15M | if (transparent) |
1293 | 25.4k | { |
1294 | 25.4k | if (i == (long) (colors-1)) |
1295 | 342 | (void) strlcpy(name,"None",sizeof(name)); |
1296 | 25.4k | } |
1297 | | /* |
1298 | | Write XPM color. |
1299 | | */ |
1300 | 1.15M | k=i % MaxCixels; |
1301 | 1.15M | symbol[0]=Cixel[k]; |
1302 | 3.32M | for (j=1; j < (long) characters_per_pixel; j++) |
1303 | 2.16M | { |
1304 | 2.16M | k=((i-k)/MaxCixels) % MaxCixels; |
1305 | 2.16M | symbol[j]=Cixel[k]; |
1306 | 2.16M | } |
1307 | 1.15M | symbol[j]='\0'; |
1308 | 1.15M | MagickFormatString(buffer,sizeof(buffer),"\"%.1024s c %.1024s\",\n",symbol,name); |
1309 | 1.15M | (void) WriteBlobString(image,buffer); |
1310 | 1.15M | } |
1311 | | /* |
1312 | | Define XPM pixels. |
1313 | | */ |
1314 | 1.13k | (void) WriteBlobString(image,"/* pixels */\n"); |
1315 | 51.4k | for (y=0; y < (long) image->rows; y++) |
1316 | 50.3k | { |
1317 | 50.3k | register const IndexPacket |
1318 | 50.3k | *indexes; |
1319 | | |
1320 | 50.3k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
1321 | 50.3k | if (p == (const PixelPacket *) NULL) |
1322 | 28 | break; |
1323 | 50.3k | indexes=AccessImmutableIndexes(image); |
1324 | 50.3k | (void) WriteBlobString(image,"\""); |
1325 | 2.05M | for (x=0; x < (long) image->columns; x++) |
1326 | 2.00M | { |
1327 | 2.00M | k=indexes[x] % MaxCixels; |
1328 | 2.00M | symbol[0]=Cixel[k]; |
1329 | 3.93M | for (j=1; j < (long) characters_per_pixel; j++) |
1330 | 1.92M | { |
1331 | 1.92M | k=(((int) indexes[x]-k)/MaxCixels) % MaxCixels; |
1332 | 1.92M | symbol[j]=Cixel[k]; |
1333 | 1.92M | } |
1334 | 2.00M | symbol[j]='\0'; |
1335 | 2.00M | (void) strlcpy(buffer,symbol,MaxTextExtent); |
1336 | 2.00M | (void) WriteBlobString(image,buffer); |
1337 | 2.00M | } |
1338 | 50.3k | MagickFormatString(buffer,sizeof(buffer),"\"%.1024s\n", |
1339 | 50.3k | (y == (long) (image->rows-1) ? "" : ",")); |
1340 | 50.3k | (void) WriteBlobString(image,buffer); |
1341 | 50.3k | if (QuantumTick(y,image->rows)) |
1342 | 29.6k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
1343 | 29.6k | SaveImageText,image->filename, |
1344 | 29.6k | image->columns,image->rows)) |
1345 | 0 | break; |
1346 | 50.3k | } |
1347 | 1.13k | (void) WriteBlobString(image,"};\n"); |
1348 | 1.13k | status &= CloseBlob(image); |
1349 | 1.13k | return(status); |
1350 | 1.13k | } |