blob: b9901ba73023e33142a4fe14b0bd63dc0e261f8d [file] [log] [blame]
cristy77f78f72014-10-04 13:29:40 +00001/*
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 %
dirkfc313e52014-10-07 19:28:14 +000018% Based on kmiya's sixel (2014-03-28) %
cristy77f78f72014-10-04 13:29:40 +000019% %
20% %
Cristy7ce65e72015-12-12 18:03:16 -050021% Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
cristy77f78f72014-10-04 13:29:40 +000022% 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% http://www.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 256
81#define SIXEL_OUTPUT_PACKET_SIZE 1024
82
83/*
84 Macros
85*/
86#define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) + (b))
87#define SIXEL_PALVAL(n,a,m) (((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/*
91 Structure declarations.
92*/
93typedef struct sixel_node {
94 struct sixel_node *next;
95 int color;
96 int left;
97 int right;
98 unsigned char *map;
99} sixel_node_t;
100
101typedef struct sixel_output {
102
103 /* compatiblity flags */
104
105 /* 0: 7bit terminal,
106 * 1: 8bit terminal */
107 unsigned char has_8bit_control;
108
cristy77f78f72014-10-04 13:29:40 +0000109 int save_pixel;
110 int save_count;
111 int active_palette;
112
113 sixel_node_t *node_top;
114 sixel_node_t *node_free;
115
116 Image *image;
117 int pos;
118 unsigned char buffer[1];
119
120} sixel_output_t;
121
122static int const sixel_default_color_table[] = {
123 SIXEL_XRGB(0, 0, 0), /* 0 Black */
124 SIXEL_XRGB(20, 20, 80), /* 1 Blue */
125 SIXEL_XRGB(80, 13, 13), /* 2 Red */
126 SIXEL_XRGB(20, 80, 20), /* 3 Green */
127 SIXEL_XRGB(80, 20, 80), /* 4 Magenta */
128 SIXEL_XRGB(20, 80, 80), /* 5 Cyan */
129 SIXEL_XRGB(80, 80, 20), /* 6 Yellow */
130 SIXEL_XRGB(53, 53, 53), /* 7 Gray 50% */
131 SIXEL_XRGB(26, 26, 26), /* 8 Gray 25% */
132 SIXEL_XRGB(33, 33, 60), /* 9 Blue* */
133 SIXEL_XRGB(60, 26, 26), /* 10 Red* */
134 SIXEL_XRGB(33, 60, 33), /* 11 Green* */
135 SIXEL_XRGB(60, 33, 60), /* 12 Magenta* */
136 SIXEL_XRGB(33, 60, 60), /* 13 Cyan* */
137 SIXEL_XRGB(60, 60, 33), /* 14 Yellow* */
138 SIXEL_XRGB(80, 80, 80), /* 15 Gray 75% */
139};
140
141/*
142 Forward declarations.
143*/
144static MagickBooleanType
dirka1e4ab82014-10-07 05:06:49 +0000145 WriteSIXELImage(const ImageInfo *,Image *,ExceptionInfo *);
cristy77f78f72014-10-04 13:29:40 +0000146
147static int hue_to_rgb(int n1, int n2, int hue)
148{
149 const int HLSMAX = 100;
150
151 if (hue < 0) {
152 hue += HLSMAX;
153 }
154
155 if (hue > HLSMAX) {
156 hue -= HLSMAX;
157 }
158
159 if (hue < (HLSMAX / 6)) {
160 return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
161 }
162 if (hue < (HLSMAX / 2)) {
163 return (n2);
164 }
165 if (hue < ((HLSMAX * 2) / 3)) {
166 return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12))/(HLSMAX / 6)));
167 }
168 return (n1);
169}
170
171static int hls_to_rgb(int hue, int lum, int sat)
172{
173 int R, G, B;
174 int Magic1, Magic2;
175 const int RGBMAX = 255;
176 const int HLSMAX = 100;
177
178 if (sat == 0) {
179 R = G = B = (lum * RGBMAX) / HLSMAX;
180 } else {
181 if (lum <= (HLSMAX / 2)) {
182 Magic2 = (lum * (HLSMAX + sat) + (HLSMAX / 2)) / HLSMAX;
183 } else {
184 Magic2 = lum + sat - ((lum * sat) + (HLSMAX / 2)) / HLSMAX;
185 }
186 Magic1 = 2 * lum - Magic2;
187
188 R = (hue_to_rgb(Magic1, Magic2, hue + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
189 G = (hue_to_rgb(Magic1, Magic2, hue) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
190 B = (hue_to_rgb(Magic1, Magic2, hue - (HLSMAX / 3)) * RGBMAX + (HLSMAX/2)) / HLSMAX;
191 }
192 return SIXEL_RGB(R, G, B);
193}
194
195static unsigned char *get_params(unsigned char *p, int *param, int *len)
196{
197 int n;
198
199 *len = 0;
200 while (*p != '\0') {
201 while (*p == ' ' || *p == '\t') {
202 p++;
203 }
204 if (isdigit(*p)) {
205 for (n = 0; isdigit(*p); p++) {
206 n = n * 10 + (*p - '0');
207 }
208 if (*len < 10) {
209 param[(*len)++] = n;
210 }
211 while (*p == ' ' || *p == '\t') {
212 p++;
213 }
214 if (*p == ';') {
215 p++;
216 }
217 } else if (*p == ';') {
218 if (*len < 10) {
219 param[(*len)++] = 0;
220 }
221 p++;
222 } else
223 break;
224 }
225 return p;
226}
227
228/* convert sixel data into indexed pixel bytes and palette data */
229MagickBooleanType sixel_decode(unsigned char /* in */ *p, /* sixel bytes */
230 unsigned char /* out */ **pixels, /* decoded pixels */
231 size_t /* out */ *pwidth, /* image width */
232 size_t /* out */ *pheight, /* image height */
233 unsigned char /* out */ **palette, /* ARGB palette */
234 size_t /* out */ *ncolors /* palette size (<= 256) */)
235{
236 int n, i, r, g, b, sixel_vertical_mask, c;
237 int posision_x, posision_y;
238 int max_x, max_y;
239 int attributed_pan, attributed_pad;
240 int attributed_ph, attributed_pv;
241 int repeat_count, color_index, max_color_index = 2, background_color_index;
242 int param[10];
cristy77f78f72014-10-04 13:29:40 +0000243 int sixel_palet[SIXEL_PALETTE_MAX];
244 unsigned char *imbuf, *dmbuf;
245 int imsx, imsy;
246 int dmsx, dmsy;
247 int y;
248
249 posision_x = posision_y = 0;
250 max_x = max_y = 0;
251 attributed_pan = 2;
252 attributed_pad = 1;
253 attributed_ph = attributed_pv = 0;
254 repeat_count = 1;
255 color_index = 0;
256 background_color_index = 0;
257
258 imsx = 2048;
259 imsy = 2048;
260 imbuf = (unsigned char *) AcquireQuantumMemory(imsx * imsy,1);
261
262 if (imbuf == NULL) {
263 return(MagickFalse);
264 }
265
266 for (n = 0; n < 16; n++) {
267 sixel_palet[n] = sixel_default_color_table[n];
268 }
269
270 /* colors 16-231 are a 6x6x6 color cube */
271 for (r = 0; r < 6; r++) {
272 for (g = 0; g < 6; g++) {
273 for (b = 0; b < 6; b++) {
274 sixel_palet[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
275 }
276 }
277 }
278 /* colors 232-255 are a grayscale ramp, intentionally leaving out */
279 for (i = 0; i < 24; i++) {
280 sixel_palet[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
281 }
282
283 for (; n < SIXEL_PALETTE_MAX; n++) {
284 sixel_palet[n] = SIXEL_RGB(255, 255, 255);
285 }
286
287 (void) ResetMagickMemory(imbuf, background_color_index, imsx * imsy);
288
cristy77f78f72014-10-04 13:29:40 +0000289 while (*p != '\0') {
290 if ((p[0] == '\033' && p[1] == 'P') || *p == 0x90) {
291 if (*p == '\033') {
292 p++;
293 }
294
dirka1e4ab82014-10-07 05:06:49 +0000295 p = get_params(++p, param, &n);
cristy77f78f72014-10-04 13:29:40 +0000296
297 if (*p == 'q') {
298 p++;
299
300 if (n > 0) { /* Pn1 */
301 switch(param[0]) {
302 case 0:
303 case 1:
304 attributed_pad = 2;
305 break;
306 case 2:
307 attributed_pad = 5;
308 break;
309 case 3:
310 attributed_pad = 4;
311 break;
312 case 4:
313 attributed_pad = 4;
314 break;
315 case 5:
316 attributed_pad = 3;
317 break;
318 case 6:
319 attributed_pad = 3;
320 break;
321 case 7:
322 attributed_pad = 2;
323 break;
324 case 8:
325 attributed_pad = 2;
326 break;
327 case 9:
328 attributed_pad = 1;
329 break;
330 }
331 }
332
333 if (n > 2) { /* Pn3 */
334 if (param[2] == 0) {
335 param[2] = 10;
336 }
337 attributed_pan = attributed_pan * param[2] / 10;
338 attributed_pad = attributed_pad * param[2] / 10;
339 if (attributed_pan <= 0) attributed_pan = 1;
340 if (attributed_pad <= 0) attributed_pad = 1;
341 }
342 }
343
344 } else if ((p[0] == '\033' && p[1] == '\\') || *p == 0x9C) {
345 break;
346 } else if (*p == '"') {
347 /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
dirka1e4ab82014-10-07 05:06:49 +0000348 p = get_params(++p, param, &n);
cristy77f78f72014-10-04 13:29:40 +0000349
350 if (n > 0) attributed_pad = param[0];
351 if (n > 1) attributed_pan = param[1];
352 if (n > 2 && param[2] > 0) attributed_ph = param[2];
353 if (n > 3 && param[3] > 0) attributed_pv = param[3];
354
355 if (attributed_pan <= 0) attributed_pan = 1;
356 if (attributed_pad <= 0) attributed_pad = 1;
357
358 if (imsx < attributed_ph || imsy < attributed_pv) {
359 dmsx = imsx > attributed_ph ? imsx : attributed_ph;
360 dmsy = imsy > attributed_pv ? imsy : attributed_pv;
361 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1);
362 if (dmbuf == (unsigned char *) NULL) {
363 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
364 return (MagickFalse);
365 }
366 (void) ResetMagickMemory(dmbuf, background_color_index, dmsx * dmsy);
367 for (y = 0; y < imsy; ++y) {
368 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
369 }
370 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
371 imsx = dmsx;
372 imsy = dmsy;
373 imbuf = dmbuf;
374 }
375
376 } else if (*p == '!') {
377 /* DECGRI Graphics Repeat Introducer ! Pn Ch */
378 p = get_params(++p, param, &n);
379
380 if (n > 0) {
381 repeat_count = param[0];
382 }
383
384 } else if (*p == '#') {
385 /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
386 p = get_params(++p, param, &n);
387
388 if (n > 0) {
389 if ((color_index = param[0]) < 0) {
390 color_index = 0;
391 } else if (color_index >= SIXEL_PALETTE_MAX) {
392 color_index = SIXEL_PALETTE_MAX - 1;
393 }
394 }
395
396 if (n > 4) {
397 if (param[1] == 1) { /* HLS */
398 if (param[2] > 360) param[2] = 360;
399 if (param[3] > 100) param[3] = 100;
400 if (param[4] > 100) param[4] = 100;
401 sixel_palet[color_index] = hls_to_rgb(param[2] * 100 / 360, param[3], param[4]);
402 } else if (param[1] == 2) { /* RGB */
403 if (param[2] > 100) param[2] = 100;
404 if (param[3] > 100) param[3] = 100;
405 if (param[4] > 100) param[4] = 100;
406 sixel_palet[color_index] = SIXEL_XRGB(param[2], param[3], param[4]);
407 }
408 }
409
410 } else if (*p == '$') {
411 /* DECGCR Graphics Carriage Return */
412 p++;
413 posision_x = 0;
414 repeat_count = 1;
415
416 } else if (*p == '-') {
417 /* DECGNL Graphics Next Line */
418 p++;
419 posision_x = 0;
420 posision_y += 6;
421 repeat_count = 1;
422
423 } else if (*p >= '?' && *p <= '\177') {
424 if (imsx < (posision_x + repeat_count) || imsy < (posision_y + 6)) {
425 int nx = imsx * 2;
426 int ny = imsy * 2;
427
428 while (nx < (posision_x + repeat_count) || ny < (posision_y + 6)) {
429 nx *= 2;
430 ny *= 2;
431 }
432
433 dmsx = nx;
434 dmsy = ny;
435 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1);
436 if (dmbuf == (unsigned char *) NULL) {
437 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
438 return (MagickFalse);
439 }
440 (void) ResetMagickMemory(dmbuf, background_color_index, dmsx * dmsy);
441 for (y = 0; y < imsy; ++y) {
442 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
443 }
444 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
445 imsx = dmsx;
446 imsy = dmsy;
447 imbuf = dmbuf;
448 }
449
450 if (color_index > max_color_index) {
451 max_color_index = color_index;
452 }
453 if ((b = *(p++) - '?') == 0) {
454 posision_x += repeat_count;
455
456 } else {
457 sixel_vertical_mask = 0x01;
458
459 if (repeat_count <= 1) {
460 for (i = 0; i < 6; i++) {
461 if ((b & sixel_vertical_mask) != 0) {
462 imbuf[imsx * (posision_y + i) + posision_x] = color_index;
463 if (max_x < posision_x) {
464 max_x = posision_x;
465 }
466 if (max_y < (posision_y + i)) {
467 max_y = posision_y + i;
468 }
469 }
470 sixel_vertical_mask <<= 1;
471 }
472 posision_x += 1;
473
474 } else { /* repeat_count > 1 */
475 for (i = 0; i < 6; i++) {
476 if ((b & sixel_vertical_mask) != 0) {
477 c = sixel_vertical_mask << 1;
478 for (n = 1; (i + n) < 6; n++) {
479 if ((b & c) == 0) {
480 break;
481 }
482 c <<= 1;
483 }
484 for (y = posision_y + i; y < posision_y + i + n; ++y) {
485 (void) ResetMagickMemory(imbuf + imsx * y + posision_x, color_index, repeat_count);
486 }
487 if (max_x < (posision_x + repeat_count - 1)) {
488 max_x = posision_x + repeat_count - 1;
489 }
490 if (max_y < (posision_y + i + n - 1)) {
491 max_y = posision_y + i + n - 1;
492 }
493
494 i += (n - 1);
495 sixel_vertical_mask <<= (n - 1);
496 }
497 sixel_vertical_mask <<= 1;
498 }
499 posision_x += repeat_count;
500 }
501 }
502 repeat_count = 1;
503 } else {
504 p++;
505 }
506 }
507
508 if (++max_x < attributed_ph) {
509 max_x = attributed_ph;
510 }
511 if (++max_y < attributed_pv) {
512 max_y = attributed_pv;
513 }
514
515 if (imsx > max_x || imsy > max_y) {
516 dmsx = max_x;
517 dmsy = max_y;
518 if ((dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1)) == NULL) {
519 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
520 return (MagickFalse);
521 }
522 for (y = 0; y < dmsy; ++y) {
523 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, dmsx);
524 }
525 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
526 imsx = dmsx;
527 imsy = dmsy;
528 imbuf = dmbuf;
529 }
530
531 *pixels = imbuf;
532 *pwidth = imsx;
533 *pheight = imsy;
534 *ncolors = max_color_index + 1;
535 *palette = (unsigned char *) AcquireQuantumMemory(*ncolors,4);
cristy02bcb982014-10-05 19:22:25 +0000536 for (n = 0; n < (ssize_t) *ncolors; ++n) {
cristy77f78f72014-10-04 13:29:40 +0000537 (*palette)[n * 4 + 0] = sixel_palet[n] >> 16 & 0xff;
538 (*palette)[n * 4 + 1] = sixel_palet[n] >> 8 & 0xff;
539 (*palette)[n * 4 + 2] = sixel_palet[n] & 0xff;
540 (*palette)[n * 4 + 3] = 0xff;
541 }
542 return(MagickTrue);
543}
544
545sixel_output_t *sixel_output_create(Image *image)
546{
547 sixel_output_t *output;
548
549 output = (sixel_output_t *) AcquireQuantumMemory(sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2, 1);
550 output->has_8bit_control = 0;
551 output->save_pixel = 0;
552 output->save_count = 0;
553 output->active_palette = (-1);
554 output->node_top = NULL;
555 output->node_free = NULL;
556 output->image = image;
557 output->pos = 0;
558
559 return output;
560}
561
562static void sixel_advance(sixel_output_t *context, int nwrite)
563{
564 if ((context->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) {
565 WriteBlob(context->image,SIXEL_OUTPUT_PACKET_SIZE,context->buffer);
566 CopyMagickMemory(context->buffer,
567 context->buffer + SIXEL_OUTPUT_PACKET_SIZE,
568 (context->pos -= SIXEL_OUTPUT_PACKET_SIZE));
569 }
570}
571
572static int sixel_put_flash(sixel_output_t *const context)
573{
574 int n;
575 int nwrite;
576
577#if defined(USE_VT240) /* VT240 Max 255 ? */
578 while (context->save_count > 255) {
579 nwrite = spritf((char *)context->buffer + context->pos, "!255%c", context->save_pixel);
580 if (nwrite <= 0) {
581 return (-1);
582 }
583 sixel_advance(context, nwrite);
584 context->save_count -= 255;
585 }
586#endif /* defined(USE_VT240) */
587
588 if (context->save_count > 3) {
589 /* DECGRI Graphics Repeat Introducer ! Pn Ch */
590 nwrite = sprintf((char *)context->buffer + context->pos, "!%d%c", context->save_count, context->save_pixel);
591 if (nwrite <= 0) {
592 return (-1);
593 }
594 sixel_advance(context, nwrite);
595 } else {
596 for (n = 0; n < context->save_count; n++) {
597 context->buffer[context->pos] = (char)context->save_pixel;
598 sixel_advance(context, 1);
599 }
600 }
601
602 context->save_pixel = 0;
603 context->save_count = 0;
604
605 return 0;
606}
607
608static void sixel_put_pixel(sixel_output_t *const context, int pix)
609{
610 if (pix < 0 || pix > '?') {
611 pix = 0;
612 }
613
614 pix += '?';
615
616 if (pix == context->save_pixel) {
617 context->save_count++;
618 } else {
619 sixel_put_flash(context);
620 context->save_pixel = pix;
621 context->save_count = 1;
622 }
623}
624
625static void sixel_node_del(sixel_output_t *const context, sixel_node_t *np)
626{
627 sixel_node_t *tp;
628
629 if ((tp = context->node_top) == np) {
630 context->node_top = np->next;
631 }
632
633 else {
634 while (tp->next != NULL) {
635 if (tp->next == np) {
636 tp->next = np->next;
637 break;
638 }
639 tp = tp->next;
640 }
641 }
642
643 np->next = context->node_free;
644 context->node_free = np;
645}
646
647static int sixel_put_node(sixel_output_t *const context, int x,
648 sixel_node_t *np, int ncolors, int keycolor)
649{
650 int nwrite;
651
652 if (ncolors != 2 || keycolor == -1) {
653 /* designate palette index */
654 if (context->active_palette != np->color) {
655 nwrite = sprintf((char *)context->buffer + context->pos,
dirkfc313e52014-10-07 19:28:14 +0000656 "#%d", np->color);
cristy77f78f72014-10-04 13:29:40 +0000657 sixel_advance(context, nwrite);
658 context->active_palette = np->color;
659 }
660 }
661
662 for (; x < np->left; x++) {
663 sixel_put_pixel(context, 0);
664 }
665
666 for (; x < np->right; x++) {
667 sixel_put_pixel(context, np->map[x]);
668 }
669
670 sixel_put_flash(context);
671
672 return x;
673}
674
cristy65443042014-10-25 17:41:18 +0000675static MagickBooleanType sixel_encode_impl(unsigned char *pixels, size_t width,size_t height,
676 unsigned char *palette, size_t ncolors, int keycolor,
cristy77f78f72014-10-04 13:29:40 +0000677 sixel_output_t *context)
678{
679#define RelinquishNodesAndMap \
680 while ((np = context->node_free) != NULL) { \
681 context->node_free = np->next; \
682 np=(sixel_node_t *) RelinquishMagickMemory(np); \
683 } \
684 map = (unsigned char *) RelinquishMagickMemory(map)
685
686 int x, y, i, n, c;
687 int left, right;
dirk39c51eb2014-12-27 01:01:27 +0000688 int pix;
cristy77f78f72014-10-04 13:29:40 +0000689 unsigned char *map;
690 sixel_node_t *np, *tp, top;
691 int nwrite;
dirk39c51eb2014-12-27 01:01:27 +0000692 size_t len;
cristy77f78f72014-10-04 13:29:40 +0000693
694 context->pos = 0;
695
696 if (ncolors < 1) {
697 return (MagickFalse);
698 }
699 len = ncolors * width;
700 context->active_palette = (-1);
701
702 if ((map = (unsigned char *)AcquireQuantumMemory(len, sizeof(unsigned char))) == NULL) {
703 return (MagickFalse);
704 }
705 (void) ResetMagickMemory(map, 0, len);
cristy77f78f72014-10-04 13:29:40 +0000706
707 if (context->has_8bit_control) {
708 nwrite = sprintf((char *)context->buffer, "\x90" "0;0;0" "q");
709 } else {
710 nwrite = sprintf((char *)context->buffer, "\x1bP" "0;0;0" "q");
711 }
712 if (nwrite <= 0) {
713 return (MagickFalse);
714 }
715 sixel_advance(context, nwrite);
cristy53c56cb2014-10-26 13:11:53 +0000716 nwrite = sprintf((char *)context->buffer + context->pos, "\"1;1;%d;%d", (int) width, (int) height);
cristy77f78f72014-10-04 13:29:40 +0000717 if (nwrite <= 0) {
718 RelinquishNodesAndMap;
719 return (MagickFalse);
720 }
721 sixel_advance(context, nwrite);
722
723 if (ncolors != 2 || keycolor == -1) {
cristy53c56cb2014-10-26 13:11:53 +0000724 for (n = 0; n < (ssize_t) ncolors; n++) {
cristy77f78f72014-10-04 13:29:40 +0000725 /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */
726 nwrite = sprintf((char *)context->buffer + context->pos, "#%d;2;%d;%d;%d",
dirkfc313e52014-10-07 19:28:14 +0000727 n,
cristy77f78f72014-10-04 13:29:40 +0000728 (palette[n * 3 + 0] * 100 + 127) / 255,
729 (palette[n * 3 + 1] * 100 + 127) / 255,
730 (palette[n * 3 + 2] * 100 + 127) / 255);
731 if (nwrite <= 0) {
732 RelinquishNodesAndMap;
733 return (MagickFalse);
734 }
735 sixel_advance(context, nwrite);
736 if (nwrite <= 0) {
737 RelinquishNodesAndMap;
738 return (MagickFalse);
739 }
740 }
741 }
742
cristy53c56cb2014-10-26 13:11:53 +0000743 for (y = i = 0; y < (ssize_t) height; y++) {
744 for (x = 0; x < (ssize_t) width; x++) {
cristy77f78f72014-10-04 13:29:40 +0000745 pix = pixels[y * width + x];
cristy53c56cb2014-10-26 13:11:53 +0000746 if (pix >= 0 && pix < (ssize_t) ncolors && pix != keycolor) {
cristy77f78f72014-10-04 13:29:40 +0000747 map[pix * width + x] |= (1 << i);
748 }
749 }
750
cristy53c56cb2014-10-26 13:11:53 +0000751 if (++i < 6 && (y + 1) < (ssize_t) height) {
cristy77f78f72014-10-04 13:29:40 +0000752 continue;
753 }
754
cristy53c56cb2014-10-26 13:11:53 +0000755 for (c = 0; c < (ssize_t) ncolors; c++) {
756 for (left = 0; left < (ssize_t) width; left++) {
cristy77f78f72014-10-04 13:29:40 +0000757 if (*(map + c * width + left) == 0) {
758 continue;
759 }
760
cristy53c56cb2014-10-26 13:11:53 +0000761 for (right = left + 1; right < (ssize_t) width; right++) {
cristy77f78f72014-10-04 13:29:40 +0000762 if (*(map + c * width + right) != 0) {
763 continue;
764 }
765
cristy53c56cb2014-10-26 13:11:53 +0000766 for (n = 1; (right + n) < (ssize_t) width; n++) {
cristy77f78f72014-10-04 13:29:40 +0000767 if (*(map + c * width + right + n) != 0) {
768 break;
769 }
770 }
771
cristy53c56cb2014-10-26 13:11:53 +0000772 if (n >= 10 || right + n >= (ssize_t) width) {
cristy77f78f72014-10-04 13:29:40 +0000773 break;
774 }
775 right = right + n - 1;
776 }
777
778 if ((np = context->node_free) != NULL) {
779 context->node_free = np->next;
780 } else if ((np = (sixel_node_t *)AcquireMagickMemory(sizeof(sixel_node_t))) == NULL) {
781 RelinquishNodesAndMap;
782 return (MagickFalse);
783 }
784
785 np->color = c;
786 np->left = left;
787 np->right = right;
788 np->map = map + c * width;
789
790 top.next = context->node_top;
791 tp = &top;
792
793 while (tp->next != NULL) {
794 if (np->left < tp->next->left) {
795 break;
796 }
797 if (np->left == tp->next->left && np->right > tp->next->right) {
798 break;
799 }
800 tp = tp->next;
801 }
802
803 np->next = tp->next;
804 tp->next = np;
805 context->node_top = top.next;
806
807 left = right - 1;
808 }
809
810 }
811
812 for (x = 0; (np = context->node_top) != NULL;) {
813 if (x > np->left) {
814 /* DECGCR Graphics Carriage Return */
815 context->buffer[context->pos] = '$';
816 sixel_advance(context, 1);
817 x = 0;
818 }
819
dirk39c51eb2014-12-27 01:01:27 +0000820 x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
cristy77f78f72014-10-04 13:29:40 +0000821 sixel_node_del(context, np);
822 np = context->node_top;
823
824 while (np != NULL) {
825 if (np->left < x) {
826 np = np->next;
827 continue;
828 }
829
dirk39c51eb2014-12-27 01:01:27 +0000830 x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
cristy77f78f72014-10-04 13:29:40 +0000831 sixel_node_del(context, np);
832 np = context->node_top;
833 }
834 }
835
836 /* DECGNL Graphics Next Line */
837 context->buffer[context->pos] = '-';
838 sixel_advance(context, 1);
839 if (nwrite <= 0) {
840 RelinquishNodesAndMap;
841 return (MagickFalse);
842 }
843
844 i = 0;
845 (void) ResetMagickMemory(map, 0, len);
846 }
847
848 if (context->has_8bit_control) {
849 context->buffer[context->pos] = 0x9c;
850 sixel_advance(context, 1);
851 } else {
852 context->buffer[context->pos] = 0x1b;
853 context->buffer[context->pos + 1] = '\\';
854 sixel_advance(context, 2);
855 }
856 if (nwrite <= 0) {
857 RelinquishNodesAndMap;
858 return (MagickFalse);
859 }
860
861 /* flush buffer */
862 if (context->pos > 0) {
863 WriteBlob(context->image,context->pos,context->buffer);
864 }
865
866 RelinquishNodesAndMap;
867
868 return(MagickTrue);
869}
870
871/*
872%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873% %
874% %
875% %
876% I s S I X E L %
877% %
878% %
879% %
880%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
881%
882% IsSIXEL() returns MagickTrue if the image format type, identified by the
883% magick string, is SIXEL.
884%
885% The format of the IsSIXEL method is:
886%
cristy565117b2015-06-21 19:59:07 +0000887% MagickBooleanType IsSIXEL(const unsigned char *magick,
888% const size_t length)
cristy77f78f72014-10-04 13:29:40 +0000889%
890% A description of each parameter follows:
891%
892% o magick: compare image format pattern against these bytes. or
893% blob.
894%
895% o length: Specifies the length of the magick string.
896%
897*/
cristy565117b2015-06-21 19:59:07 +0000898static MagickBooleanType IsSIXEL(const unsigned char *magick,
899 const size_t length)
cristy77f78f72014-10-04 13:29:40 +0000900{
901 const unsigned char
902 *end = magick + length;
903
904 if (length < 3)
905 return(MagickFalse);
906
907 if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) {
908 while (++magick != end) {
909 if (*magick == 'q')
910 return(MagickTrue);
911 if (!(*magick >= '0' && *magick <= '9') && *magick != ';')
912 return(MagickFalse);
913 }
914 }
915 return(MagickFalse);
916}
917
918/*
919%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920% %
921% %
922% %
923% R e a d S I X E L I m a g e %
924% %
925% %
926% %
927%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
928%
929% ReadSIXELImage() reads an X11 pixmap image file and returns it. It
930% allocates the memory necessary for the new Image structure and returns a
931% pointer to the new image.
932%
933% The format of the ReadSIXELImage method is:
934%
cristy565117b2015-06-21 19:59:07 +0000935% Image *ReadSIXELImage(const ImageInfo *image_info,
936% ExceptionInfo *exception)
cristy77f78f72014-10-04 13:29:40 +0000937%
938% A description of each parameter follows:
939%
940% o image_info: the image info.
941%
942% o exception: return any errors or warnings in this structure.
943%
944*/
cristy77f78f72014-10-04 13:29:40 +0000945static Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
946{
947 char
948 *sixel_buffer;
949
950 Image
951 *image;
952
953 MagickBooleanType
954 status;
955
956 register char
957 *p;
958
cristy77f78f72014-10-04 13:29:40 +0000959 register ssize_t
960 x;
961
dirka1e4ab82014-10-07 05:06:49 +0000962 register Quantum
963 *q;
cristy77f78f72014-10-04 13:29:40 +0000964
965 size_t
966 length;
967
968 ssize_t
969 i,
970 j,
971 y;
972
973 unsigned char
974 *sixel_pixels,
975 *sixel_palette;
976
977 /*
978 Open image file.
979 */
980 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000981 assert(image_info->signature == MagickCoreSignature);
cristy77f78f72014-10-04 13:29:40 +0000982 if (image_info->debug != MagickFalse)
983 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
984 image_info->filename);
985 assert(exception != (ExceptionInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +0000986 assert(exception->signature == MagickCoreSignature);
dirka1e4ab82014-10-07 05:06:49 +0000987 image=AcquireImage(image_info,exception);
cristy77f78f72014-10-04 13:29:40 +0000988 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
989 if (status == MagickFalse)
990 {
991 image=DestroyImageList(image);
992 return((Image *) NULL);
993 }
994 /*
995 Read SIXEL file.
996 */
cristy151b66d2015-04-15 10:50:31 +0000997 length=MagickPathExtent;
cristy565117b2015-06-21 19:59:07 +0000998 sixel_buffer=(char *) AcquireQuantumMemory((size_t) length,
999 sizeof(*sixel_buffer));
cristy77f78f72014-10-04 13:29:40 +00001000 p=sixel_buffer;
1001 if (sixel_buffer != (char *) NULL)
1002 while (ReadBlobString(image,p) != (char *) NULL)
1003 {
1004 if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n')))
1005 continue;
1006 if ((*p == '}') && (*(p+1) == ';'))
1007 break;
1008 p+=strlen(p);
cristy151b66d2015-04-15 10:50:31 +00001009 if ((size_t) (p-sixel_buffer+MagickPathExtent) < length)
cristy77f78f72014-10-04 13:29:40 +00001010 continue;
1011 length<<=1;
cristy565117b2015-06-21 19:59:07 +00001012 sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+
1013 MagickPathExtent,sizeof(*sixel_buffer));
cristy77f78f72014-10-04 13:29:40 +00001014 if (sixel_buffer == (char *) NULL)
1015 break;
1016 p=sixel_buffer+strlen(sixel_buffer);
1017 }
1018 if (sixel_buffer == (char *) NULL)
1019 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
cristy77f78f72014-10-04 13:29:40 +00001020 /*
1021 Decode SIXEL
1022 */
cristy565117b2015-06-21 19:59:07 +00001023 if (sixel_decode((unsigned char *) sixel_buffer,&sixel_pixels,&image->columns,&image->rows,&sixel_palette,&image->colors) == MagickFalse)
cristy77f78f72014-10-04 13:29:40 +00001024 {
1025 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1026 ThrowReaderException(CorruptImageError,"CorruptImage");
1027 }
1028 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1029 image->depth=24;
1030 image->storage_class=PseudoClass;
cristyacabb842014-12-14 23:36:33 +00001031 status=SetImageExtent(image,image->columns,image->rows,exception);
1032 if (status == MagickFalse)
1033 return(DestroyImageList(image));
cristy77f78f72014-10-04 13:29:40 +00001034
dirka1e4ab82014-10-07 05:06:49 +00001035 if (AcquireImageColormap(image,image->colors, exception) == MagickFalse)
cristy77f78f72014-10-04 13:29:40 +00001036 {
1037 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1038 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1039 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1040 }
cristy53c56cb2014-10-26 13:11:53 +00001041 for (i = 0; i < (ssize_t) image->colors; ++i) {
cristy77f78f72014-10-04 13:29:40 +00001042 image->colormap[i].red = ScaleCharToQuantum(sixel_palette[i * 4 + 0]);
1043 image->colormap[i].green = ScaleCharToQuantum(sixel_palette[i * 4 + 1]);
1044 image->colormap[i].blue = ScaleCharToQuantum(sixel_palette[i * 4 + 2]);
1045 }
1046
1047 j=0;
1048 if (image_info->ping == MagickFalse)
1049 {
1050 /*
1051 Read image pixels.
1052 */
1053 for (y=0; y < (ssize_t) image->rows; y++)
1054 {
dirka1e4ab82014-10-07 05:06:49 +00001055 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1056 if (q == (Quantum *) NULL)
cristy77f78f72014-10-04 13:29:40 +00001057 break;
cristy77f78f72014-10-04 13:29:40 +00001058 for (x=0; x < (ssize_t) image->columns; x++)
1059 {
1060 j=(ssize_t) sixel_pixels[y * image->columns + x];
dirka1e4ab82014-10-07 05:06:49 +00001061 SetPixelIndex(image,j,q);
1062 q+=GetPixelChannels(image);
cristy77f78f72014-10-04 13:29:40 +00001063 }
1064 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1065 break;
1066 }
1067 if (y < (ssize_t) image->rows)
1068 {
1069 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1070 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1071 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
1072 }
1073 }
1074 /*
1075 Relinquish resources.
1076 */
1077 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1078 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1079 (void) CloseBlob(image);
1080 return(GetFirstImageInList(image));
1081}
cristy77f78f72014-10-04 13:29:40 +00001082
1083/*
1084%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1085% %
1086% %
1087% %
1088% R e g i s t e r S I X E L I m a g e %
1089% %
1090% %
1091% %
1092%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1093%
1094% RegisterSIXELImage() adds attributes for the SIXEL image format to
1095% the list of supported formats. The attributes include the image format
1096% tag, a method to read and/or write the format, whether the format
1097% supports the saving of more than one frame to the same file or blob,
1098% whether the format supports native in-memory I/O, and a brief
1099% description of the format.
1100%
1101% The format of the RegisterSIXELImage method is:
1102%
1103% size_t RegisterSIXELImage(void)
1104%
1105*/
1106ModuleExport size_t RegisterSIXELImage(void)
1107{
1108 MagickInfo
1109 *entry;
1110
dirk06b627a2015-04-06 18:59:17 +00001111 entry=AcquireMagickInfo("SIXEL","SIXEL","DEC SIXEL Graphics Format");
cristy77f78f72014-10-04 13:29:40 +00001112 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1113 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
cristy77f78f72014-10-04 13:29:40 +00001114 entry->magick=(IsImageFormatHandler *) IsSIXEL;
dirk08e9a112015-02-22 01:51:41 +00001115 entry->flags^=CoderAdjoinFlag;
cristy77f78f72014-10-04 13:29:40 +00001116 (void) RegisterMagickInfo(entry);
dirk06b627a2015-04-06 18:59:17 +00001117 entry=AcquireMagickInfo("SIX","SIX","DEC SIXEL Graphics Format");
cristy77f78f72014-10-04 13:29:40 +00001118 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1119 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
cristy77f78f72014-10-04 13:29:40 +00001120 entry->magick=(IsImageFormatHandler *) IsSIXEL;
dirk08e9a112015-02-22 01:51:41 +00001121 entry->flags^=CoderAdjoinFlag;
cristy77f78f72014-10-04 13:29:40 +00001122 (void) RegisterMagickInfo(entry);
1123 return(MagickImageCoderSignature);
1124}
1125
1126/*
1127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128% %
1129% %
1130% %
1131% U n r e g i s t e r S I X E L I m a g e %
1132% %
1133% %
1134% %
1135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1136%
1137% UnregisterSIXELImage() removes format registrations made by the
1138% SIXEL module from the list of supported formats.
1139%
1140% The format of the UnregisterSIXELImage method is:
1141%
1142% UnregisterSIXELImage(void)
1143%
1144*/
1145ModuleExport void UnregisterSIXELImage(void)
1146{
1147 (void) UnregisterMagickInfo("SIXEL");
1148 (void) UnregisterMagickInfo("SIX");
1149}
1150
1151/*
1152%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153% %
1154% %
1155% %
1156% W r i t e S I X E L I m a g e %
1157% %
1158% %
1159% %
1160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161%
1162% WriteSIXELImage() writes an image to a file in the X pixmap format.
1163%
1164% The format of the WriteSIXELImage method is:
1165%
1166% MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1167% Image *image,ExceptionInfo *exception)
1168%
1169% A description of each parameter follows.
1170%
1171% o image_info: the image info.
1172%
1173% o image: The image.
1174%
1175% o exception: return any errors or warnings in this structure.
1176%
1177*/
cristy565117b2015-06-21 19:59:07 +00001178static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1179 Image *image,ExceptionInfo *exception)
cristy77f78f72014-10-04 13:29:40 +00001180{
cristy77f78f72014-10-04 13:29:40 +00001181 MagickBooleanType
1182 status;
1183
dirka1e4ab82014-10-07 05:06:49 +00001184 register const Quantum
1185 *q;
cristy77f78f72014-10-04 13:29:40 +00001186
1187 register ssize_t
1188 i,
1189 x;
1190
1191 ssize_t
1192 opacity,
1193 y;
1194
1195 sixel_output_t
1196 *output;
1197
1198 unsigned char
cristy565117b2015-06-21 19:59:07 +00001199 sixel_palette[256*3],
cristy77f78f72014-10-04 13:29:40 +00001200 *sixel_pixels;
1201
1202 /*
1203 Open output image file.
1204 */
1205 assert(image_info != (const ImageInfo *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001206 assert(image_info->signature == MagickCoreSignature);
cristy77f78f72014-10-04 13:29:40 +00001207 assert(image != (Image *) NULL);
cristye1c94d92015-06-28 12:16:33 +00001208 assert(image->signature == MagickCoreSignature);
cristy77f78f72014-10-04 13:29:40 +00001209 if (image->debug != MagickFalse)
1210 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
cristy77f78f72014-10-04 13:29:40 +00001211 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1212 if (status == MagickFalse)
1213 return(status);
1214 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
dirka1e4ab82014-10-07 05:06:49 +00001215 (void) TransformImageColorspace(image,sRGBColorspace,exception);
cristy77f78f72014-10-04 13:29:40 +00001216 opacity=(-1);
cristy17f11b02014-12-20 19:37:04 +00001217 if (image->alpha_trait == UndefinedPixelTrait)
cristy77f78f72014-10-04 13:29:40 +00001218 {
1219 if ((image->storage_class == DirectClass) || (image->colors > 256))
dirka1e4ab82014-10-07 05:06:49 +00001220 (void) SetImageType(image,PaletteType,exception);
cristy77f78f72014-10-04 13:29:40 +00001221 }
1222 else
1223 {
1224 MagickRealType
1225 alpha,
1226 beta;
1227
1228 /*
1229 Identify transparent colormap index.
1230 */
1231 if ((image->storage_class == DirectClass) || (image->colors > 256))
cristydef23e52015-01-22 11:52:01 +00001232 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristy77f78f72014-10-04 13:29:40 +00001233 for (i=0; i < (ssize_t) image->colors; i++)
dirka1e4ab82014-10-07 05:06:49 +00001234 if (image->colormap[i].alpha != OpaqueAlpha)
cristy77f78f72014-10-04 13:29:40 +00001235 {
1236 if (opacity < 0)
1237 {
1238 opacity=i;
1239 continue;
1240 }
dirka1e4ab82014-10-07 05:06:49 +00001241 alpha=image->colormap[i].alpha;
1242 beta=image->colormap[opacity].alpha;
cristy77f78f72014-10-04 13:29:40 +00001243 if (alpha < beta)
1244 opacity=i;
1245 }
1246 if (opacity == -1)
1247 {
cristydef23e52015-01-22 11:52:01 +00001248 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
cristy77f78f72014-10-04 13:29:40 +00001249 for (i=0; i < (ssize_t) image->colors; i++)
dirka1e4ab82014-10-07 05:06:49 +00001250 if (image->colormap[i].alpha != OpaqueAlpha)
cristy77f78f72014-10-04 13:29:40 +00001251 {
1252 if (opacity < 0)
1253 {
1254 opacity=i;
1255 continue;
1256 }
dirka1e4ab82014-10-07 05:06:49 +00001257 alpha=image->colormap[i].alpha;
1258 beta=image->colormap[opacity].alpha;
cristy77f78f72014-10-04 13:29:40 +00001259 if (alpha < beta)
1260 opacity=i;
1261 }
1262 }
1263 if (opacity >= 0)
1264 {
1265 image->colormap[opacity].red=image->transparent_color.red;
1266 image->colormap[opacity].green=image->transparent_color.green;
1267 image->colormap[opacity].blue=image->transparent_color.blue;
1268 }
1269 }
1270 /*
1271 SIXEL header.
1272 */
1273 for (i=0; i < (ssize_t) image->colors; i++)
1274 {
cristy565117b2015-06-21 19:59:07 +00001275 sixel_palette[3*i+0]=ScaleQuantumToChar(image->colormap[i].red);
1276 sixel_palette[3*i+1]=ScaleQuantumToChar(image->colormap[i].green);
1277 sixel_palette[3*i+2]=ScaleQuantumToChar(image->colormap[i].blue);
cristy77f78f72014-10-04 13:29:40 +00001278 }
1279
1280 /*
1281 Define SIXEL pixels.
1282 */
1283 output = sixel_output_create(image);
cristy565117b2015-06-21 19:59:07 +00001284 sixel_pixels=(unsigned char *) AcquireQuantumMemory(image->columns,
1285 image->rows*sizeof(*sixel_pixels));
cristy77f78f72014-10-04 13:29:40 +00001286 for (y=0; y < (ssize_t) image->rows; y++)
1287 {
dirka1e4ab82014-10-07 05:06:49 +00001288 q=GetVirtualPixels(image,0,y,image->columns,1,exception);
dirkccad00b2015-06-21 16:58:22 +00001289 if (q == (Quantum *) NULL)
dirk95f893f2015-06-22 19:37:37 +00001290 break;
cristy77f78f72014-10-04 13:29:40 +00001291 for (x=0; x < (ssize_t) image->columns; x++)
dirka1e4ab82014-10-07 05:06:49 +00001292 {
cristy565117b2015-06-21 19:59:07 +00001293 sixel_pixels[y*image->columns+x]= ((ssize_t) GetPixelIndex(image,q));
dirka1e4ab82014-10-07 05:06:49 +00001294 q+=GetPixelChannels(image);
1295 }
cristy77f78f72014-10-04 13:29:40 +00001296 }
cristy565117b2015-06-21 19:59:07 +00001297 status = sixel_encode_impl(sixel_pixels,image->columns,image->rows,
1298 sixel_palette,image->colors,-1,output);
1299 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1300 output=(sixel_output_t *) RelinquishMagickMemory(output);
cristy77f78f72014-10-04 13:29:40 +00001301 (void) CloseBlob(image);
1302 return(status);
1303}