2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % SSSSS IIIII X X EEEEE L %
10 % SSSSS IIIII X X EEEEE LLLLL %
13 % Read/Write DEC SIXEL Format %
18 % Based on kmiya's sixel (2014-03-28) %
21 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
22 % dedicated to making software imaging solutions freely available. %
24 % You may not use this file except in compliance with the License. You may %
25 % obtain a copy of the License at %
27 % https://www.imagemagick.org/script/license.php %
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. %
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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"
80 #define SIXEL_PALETTE_MAX 256
81 #define SIXEL_OUTPUT_PACKET_SIZE 1024
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))
91 Structure declarations.
93 typedef struct sixel_node {
94 struct sixel_node *next;
101 typedef struct sixel_output {
103 /* compatiblity flags */
106 * 1: 8bit terminal */
107 unsigned char has_8bit_control;
113 sixel_node_t *node_top;
114 sixel_node_t *node_free;
118 unsigned char buffer[1];
122 static 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% */
142 Forward declarations.
144 static MagickBooleanType
145 WriteSIXELImage(const ImageInfo *,Image *,ExceptionInfo *);
147 static int hue_to_rgb(int n1, int n2, int hue)
149 const int HLSMAX = 100;
159 if (hue < (HLSMAX / 6)) {
160 return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
162 if (hue < (HLSMAX / 2)) {
165 if (hue < ((HLSMAX * 2) / 3)) {
166 return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12))/(HLSMAX / 6)));
171 static int hls_to_rgb(int hue, int lum, int sat)
175 const int RGBMAX = 255;
176 const int HLSMAX = 100;
179 R = G = B = (lum * RGBMAX) / HLSMAX;
181 if (lum <= (HLSMAX / 2)) {
182 Magic2 = (int) (((ssize_t) lum * (HLSMAX + sat) + (HLSMAX / 2)) / HLSMAX);
184 Magic2 = (int) (lum + sat - (((ssize_t) lum * sat) + (HLSMAX / 2)) / HLSMAX);
186 Magic1 = 2 * lum - Magic2;
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;
192 return SIXEL_RGB(R, G, B);
195 static unsigned char *get_params(unsigned char *p, int *param, int *len)
201 while (*p == ' ' || *p == '\t') {
204 if (isdigit((int) ((unsigned char) *p))) {
205 for (n = 0; isdigit((int) ((unsigned char) *p)); p++) {
206 n = (int) ((ssize_t) n * 10 + (*p - '0'));
211 while (*p == ' ' || *p == '\t') {
217 } else if (*p == ';') {
228 /* convert sixel data into indexed pixel bytes and palette data */
229 MagickBooleanType sixel_decode(Image *image,
230 unsigned char /* in */ *p, /* sixel bytes */
231 unsigned char /* out */ **pixels, /* decoded pixels */
232 size_t /* out */ *pwidth, /* image width */
233 size_t /* out */ *pheight, /* image height */
234 unsigned char /* out */ **palette, /* ARGB palette */
235 size_t /* out */ *ncolors, /* palette size (<= 256) */
236 ExceptionInfo *exception)
238 int n, i, r, g, b, sixel_vertical_mask, c;
239 int posision_x, posision_y;
241 int attributed_pan, attributed_pad;
242 int attributed_ph, attributed_pv;
243 int repeat_count, color_index, max_color_index = 2, background_color_index;
245 int sixel_palet[SIXEL_PALETTE_MAX];
246 unsigned char *imbuf, *dmbuf;
250 size_t extent,offset;
252 extent=strlen((char *) p);
253 posision_x = posision_y = 0;
257 attributed_ph = attributed_pv = 0;
260 background_color_index = 0;
264 if (SetImageExtent(image,imsx,imsy,exception) == MagickFalse)
266 imbuf = (unsigned char *) AcquireQuantumMemory(imsx , imsy);
272 for (n = 0; n < 16; n++) {
273 sixel_palet[n] = sixel_default_color_table[n];
276 /* colors 16-231 are a 6x6x6 color cube */
277 for (r = 0; r < 6; r++) {
278 for (g = 0; g < 6; g++) {
279 for (b = 0; b < 6; b++) {
280 sixel_palet[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
284 /* colors 232-255 are a grayscale ramp, intentionally leaving out */
285 for (i = 0; i < 24; i++) {
286 sixel_palet[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
289 for (; n < SIXEL_PALETTE_MAX; n++) {
290 sixel_palet[n] = SIXEL_RGB(255, 255, 255);
293 (void) memset(imbuf, background_color_index, (size_t) imsx * imsy);
296 if ((p[0] == '\033' && p[1] == 'P') || *p == 0x90) {
301 p = get_params(++p, param, &n);
306 if (n > 0) { /* Pn1 */
339 if (n > 2) { /* Pn3 */
343 attributed_pan = (int) (((ssize_t) attributed_pan * param[2]) / 10);
344 attributed_pad = (int) (((ssize_t) attributed_pad * param[2]) / 10);
345 if (attributed_pan <= 0) attributed_pan = 1;
346 if (attributed_pad <= 0) attributed_pad = 1;
350 } else if ((p[0] == '\033' && p[1] == '\\') || *p == 0x9C) {
352 } else if (*p == '"') {
353 /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
354 p = get_params(++p, param, &n);
356 if (n > 0) attributed_pad = param[0];
357 if (n > 1) attributed_pan = param[1];
358 if (n > 2 && param[2] > 0) attributed_ph = param[2];
359 if (n > 3 && param[3] > 0) attributed_pv = param[3];
361 if (attributed_pan <= 0) attributed_pan = 1;
362 if (attributed_pad <= 0) attributed_pad = 1;
364 if (imsx < attributed_ph || imsy < attributed_pv) {
365 dmsx = imsx > attributed_ph ? imsx : attributed_ph;
366 dmsy = imsy > attributed_pv ? imsy : attributed_pv;
367 if (SetImageExtent(image,dmsx,dmsy,exception) == MagickFalse)
369 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy);
370 if (dmbuf == (unsigned char *) NULL) {
371 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
372 return (MagickFalse);
374 (void) memset(dmbuf, background_color_index, (size_t) dmsx * dmsy);
375 for (y = 0; y < imsy; ++y) {
376 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
378 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
384 } else if (*p == '!') {
385 /* DECGRI Graphics Repeat Introducer ! Pn Ch */
386 p = get_params(++p, param, &n);
388 if ((n > 0) && (param[0] > 0)) {
389 repeat_count = param[0];
390 if (repeat_count > (ssize_t) extent)
394 } else if (*p == '#') {
395 /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
396 p = get_params(++p, param, &n);
399 if ((color_index = param[0]) < 0) {
401 } else if (color_index >= SIXEL_PALETTE_MAX) {
402 color_index = SIXEL_PALETTE_MAX - 1;
407 if (param[1] == 1) { /* HLS */
408 if (param[2] > 360) param[2] = 360;
409 if (param[3] > 100) param[3] = 100;
410 if (param[4] > 100) param[4] = 100;
411 sixel_palet[color_index] = hls_to_rgb(param[2] * 100 / 360, param[3], param[4]);
412 } else if (param[1] == 2) { /* RGB */
413 if (param[2] > 100) param[2] = 100;
414 if (param[3] > 100) param[3] = 100;
415 if (param[4] > 100) param[4] = 100;
416 sixel_palet[color_index] = SIXEL_XRGB(param[2], param[3], param[4]);
420 } else if (*p == '$') {
421 /* DECGCR Graphics Carriage Return */
426 } else if (*p == '-') {
427 /* DECGNL Graphics Next Line */
433 } else if (*p >= '?' && *p <= '\177') {
434 if (imsx < (posision_x + repeat_count) || imsy < (posision_y + 6)) {
438 while (nx < (posision_x + repeat_count) || ny < (posision_y + 6)) {
445 if (SetImageExtent(image,dmsx,dmsy,exception) == MagickFalse)
447 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy);
448 if (dmbuf == (unsigned char *) NULL) {
449 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
450 return (MagickFalse);
452 (void) memset(dmbuf, background_color_index, (size_t) dmsx * dmsy);
453 for (y = 0; y < imsy; ++y) {
454 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, imsx);
456 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
462 if (color_index > max_color_index) {
463 max_color_index = color_index;
465 if ((b = *(p++) - '?') == 0) {
466 posision_x += repeat_count;
469 sixel_vertical_mask = 0x01;
471 if (repeat_count <= 1) {
472 for (i = 0; i < 6; i++) {
473 if ((b & sixel_vertical_mask) != 0) {
474 offset=(size_t) imsx * (posision_y + i) + posision_x;
475 if (offset >= (size_t) imsx * imsy)
477 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
478 return (MagickFalse);
480 imbuf[offset] = color_index;
481 if (max_x < posision_x) {
484 if (max_y < (posision_y + i)) {
485 max_y = posision_y + i;
488 sixel_vertical_mask <<= 1;
492 } else { /* repeat_count > 1 */
493 for (i = 0; i < 6; i++) {
494 if ((b & sixel_vertical_mask) != 0) {
495 c = sixel_vertical_mask << 1;
496 for (n = 1; (i + n) < 6; n++) {
502 for (y = posision_y + i; y < posision_y + i + n; ++y) {
503 offset=(size_t) imsx * y + posision_x;
504 if (offset + repeat_count >= (size_t) imsx * imsy)
506 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
507 return (MagickFalse);
509 (void) memset(imbuf + offset, color_index, repeat_count);
511 if (max_x < (posision_x + repeat_count - 1)) {
512 max_x = posision_x + repeat_count - 1;
514 if (max_y < (posision_y + i + n - 1)) {
515 max_y = posision_y + i + n - 1;
519 sixel_vertical_mask <<= (n - 1);
521 sixel_vertical_mask <<= 1;
523 posision_x += repeat_count;
532 if (++max_x < attributed_ph) {
533 max_x = attributed_ph;
535 if (++max_y < attributed_pv) {
536 max_y = attributed_pv;
539 if (imsx > max_x || imsy > max_y) {
542 if (SetImageExtent(image,dmsx,dmsy,exception) == MagickFalse)
544 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
545 return (MagickFalse);
547 if ((dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy)) == NULL) {
548 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
549 return (MagickFalse);
551 for (y = 0; y < dmsy; ++y) {
552 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, dmsx);
554 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
563 *ncolors = max_color_index + 1;
564 *palette = (unsigned char *) AcquireQuantumMemory(*ncolors,4);
565 if (*palette == (unsigned char *) NULL)
567 for (n = 0; n < (ssize_t) *ncolors; ++n) {
568 (*palette)[n * 4 + 0] = sixel_palet[n] >> 16 & 0xff;
569 (*palette)[n * 4 + 1] = sixel_palet[n] >> 8 & 0xff;
570 (*palette)[n * 4 + 2] = sixel_palet[n] & 0xff;
571 (*palette)[n * 4 + 3] = 0xff;
576 sixel_output_t *sixel_output_create(Image *image)
578 sixel_output_t *output;
580 output = (sixel_output_t *) AcquireQuantumMemory(sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2, 1);
581 if (output == (sixel_output_t *) NULL)
582 return((sixel_output_t *) NULL);
583 output->has_8bit_control = 0;
584 output->save_pixel = 0;
585 output->save_count = 0;
586 output->active_palette = (-1);
587 output->node_top = NULL;
588 output->node_free = NULL;
589 output->image = image;
595 static void sixel_advance(sixel_output_t *context, int nwrite)
597 if ((context->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) {
598 WriteBlob(context->image,SIXEL_OUTPUT_PACKET_SIZE,context->buffer);
599 CopyMagickMemory(context->buffer,
600 context->buffer + SIXEL_OUTPUT_PACKET_SIZE,
601 (context->pos -= SIXEL_OUTPUT_PACKET_SIZE));
605 static int sixel_put_flash(sixel_output_t *const context)
610 #if defined(USE_VT240) /* VT240 Max 255 ? */
611 while (context->save_count > 255) {
612 nwrite = spritf((char *)context->buffer + context->pos, "!255%c", context->save_pixel);
616 sixel_advance(context, nwrite);
617 context->save_count -= 255;
619 #endif /* defined(USE_VT240) */
621 if (context->save_count > 3) {
622 /* DECGRI Graphics Repeat Introducer ! Pn Ch */
623 nwrite = sprintf((char *)context->buffer + context->pos, "!%d%c", context->save_count, context->save_pixel);
627 sixel_advance(context, nwrite);
629 for (n = 0; n < context->save_count; n++) {
630 context->buffer[context->pos] = (char)context->save_pixel;
631 sixel_advance(context, 1);
635 context->save_pixel = 0;
636 context->save_count = 0;
641 static void sixel_put_pixel(sixel_output_t *const context, int pix)
643 if (pix < 0 || pix > '?') {
649 if (pix == context->save_pixel) {
650 context->save_count++;
652 sixel_put_flash(context);
653 context->save_pixel = pix;
654 context->save_count = 1;
658 static void sixel_node_del(sixel_output_t *const context, sixel_node_t *np)
662 if ((tp = context->node_top) == np) {
663 context->node_top = np->next;
667 while (tp->next != NULL) {
668 if (tp->next == np) {
676 np->next = context->node_free;
677 context->node_free = np;
680 static int sixel_put_node(sixel_output_t *const context, int x,
681 sixel_node_t *np, int ncolors, int keycolor)
685 if (ncolors != 2 || keycolor == -1) {
686 /* designate palette index */
687 if (context->active_palette != np->color) {
688 nwrite = sprintf((char *)context->buffer + context->pos,
690 sixel_advance(context, nwrite);
691 context->active_palette = np->color;
695 for (; x < np->left; x++) {
696 sixel_put_pixel(context, 0);
699 for (; x < np->right; x++) {
700 sixel_put_pixel(context, np->map[x]);
703 sixel_put_flash(context);
708 static MagickBooleanType sixel_encode_impl(unsigned char *pixels, size_t width,size_t height,
709 unsigned char *palette, size_t ncolors, int keycolor,
710 sixel_output_t *context)
712 #define RelinquishNodesAndMap \
713 while ((np = context->node_free) != NULL) { \
714 context->node_free = np->next; \
715 np=(sixel_node_t *) RelinquishMagickMemory(np); \
717 map = (unsigned char *) RelinquishMagickMemory(map)
723 sixel_node_t *np, *tp, top;
730 return (MagickFalse);
732 len = ncolors * width;
733 context->active_palette = (-1);
735 if ((map = (unsigned char *)AcquireQuantumMemory(len, sizeof(unsigned char))) == NULL) {
736 return (MagickFalse);
738 (void) memset(map, 0, len);
740 if (context->has_8bit_control) {
741 nwrite = sprintf((char *)context->buffer, "\x90" "0;0;0" "q");
743 nwrite = sprintf((char *)context->buffer, "\x1bP" "0;0;0" "q");
746 return (MagickFalse);
748 sixel_advance(context, nwrite);
749 nwrite = sprintf((char *)context->buffer + context->pos, "\"1;1;%d;%d", (int) width, (int) height);
751 RelinquishNodesAndMap;
752 return (MagickFalse);
754 sixel_advance(context, nwrite);
756 if (ncolors != 2 || keycolor == -1) {
757 for (n = 0; n < (ssize_t) ncolors; n++) {
758 /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */
759 nwrite = sprintf((char *)context->buffer + context->pos, "#%d;2;%d;%d;%d",
761 (palette[n * 3 + 0] * 100 + 127) / 255,
762 (palette[n * 3 + 1] * 100 + 127) / 255,
763 (palette[n * 3 + 2] * 100 + 127) / 255);
765 RelinquishNodesAndMap;
766 return (MagickFalse);
768 sixel_advance(context, nwrite);
770 RelinquishNodesAndMap;
771 return (MagickFalse);
776 for (y = i = 0; y < (ssize_t) height; y++) {
777 for (x = 0; x < (ssize_t) width; x++) {
778 pix = pixels[y * width + x];
779 if (pix >= 0 && pix < (ssize_t) ncolors && pix != keycolor) {
780 map[pix * width + x] |= (1 << i);
784 if (++i < 6 && (y + 1) < (ssize_t) height) {
788 for (c = 0; c < (ssize_t) ncolors; c++) {
789 for (left = 0; left < (ssize_t) width; left++) {
790 if (*(map + c * width + left) == 0) {
794 for (right = left + 1; right < (ssize_t) width; right++) {
795 if (*(map + c * width + right) != 0) {
799 for (n = 1; (right + n) < (ssize_t) width; n++) {
800 if (*(map + c * width + right + n) != 0) {
805 if (n >= 10 || right + n >= (ssize_t) width) {
808 right = right + n - 1;
811 if ((np = context->node_free) != NULL) {
812 context->node_free = np->next;
813 } else if ((np = (sixel_node_t *)AcquireMagickMemory(sizeof(sixel_node_t))) == NULL) {
814 RelinquishNodesAndMap;
815 return (MagickFalse);
821 np->map = map + c * width;
823 top.next = context->node_top;
826 while (tp->next != NULL) {
827 if (np->left < tp->next->left) {
830 if (np->left == tp->next->left && np->right > tp->next->right) {
838 context->node_top = top.next;
845 for (x = 0; (np = context->node_top) != NULL;) {
847 /* DECGCR Graphics Carriage Return */
848 context->buffer[context->pos] = '$';
849 sixel_advance(context, 1);
853 x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
854 sixel_node_del(context, np);
855 np = context->node_top;
863 x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
864 sixel_node_del(context, np);
865 np = context->node_top;
869 /* DECGNL Graphics Next Line */
870 context->buffer[context->pos] = '-';
871 sixel_advance(context, 1);
873 RelinquishNodesAndMap;
874 return (MagickFalse);
878 (void) memset(map, 0, len);
881 if (context->has_8bit_control) {
882 context->buffer[context->pos] = 0x9c;
883 sixel_advance(context, 1);
885 context->buffer[context->pos] = 0x1b;
886 context->buffer[context->pos + 1] = '\\';
887 sixel_advance(context, 2);
890 RelinquishNodesAndMap;
891 return (MagickFalse);
895 if (context->pos > 0) {
896 WriteBlob(context->image,context->pos,context->buffer);
899 RelinquishNodesAndMap;
905 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
915 % IsSIXEL() returns MagickTrue if the image format type, identified by the
916 % magick string, is SIXEL.
918 % The format of the IsSIXEL method is:
920 % MagickBooleanType IsSIXEL(const unsigned char *magick,
921 % const size_t length)
923 % A description of each parameter follows:
925 % o magick: compare image format pattern against these bytes. or
928 % o length: Specifies the length of the magick string.
931 static MagickBooleanType IsSIXEL(const unsigned char *magick,
935 *end = magick + length;
940 if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) {
941 while (++magick != end) {
944 if (!(*magick >= '0' && *magick <= '9') && *magick != ';')
952 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956 % R e a d S I X E L I m a g e %
960 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962 % ReadSIXELImage() reads an X11 pixmap image file and returns it. It
963 % allocates the memory necessary for the new Image structure and returns a
964 % pointer to the new image.
966 % The format of the ReadSIXELImage method is:
968 % Image *ReadSIXELImage(const ImageInfo *image_info,
969 % ExceptionInfo *exception)
971 % A description of each parameter follows:
973 % o image_info: the image info.
975 % o exception: return any errors or warnings in this structure.
978 static Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
1013 assert(image_info != (const ImageInfo *) NULL);
1014 assert(image_info->signature == MagickCoreSignature);
1015 if (image_info->debug != MagickFalse)
1016 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1017 image_info->filename);
1018 assert(exception != (ExceptionInfo *) NULL);
1019 assert(exception->signature == MagickCoreSignature);
1020 image=AcquireImage(image_info,exception);
1021 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1022 if (status == MagickFalse)
1024 image=DestroyImageList(image);
1025 return((Image *) NULL);
1030 length=MagickPathExtent;
1031 sixel_buffer=(char *) AcquireQuantumMemory((size_t) length+MagickPathExtent,
1032 sizeof(*sixel_buffer));
1034 if (sixel_buffer != (char *) NULL)
1035 while (ReadBlobString(image,p) != (char *) NULL)
1037 if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n')))
1039 if ((*p == '}') && (*(p+1) == ';'))
1042 if ((size_t) (p-sixel_buffer+MagickPathExtent) < length)
1045 sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+
1046 MagickPathExtent,sizeof(*sixel_buffer));
1047 if (sixel_buffer == (char *) NULL)
1049 p=sixel_buffer+strlen(sixel_buffer);
1051 if (sixel_buffer == (char *) NULL)
1052 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1053 sixel_buffer[length]='\0';
1057 if (sixel_decode(image,(unsigned char *) sixel_buffer,&sixel_pixels,&image->columns,&image->rows,&sixel_palette,&image->colors,exception) == MagickFalse)
1059 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1060 ThrowReaderException(CorruptImageError,"CorruptImage");
1062 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1064 image->storage_class=PseudoClass;
1065 status=SetImageExtent(image,image->columns,image->rows,exception);
1066 if (status == MagickFalse)
1068 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1069 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1070 return(DestroyImageList(image));
1073 if (AcquireImageColormap(image,image->colors, exception) == MagickFalse)
1075 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1076 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1077 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1079 for (i = 0; i < (ssize_t) image->colors; ++i) {
1080 image->colormap[i].red = ScaleCharToQuantum(sixel_palette[i * 4 + 0]);
1081 image->colormap[i].green = ScaleCharToQuantum(sixel_palette[i * 4 + 1]);
1082 image->colormap[i].blue = ScaleCharToQuantum(sixel_palette[i * 4 + 2]);
1086 if (image_info->ping == MagickFalse)
1091 for (y=0; y < (ssize_t) image->rows; y++)
1093 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1094 if (q == (Quantum *) NULL)
1096 for (x=0; x < (ssize_t) image->columns; x++)
1098 j=(ssize_t) sixel_pixels[y * image->columns + x];
1099 SetPixelIndex(image,j,q);
1100 q+=GetPixelChannels(image);
1102 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1105 if (y < (ssize_t) image->rows)
1107 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1108 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1109 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
1113 Relinquish resources.
1115 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1116 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1117 (void) CloseBlob(image);
1118 return(GetFirstImageInList(image));
1122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1126 % R e g i s t e r S I X E L I m a g e %
1130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1132 % RegisterSIXELImage() adds attributes for the SIXEL image format to
1133 % the list of supported formats. The attributes include the image format
1134 % tag, a method to read and/or write the format, whether the format
1135 % supports the saving of more than one frame to the same file or blob,
1136 % whether the format supports native in-memory I/O, and a brief
1137 % description of the format.
1139 % The format of the RegisterSIXELImage method is:
1141 % size_t RegisterSIXELImage(void)
1144 ModuleExport size_t RegisterSIXELImage(void)
1149 entry=AcquireMagickInfo("SIXEL","SIXEL","DEC SIXEL Graphics Format");
1150 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1151 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1152 entry->magick=(IsImageFormatHandler *) IsSIXEL;
1153 entry->flags^=CoderAdjoinFlag;
1154 (void) RegisterMagickInfo(entry);
1155 entry=AcquireMagickInfo("SIXEL","SIX","DEC SIXEL Graphics Format");
1156 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1157 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1158 entry->magick=(IsImageFormatHandler *) IsSIXEL;
1159 entry->flags^=CoderAdjoinFlag;
1160 (void) RegisterMagickInfo(entry);
1161 return(MagickImageCoderSignature);
1165 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169 % U n r e g i s t e r S I X E L I m a g e %
1173 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175 % UnregisterSIXELImage() removes format registrations made by the
1176 % SIXEL module from the list of supported formats.
1178 % The format of the UnregisterSIXELImage method is:
1180 % UnregisterSIXELImage(void)
1183 ModuleExport void UnregisterSIXELImage(void)
1185 (void) UnregisterMagickInfo("SIXEL");
1186 (void) UnregisterMagickInfo("SIX");
1190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1194 % W r i t e S I X E L I m a g e %
1198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1200 % WriteSIXELImage() writes an image to a file in the X pixmap format.
1202 % The format of the WriteSIXELImage method is:
1204 % MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1205 % Image *image,ExceptionInfo *exception)
1207 % A description of each parameter follows.
1209 % o image_info: the image info.
1211 % o image: The image.
1213 % o exception: return any errors or warnings in this structure.
1216 static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1217 Image *image,ExceptionInfo *exception)
1222 register const Quantum
1237 sixel_palette[256*3],
1241 Open output image file.
1243 assert(image_info != (const ImageInfo *) NULL);
1244 assert(image_info->signature == MagickCoreSignature);
1245 assert(image != (Image *) NULL);
1246 assert(image->signature == MagickCoreSignature);
1247 if (image->debug != MagickFalse)
1248 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1249 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1250 if (status == MagickFalse)
1252 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1253 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1255 if (image->alpha_trait == UndefinedPixelTrait)
1257 if ((image->storage_class == DirectClass) || (image->colors > 256))
1258 (void) SetImageType(image,PaletteType,exception);
1267 Identify transparent colormap index.
1269 if ((image->storage_class == DirectClass) || (image->colors > 256))
1270 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1271 for (i=0; i < (ssize_t) image->colors; i++)
1272 if (image->colormap[i].alpha != OpaqueAlpha)
1279 alpha=image->colormap[i].alpha;
1280 beta=image->colormap[opacity].alpha;
1286 (void) SetImageType(image,PaletteBilevelAlphaType,exception);
1287 for (i=0; i < (ssize_t) image->colors; i++)
1288 if (image->colormap[i].alpha != OpaqueAlpha)
1295 alpha=image->colormap[i].alpha;
1296 beta=image->colormap[opacity].alpha;
1303 image->colormap[opacity].red=image->transparent_color.red;
1304 image->colormap[opacity].green=image->transparent_color.green;
1305 image->colormap[opacity].blue=image->transparent_color.blue;
1311 for (i=0; i < (ssize_t) image->colors; i++)
1313 sixel_palette[3*i+0]=ScaleQuantumToChar(image->colormap[i].red);
1314 sixel_palette[3*i+1]=ScaleQuantumToChar(image->colormap[i].green);
1315 sixel_palette[3*i+2]=ScaleQuantumToChar(image->colormap[i].blue);
1319 Define SIXEL pixels.
1321 output = sixel_output_create(image);
1322 if (output == (sixel_output_t *) NULL)
1323 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1324 sixel_pixels=(unsigned char *) AcquireQuantumMemory(image->columns,
1325 image->rows*sizeof(*sixel_pixels));
1326 if (sixel_pixels == (unsigned char *) NULL)
1328 output = (sixel_output_t *) RelinquishMagickMemory(output);
1329 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
1331 for (y=0; y < (ssize_t) image->rows; y++)
1333 q=GetVirtualPixels(image,0,y,image->columns,1,exception);
1334 if (q == (Quantum *) NULL)
1336 for (x=0; x < (ssize_t) image->columns; x++)
1338 sixel_pixels[y*image->columns+x]= ((ssize_t) GetPixelIndex(image,q));
1339 q+=GetPixelChannels(image);
1342 status = sixel_encode_impl(sixel_pixels,image->columns,image->rows,
1343 sixel_palette,image->colors,-1,output);
1344 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1345 output=(sixel_output_t *) RelinquishMagickMemory(output);
1346 (void) CloseBlob(image);