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