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-2014 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 % http://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) (((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))
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 = (lum * (HLSMAX + sat) + (HLSMAX / 2)) / HLSMAX;
184 Magic2 = lum + sat - ((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') {
205 for (n = 0; isdigit(*p); p++) {
206 n = 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(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) */)
236 int n, i, r, g, b, sixel_vertical_mask, c;
237 int posision_x, posision_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;
243 int sixel_palet[SIXEL_PALETTE_MAX];
244 unsigned char *imbuf, *dmbuf;
249 posision_x = posision_y = 0;
253 attributed_ph = attributed_pv = 0;
256 background_color_index = 0;
260 imbuf = (unsigned char *) AcquireQuantumMemory(imsx * imsy,1);
266 for (n = 0; n < 16; n++) {
267 sixel_palet[n] = sixel_default_color_table[n];
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);
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);
283 for (; n < SIXEL_PALETTE_MAX; n++) {
284 sixel_palet[n] = SIXEL_RGB(255, 255, 255);
287 (void) ResetMagickMemory(imbuf, background_color_index, imsx * imsy);
290 if ((p[0] == '\033' && p[1] == 'P') || *p == 0x90) {
295 p = get_params(++p, param, &n);
300 if (n > 0) { /* Pn1 */
333 if (n > 2) { /* Pn3 */
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;
344 } else if ((p[0] == '\033' && p[1] == '\\') || *p == 0x9C) {
346 } else if (*p == '"') {
347 /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
348 p = get_params(++p, param, &n);
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];
355 if (attributed_pan <= 0) attributed_pan = 1;
356 if (attributed_pad <= 0) attributed_pad = 1;
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);
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);
370 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
376 } else if (*p == '!') {
377 /* DECGRI Graphics Repeat Introducer ! Pn Ch */
378 p = get_params(++p, param, &n);
381 repeat_count = param[0];
384 } else if (*p == '#') {
385 /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
386 p = get_params(++p, param, &n);
389 if ((color_index = param[0]) < 0) {
391 } else if (color_index >= SIXEL_PALETTE_MAX) {
392 color_index = SIXEL_PALETTE_MAX - 1;
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]);
410 } else if (*p == '$') {
411 /* DECGCR Graphics Carriage Return */
416 } else if (*p == '-') {
417 /* DECGNL Graphics Next Line */
423 } else if (*p >= '?' && *p <= '\177') {
424 if (imsx < (posision_x + repeat_count) || imsy < (posision_y + 6)) {
428 while (nx < (posision_x + repeat_count) || ny < (posision_y + 6)) {
435 dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1);
436 if (dmbuf == (unsigned char *) NULL) {
437 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
438 return (MagickFalse);
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);
444 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
450 if (color_index > max_color_index) {
451 max_color_index = color_index;
453 if ((b = *(p++) - '?') == 0) {
454 posision_x += repeat_count;
457 sixel_vertical_mask = 0x01;
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) {
466 if (max_y < (posision_y + i)) {
467 max_y = posision_y + i;
470 sixel_vertical_mask <<= 1;
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++) {
484 for (y = posision_y + i; y < posision_y + i + n; ++y) {
485 (void) ResetMagickMemory(imbuf + imsx * y + posision_x, color_index, repeat_count);
487 if (max_x < (posision_x + repeat_count - 1)) {
488 max_x = posision_x + repeat_count - 1;
490 if (max_y < (posision_y + i + n - 1)) {
491 max_y = posision_y + i + n - 1;
495 sixel_vertical_mask <<= (n - 1);
497 sixel_vertical_mask <<= 1;
499 posision_x += repeat_count;
508 if (++max_x < attributed_ph) {
509 max_x = attributed_ph;
511 if (++max_y < attributed_pv) {
512 max_y = attributed_pv;
515 if (imsx > max_x || imsy > max_y) {
518 if ((dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx * dmsy,1)) == NULL) {
519 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
520 return (MagickFalse);
522 for (y = 0; y < dmsy; ++y) {
523 (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, dmsx);
525 imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
534 *ncolors = max_color_index + 1;
535 *palette = (unsigned char *) AcquireQuantumMemory(*ncolors,4);
536 for (n = 0; n < (ssize_t) *ncolors; ++n) {
537 (*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;
545 sixel_output_t *sixel_output_create(Image *image)
547 sixel_output_t *output;
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;
562 static void sixel_advance(sixel_output_t *context, int nwrite)
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));
572 static int sixel_put_flash(sixel_output_t *const context)
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);
583 sixel_advance(context, nwrite);
584 context->save_count -= 255;
586 #endif /* defined(USE_VT240) */
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);
594 sixel_advance(context, nwrite);
596 for (n = 0; n < context->save_count; n++) {
597 context->buffer[context->pos] = (char)context->save_pixel;
598 sixel_advance(context, 1);
602 context->save_pixel = 0;
603 context->save_count = 0;
608 static void sixel_put_pixel(sixel_output_t *const context, int pix)
610 if (pix < 0 || pix > '?') {
616 if (pix == context->save_pixel) {
617 context->save_count++;
619 sixel_put_flash(context);
620 context->save_pixel = pix;
621 context->save_count = 1;
625 static void sixel_node_del(sixel_output_t *const context, sixel_node_t *np)
629 if ((tp = context->node_top) == np) {
630 context->node_top = np->next;
634 while (tp->next != NULL) {
635 if (tp->next == np) {
643 np->next = context->node_free;
644 context->node_free = np;
647 static int sixel_put_node(sixel_output_t *const context, int x,
648 sixel_node_t *np, int ncolors, int keycolor)
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,
657 sixel_advance(context, nwrite);
658 context->active_palette = np->color;
662 for (; x < np->left; x++) {
663 sixel_put_pixel(context, 0);
666 for (; x < np->right; x++) {
667 sixel_put_pixel(context, np->map[x]);
670 sixel_put_flash(context);
675 static MagickBooleanType sixel_encode_impl(unsigned char *pixels, size_t width,size_t height,
676 unsigned char *palette, size_t ncolors, int keycolor,
677 sixel_output_t *context)
679 #define RelinquishNodesAndMap \
680 while ((np = context->node_free) != NULL) { \
681 context->node_free = np->next; \
682 np=(sixel_node_t *) RelinquishMagickMemory(np); \
684 map = (unsigned char *) RelinquishMagickMemory(map)
690 sixel_node_t *np, *tp, top;
696 return (MagickFalse);
698 len = ncolors * width;
699 context->active_palette = (-1);
701 if ((map = (unsigned char *)AcquireQuantumMemory(len, sizeof(unsigned char))) == NULL) {
702 return (MagickFalse);
704 (void) ResetMagickMemory(map, 0, len);
706 if (context->has_8bit_control) {
707 nwrite = sprintf((char *)context->buffer, "\x90" "0;0;0" "q");
709 nwrite = sprintf((char *)context->buffer, "\x1bP" "0;0;0" "q");
712 return (MagickFalse);
714 sixel_advance(context, nwrite);
715 nwrite = sprintf((char *)context->buffer + context->pos, "\"1;1;%d;%d", (int) width, (int) height);
717 RelinquishNodesAndMap;
718 return (MagickFalse);
720 sixel_advance(context, nwrite);
722 if (ncolors != 2 || keycolor == -1) {
723 for (n = 0; n < (ssize_t) ncolors; n++) {
724 /* DECGCI Graphics Color Introducer # Pc ; Pu; Px; Py; Pz */
725 nwrite = sprintf((char *)context->buffer + context->pos, "#%d;2;%d;%d;%d",
727 (palette[n * 3 + 0] * 100 + 127) / 255,
728 (palette[n * 3 + 1] * 100 + 127) / 255,
729 (palette[n * 3 + 2] * 100 + 127) / 255);
731 RelinquishNodesAndMap;
732 return (MagickFalse);
734 sixel_advance(context, nwrite);
736 RelinquishNodesAndMap;
737 return (MagickFalse);
742 for (y = i = 0; y < (ssize_t) height; y++) {
743 for (x = 0; x < (ssize_t) width; x++) {
744 pix = pixels[y * width + x];
745 if (pix >= 0 && pix < (ssize_t) ncolors && pix != keycolor) {
746 map[pix * width + x] |= (1 << i);
750 if (++i < 6 && (y + 1) < (ssize_t) height) {
754 for (c = 0; c < (ssize_t) ncolors; c++) {
755 for (left = 0; left < (ssize_t) width; left++) {
756 if (*(map + c * width + left) == 0) {
760 for (right = left + 1; right < (ssize_t) width; right++) {
761 if (*(map + c * width + right) != 0) {
765 for (n = 1; (right + n) < (ssize_t) width; n++) {
766 if (*(map + c * width + right + n) != 0) {
771 if (n >= 10 || right + n >= (ssize_t) width) {
774 right = right + n - 1;
777 if ((np = context->node_free) != NULL) {
778 context->node_free = np->next;
779 } else if ((np = (sixel_node_t *)AcquireMagickMemory(sizeof(sixel_node_t))) == NULL) {
780 RelinquishNodesAndMap;
781 return (MagickFalse);
787 np->map = map + c * width;
789 top.next = context->node_top;
792 while (tp->next != NULL) {
793 if (np->left < tp->next->left) {
796 if (np->left == tp->next->left && np->right > tp->next->right) {
804 context->node_top = top.next;
811 for (x = 0; (np = context->node_top) != NULL;) {
813 /* DECGCR Graphics Carriage Return */
814 context->buffer[context->pos] = '$';
815 sixel_advance(context, 1);
819 x = sixel_put_node(context, x, np, ncolors, keycolor);
820 sixel_node_del(context, np);
821 np = context->node_top;
829 x = sixel_put_node(context, x, np, ncolors, keycolor);
830 sixel_node_del(context, np);
831 np = context->node_top;
835 /* DECGNL Graphics Next Line */
836 context->buffer[context->pos] = '-';
837 sixel_advance(context, 1);
839 RelinquishNodesAndMap;
840 return (MagickFalse);
844 (void) ResetMagickMemory(map, 0, len);
847 if (context->has_8bit_control) {
848 context->buffer[context->pos] = 0x9c;
849 sixel_advance(context, 1);
851 context->buffer[context->pos] = 0x1b;
852 context->buffer[context->pos + 1] = '\\';
853 sixel_advance(context, 2);
856 RelinquishNodesAndMap;
857 return (MagickFalse);
861 if (context->pos > 0) {
862 WriteBlob(context->image,context->pos,context->buffer);
865 RelinquishNodesAndMap;
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
879 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
881 % IsSIXEL() returns MagickTrue if the image format type, identified by the
882 % magick string, is SIXEL.
884 % The format of the IsSIXEL method is:
886 % MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
888 % A description of each parameter follows:
890 % o magick: compare image format pattern against these bytes. or
893 % o length: Specifies the length of the magick string.
896 static MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
899 *end = magick + length;
904 if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) {
905 while (++magick != end) {
908 if (!(*magick >= '0' && *magick <= '9') && *magick != ';')
916 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
920 % R e a d S I X E L I m a g e %
924 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
926 % ReadSIXELImage() reads an X11 pixmap image file and returns it. It
927 % allocates the memory necessary for the new Image structure and returns a
928 % pointer to the new image.
930 % The format of the ReadSIXELImage method is:
932 % Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
934 % A description of each parameter follows:
936 % o image_info: the image info.
938 % o exception: return any errors or warnings in this structure.
941 static Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
976 assert(image_info != (const ImageInfo *) NULL);
977 assert(image_info->signature == MagickSignature);
978 if (image_info->debug != MagickFalse)
979 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
980 image_info->filename);
981 assert(exception != (ExceptionInfo *) NULL);
982 assert(exception->signature == MagickSignature);
983 image=AcquireImage(image_info,exception);
984 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
985 if (status == MagickFalse)
987 image=DestroyImageList(image);
988 return((Image *) NULL);
993 length=MaxTextExtent;
994 sixel_buffer=(char *) AcquireQuantumMemory((size_t) length,sizeof(*sixel_buffer));
996 if (sixel_buffer != (char *) NULL)
997 while (ReadBlobString(image,p) != (char *) NULL)
999 if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n')))
1001 if ((*p == '}') && (*(p+1) == ';'))
1004 if ((size_t) (p-sixel_buffer+MaxTextExtent) < length)
1007 sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+MaxTextExtent,
1008 sizeof(*sixel_buffer));
1009 if (sixel_buffer == (char *) NULL)
1011 p=sixel_buffer+strlen(sixel_buffer);
1013 if (sixel_buffer == (char *) NULL)
1014 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1019 if (sixel_decode((unsigned char *)sixel_buffer, &sixel_pixels, &image->columns, &image->rows, &sixel_palette, &image->colors) == MagickFalse)
1021 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1022 ThrowReaderException(CorruptImageError,"CorruptImage");
1024 sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
1026 image->storage_class=PseudoClass;
1028 if (AcquireImageColormap(image,image->colors, exception) == MagickFalse)
1030 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1031 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1032 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1034 for (i = 0; i < (ssize_t) image->colors; ++i) {
1035 image->colormap[i].red = ScaleCharToQuantum(sixel_palette[i * 4 + 0]);
1036 image->colormap[i].green = ScaleCharToQuantum(sixel_palette[i * 4 + 1]);
1037 image->colormap[i].blue = ScaleCharToQuantum(sixel_palette[i * 4 + 2]);
1041 if (image_info->ping == MagickFalse)
1046 for (y=0; y < (ssize_t) image->rows; y++)
1048 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1049 if (q == (Quantum *) NULL)
1051 for (x=0; x < (ssize_t) image->columns; x++)
1053 j=(ssize_t) sixel_pixels[y * image->columns + x];
1054 SetPixelIndex(image,j,q);
1055 q+=GetPixelChannels(image);
1057 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1060 if (y < (ssize_t) image->rows)
1062 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1063 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1064 ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
1068 Relinquish resources.
1070 sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1071 sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
1072 (void) CloseBlob(image);
1073 return(GetFirstImageInList(image));
1077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1081 % R e g i s t e r S I X E L I m a g e %
1085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1087 % RegisterSIXELImage() adds attributes for the SIXEL image format to
1088 % the list of supported formats. The attributes include the image format
1089 % tag, a method to read and/or write the format, whether the format
1090 % supports the saving of more than one frame to the same file or blob,
1091 % whether the format supports native in-memory I/O, and a brief
1092 % description of the format.
1094 % The format of the RegisterSIXELImage method is:
1096 % size_t RegisterSIXELImage(void)
1099 ModuleExport size_t RegisterSIXELImage(void)
1104 entry=SetMagickInfo("SIXEL");
1105 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1106 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1107 entry->magick=(IsImageFormatHandler *) IsSIXEL;
1108 entry->adjoin=MagickFalse;
1109 entry->description=ConstantString("DEC SIXEL Graphics Format");
1110 entry->module=ConstantString("SIXEL");
1111 (void) RegisterMagickInfo(entry);
1112 entry=SetMagickInfo("SIX");
1113 entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
1114 entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
1115 entry->magick=(IsImageFormatHandler *) IsSIXEL;
1116 entry->adjoin=MagickFalse;
1117 entry->description=ConstantString("DEC SIXEL Graphics Format");
1118 entry->module=ConstantString("SIX");
1119 (void) RegisterMagickInfo(entry);
1120 return(MagickImageCoderSignature);
1124 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1128 % U n r e g i s t e r S I X E L I m a g e %
1132 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1134 % UnregisterSIXELImage() removes format registrations made by the
1135 % SIXEL module from the list of supported formats.
1137 % The format of the UnregisterSIXELImage method is:
1139 % UnregisterSIXELImage(void)
1142 ModuleExport void UnregisterSIXELImage(void)
1144 (void) UnregisterMagickInfo("SIXEL");
1145 (void) UnregisterMagickInfo("SIX");
1149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 % W r i t e S I X E L I m a g e %
1157 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1159 % WriteSIXELImage() writes an image to a file in the X pixmap format.
1161 % The format of the WriteSIXELImage method is:
1163 % MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
1164 % Image *image,ExceptionInfo *exception)
1166 % A description of each parameter follows.
1168 % o image_info: the image info.
1170 % o image: The image.
1172 % o exception: return any errors or warnings in this structure.
1175 static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,Image *image,
1176 ExceptionInfo *exception)
1181 register const Quantum
1196 sixel_palette[256 * 3],
1200 Open output image file.
1202 assert(image_info != (const ImageInfo *) NULL);
1203 assert(image_info->signature == MagickSignature);
1204 assert(image != (Image *) NULL);
1205 assert(image->signature == MagickSignature);
1206 if (image->debug != MagickFalse)
1207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1208 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
1209 if (status == MagickFalse)
1211 if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1212 (void) TransformImageColorspace(image,sRGBColorspace,exception);
1214 if (image->alpha_trait != BlendPixelTrait)
1216 if ((image->storage_class == DirectClass) || (image->colors > 256))
1217 (void) SetImageType(image,PaletteType,exception);
1226 Identify transparent colormap index.
1228 if ((image->storage_class == DirectClass) || (image->colors > 256))
1229 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1230 for (i=0; i < (ssize_t) image->colors; i++)
1231 if (image->colormap[i].alpha != OpaqueAlpha)
1238 alpha=image->colormap[i].alpha;
1239 beta=image->colormap[opacity].alpha;
1245 (void) SetImageType(image,PaletteBilevelMatteType,exception);
1246 for (i=0; i < (ssize_t) image->colors; i++)
1247 if (image->colormap[i].alpha != OpaqueAlpha)
1254 alpha=image->colormap[i].alpha;
1255 beta=image->colormap[opacity].alpha;
1262 image->colormap[opacity].red=image->transparent_color.red;
1263 image->colormap[opacity].green=image->transparent_color.green;
1264 image->colormap[opacity].blue=image->transparent_color.blue;
1270 for (i=0; i < (ssize_t) image->colors; i++)
1272 sixel_palette[i * 3 + 0] = ScaleQuantumToChar(image->colormap[i].red);
1273 sixel_palette[i * 3 + 1] = ScaleQuantumToChar(image->colormap[i].green);
1274 sixel_palette[i * 3 + 2] = ScaleQuantumToChar(image->colormap[i].blue);
1278 Define SIXEL pixels.
1280 output = sixel_output_create(image);
1281 sixel_pixels =(unsigned char *) AcquireQuantumMemory((size_t) image->columns * image->rows,1);
1282 for (y=0; y < (ssize_t) image->rows; y++)
1284 q=GetVirtualPixels(image,0,y,image->columns,1,exception);
1285 for (x=0; x < (ssize_t) image->columns; x++)
1287 sixel_pixels[y * image->columns + x] = ((ssize_t) GetPixelIndex(image, q));
1288 q+=GetPixelChannels(image);
1291 status = sixel_encode_impl(sixel_pixels, image->columns, image->rows,
1292 sixel_palette, image->colors, -1,
1294 sixel_pixels =(unsigned char *) RelinquishMagickMemory(sixel_pixels);
1295 output = (sixel_output_t *) RelinquishMagickMemory(output);
1296 (void) CloseBlob(image);