/src/imagemagick/coders/sixel.c
Line | Count | Source |
1 | | /* |
2 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
3 | | % % |
4 | | % % |
5 | | % % |
6 | | % SSSSS IIIII X X EEEEE L % |
7 | | % SS I X X E L % |
8 | | % SSS I X EEE L % |
9 | | % SS I X X E L % |
10 | | % SSSSS IIIII X X EEEEE LLLLL % |
11 | | % % |
12 | | % % |
13 | | % Read/Write DEC SIXEL Format % |
14 | | % % |
15 | | % Software Design % |
16 | | % Hayaki Saito % |
17 | | % September 2014 % |
18 | | % Based on kmiya's sixel (2014-03-28) % |
19 | | % % |
20 | | % % |
21 | | % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization % |
22 | | % dedicated to making software imaging solutions freely available. % |
23 | | % % |
24 | | % You may not use this file except in compliance with the License. You may % |
25 | | % obtain a copy of the License at % |
26 | | % % |
27 | | % https://imagemagick.org/script/license.php % |
28 | | % % |
29 | | % Unless required by applicable law or agreed to in writing, software % |
30 | | % distributed under the License is distributed on an "AS IS" BASIS, % |
31 | | % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % |
32 | | % See the License for the specific language governing permissions and % |
33 | | % limitations under the License. % |
34 | | % % |
35 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
36 | | % |
37 | | % |
38 | | */ |
39 | | |
40 | | /* |
41 | | Include declarations. |
42 | | */ |
43 | | #include "MagickCore/studio.h" |
44 | | #include "MagickCore/attribute.h" |
45 | | #include "MagickCore/blob.h" |
46 | | #include "MagickCore/blob-private.h" |
47 | | #include "MagickCore/cache.h" |
48 | | #include "MagickCore/color.h" |
49 | | #include "MagickCore/color-private.h" |
50 | | #include "MagickCore/colormap.h" |
51 | | #include "MagickCore/colormap-private.h" |
52 | | #include "MagickCore/colorspace.h" |
53 | | #include "MagickCore/colorspace-private.h" |
54 | | #include "MagickCore/exception.h" |
55 | | #include "MagickCore/exception-private.h" |
56 | | #include "MagickCore/geometry.h" |
57 | | #include "MagickCore/image.h" |
58 | | #include "MagickCore/image-private.h" |
59 | | #include "MagickCore/list.h" |
60 | | #include "MagickCore/locale_.h" |
61 | | #include "MagickCore/magick.h" |
62 | | #include "MagickCore/memory_.h" |
63 | | #include "MagickCore/monitor.h" |
64 | | #include "MagickCore/monitor-private.h" |
65 | | #include "MagickCore/pixel-accessor.h" |
66 | | #include "MagickCore/quantize.h" |
67 | | #include "MagickCore/quantum-private.h" |
68 | | #include "MagickCore/resize.h" |
69 | | #include "MagickCore/resource_.h" |
70 | | #include "MagickCore/splay-tree.h" |
71 | | #include "MagickCore/static.h" |
72 | | #include "MagickCore/string_.h" |
73 | | #include "MagickCore/thread-private.h" |
74 | | #include "MagickCore/module.h" |
75 | | #include "MagickCore/threshold.h" |
76 | | #include "MagickCore/utility.h" |
77 | | |
78 | | /* |
79 | | Definitions |
80 | | */ |
81 | 1.85M | #define SIXEL_PALETTE_MAX 1024 |
82 | 251k | #define SIXEL_OUTPUT_PACKET_SIZE 1024 |
83 | | |
84 | | /* |
85 | | Macros |
86 | | */ |
87 | 2.41M | #define SIXEL_RGB(r, g, b) ((int) (((ssize_t) ((r) & 0xff) << 16) + (((g) & 0xff) << 8) + ((b) & 0xff))) |
88 | | #define SIXEL_PALVAL(n,a,m) ((int) (((ssize_t) (n) * (a) + ((m) / 2)) / (m))) |
89 | 1.46k | #define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100)) |
90 | | |
91 | | typedef unsigned short sixel_pixel_t; |
92 | | |
93 | | /* |
94 | | Structure declarations. |
95 | | */ |
96 | | typedef struct sixel_node { |
97 | | int |
98 | | color, |
99 | | left, |
100 | | right; |
101 | | |
102 | | sixel_pixel_t |
103 | | *map; |
104 | | |
105 | | struct sixel_node |
106 | | *next; |
107 | | } sixel_node_t; |
108 | | |
109 | | typedef struct sixel_output { |
110 | | Image |
111 | | *image; |
112 | | |
113 | | int |
114 | | active_palette, |
115 | | pos, |
116 | | save_count, |
117 | | save_pixel; |
118 | | |
119 | | sixel_node_t |
120 | | *node_free, |
121 | | *node_top; |
122 | | |
123 | | unsigned char |
124 | | buffer[MagickMax(SIXEL_OUTPUT_PACKET_SIZE*2,MagickPathExtent)], |
125 | | has_8bit_control; /* 0: 7bit terminal, 1: 8bit terminal */ |
126 | | } sixel_output_t; |
127 | | |
128 | | static int const sixel_default_color_table[] = { |
129 | | SIXEL_XRGB(0, 0, 0), /* 0 Black */ |
130 | | SIXEL_XRGB(20, 20, 80), /* 1 Blue */ |
131 | | SIXEL_XRGB(80, 13, 13), /* 2 Red */ |
132 | | SIXEL_XRGB(20, 80, 20), /* 3 Green */ |
133 | | SIXEL_XRGB(80, 20, 80), /* 4 Magenta */ |
134 | | SIXEL_XRGB(20, 80, 80), /* 5 Cyan */ |
135 | | SIXEL_XRGB(80, 80, 20), /* 6 Yellow */ |
136 | | SIXEL_XRGB(53, 53, 53), /* 7 Gray 50% */ |
137 | | SIXEL_XRGB(26, 26, 26), /* 8 Gray 25% */ |
138 | | SIXEL_XRGB(33, 33, 60), /* 9 Blue* */ |
139 | | SIXEL_XRGB(60, 26, 26), /* 10 Red* */ |
140 | | SIXEL_XRGB(33, 60, 33), /* 11 Green* */ |
141 | | SIXEL_XRGB(60, 33, 60), /* 12 Magenta* */ |
142 | | SIXEL_XRGB(33, 60, 60), /* 13 Cyan* */ |
143 | | SIXEL_XRGB(60, 60, 33), /* 14 Yellow* */ |
144 | | SIXEL_XRGB(80, 80, 80), /* 15 Gray 75% */ |
145 | | }; |
146 | | |
147 | | /* |
148 | | Forward declarations. |
149 | | */ |
150 | | static MagickBooleanType |
151 | | WriteSIXELImage(const ImageInfo *,Image *,ExceptionInfo *); |
152 | | |
153 | | static int hue_to_rgb(int n1,int n2,int hue) |
154 | 11.6k | { |
155 | 11.6k | const int |
156 | 11.6k | HLSMAX=100; |
157 | | |
158 | 11.6k | if (hue < 0) |
159 | 1.66k | hue += HLSMAX; |
160 | 11.6k | if (hue > HLSMAX) |
161 | 2.11k | hue -= HLSMAX; |
162 | 11.6k | if (hue < (HLSMAX/6)) |
163 | 3.18k | return(n1 + (((ssize_t) (n2-n1)*hue+(HLSMAX/12))/(HLSMAX/6))); |
164 | 8.48k | if (hue < (HLSMAX/2)) |
165 | 3.70k | return(n2); |
166 | 4.78k | if (hue < ((HLSMAX*2)/3)) |
167 | 958 | return(n1+(((ssize_t) (n2-n1)*(((HLSMAX*2)/3)-hue)+(HLSMAX/12))/(HLSMAX/6))); |
168 | 3.82k | return(n1); |
169 | 4.78k | } |
170 | | |
171 | | static int hls_to_rgb(int hue, int lum, int sat) |
172 | 4.22k | { |
173 | 4.22k | const int |
174 | 4.22k | HLSMAX = 100, |
175 | 4.22k | RGBMAX = 255; |
176 | | |
177 | 4.22k | int |
178 | 4.22k | b, |
179 | 4.22k | g, |
180 | 4.22k | magic1, |
181 | 4.22k | magic2, |
182 | 4.22k | r; |
183 | | |
184 | 4.22k | if (sat == 0) |
185 | 328 | r=g=b=(lum*(ssize_t) RGBMAX)/HLSMAX; |
186 | 3.89k | else |
187 | 3.89k | { |
188 | 3.89k | if (lum <= (HLSMAX / 2)) |
189 | 3.47k | magic2=(int) (((ssize_t) lum*((ssize_t) HLSMAX+sat)+(HLSMAX/2))/HLSMAX); |
190 | 421 | else |
191 | 421 | magic2=(int) (lum+sat-(((ssize_t) lum*sat)+(HLSMAX/2))/HLSMAX); |
192 | 3.89k | magic1=(int) (2*(ssize_t) lum-magic2); |
193 | 3.89k | b=(hue_to_rgb(magic1,magic2,(ssize_t) hue+(HLSMAX/3))*(ssize_t) RGBMAX+ |
194 | 3.89k | (HLSMAX/2))/HLSMAX; |
195 | 3.89k | r=(hue_to_rgb(magic1,magic2,hue)*(ssize_t) RGBMAX+(ssize_t) (HLSMAX/2))/ |
196 | 3.89k | HLSMAX; |
197 | 3.89k | g=(hue_to_rgb(magic1,magic2,(ssize_t) hue-(HLSMAX/3))*(ssize_t) RGBMAX+ |
198 | 3.89k | (HLSMAX/2))/HLSMAX; |
199 | 3.89k | } |
200 | 4.22k | return(SIXEL_RGB(r,g,b)); |
201 | 4.22k | } |
202 | | |
203 | | static unsigned char *get_params(unsigned char *p, int *param, int *len) |
204 | 33.0k | { |
205 | 33.0k | int |
206 | 33.0k | n; |
207 | | |
208 | 33.0k | *len=0; |
209 | 94.3k | while (*p != '\0') |
210 | 93.3k | { |
211 | 94.4k | while ((*p == ' ') || (*p == '\t')) |
212 | 1.15k | p++; |
213 | 93.3k | if (isdigit((int) ((unsigned char) *p))) |
214 | 40.0k | { |
215 | 63.7k | for (n = 0; isdigit((int) ((unsigned char) *p)); p++) |
216 | 63.7k | { |
217 | 63.7k | if (n <= (INT_MAX/10)) |
218 | 63.1k | n=(int) ((ssize_t) n*10+(*p-'0')); |
219 | 63.7k | } |
220 | 40.0k | if (*len < 10) |
221 | 39.6k | param[(*len)++]=n; |
222 | 41.1k | while (*p == ' ' || *p == '\t') |
223 | 1.12k | p++; |
224 | 40.0k | if (*p == ';') |
225 | 12.2k | p++; |
226 | 40.0k | } |
227 | 53.2k | else if (*p == ';') |
228 | 21.2k | { |
229 | 21.2k | if (*len < 10) |
230 | 20.8k | param[(*len)++]=0; |
231 | 21.2k | p++; |
232 | 21.2k | } |
233 | 32.0k | else |
234 | 32.0k | break; |
235 | 93.3k | } |
236 | 33.0k | return p; |
237 | 33.0k | } |
238 | | |
239 | | /* convert sixel data into indexed pixel bytes and palette data */ |
240 | | static MagickBooleanType sixel_decode(Image *image,unsigned char *p, |
241 | | sixel_pixel_t **pixels,size_t *pwidth,size_t *pheight, |
242 | | unsigned char **palette,size_t *ncolors,ExceptionInfo *exception) |
243 | 2.38k | { |
244 | 2.38k | int |
245 | 2.38k | attributed_pad, |
246 | 2.38k | attributed_pan, |
247 | 2.38k | attributed_ph, |
248 | 2.38k | attributed_pv, |
249 | 2.38k | b, |
250 | 2.38k | background_color_index, |
251 | 2.38k | c, |
252 | 2.38k | color_index, |
253 | 2.38k | dmsx, |
254 | 2.38k | dmsy, |
255 | 2.38k | g, |
256 | 2.38k | i, |
257 | 2.38k | imsx, |
258 | 2.38k | imsy, |
259 | 2.38k | n, |
260 | 2.38k | max_color_index, |
261 | 2.38k | max_x, |
262 | 2.38k | max_y, |
263 | 2.38k | param[10], |
264 | 2.38k | position_x, |
265 | 2.38k | position_y, |
266 | 2.38k | r, |
267 | 2.38k | repeat_count, |
268 | 2.38k | sixel_palet[SIXEL_PALETTE_MAX], |
269 | 2.38k | sixel_vertical_mask, |
270 | 2.38k | x, |
271 | 2.38k | y; |
272 | | |
273 | 2.38k | sixel_pixel_t |
274 | 2.38k | *dmbuf, |
275 | 2.38k | *imbuf; |
276 | | |
277 | 2.38k | size_t |
278 | 2.38k | extent, |
279 | 2.38k | offset; |
280 | | |
281 | 2.38k | extent=strlen((char *) p); |
282 | 2.38k | position_x=position_y=0; |
283 | 2.38k | max_x=max_y=0; |
284 | 2.38k | attributed_pan=2; |
285 | 2.38k | attributed_pad=1; |
286 | 2.38k | attributed_ph=attributed_pv=0; |
287 | 2.38k | repeat_count=1; |
288 | 2.38k | color_index=0; |
289 | 2.38k | background_color_index=0; |
290 | 2.38k | max_color_index=2; |
291 | 2.38k | memset(param,0,sizeof(param)); |
292 | 2.38k | imsx=2048; |
293 | 2.38k | imsy=2048; |
294 | 2.38k | if (SetImageExtent(image,(size_t) imsx,(size_t) imsy,exception) == MagickFalse) |
295 | 0 | return(MagickFalse); |
296 | 2.38k | imbuf=(sixel_pixel_t *) AcquireQuantumMemory((size_t) imsx,(size_t) imsy*sizeof(sixel_pixel_t)); |
297 | 2.38k | if (imbuf == (sixel_pixel_t *) NULL) |
298 | 0 | return(MagickFalse); |
299 | 40.4k | for (n = 0; n < 16; n++) |
300 | 38.1k | sixel_palet[n]=sixel_default_color_table[n]; |
301 | | /* colors 16-231 are a 6x6x6 color cube */ |
302 | 16.6k | for (r=0; r < 6; r++) |
303 | 14.2k | { |
304 | 100k | for (g=0; g < 6; g++) |
305 | 85.7k | { |
306 | 600k | for (b=0; b < 6; b++) |
307 | 514k | { |
308 | 514k | sixel_palet[n++]=SIXEL_RGB(r*51,g*51,b*51); |
309 | 514k | } |
310 | 85.7k | } |
311 | 14.2k | } |
312 | | /* colors 232-255 are a grayscale ramp, intentionally leaving out */ |
313 | 59.5k | for (i=0; i < 24; i++) |
314 | 57.1k | sixel_palet[n++]=SIXEL_RGB(i*11,i*11,i*11); |
315 | 1.83M | for (; n < SIXEL_PALETTE_MAX; n++) |
316 | 1.82M | sixel_palet[n]=SIXEL_RGB(255,255,255); |
317 | 9.99G | for (i = 0; i < imsx * imsy; i++) |
318 | 9.99G | imbuf[i]=(sixel_pixel_t) background_color_index; |
319 | 201k | while (*p != '\0') |
320 | 198k | { |
321 | 198k | if ((p[0] == '\033' && p[1] == 'P') || (*p == 0x90)) |
322 | 2.02k | { |
323 | 2.02k | if (*p == '\033') |
324 | 197 | p++; |
325 | 2.02k | p=get_params(++p,param,&n); |
326 | 2.02k | if (*p == 'q') |
327 | 1.35k | { |
328 | 1.35k | p++; |
329 | 1.35k | if (n > 0) |
330 | 874 | { |
331 | | /* Pn1 */ |
332 | 874 | switch(param[0]) |
333 | 874 | { |
334 | 627 | case 0: |
335 | 628 | case 1: |
336 | 628 | attributed_pad = 2; |
337 | 628 | break; |
338 | 5 | case 2: |
339 | 5 | attributed_pad = 5; |
340 | 5 | break; |
341 | 3 | case 3: |
342 | 3 | attributed_pad = 4; |
343 | 3 | break; |
344 | 1 | case 4: |
345 | 1 | attributed_pad = 4; |
346 | 1 | break; |
347 | 2 | case 5: |
348 | 2 | attributed_pad = 3; |
349 | 2 | break; |
350 | 5 | case 6: |
351 | 5 | attributed_pad = 3; |
352 | 5 | break; |
353 | 1 | case 7: |
354 | 1 | attributed_pad = 2; |
355 | 1 | break; |
356 | 0 | case 8: |
357 | 0 | attributed_pad = 2; |
358 | 0 | break; |
359 | 2 | case 9: |
360 | 2 | attributed_pad = 1; |
361 | 2 | break; |
362 | 874 | } |
363 | 874 | } |
364 | 1.35k | if (n > 2) |
365 | 472 | { |
366 | | /* Pn3 */ |
367 | 472 | if (param[2] == 0) |
368 | 196 | param[2]=10; |
369 | 472 | attributed_pan=(int) (((ssize_t) attributed_pan*param[2])/10); |
370 | 472 | attributed_pad=(int) (((ssize_t) attributed_pad*param[2])/10); |
371 | 472 | if (attributed_pan <= 0) |
372 | 200 | attributed_pan=1; |
373 | 472 | if (attributed_pad <= 0) |
374 | 221 | attributed_pad=1; |
375 | 472 | } |
376 | 1.35k | } |
377 | 2.02k | } |
378 | 196k | else if ((p[0] == '\033' && p[1] == '\\') || (*p == 0x9C)) |
379 | 4 | break; |
380 | 196k | else if (*p == '"') |
381 | 2.34k | { |
382 | | /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */ |
383 | 2.34k | p=get_params(++p,param,&n); |
384 | 2.34k | if (n > 0) |
385 | 1.90k | attributed_pad=param[0]; |
386 | 2.34k | if (n > 1) |
387 | 1.48k | attributed_pan=param[1]; |
388 | 2.34k | if ((n > 2) && (param[2] > 0)) |
389 | 582 | attributed_ph=param[2]&0xffff; |
390 | 2.34k | if ((n > 3) && (param[3] > 0)) |
391 | 437 | attributed_pv=param[3]&0xffff; |
392 | 2.34k | if (attributed_pan <= 0) |
393 | 1.47k | attributed_pan=1; |
394 | 2.34k | if (attributed_pad <= 0) |
395 | 1.78k | attributed_pad=1; |
396 | 2.34k | if ((imsx < attributed_ph) || (imsy < attributed_pv)) |
397 | 115 | { |
398 | 115 | dmsx=imsx > attributed_ph ? imsx : attributed_ph; |
399 | 115 | dmsy=imsy > attributed_pv ? imsy : attributed_pv; |
400 | 115 | if (SetImageExtent(image,(size_t) dmsx,(size_t) dmsy,exception) == MagickFalse) |
401 | 115 | break; |
402 | 0 | dmbuf=(sixel_pixel_t *) AcquireQuantumMemory((size_t) dmsx,(size_t) |
403 | 0 | dmsy*sizeof(sixel_pixel_t)); |
404 | 0 | if (dmbuf == (sixel_pixel_t *) NULL) |
405 | 0 | { |
406 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
407 | 0 | return(MagickFalse); |
408 | 0 | } |
409 | 0 | (void) memset(dmbuf,background_color_index,(size_t) dmsx*(size_t) |
410 | 0 | dmsy*sizeof(sixel_pixel_t)); |
411 | 0 | for (y = 0; y < imsy; ++y) |
412 | 0 | (void) memcpy(dmbuf+dmsx*y,imbuf+imsx*y,(size_t) imsx* |
413 | 0 | sizeof(sixel_pixel_t)); |
414 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
415 | 0 | imsx=dmsx; |
416 | 0 | imsy=dmsy; |
417 | 0 | imbuf=dmbuf; |
418 | 0 | } |
419 | 2.34k | } |
420 | 194k | else if (*p == '!') |
421 | 7.64k | { |
422 | | /* DECGRI Graphics Repeat Introducer ! Pn Ch */ |
423 | 7.64k | p=get_params(++p,param,&n); |
424 | 7.64k | if ((n > 0) && (param[0] > 0)) |
425 | 6.64k | { |
426 | 6.64k | repeat_count=param[0]; |
427 | 6.64k | if (repeat_count > (ssize_t) extent) |
428 | 69 | break; |
429 | 6.64k | } |
430 | 7.64k | } |
431 | 186k | else if (*p == '#') |
432 | 21.0k | { |
433 | | /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */ |
434 | 21.0k | p=get_params(++p,param,&n); |
435 | | |
436 | 21.0k | if (n > 0) |
437 | 19.4k | { |
438 | 19.4k | if ((color_index = param[0]) < 0) |
439 | 236 | color_index=0; |
440 | 19.2k | else if (color_index >= SIXEL_PALETTE_MAX) |
441 | 283 | color_index=SIXEL_PALETTE_MAX-1; |
442 | 19.4k | } |
443 | 21.0k | if (n > 4) |
444 | 5.99k | { |
445 | 5.99k | if (param[1] == 1) |
446 | 4.22k | { |
447 | | /* HLS */ |
448 | 4.22k | if (param[2] > 360) |
449 | 217 | param[2]=360; |
450 | 4.22k | if (param[3] > 100) |
451 | 259 | param[3]=100; |
452 | 4.22k | if (param[4] > 100) |
453 | 232 | param[4]=100; |
454 | 4.22k | sixel_palet[color_index]=hls_to_rgb((int) ((ssize_t) param[2]* |
455 | 4.22k | 100/360),param[3],param[4]); |
456 | 4.22k | } |
457 | 1.77k | else if (param[1] == 2) |
458 | 1.46k | { |
459 | | /* RGB */ |
460 | 1.46k | if (param[2] > 100) |
461 | 299 | param[2]=100; |
462 | 1.46k | if (param[3] > 100) |
463 | 667 | param[3]=100; |
464 | 1.46k | if (param[4] > 100) |
465 | 266 | param[4]=100; |
466 | 1.46k | sixel_palet[color_index]=SIXEL_XRGB(param[2],param[3], |
467 | 1.46k | param[4]); |
468 | 1.46k | } |
469 | 5.99k | } |
470 | 21.0k | } |
471 | 165k | else if (*p == '$') |
472 | 629 | { |
473 | | /* DECGCR Graphics Carriage Return */ |
474 | 629 | p++; |
475 | 629 | position_x=0; |
476 | 629 | repeat_count=1; |
477 | 629 | } |
478 | 165k | else if (*p == '-') |
479 | 62.4k | { |
480 | | /* DECGNL Graphics Next Line */ |
481 | 62.4k | p++; |
482 | 62.4k | position_x=0; |
483 | 62.4k | position_y+=6; |
484 | 62.4k | repeat_count=1; |
485 | 62.4k | } |
486 | 102k | else if ((*p >= '?') && (*p <= '\177')) |
487 | 65.5k | { |
488 | 65.5k | if ((imsx < (position_x + repeat_count)) || (imsy < (position_y + 6))) |
489 | 61 | { |
490 | 61 | int |
491 | 61 | nx, |
492 | 61 | ny; |
493 | | |
494 | 61 | nx=imsx*2; |
495 | 61 | ny=imsy*2; |
496 | | |
497 | 96 | while ((nx < (position_x + repeat_count)) || (ny < (position_y + 6))) |
498 | 35 | { |
499 | 35 | nx *= 2; |
500 | 35 | ny *= 2; |
501 | 35 | } |
502 | | |
503 | 61 | dmsx=nx; |
504 | 61 | dmsy=ny; |
505 | 61 | if (SetImageExtent(image,(size_t) dmsx,(size_t) dmsy,exception) == MagickFalse) |
506 | 61 | break; |
507 | 0 | dmbuf=(sixel_pixel_t *) AcquireQuantumMemory((size_t) dmsx,(size_t) |
508 | 0 | dmsy*sizeof(sixel_pixel_t)); |
509 | 0 | if (dmbuf == (sixel_pixel_t *) NULL) |
510 | 0 | { |
511 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
512 | 0 | return(MagickFalse); |
513 | 0 | } |
514 | 0 | (void) memset(dmbuf,background_color_index,(size_t) dmsx*(size_t) |
515 | 0 | dmsy*sizeof(sixel_pixel_t)); |
516 | 0 | for (y = 0; y < imsy; ++y) |
517 | 0 | (void) memcpy(dmbuf+dmsx*y,imbuf+imsx*y,(size_t) imsx* |
518 | 0 | sizeof(sixel_pixel_t)); |
519 | 0 | imbuf = (sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
520 | 0 | imsx=dmsx; |
521 | 0 | imsy=dmsy; |
522 | 0 | imbuf=dmbuf; |
523 | 0 | } |
524 | 65.5k | if (color_index > max_color_index) |
525 | 542 | max_color_index = color_index; |
526 | 65.5k | if ((b = *(p++) - '?') == 0) |
527 | 681 | position_x += repeat_count; |
528 | 64.8k | else |
529 | 64.8k | { |
530 | 64.8k | sixel_vertical_mask=0x01; |
531 | 64.8k | if (repeat_count <= 1) |
532 | 58.7k | { |
533 | 410k | for (i = 0; i < 6; i++) |
534 | 352k | { |
535 | 352k | if ((b & sixel_vertical_mask) != 0) |
536 | 164k | { |
537 | 164k | offset=(size_t) (imsx*((ssize_t) position_y+i)+ |
538 | 164k | (ssize_t) position_x); |
539 | 164k | if (offset >= (size_t) (imsx*imsy)) |
540 | 0 | { |
541 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
542 | 0 | return(MagickFalse); |
543 | 0 | } |
544 | 164k | imbuf[offset]=(sixel_pixel_t) color_index; |
545 | 164k | if (max_x < position_x) |
546 | 12.5k | max_x = position_x; |
547 | 164k | if (max_y < (position_y + i)) |
548 | 39.2k | max_y = position_y + i; |
549 | 164k | } |
550 | 352k | sixel_vertical_mask <<= 1; |
551 | 352k | } |
552 | 58.7k | position_x += 1; |
553 | 58.7k | } |
554 | 6.14k | else /* repeat_count > 1 */ |
555 | 6.14k | { |
556 | 21.9k | for (i = 0; i < 6; i++) |
557 | 15.8k | { |
558 | 15.8k | if ((b & sixel_vertical_mask) != 0) |
559 | 8.41k | { |
560 | 8.41k | c=sixel_vertical_mask << 1; |
561 | 29.4k | for (n=1; (i+n) < 6; n++) |
562 | 23.9k | { |
563 | 23.9k | if ((b & c) == 0) |
564 | 2.95k | break; |
565 | 21.0k | c <<= 1; |
566 | 21.0k | } |
567 | 37.8k | for (y = position_y + i; y < position_y + i + n; ++y) |
568 | 29.4k | { |
569 | 29.4k | offset=(size_t) ((ssize_t) imsx*y+(ssize_t) position_x); |
570 | 29.4k | if ((offset+(size_t) repeat_count) >= (size_t) (imsx*imsy)) |
571 | 0 | { |
572 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
573 | 0 | return(MagickFalse); |
574 | 0 | } |
575 | 623k | for (x = 0; x < repeat_count; x++) |
576 | 594k | imbuf[(int) offset+x]=(sixel_pixel_t) color_index; |
577 | 29.4k | } |
578 | 8.41k | if (max_x < (position_x+repeat_count-1)) |
579 | 3.78k | max_x = position_x+repeat_count-1; |
580 | 8.41k | if (max_y < (position_y+i+n-1)) |
581 | 497 | max_y = position_y+i+n-1; |
582 | 8.41k | i+=(n-1); |
583 | 8.41k | sixel_vertical_mask <<= (n-1); |
584 | 8.41k | } |
585 | 15.8k | sixel_vertical_mask <<= 1; |
586 | 15.8k | } |
587 | 6.14k | position_x += repeat_count; |
588 | 6.14k | } |
589 | 64.8k | } |
590 | 65.5k | repeat_count = 1; |
591 | 65.5k | } |
592 | 37.2k | else |
593 | 37.2k | p++; |
594 | 198k | } |
595 | 2.38k | if (++max_x < attributed_ph) |
596 | 246 | max_x = attributed_ph; |
597 | 2.38k | if (++max_y < attributed_pv) |
598 | 165 | max_y = attributed_pv; |
599 | 2.38k | if ((imsx > max_x) || (imsy > max_y)) |
600 | 2.37k | { |
601 | 2.37k | dmsx=max_x; |
602 | 2.37k | dmsy=max_y; |
603 | 2.37k | if (SetImageExtent(image,(size_t) dmsx,(size_t) dmsy,exception) == MagickFalse) |
604 | 109 | { |
605 | 109 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
606 | 109 | return(MagickFalse); |
607 | 109 | } |
608 | 2.26k | dmbuf=(sixel_pixel_t *) AcquireQuantumMemory((size_t) dmsx,(size_t) dmsy* |
609 | 2.26k | sizeof(sixel_pixel_t)); |
610 | 2.26k | if (dmbuf == (sixel_pixel_t *) NULL) |
611 | 0 | { |
612 | 0 | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
613 | 0 | return(MagickFalse); |
614 | 0 | } |
615 | 183k | for (y=0; y < dmsy; ++y) |
616 | 181k | (void) memcpy(dmbuf+dmsx*y,imbuf+imsx*y,(size_t) dmsx* |
617 | 181k | sizeof(sixel_pixel_t)); |
618 | 2.26k | imbuf=(sixel_pixel_t *) RelinquishMagickMemory(imbuf); |
619 | 2.26k | imsx=dmsx; |
620 | 2.26k | imsy=dmsy; |
621 | 2.26k | imbuf=dmbuf; |
622 | 2.26k | } |
623 | | |
624 | 2.27k | *pixels=imbuf; |
625 | 2.27k | *pwidth=(size_t) imsx; |
626 | 2.27k | *pheight=(size_t) imsy; |
627 | 2.27k | *ncolors=(size_t) max_color_index+1; |
628 | 2.27k | *palette=(unsigned char *) AcquireQuantumMemory(*ncolors,4); |
629 | 2.27k | if (*palette == (unsigned char *) NULL) |
630 | 0 | return(MagickFalse); |
631 | 68.1k | for (n = 0; n < (ssize_t) *ncolors; ++n) |
632 | 65.8k | { |
633 | 65.8k | (*palette)[n*4+0]=sixel_palet[n] >> 16 & 0xff; |
634 | 65.8k | (*palette)[n*4+1]=sixel_palet[n] >> 8 & 0xff; |
635 | 65.8k | (*palette)[n*4+2]=sixel_palet[n] & 0xff; |
636 | 65.8k | (*palette)[n*4+3]=0xff; |
637 | 65.8k | } |
638 | 2.27k | return(MagickTrue); |
639 | 2.27k | } |
640 | | |
641 | | static sixel_output_t *sixel_output_create(Image *image) |
642 | 1.69k | { |
643 | 1.69k | sixel_output_t |
644 | 1.69k | *output; |
645 | | |
646 | 1.69k | output=(sixel_output_t *) AcquireMagickMemory(sizeof(sixel_output_t)); |
647 | 1.69k | if (output == (sixel_output_t *) NULL) |
648 | 0 | return((sixel_output_t *) NULL); |
649 | 1.69k | output->has_8bit_control=0; |
650 | 1.69k | output->save_pixel=0; |
651 | 1.69k | output->save_count=0; |
652 | 1.69k | output->active_palette=(-1); |
653 | 1.69k | output->node_top=(sixel_node_t *) NULL; |
654 | 1.69k | output->node_free=(sixel_node_t *) NULL; |
655 | 1.69k | output->image=image; |
656 | 1.69k | output->pos=0; |
657 | 1.69k | return(output); |
658 | 1.69k | } |
659 | | |
660 | | static void sixel_advance(sixel_output_t *context, int nwrite) |
661 | 248k | { |
662 | 248k | if ((context->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) |
663 | 1.05k | { |
664 | 1.05k | WriteBlob(context->image,SIXEL_OUTPUT_PACKET_SIZE,context->buffer); |
665 | 1.05k | memmove(context->buffer, |
666 | 1.05k | context->buffer + SIXEL_OUTPUT_PACKET_SIZE,(size_t) |
667 | 1.05k | (context->pos -= SIXEL_OUTPUT_PACKET_SIZE)); |
668 | 1.05k | } |
669 | 248k | } |
670 | | |
671 | | static int sixel_put_flash(sixel_output_t *const context) |
672 | 165k | { |
673 | 165k | int |
674 | 165k | n, |
675 | 165k | nwrite; |
676 | | |
677 | 165k | if (context->save_count > 3) |
678 | 24.4k | { |
679 | | /* DECGRI Graphics Repeat Introducer ! Pn Ch */ |
680 | 24.4k | nwrite=(int) FormatLocaleString((char *) context->buffer+context->pos, |
681 | 24.4k | sizeof(context->buffer),"!%d%c",context->save_count, |
682 | 24.4k | (char) context->save_pixel); |
683 | 24.4k | if (nwrite <= 0) |
684 | 0 | return(-1); |
685 | 24.4k | sixel_advance(context,nwrite); |
686 | 24.4k | } |
687 | 140k | else |
688 | 140k | { |
689 | 242k | for (n = 0; n < context->save_count; n++) |
690 | 101k | { |
691 | 101k | context->buffer[context->pos]=(unsigned char)context->save_pixel; |
692 | 101k | sixel_advance(context,1); |
693 | 101k | } |
694 | 140k | } |
695 | 165k | context->save_pixel=0; |
696 | 165k | context->save_count=0; |
697 | 165k | return(0); |
698 | 165k | } |
699 | | |
700 | | static void sixel_put_pixel(sixel_output_t *const context,int pix) |
701 | 18.7M | { |
702 | 18.7M | if ((pix < 0) || (pix > '?')) |
703 | 0 | pix=0; |
704 | 18.7M | pix+='?'; |
705 | 18.7M | if (pix == context->save_pixel) |
706 | 18.5M | context->save_count++; |
707 | 120k | else |
708 | 120k | { |
709 | 120k | sixel_put_flash(context); |
710 | 120k | context->save_pixel=pix; |
711 | 120k | context->save_count=1; |
712 | 120k | } |
713 | 18.7M | } |
714 | | |
715 | | static void sixel_node_del(sixel_output_t *const context,sixel_node_t *np) |
716 | 45.3k | { |
717 | 45.3k | sixel_node_t |
718 | 45.3k | *tp; |
719 | | |
720 | 45.3k | if ((tp = context->node_top) == np) |
721 | 37.0k | context->node_top = np->next; |
722 | 8.30k | else |
723 | 8.30k | { |
724 | 175k | while (tp->next != (struct sixel_node *) NULL) |
725 | 175k | { |
726 | 175k | if (tp->next == np) |
727 | 8.30k | { |
728 | 8.30k | tp->next=np->next; |
729 | 8.30k | break; |
730 | 8.30k | } |
731 | 166k | tp=tp->next; |
732 | 166k | } |
733 | 8.30k | } |
734 | 45.3k | np->next=context->node_free; |
735 | 45.3k | context->node_free=np; |
736 | 45.3k | } |
737 | | |
738 | | static int sixel_put_node(sixel_output_t *const context,int x,sixel_node_t *np, |
739 | | int ncolors,int keycolor) |
740 | 45.3k | { |
741 | 45.3k | int |
742 | 45.3k | nwrite; |
743 | | |
744 | 45.3k | if ((ncolors != 2) || (keycolor == -1)) |
745 | 45.3k | { |
746 | | /* designate palette index */ |
747 | 45.3k | if (context->active_palette != np->color) |
748 | 24.2k | { |
749 | 24.2k | nwrite=(int) FormatLocaleString((char *) context->buffer+context->pos, |
750 | 24.2k | sizeof(context->buffer),"#%d",np->color); |
751 | 24.2k | sixel_advance(context,nwrite); |
752 | 24.2k | context->active_palette=np->color; |
753 | 24.2k | } |
754 | 45.3k | } |
755 | 130k | for (; x < np->left; x++) |
756 | 84.7k | sixel_put_pixel(context,0); |
757 | 18.6M | for (; x < np->right; x++) |
758 | 18.6M | sixel_put_pixel(context,np->map[x]); |
759 | 45.3k | sixel_put_flash(context); |
760 | 45.3k | return(x); |
761 | 45.3k | } |
762 | | |
763 | | static MagickBooleanType sixel_encode_impl(sixel_pixel_t *pixels,size_t width, |
764 | | size_t height,unsigned char *palette,size_t ncolors,int keycolor, |
765 | | sixel_output_t *context) |
766 | 1.69k | { |
767 | 1.69k | #define RelinquishNodesAndMap \ |
768 | 9.44k | while ((np = context->node_free) != NULL) \ |
769 | 7.74k | { \ |
770 | 7.74k | context->node_free=np->next; \ |
771 | 7.74k | np=(sixel_node_t *) RelinquishMagickMemory(np); \ |
772 | 7.74k | } \ |
773 | 1.69k | map=(sixel_pixel_t *) RelinquishMagickMemory(map) |
774 | | |
775 | 1.69k | int |
776 | 1.69k | c, |
777 | 1.69k | i, |
778 | 1.69k | left, |
779 | 1.69k | pix, |
780 | 1.69k | n, |
781 | 1.69k | nwrite, |
782 | 1.69k | right, |
783 | 1.69k | x, |
784 | 1.69k | y; |
785 | | |
786 | 1.69k | sixel_pixel_t |
787 | 1.69k | *map; |
788 | | |
789 | 1.69k | sixel_node_t |
790 | 1.69k | *np, |
791 | 1.69k | top, |
792 | 1.69k | *tp; |
793 | | |
794 | 1.69k | size_t |
795 | 1.69k | len; |
796 | | |
797 | 1.69k | context->pos = 0; |
798 | 1.69k | if (ncolors < 1) |
799 | 0 | return(MagickFalse); |
800 | 1.69k | len=ncolors*width; |
801 | 1.69k | context->active_palette=(-1); |
802 | 1.69k | map=(sixel_pixel_t *) AcquireQuantumMemory(len,sizeof(sixel_pixel_t)); |
803 | 1.69k | if (map == (sixel_pixel_t *) NULL) |
804 | 0 | return (MagickFalse); |
805 | 1.69k | (void) memset(map,0,len*sizeof(sixel_pixel_t)); |
806 | 1.69k | if (context->has_8bit_control) |
807 | 0 | nwrite=(int) FormatLocaleString((char *) context->buffer,sizeof(context->buffer), |
808 | 0 | "\x90" "0;0;0" "q"); |
809 | 1.69k | else |
810 | 1.69k | nwrite=(int) FormatLocaleString((char *) context->buffer,sizeof(context->buffer), |
811 | 1.69k | "\x1bP" "0;0;0" "q"); |
812 | 1.69k | if (nwrite <= 0) |
813 | 0 | return(MagickFalse); |
814 | 1.69k | sixel_advance(context,nwrite); |
815 | 1.69k | nwrite=(int) FormatLocaleString((char *) context->buffer+context->pos, |
816 | 1.69k | sizeof(context->buffer),"\"1;1;%d;%d",(int) width,(int) height); |
817 | 1.69k | if (nwrite <= 0) |
818 | 0 | { |
819 | 0 | RelinquishNodesAndMap; |
820 | 0 | return(MagickFalse); |
821 | 0 | } |
822 | 1.69k | sixel_advance(context,nwrite); |
823 | 1.69k | if ((ncolors != 2) || (keycolor == -1)) |
824 | 61.3k | for (n = 0; n < (ssize_t) ncolors; n++) |
825 | 59.6k | { |
826 | | /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */ |
827 | 59.6k | nwrite=(int) FormatLocaleString((char *) context->buffer+context->pos, |
828 | 59.6k | sizeof(context->buffer),"#%d;2;%d;%d;%d",n,(palette[n*3+0]*100+127)/ |
829 | 59.6k | 255,(palette[n*3+1]*100+127)/255,(palette[n*3+2]*100+127)/255); |
830 | 59.6k | if (nwrite <= 0) |
831 | 0 | { |
832 | 0 | RelinquishNodesAndMap; |
833 | 0 | return(MagickFalse); |
834 | 0 | } |
835 | 59.6k | sixel_advance(context,nwrite); |
836 | 59.6k | if (nwrite <= 0) |
837 | 0 | { |
838 | 0 | RelinquishNodesAndMap; |
839 | 0 | return (MagickFalse); |
840 | 0 | } |
841 | 59.6k | } |
842 | 164k | for (y=i=0; y < (int) height; y++) |
843 | 163k | { |
844 | 111M | for (x=0; x < (int) width; x++) |
845 | 110M | { |
846 | 110M | pix=pixels[y*(ssize_t) width+x]; |
847 | 110M | if ((pix >= 0) && (pix < (int) ncolors) && (pix != keycolor)) |
848 | 110M | map[pix*(ssize_t) width+x]|=(1 << i); |
849 | 110M | } |
850 | 163k | if (++i < 6 && (y + 1) < (int) height) |
851 | 135k | continue; |
852 | 4.20M | for (c=0; c < (int) ncolors; c++) |
853 | 4.17M | { |
854 | 4.13G | for (left=0; left < (int) width; left++) |
855 | 4.13G | { |
856 | 4.13G | if (*(map+c*(ssize_t) width+left) == 0) |
857 | 4.13G | continue; |
858 | 18.6M | for (right=left+1; right < (int) width; right++) |
859 | 18.5M | { |
860 | 18.5M | if (*(map+c*(ssize_t) width+right) != 0) |
861 | 18.5M | continue; |
862 | 5.15M | for (n = 1; (right+n) < (int) width; n++) |
863 | 5.14M | { |
864 | 5.14M | if (*(map+c*(ssize_t) width+right+n) != 0) |
865 | 11.9k | break; |
866 | 5.14M | } |
867 | 19.1k | if ((n >= 10) || (right+n >= (int) width)) |
868 | 16.5k | break; |
869 | 2.53k | right=right+n-1; |
870 | 2.53k | } |
871 | | |
872 | 45.3k | if ((np = context->node_free) != (sixel_node_t *) NULL) |
873 | 37.6k | context->node_free=np->next; |
874 | 7.74k | else |
875 | 7.74k | if ((np = (sixel_node_t *) AcquireMagickMemory( |
876 | 7.74k | sizeof(sixel_node_t))) == (sixel_node_t *) NULL) |
877 | 0 | { |
878 | 0 | RelinquishNodesAndMap; |
879 | 0 | return(MagickFalse); |
880 | 0 | } |
881 | 45.3k | np->color=c; |
882 | 45.3k | np->left=left; |
883 | 45.3k | np->right=right; |
884 | 45.3k | np->map=map+c*(ssize_t) width; |
885 | 45.3k | top.next=context->node_top; |
886 | 45.3k | tp=⊤ |
887 | 534k | while (tp->next != (struct sixel_node *) NULL) |
888 | 499k | { |
889 | 499k | if (np->left < tp->next->left) |
890 | 10.1k | break; |
891 | 489k | if ((np->left == tp->next->left) && (np->right > tp->next->right)) |
892 | 284 | break; |
893 | 489k | tp=tp->next; |
894 | 489k | } |
895 | 45.3k | np->next=tp->next; |
896 | 45.3k | tp->next=np; |
897 | 45.3k | context->node_top=top.next; |
898 | 45.3k | left=right-1; |
899 | 45.3k | } |
900 | 4.17M | } |
901 | 61.3k | for (x = 0; (np = context->node_top) != (sixel_node_t *) NULL;) |
902 | 33.2k | { |
903 | 33.2k | if (x > np->left) |
904 | 5.07k | { |
905 | | /* DECGCR Graphics Carriage Return */ |
906 | 5.07k | context->buffer[context->pos]='$'; |
907 | 5.07k | sixel_advance(context,1); |
908 | 5.07k | x=0; |
909 | 5.07k | } |
910 | 33.2k | x=sixel_put_node(context,x,np,(int) ncolors,keycolor); |
911 | 33.2k | sixel_node_del(context,np); |
912 | 33.2k | np=context->node_top; |
913 | 229k | while (np != (sixel_node_t *) NULL) |
914 | 196k | { |
915 | 196k | if (np->left < x) |
916 | 184k | { |
917 | 184k | np=np->next; |
918 | 184k | continue; |
919 | 184k | } |
920 | 12.1k | x=sixel_put_node(context,x,np,(int) ncolors,keycolor); |
921 | 12.1k | sixel_node_del(context,np); |
922 | 12.1k | np=context->node_top; |
923 | 12.1k | } |
924 | 33.2k | } |
925 | | /* DECGNL Graphics Next Line */ |
926 | 28.1k | context->buffer[context->pos]='-'; |
927 | 28.1k | sixel_advance(context,1); |
928 | 28.1k | if (nwrite <= 0) |
929 | 0 | { |
930 | 0 | RelinquishNodesAndMap; |
931 | 0 | return(MagickFalse); |
932 | 0 | } |
933 | 28.1k | i=0; |
934 | 28.1k | (void) memset(map,0,len*sizeof(sixel_pixel_t)); |
935 | 28.1k | } |
936 | 1.69k | if (context->has_8bit_control) |
937 | 0 | { |
938 | 0 | context->buffer[context->pos]=0x9c; |
939 | 0 | sixel_advance(context,1); |
940 | 0 | } |
941 | 1.69k | else |
942 | 1.69k | { |
943 | 1.69k | context->buffer[context->pos]=0x1b; |
944 | 1.69k | context->buffer[context->pos+1]='\\'; |
945 | 1.69k | sixel_advance(context,2); |
946 | 1.69k | } |
947 | 1.69k | if (nwrite <= 0) |
948 | 0 | { |
949 | 0 | RelinquishNodesAndMap; |
950 | 0 | return(MagickFalse); |
951 | 0 | } |
952 | | /* flush buffer */ |
953 | 1.69k | if (context->pos > 0) |
954 | 1.69k | WriteBlob(context->image,(size_t) context->pos,context->buffer); |
955 | 1.69k | RelinquishNodesAndMap; |
956 | 1.69k | return(MagickTrue); |
957 | 1.69k | } |
958 | | |
959 | | /* |
960 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
961 | | % % |
962 | | % % |
963 | | % % |
964 | | % I s S I X E L % |
965 | | % % |
966 | | % % |
967 | | % % |
968 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
969 | | % |
970 | | % IsSIXEL() returns MagickTrue if the image format type, identified by the |
971 | | % magick string, is SIXEL. |
972 | | % |
973 | | % The format of the IsSIXEL method is: |
974 | | % |
975 | | % MagickBooleanType IsSIXEL(const unsigned char *magick, |
976 | | % const size_t length) |
977 | | % |
978 | | % A description of each parameter follows: |
979 | | % |
980 | | % o magick: compare image format pattern against these bytes. or |
981 | | % blob. |
982 | | % |
983 | | % o length: Specifies the length of the magick string. |
984 | | % |
985 | | */ |
986 | | static MagickBooleanType IsSIXEL(const unsigned char *magick, |
987 | | const size_t length) |
988 | 0 | { |
989 | 0 | const unsigned char |
990 | 0 | *end = magick + length; |
991 | |
|
992 | 0 | if (length < 3) |
993 | 0 | return(MagickFalse); |
994 | | |
995 | 0 | if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) { |
996 | 0 | while (++magick != end) { |
997 | 0 | if (*magick == 'q') |
998 | 0 | return(MagickTrue); |
999 | 0 | if (!(*magick >= '0' && *magick <= '9') && *magick != ';') |
1000 | 0 | return(MagickFalse); |
1001 | 0 | } |
1002 | 0 | } |
1003 | 0 | return(MagickFalse); |
1004 | 0 | } |
1005 | | |
1006 | | /* |
1007 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1008 | | % % |
1009 | | % % |
1010 | | % % |
1011 | | % R e a d S I X E L I m a g e % |
1012 | | % % |
1013 | | % % |
1014 | | % % |
1015 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1016 | | % |
1017 | | % ReadSIXELImage() reads an X11 pixmap image file and returns it. It |
1018 | | % allocates the memory necessary for the new Image structure and returns a |
1019 | | % pointer to the new image. |
1020 | | % |
1021 | | % The format of the ReadSIXELImage method is: |
1022 | | % |
1023 | | % Image *ReadSIXELImage(const ImageInfo *image_info, |
1024 | | % ExceptionInfo *exception) |
1025 | | % |
1026 | | % A description of each parameter follows: |
1027 | | % |
1028 | | % o image_info: the image info. |
1029 | | % |
1030 | | % o exception: return any errors or warnings in this structure. |
1031 | | % |
1032 | | */ |
1033 | | static Image *ReadSIXELImage(const ImageInfo *image_info, |
1034 | | ExceptionInfo *exception) |
1035 | 2.91k | { |
1036 | 2.91k | char |
1037 | 2.91k | *p, |
1038 | 2.91k | *sixel_buffer; |
1039 | | |
1040 | 2.91k | Image |
1041 | 2.91k | *image; |
1042 | | |
1043 | 2.91k | MagickBooleanType |
1044 | 2.91k | status; |
1045 | | |
1046 | 2.91k | Quantum |
1047 | 2.91k | *q; |
1048 | | |
1049 | 2.91k | size_t |
1050 | 2.91k | length; |
1051 | | |
1052 | 2.91k | sixel_pixel_t |
1053 | 2.91k | *sixel_pixels; |
1054 | | |
1055 | 2.91k | ssize_t |
1056 | 2.91k | i, |
1057 | 2.91k | j, |
1058 | 2.91k | y; |
1059 | | |
1060 | 2.91k | unsigned char |
1061 | 2.91k | *sixel_palette; |
1062 | | |
1063 | | /* |
1064 | | Open image file. |
1065 | | */ |
1066 | 2.91k | assert(image_info != (const ImageInfo *) NULL); |
1067 | 2.91k | assert(image_info->signature == MagickCoreSignature); |
1068 | 2.91k | assert(exception != (ExceptionInfo *) NULL); |
1069 | 2.91k | assert(exception->signature == MagickCoreSignature); |
1070 | 2.91k | if (IsEventLogging() != MagickFalse) |
1071 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", |
1072 | 0 | image_info->filename); |
1073 | 2.91k | image=AcquireImage(image_info,exception); |
1074 | 2.91k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
1075 | 2.91k | if (status == MagickFalse) |
1076 | 529 | { |
1077 | 529 | image=DestroyImageList(image); |
1078 | 529 | return((Image *) NULL); |
1079 | 529 | } |
1080 | | /* |
1081 | | Read SIXEL file. |
1082 | | */ |
1083 | 2.38k | length=MagickPathExtent; |
1084 | 2.38k | sixel_buffer=(char *) AcquireQuantumMemory((size_t) length+MagickPathExtent, |
1085 | 2.38k | sizeof(*sixel_buffer)); |
1086 | 2.38k | p=sixel_buffer; |
1087 | 2.38k | if (sixel_buffer != (char *) NULL) |
1088 | 5.68k | while (ReadBlobString(image,p) != (char *) NULL) |
1089 | 3.30k | { |
1090 | 3.30k | ssize_t |
1091 | 3.30k | offset; |
1092 | | |
1093 | 3.30k | if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n'))) |
1094 | 194 | continue; |
1095 | 3.11k | if ((*p == '}') && (*(p+1) == ';')) |
1096 | 3 | break; |
1097 | 3.10k | p+=(ptrdiff_t) strlen(p); |
1098 | 3.10k | offset=p-sixel_buffer; |
1099 | 3.10k | if ((size_t) (offset+MagickPathExtent+1) < length) |
1100 | 1.22k | continue; |
1101 | 1.88k | length<<=1; |
1102 | 1.88k | sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+ |
1103 | 1.88k | MagickPathExtent+1,sizeof(*sixel_buffer)); |
1104 | 1.88k | if (sixel_buffer == (char *) NULL) |
1105 | 0 | break; |
1106 | 1.88k | p=sixel_buffer+offset; |
1107 | 1.88k | } |
1108 | 2.38k | if (sixel_buffer == (char *) NULL) |
1109 | 2.38k | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
1110 | 2.38k | sixel_buffer[length]='\0'; |
1111 | | /* |
1112 | | Decode SIXEL. |
1113 | | */ |
1114 | 2.38k | sixel_pixels=(sixel_pixel_t *) NULL; |
1115 | 2.38k | status=sixel_decode(image,(unsigned char *) sixel_buffer,&sixel_pixels, |
1116 | 2.38k | &image->columns,&image->rows,&sixel_palette,&image->colors, |
1117 | 2.38k | exception); |
1118 | 2.38k | if (status == MagickFalse) |
1119 | 109 | { |
1120 | 109 | sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer); |
1121 | 109 | if (sixel_pixels != (sixel_pixel_t *) NULL) |
1122 | 0 | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1123 | 109 | ThrowReaderException(CorruptImageError,"CorruptImage"); |
1124 | 0 | } |
1125 | 2.27k | sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer); |
1126 | 2.27k | image->depth=24; |
1127 | 2.27k | image->storage_class=PseudoClass; |
1128 | 2.27k | status=SetImageExtent(image,image->columns,image->rows,exception); |
1129 | 2.27k | if (status == MagickFalse) |
1130 | 0 | { |
1131 | 0 | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1132 | 0 | sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette); |
1133 | 0 | return(DestroyImageList(image)); |
1134 | 0 | } |
1135 | 2.27k | if (AcquireImageColormap(image,image->colors, exception) == MagickFalse) |
1136 | 0 | { |
1137 | 0 | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1138 | 0 | sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette); |
1139 | 0 | ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); |
1140 | 0 | } |
1141 | 68.1k | for (i = 0; i < (ssize_t) image->colors; ++i) |
1142 | 65.8k | { |
1143 | 65.8k | image->colormap[i].red=ScaleCharToQuantum(sixel_palette[i * 4 + 0]); |
1144 | 65.8k | image->colormap[i].green=ScaleCharToQuantum(sixel_palette[i * 4 + 1]); |
1145 | 65.8k | image->colormap[i].blue=ScaleCharToQuantum(sixel_palette[i * 4 + 2]); |
1146 | 65.8k | } |
1147 | 2.27k | j=0; |
1148 | 2.27k | if (image_info->ping == MagickFalse) |
1149 | 2.27k | { |
1150 | | /* |
1151 | | Read image pixels. |
1152 | | */ |
1153 | 197k | for (y=0; y < (ssize_t) image->rows; y++) |
1154 | 195k | { |
1155 | 195k | ssize_t |
1156 | 195k | x; |
1157 | | |
1158 | 195k | q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); |
1159 | 195k | if (q == (Quantum *) NULL) |
1160 | 0 | break; |
1161 | 149M | for (x=0; x < (ssize_t) image->columns; x++) |
1162 | 148M | { |
1163 | 148M | j=(ssize_t) sixel_pixels[y*(ssize_t) image->columns+x]; |
1164 | 148M | j=ConstrainColormapIndex(image,j,exception); |
1165 | 148M | SetPixelIndex(image,(Quantum) j,q); |
1166 | 148M | SetPixelRed(image,(Quantum) image->colormap[j].red,q); |
1167 | 148M | SetPixelGreen(image,(Quantum) image->colormap[j].green,q); |
1168 | 148M | SetPixelBlue(image,(Quantum) image->colormap[j].blue,q); |
1169 | 148M | q+=(ptrdiff_t) GetPixelChannels(image); |
1170 | 148M | } |
1171 | 195k | if (SyncAuthenticPixels(image,exception) == MagickFalse) |
1172 | 0 | break; |
1173 | 195k | } |
1174 | 2.27k | if (y < (ssize_t) image->rows) |
1175 | 0 | { |
1176 | 0 | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1177 | 0 | sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette); |
1178 | 0 | ThrowReaderException(CorruptImageError,"NotEnoughPixelData"); |
1179 | 0 | } |
1180 | 2.27k | } |
1181 | | /* |
1182 | | Relinquish resources. |
1183 | | */ |
1184 | 2.27k | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1185 | 2.27k | sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette); |
1186 | 2.27k | if (CloseBlob(image) == MagickFalse) |
1187 | 185 | status=MagickFalse; |
1188 | 2.27k | if (status == MagickFalse) |
1189 | 185 | return(DestroyImageList(image)); |
1190 | 2.08k | return(GetFirstImageInList(image)); |
1191 | 2.27k | } |
1192 | | |
1193 | | /* |
1194 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1195 | | % % |
1196 | | % % |
1197 | | % % |
1198 | | % R e g i s t e r S I X E L I m a g e % |
1199 | | % % |
1200 | | % % |
1201 | | % % |
1202 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1203 | | % |
1204 | | % RegisterSIXELImage() adds attributes for the SIXEL image format to |
1205 | | % the list of supported formats. The attributes include the image format |
1206 | | % tag, a method to read and/or write the format, whether the format |
1207 | | % supports the saving of more than one frame to the same file or blob, |
1208 | | % whether the format supports native in-memory I/O, and a brief |
1209 | | % description of the format. |
1210 | | % |
1211 | | % The format of the RegisterSIXELImage method is: |
1212 | | % |
1213 | | % size_t RegisterSIXELImage(void) |
1214 | | % |
1215 | | */ |
1216 | | ModuleExport size_t RegisterSIXELImage(void) |
1217 | 8 | { |
1218 | 8 | MagickInfo |
1219 | 8 | *entry; |
1220 | | |
1221 | 8 | entry=AcquireMagickInfo("SIXEL","SIXEL","DEC SIXEL Graphics Format"); |
1222 | 8 | entry->decoder=(DecodeImageHandler *) ReadSIXELImage; |
1223 | 8 | entry->encoder=(EncodeImageHandler *) WriteSIXELImage; |
1224 | 8 | entry->magick=(IsImageFormatHandler *) IsSIXEL; |
1225 | 8 | entry->flags^=CoderAdjoinFlag; |
1226 | 8 | (void) RegisterMagickInfo(entry); |
1227 | 8 | entry=AcquireMagickInfo("SIXEL","SIX","DEC SIXEL Graphics Format"); |
1228 | 8 | entry->decoder=(DecodeImageHandler *) ReadSIXELImage; |
1229 | 8 | entry->encoder=(EncodeImageHandler *) WriteSIXELImage; |
1230 | 8 | entry->magick=(IsImageFormatHandler *) IsSIXEL; |
1231 | 8 | entry->flags^=CoderAdjoinFlag; |
1232 | 8 | (void) RegisterMagickInfo(entry); |
1233 | 8 | return(MagickImageCoderSignature); |
1234 | 8 | } |
1235 | | |
1236 | | /* |
1237 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1238 | | % % |
1239 | | % % |
1240 | | % % |
1241 | | % U n r e g i s t e r S I X E L I m a g e % |
1242 | | % % |
1243 | | % % |
1244 | | % % |
1245 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1246 | | % |
1247 | | % UnregisterSIXELImage() removes format registrations made by the |
1248 | | % SIXEL module from the list of supported formats. |
1249 | | % |
1250 | | % The format of the UnregisterSIXELImage method is: |
1251 | | % |
1252 | | % UnregisterSIXELImage(void) |
1253 | | % |
1254 | | */ |
1255 | | ModuleExport void UnregisterSIXELImage(void) |
1256 | 0 | { |
1257 | 0 | (void) UnregisterMagickInfo("SIXEL"); |
1258 | 0 | (void) UnregisterMagickInfo("SIX"); |
1259 | 0 | } |
1260 | | |
1261 | | /* |
1262 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1263 | | % % |
1264 | | % % |
1265 | | % % |
1266 | | % W r i t e S I X E L I m a g e % |
1267 | | % % |
1268 | | % % |
1269 | | % % |
1270 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
1271 | | % |
1272 | | % WriteSIXELImage() writes an image to a file in the X pixmap format. |
1273 | | % |
1274 | | % The format of the WriteSIXELImage method is: |
1275 | | % |
1276 | | % MagickBooleanType WriteSIXELImage(const ImageInfo *image_info, |
1277 | | % Image *image,ExceptionInfo *exception) |
1278 | | % |
1279 | | % A description of each parameter follows. |
1280 | | % |
1281 | | % o image_info: the image info. |
1282 | | % |
1283 | | % o image: The image. |
1284 | | % |
1285 | | % o exception: return any errors or warnings in this structure. |
1286 | | % |
1287 | | */ |
1288 | | static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info, |
1289 | | Image *image,ExceptionInfo *exception) |
1290 | 1.69k | { |
1291 | 1.69k | MagickBooleanType |
1292 | 1.69k | status; |
1293 | | |
1294 | 1.69k | const Quantum |
1295 | 1.69k | *q; |
1296 | | |
1297 | 1.69k | ssize_t |
1298 | 1.69k | i, |
1299 | 1.69k | x; |
1300 | | |
1301 | 1.69k | ssize_t |
1302 | 1.69k | opacity, |
1303 | 1.69k | y; |
1304 | | |
1305 | 1.69k | sixel_output_t |
1306 | 1.69k | *output; |
1307 | | |
1308 | 1.69k | unsigned char |
1309 | 1.69k | sixel_palette[SIXEL_PALETTE_MAX*3]; |
1310 | | |
1311 | 1.69k | sixel_pixel_t |
1312 | 1.69k | *sixel_pixels; |
1313 | | |
1314 | | /* |
1315 | | Open output image file. |
1316 | | */ |
1317 | 1.69k | assert(image_info != (const ImageInfo *) NULL); |
1318 | 1.69k | assert(image_info->signature == MagickCoreSignature); |
1319 | 1.69k | assert(image != (Image *) NULL); |
1320 | 1.69k | assert(image->signature == MagickCoreSignature); |
1321 | 1.69k | if (IsEventLogging() != MagickFalse) |
1322 | 0 | (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); |
1323 | 1.69k | status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception); |
1324 | 1.69k | if (status == MagickFalse) |
1325 | 0 | return(status); |
1326 | 1.69k | if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) |
1327 | 0 | (void) TransformImageColorspace(image,sRGBColorspace,exception); |
1328 | 1.69k | opacity=(-1); |
1329 | 1.69k | if ((image->alpha_trait & BlendPixelTrait) == 0) |
1330 | 1.69k | { |
1331 | 1.69k | if ((image->storage_class == DirectClass) || (image->colors > SIXEL_PALETTE_MAX)) |
1332 | 0 | (void) SetImageType(image,PaletteType,exception); |
1333 | 1.69k | } |
1334 | 0 | else |
1335 | 0 | { |
1336 | 0 | MagickRealType |
1337 | 0 | alpha, |
1338 | 0 | beta; |
1339 | | |
1340 | | /* |
1341 | | Identify transparent colormap index. |
1342 | | */ |
1343 | 0 | if ((image->storage_class == DirectClass) || |
1344 | 0 | (image->colors > SIXEL_PALETTE_MAX)) |
1345 | 0 | (void) SetImageType(image,PaletteBilevelAlphaType,exception); |
1346 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
1347 | 0 | if (image->colormap[i].alpha != (double) OpaqueAlpha) |
1348 | 0 | { |
1349 | 0 | if (opacity < 0) |
1350 | 0 | { |
1351 | 0 | opacity=i; |
1352 | 0 | continue; |
1353 | 0 | } |
1354 | 0 | alpha=image->colormap[i].alpha; |
1355 | 0 | beta=image->colormap[opacity].alpha; |
1356 | 0 | if (alpha < beta) |
1357 | 0 | opacity=i; |
1358 | 0 | } |
1359 | 0 | if (opacity == -1) |
1360 | 0 | { |
1361 | 0 | (void) SetImageType(image,PaletteBilevelAlphaType,exception); |
1362 | 0 | for (i=0; i < (ssize_t) image->colors; i++) |
1363 | 0 | if (image->colormap[i].alpha != (double) OpaqueAlpha) |
1364 | 0 | { |
1365 | 0 | if (opacity < 0) |
1366 | 0 | { |
1367 | 0 | opacity=i; |
1368 | 0 | continue; |
1369 | 0 | } |
1370 | 0 | alpha=image->colormap[i].alpha; |
1371 | 0 | beta=image->colormap[opacity].alpha; |
1372 | 0 | if (alpha < beta) |
1373 | 0 | opacity=i; |
1374 | 0 | } |
1375 | 0 | } |
1376 | 0 | if (opacity >= 0) |
1377 | 0 | { |
1378 | 0 | image->colormap[opacity].red=image->transparent_color.red; |
1379 | 0 | image->colormap[opacity].green=image->transparent_color.green; |
1380 | 0 | image->colormap[opacity].blue=image->transparent_color.blue; |
1381 | 0 | } |
1382 | 0 | } |
1383 | | /* |
1384 | | SIXEL header. |
1385 | | */ |
1386 | 61.3k | for (i=0; i < (ssize_t) image->colors; i++) |
1387 | 59.6k | { |
1388 | 59.6k | sixel_palette[3*i+0]=ScaleQuantumToChar((Quantum) image->colormap[i].red); |
1389 | 59.6k | sixel_palette[3*i+1]=ScaleQuantumToChar((Quantum) image->colormap[i].green); |
1390 | 59.6k | sixel_palette[3*i+2]=ScaleQuantumToChar((Quantum) image->colormap[i].blue); |
1391 | 59.6k | } |
1392 | | /* |
1393 | | Define SIXEL pixels. |
1394 | | */ |
1395 | 1.69k | output=sixel_output_create(image); |
1396 | 1.69k | if (output == (sixel_output_t *) NULL) |
1397 | 1.69k | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
1398 | 1.69k | sixel_pixels=(sixel_pixel_t *) AcquireQuantumMemory(image->columns, |
1399 | 1.69k | image->rows*sizeof(sixel_pixel_t)); |
1400 | 1.69k | if (sixel_pixels == (sixel_pixel_t *) NULL) |
1401 | 0 | { |
1402 | 0 | output=(sixel_output_t *) RelinquishMagickMemory(output); |
1403 | 0 | ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed"); |
1404 | 0 | } |
1405 | 164k | for (y=0; y < (ssize_t) image->rows; y++) |
1406 | 163k | { |
1407 | 163k | q=GetVirtualPixels(image,0,y,image->columns,1,exception); |
1408 | 163k | if (q == (Quantum *) NULL) |
1409 | 0 | break; |
1410 | 111M | for (x=0; x < (ssize_t) image->columns; x++) |
1411 | 110M | { |
1412 | 110M | sixel_pixels[y*(ssize_t) image->columns+x]=((sixel_pixel_t) |
1413 | 110M | GetPixelIndex(image,q)); |
1414 | 110M | q+=(ptrdiff_t) GetPixelChannels(image); |
1415 | 110M | } |
1416 | 163k | } |
1417 | 1.69k | status=sixel_encode_impl(sixel_pixels,image->columns,image->rows, |
1418 | 1.69k | sixel_palette,image->colors,-1,output); |
1419 | 1.69k | sixel_pixels=(sixel_pixel_t *) RelinquishMagickMemory(sixel_pixels); |
1420 | 1.69k | output=(sixel_output_t *) RelinquishMagickMemory(output); |
1421 | 1.69k | if (CloseBlob(image) == MagickFalse) |
1422 | 0 | status=MagickFalse; |
1423 | 1.69k | return(status); |
1424 | 1.69k | } |