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