text: /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %                     SSSSS  IIIII  X   X  EEEEE  L                           %
  %                     SS       I     X X   E      L                           %
  %                      SSS     I      X    EEE    L                           %
  %                        SS    I     X X   E      L                           %
  %                     SSSSS  IIIII  X   X  EEEEE  LLLLL                       %
  %                                                                             %
  %                                                                             %
  %                        Read/Write DEC SIXEL Format                          %
  %                                                                             %
  %                              Software Design                                %
  %                               Hayaki Saito                                  %
  %                              September 2014                                 %
  %                    Based on kmiya's sixel (2014-03-28)                      %
  %                                                                             %
  %                                                                             %
  %  Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization      %
  %  dedicated to making software imaging solutions freely available.           %
  %                                                                             %
  %  You may not use this file except in compliance with the License.  You may  %
  %  obtain a copy of the License at                                            %
  %                                                                             %
  %    https://www.imagemagick.org/script/license.php                           %
  %                                                                             %
  %  Unless required by applicable law or agreed to in writing, software        %
  %  distributed under the License is distributed on an "AS IS" BASIS,          %
  %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
  %  See the License for the specific language governing permissions and        %
  %  limitations under the License.                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %
  */
  
  /*
    Include declarations.
  */
  #include "magick/studio.h"
  #include "magick/attribute.h"
  #include "magick/blob.h"
  #include "magick/blob-private.h"
  #include "magick/cache.h"
  #include "magick/color.h"
  #include "magick/color-private.h"
  #include "magick/colormap.h"
  #include "magick/colorspace.h"
  #include "magick/colorspace-private.h"
  #include "magick/exception.h"
  #include "magick/exception-private.h"
  #include "magick/geometry.h"
  #include "magick/image.h"
  #include "magick/image-private.h"
  #include "magick/list.h"
  #include "magick/magick.h"
  #include "magick/memory_.h"
  #include "magick/monitor.h"
  #include "magick/monitor-private.h"
  #include "magick/pixel-accessor.h"
  #include "magick/pixel-private.h"
  #include "magick/quantize.h"
  #include "magick/quantum-private.h"
  #include "magick/resize.h"
  #include "magick/resource_.h"
  #include "magick/splay-tree.h"
  #include "magick/static.h"
  #include "magick/string_.h"
  #include "magick/thread-private.h"
  #include "magick/module.h"
  #include "magick/threshold.h"
  #include "magick/utility.h"
  
  /*
    Definitions
  */
  #define SIXEL_PALETTE_MAX 256
  #define SIXEL_OUTPUT_PACKET_SIZE 1024
  
  /*
    Macros
  */
  #define SIXEL_RGB(r, g, b) (((r) << 16) + ((g) << 8) +  (b))
  #define SIXEL_PALVAL(n,a,m) (((n) * (a) + ((m) / 2)) / (m))
  #define SIXEL_XRGB(r,g,b) SIXEL_RGB(SIXEL_PALVAL(r, 255, 100), SIXEL_PALVAL(g, 255, 100), SIXEL_PALVAL(b, 255, 100))
  
  /*
    Structure declarations.
  */
  typedef struct sixel_node {
      struct sixel_node *next;
      int color;
      int left;
      int right;
      unsigned char *map;
  } sixel_node_t;
  
  typedef struct sixel_output {
  
      /* compatiblity flags */
  
      /* 0: 7bit terminal,
       * 1: 8bit terminal */
      unsigned char has_8bit_control;
  
      int save_pixel;
      int save_count;
      int active_palette;
  
      sixel_node_t *node_top;
      sixel_node_t *node_free;
  
      Image *image;
      int pos;
      unsigned char buffer[1];
  
  } sixel_output_t;
  
  static int const sixel_default_color_table[] = {
      SIXEL_XRGB(0,  0,  0),   /*  0 Black    */
      SIXEL_XRGB(20, 20, 80),  /*  1 Blue     */
      SIXEL_XRGB(80, 13, 13),  /*  2 Red      */
      SIXEL_XRGB(20, 80, 20),  /*  3 Green    */
      SIXEL_XRGB(80, 20, 80),  /*  4 Magenta  */
      SIXEL_XRGB(20, 80, 80),  /*  5 Cyan     */
      SIXEL_XRGB(80, 80, 20),  /*  6 Yellow   */
      SIXEL_XRGB(53, 53, 53),  /*  7 Gray 50% */
      SIXEL_XRGB(26, 26, 26),  /*  8 Gray 25% */
      SIXEL_XRGB(33, 33, 60),  /*  9 Blue*    */
      SIXEL_XRGB(60, 26, 26),  /* 10 Red*     */
      SIXEL_XRGB(33, 60, 33),  /* 11 Green*   */
      SIXEL_XRGB(60, 33, 60),  /* 12 Magenta* */
      SIXEL_XRGB(33, 60, 60),  /* 13 Cyan*    */
      SIXEL_XRGB(60, 60, 33),  /* 14 Yellow*  */
      SIXEL_XRGB(80, 80, 80),  /* 15 Gray 75% */
  };
  
  /*
    Forward declarations.
  */
  static MagickBooleanType
    WriteSIXELImage(const ImageInfo *,Image *);
  
  static int hue_to_rgb(int n1, int n2, int hue)
  {
      const int HLSMAX = 100;
  
      if (hue < 0) {
          hue += HLSMAX;
      }
  
      if (hue > HLSMAX) {
          hue -= HLSMAX;
      }
  
      if (hue < (HLSMAX / 6)) {
          return (n1 + (((n2 - n1) * hue + (HLSMAX / 12)) / (HLSMAX / 6)));
      }
      if (hue < (HLSMAX / 2)) {
          return (n2);
      }
      if (hue < ((HLSMAX * 2) / 3)) {
          return (n1 + (((n2 - n1) * (((HLSMAX * 2) / 3) - hue) + (HLSMAX / 12))/(HLSMAX / 6)));
      }
      return (n1);
  }
  
  static int hls_to_rgb(int hue, int lum, int sat)
  {
      int R, G, B;
      int Magic1, Magic2;
      const int RGBMAX = 255;
      const int HLSMAX = 100;
  
      if (sat == 0) {
          R = G = B = (lum * RGBMAX) / HLSMAX;
      } else {
          if (lum <= (HLSMAX / 2)) {
              Magic2 = (lum * (HLSMAX + sat) + (HLSMAX / 2)) / HLSMAX;
          } else {
              Magic2 = lum + sat - ((lum * sat) + (HLSMAX / 2)) / HLSMAX;
          }
          Magic1 = 2 * lum - Magic2;
  
          R = (hue_to_rgb(Magic1, Magic2, hue + (HLSMAX / 3)) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
          G = (hue_to_rgb(Magic1, Magic2, hue) * RGBMAX + (HLSMAX / 2)) / HLSMAX;
          B = (hue_to_rgb(Magic1, Magic2, hue - (HLSMAX / 3)) * RGBMAX + (HLSMAX/2)) / HLSMAX;
      }
      return SIXEL_RGB(R, G, B);
  }
  
  static unsigned char *get_params(unsigned char *p, int *param, int *len)
  {
      int n;
  
      *len = 0;
      while (*p != '\0') {
          while (*p == ' ' || *p == '\t') {
              p++;
          }
          if (isdigit(*p)) {
              for (n = 0; isdigit(*p); p++) {
                  n = n * 10 + (*p - '0');
              }
              if (*len < 10) {
                  param[(*len)++] = n;
              }
              while (*p == ' ' || *p == '\t') {
                  p++;
              }
              if (*p == ';') {
                  p++;
              }
          } else if (*p == ';') {
              if (*len < 10) {
                  param[(*len)++] = 0;
              }
              p++;
          } else
              break;
      }
      return p;
  }
  
  /* convert sixel data into indexed pixel bytes and palette data */
  MagickBooleanType sixel_decode(unsigned char              /* in */  *p,         /* sixel bytes */
                                 unsigned char              /* out */ **pixels,   /* decoded pixels */
                                 size_t                     /* out */ *pwidth,    /* image width */
                                 size_t                     /* out */ *pheight,   /* image height */
                                 unsigned char              /* out */ **palette,  /* ARGB palette */
                                 size_t                     /* out */ *ncolors    /* palette size (<= 256) */)
  {
      int n, i, r, g, b, sixel_vertical_mask, c;
      int posision_x, posision_y;
      int max_x, max_y;
      int attributed_pan, attributed_pad;
      int attributed_ph, attributed_pv;
      int repeat_count, color_index, max_color_index = 2, background_color_index;
      int param[10];
      int sixel_palet[SIXEL_PALETTE_MAX];
      unsigned char *imbuf, *dmbuf;
      int imsx, imsy;
      int dmsx, dmsy;
      int y;
  
      posision_x = posision_y = 0;
      max_x = max_y = 0;
      attributed_pan = 2;
      attributed_pad = 1;
      attributed_ph = attributed_pv = 0;
      repeat_count = 1;
      color_index = 0;
      background_color_index = 0;
  
      imsx = 2048;
      imsy = 2048;
      imbuf = (unsigned char *) AcquireQuantumMemory(imsx , imsy);
  
      if (imbuf == NULL) {
          return(MagickFalse);
      }
  
      for (n = 0; n < 16; n++) {
          sixel_palet[n] = sixel_default_color_table[n];
      }
  
      /* colors 16-231 are a 6x6x6 color cube */
      for (r = 0; r < 6; r++) {
          for (g = 0; g < 6; g++) {
              for (b = 0; b < 6; b++) {
                  sixel_palet[n++] = SIXEL_RGB(r * 51, g * 51, b * 51);
              }
          }
      }
      /* colors 232-255 are a grayscale ramp, intentionally leaving out */
      for (i = 0; i < 24; i++) {
          sixel_palet[n++] = SIXEL_RGB(i * 11, i * 11, i * 11);
      }
  
      for (; n < SIXEL_PALETTE_MAX; n++) {
          sixel_palet[n] = SIXEL_RGB(255, 255, 255);
      }
  
      (void) ResetMagickMemory(imbuf, background_color_index, (size_t) imsx * imsy);
  
      while (*p != '\0') {
          if ((p[0] == '\033' && p[1] == 'P') || *p == 0x90) {
              if (*p == '\033') {
                  p++;
              }
  
              p = get_params(++p, param, &n);
  
              if (*p == 'q') {
                  p++;
  
                  if (n > 0) {        /* Pn1 */
                      switch(param[0]) {
                      case 0:
                      case 1:
                          attributed_pad = 2;
                          break;
                      case 2:
                          attributed_pad = 5;
                          break;
                      case 3:
                          attributed_pad = 4;
                          break;
                      case 4:
                          attributed_pad = 4;
                          break;
                      case 5:
                          attributed_pad = 3;
                          break;
                      case 6:
                          attributed_pad = 3;
                          break;
                      case 7:
                          attributed_pad = 2;
                          break;
                      case 8:
                          attributed_pad = 2;
                          break;
                      case 9:
                          attributed_pad = 1;
                          break;
                      }
                  }
  
                  if (n > 2) {        /* Pn3 */
                      if (param[2] == 0) {
                          param[2] = 10;
                      }
                      attributed_pan = attributed_pan * param[2] / 10;
                      attributed_pad = attributed_pad * param[2] / 10;
                      if (attributed_pan <= 0) attributed_pan = 1;
                      if (attributed_pad <= 0) attributed_pad = 1;
                  }
              }
  
          } else if ((p[0] == '\033' && p[1] == '\\') || *p == 0x9C) {
              break;
          } else if (*p == '"') {
              /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
              p = get_params(++p, param, &n);
  
              if (n > 0) attributed_pad = param[0];
              if (n > 1) attributed_pan = param[1];
              if (n > 2 && param[2] > 0) attributed_ph = param[2];
              if (n > 3 && param[3] > 0) attributed_pv = param[3];
  
              if (attributed_pan <= 0) attributed_pan = 1;
              if (attributed_pad <= 0) attributed_pad = 1;
  
              if (imsx < attributed_ph || imsy < attributed_pv) {
                  dmsx = imsx > attributed_ph ? imsx : attributed_ph;
                  dmsy = imsy > attributed_pv ? imsy : attributed_pv;
                  dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy);
                  if (dmbuf == (unsigned char *) NULL) {
                      imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
                      return (MagickFalse);
                  }
                  (void) ResetMagickMemory(dmbuf, background_color_index, (size_t) dmsx * dmsy);
                  for (y = 0; y < imsy; ++y) {
                      (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + (size_t) imsx * y, imsx);
                  }
                  imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
                  imsx = dmsx;
                  imsy = dmsy;
                  imbuf = dmbuf;
              }
  
          } else if (*p == '!') {
              /* DECGRI Graphics Repeat Introducer ! Pn Ch */
              p = get_params(++p, param, &n);
  
              if (n > 0) {
                  repeat_count = param[0];
              }
  
          } else if (*p == '#') {
              /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
              p = get_params(++p, param, &n);
  
              if (n > 0) {
                  if ((color_index = param[0]) < 0) {
                      color_index = 0;
                  } else if (color_index >= SIXEL_PALETTE_MAX) {
                      color_index = SIXEL_PALETTE_MAX - 1;
                  }
              }
  
              if (n > 4) {
                  if (param[1] == 1) {            /* HLS */
                      if (param[2] > 360) param[2] = 360;
                      if (param[3] > 100) param[3] = 100;
                      if (param[4] > 100) param[4] = 100;
                      sixel_palet[color_index] = hls_to_rgb(param[2] * 100 / 360, param[3], param[4]);
                  } else if (param[1] == 2) {    /* RGB */
                      if (param[2] > 100) param[2] = 100;
                      if (param[3] > 100) param[3] = 100;
                      if (param[4] > 100) param[4] = 100;
                      sixel_palet[color_index] = SIXEL_XRGB(param[2], param[3], param[4]);
                  }
              }
  
          } else if (*p == '$') {
              /* DECGCR Graphics Carriage Return */
              p++;
              posision_x = 0;
              repeat_count = 1;
  
          } else if (*p == '-') {
              /* DECGNL Graphics Next Line */
              p++;
              posision_x  = 0;
              posision_y += 6;
              repeat_count = 1;
  
          } else if (*p >= '?' && *p <= '\177') {
              if (imsx < (posision_x + repeat_count) || imsy < (posision_y + 6)) {
                  int nx = imsx * 2;
                  int ny = imsy * 2;
  
                  while (nx < (posision_x + repeat_count) || ny < (posision_y + 6)) {
                      nx *= 2;
                      ny *= 2;
                  }
  
                  dmsx = nx;
                  dmsy = ny;
                  dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy);
                  if (dmbuf == (unsigned char *) NULL) {
                      imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
                      return (MagickFalse);
                  }
                  (void) ResetMagickMemory(dmbuf, background_color_index, (size_t) dmsx * dmsy);
                  for (y = 0; y < imsy; ++y) {
                      (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + (size_t) imsx * y, imsx);
                  }
                  imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
                  imsx = dmsx;
                  imsy = dmsy;
                  imbuf = dmbuf;
              }
  
              if (color_index > max_color_index) {
                  max_color_index = color_index;
              }
              if ((b = *(p++) - '?') == 0) {
                  posision_x += repeat_count;
  
              } else {
                  sixel_vertical_mask = 0x01;
  
                  if (repeat_count <= 1) {
                      for (i = 0; i < 6; i++) {
                          if ((b & sixel_vertical_mask) != 0) {
                              imbuf[imsx * (posision_y + i) + posision_x] = color_index;
                              if (max_x < posision_x) {
                                  max_x = posision_x;
                              }
                              if (max_y < (posision_y + i)) {
                                  max_y = posision_y + i;
                              }
                          }
                          sixel_vertical_mask <<= 1;
                      }
                      posision_x += 1;
  
                  } else { /* repeat_count > 1 */
                      for (i = 0; i < 6; i++) {
                          if ((b & sixel_vertical_mask) != 0) {
                              c = sixel_vertical_mask << 1;
                              for (n = 1; (i + n) < 6; n++) {
                                  if ((b & c) == 0) {
                                      break;
                                  }
                                  c <<= 1;
                              }
                              for (y = posision_y + i; y < posision_y + i + n; ++y) {
                                  (void) ResetMagickMemory(imbuf + (size_t) imsx * y + posision_x, color_index, repeat_count);
                              }
                              if (max_x < (posision_x + repeat_count - 1)) {
                                  max_x = posision_x + repeat_count - 1;
                              }
                              if (max_y < (posision_y + i + n - 1)) {
                                  max_y = posision_y + i + n - 1;
                              }
  
                              i += (n - 1);
                              sixel_vertical_mask <<= (n - 1);
                          }
                          sixel_vertical_mask <<= 1;
                      }
                      posision_x += repeat_count;
                  }
              }
              repeat_count = 1;
          } else {
              p++;
          }
      }
  
      if (++max_x < attributed_ph) {
          max_x = attributed_ph;
      }
      if (++max_y < attributed_pv) {
          max_y = attributed_pv;
      }
  
      if (imsx > max_x || imsy > max_y) {
          dmsx = max_x;
          dmsy = max_y;
          if ((dmbuf = (unsigned char *) AcquireQuantumMemory(dmsx , dmsy)) == NULL) {
              imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
              return (MagickFalse);
          }
          for (y = 0; y < dmsy; ++y) {
              (void) CopyMagickMemory(dmbuf + dmsx * y, imbuf + imsx * y, dmsx);
          }
          imbuf = (unsigned char *) RelinquishMagickMemory(imbuf);
          imsx = dmsx;
          imsy = dmsy;
          imbuf = dmbuf;
      }
  
      *pixels = imbuf;
      *pwidth = imsx;
      *pheight = imsy;
      *ncolors = max_color_index + 1;
      *palette = (unsigned char *) AcquireQuantumMemory(*ncolors,4);
      if (*palette == (unsigned char *) NULL)
        return(MagickFalse);
      for (n = 0; n < (ssize_t) *ncolors; ++n) {
          (*palette)[n * 4 + 0] = sixel_palet[n] >> 16 & 0xff;
          (*palette)[n * 4 + 1] = sixel_palet[n] >> 8 & 0xff;
          (*palette)[n * 4 + 2] = sixel_palet[n] & 0xff;
          (*palette)[n * 4 + 3] = 0xff;
      }
      return(MagickTrue);
  }
  
  sixel_output_t *sixel_output_create(Image *image)
  {
      sixel_output_t *output;
  
      output = (sixel_output_t *) AcquireQuantumMemory(sizeof(sixel_output_t) + SIXEL_OUTPUT_PACKET_SIZE * 2, 1);
      if (output == (sixel_output_t *) NULL)
        return((sixel_output_t *) NULL);
      output->has_8bit_control = 0;
      output->save_pixel = 0;
      output->save_count = 0;
      output->active_palette = (-1);
      output->node_top = NULL;
      output->node_free = NULL;
      output->image = image;
      output->pos = 0;
  
      return output;
  }
  
  static void sixel_advance(sixel_output_t *context, int nwrite)
  {
      if ((context->pos += nwrite) >= SIXEL_OUTPUT_PACKET_SIZE) {
          WriteBlob(context->image,SIXEL_OUTPUT_PACKET_SIZE,context->buffer);
          CopyMagickMemory(context->buffer,
                 context->buffer + SIXEL_OUTPUT_PACKET_SIZE,
                 (context->pos -= SIXEL_OUTPUT_PACKET_SIZE));
      }
  }
  
  static int sixel_put_flash(sixel_output_t *const context)
  {
      int n;
      int nwrite;
  
  #if defined(USE_VT240)        /* VT240 Max 255 ? */
      while (context->save_count > 255) {
          nwrite = spritf((char *)context->buffer + context->pos, "!255%c", context->save_pixel);
          if (nwrite <= 0) {
              return (-1);
          }
          sixel_advance(context, nwrite);
          context->save_count -= 255;
      }
  #endif  /* defined(USE_VT240) */
  
      if (context->save_count > 3) {
          /* DECGRI Graphics Repeat Introducer ! Pn Ch */
          nwrite = sprintf((char *)context->buffer + context->pos, "!%d%c", context->save_count, context->save_pixel);
          if (nwrite <= 0) {
              return (-1);
          }
          sixel_advance(context, nwrite);
      } else {
          for (n = 0; n < context->save_count; n++) {
              context->buffer[context->pos] = (char)context->save_pixel;
              sixel_advance(context, 1);
          }
      }
  
      context->save_pixel = 0;
      context->save_count = 0;
  
      return 0;
  }
  
  static void sixel_put_pixel(sixel_output_t *const context, int pix)
  {
      if (pix < 0 || pix > '?') {
          pix = 0;
      }
  
      pix += '?';
  
      if (pix == context->save_pixel) {
          context->save_count++;
      } else {
          sixel_put_flash(context);
          context->save_pixel = pix;
          context->save_count = 1;
      }
  }
  
  static void sixel_node_del(sixel_output_t *const context, sixel_node_t *np)
  {
      sixel_node_t *tp;
  
      if ((tp = context->node_top) == np) {
          context->node_top = np->next;
      }
  
      else {
          while (tp->next != NULL) {
              if (tp->next == np) {
                  tp->next = np->next;
                  break;
              }
              tp = tp->next;
          }
      }
  
      np->next = context->node_free;
      context->node_free = np;
  }
  
  static int sixel_put_node(sixel_output_t *const context, int x,
                 sixel_node_t *np, int ncolors, int keycolor)
  {
      int nwrite;
  
      if (ncolors != 2 || keycolor == -1) {
          /* designate palette index */
          if (context->active_palette != np->color) {
              nwrite = sprintf((char *)context->buffer + context->pos,
                               "#%d", np->color);
              sixel_advance(context, nwrite);
              context->active_palette = np->color;
          }
      }
  
      for (; x < np->left; x++) {
          sixel_put_pixel(context, 0);
      }
  
      for (; x < np->right; x++) {
          sixel_put_pixel(context, np->map[x]);
      }
  
      sixel_put_flash(context);
  
      return x;
  }
  
  static MagickBooleanType sixel_encode_impl(unsigned char *pixels, size_t width,size_t height,
                    unsigned char *palette, size_t ncolors, int keycolor,
                    sixel_output_t *context)
  {
  #define RelinquishNodesAndMap \
      while ((np = context->node_free) != NULL) { \
          context->node_free = np->next; \
          np=(sixel_node_t *) RelinquishMagickMemory(np); \
      } \
      map = (unsigned char *) RelinquishMagickMemory(map)
  
      int x, y, i, n, c;
      int left, right;
      int pix;
      size_t len;
      unsigned char *map;
      sixel_node_t *np, *tp, top;
      int nwrite;
  
      context->pos = 0;
  
      if (ncolors < 1) {
          return (MagickFalse);
      }
      len = ncolors * width;
      context->active_palette = (-1);
  
      if ((map = (unsigned char *)AcquireQuantumMemory(len, sizeof(unsigned char))) == NULL) {
          return (MagickFalse);
      }
      (void) ResetMagickMemory(map, 0, len);
  
      if (context->has_8bit_control) {
          nwrite = sprintf((char *)context->buffer, "\x90" "0;0;0" "q");
      } else {
          nwrite = sprintf((char *)context->buffer, "\x1bP" "0;0;0" "q");
      }
      if (nwrite <= 0) {
          return (MagickFalse);
      }
      sixel_advance(context, nwrite);
      nwrite = sprintf((char *)context->buffer + context->pos, "\"1;1;%d;%d", (int) width, (int) height);
      if (nwrite <= 0) {
          RelinquishNodesAndMap;
          return (MagickFalse);
      }
      sixel_advance(context, nwrite);
  
      if (ncolors != 2 || keycolor == -1) {
          for (n = 0; n < (ssize_t) ncolors; n++) {
              /* DECGCI Graphics Color Introducer  # Pc ; Pu; Px; Py; Pz */
              nwrite = sprintf((char *)context->buffer + context->pos, "#%d;2;%d;%d;%d",
                               n,
                               (palette[n * 3 + 0] * 100 + 127) / 255,
                               (palette[n * 3 + 1] * 100 + 127) / 255,
                               (palette[n * 3 + 2] * 100 + 127) / 255);
              if (nwrite <= 0) {
                  RelinquishNodesAndMap;
                  return (MagickFalse);
              }
              sixel_advance(context, nwrite);
              if (nwrite <= 0) {
                  RelinquishNodesAndMap;
                  return (MagickFalse);
              }
          }
      }
  
      for (y = i = 0; y < (ssize_t) height; y++) {
          for (x = 0; x < (ssize_t) width; x++) {
              pix = pixels[y * width + x];
              if (pix >= 0 && pix < (ssize_t) ncolors && pix != keycolor) {
                  map[pix * width + x] |= (1 << i);
              }
          }
  
          if (++i < 6 && (y + 1) < (ssize_t) height) {
              continue;
          }
  
          for (c = 0; c < (ssize_t) ncolors; c++) {
              for (left = 0; left < (ssize_t) width; left++) {
                  if (*(map + c * width + left) == 0) {
                      continue;
                  }
  
                  for (right = left + 1; right < (ssize_t) width; right++) {
                      if (*(map + c * width + right) != 0) {
                          continue;
                      }
  
                      for (n = 1; (right + n) < (ssize_t) width; n++) {
                          if (*(map + c * width + right + n) != 0) {
                              break;
                          }
                      }
  
                      if (n >= 10 || right + n >= (ssize_t) width) {
                          break;
                      }
                      right = right + n - 1;
                  }
  
                  if ((np = context->node_free) != NULL) {
                      context->node_free = np->next;
                  } else if ((np = (sixel_node_t *)AcquireMagickMemory(sizeof(sixel_node_t))) == NULL) {
                      RelinquishNodesAndMap;
                      return (MagickFalse);
                  }
  
                  np->color = c;
                  np->left = left;
                  np->right = right;
                  np->map = map + c * width;
  
                  top.next = context->node_top;
                  tp = &top;
  
                  while (tp->next != NULL) {
                      if (np->left < tp->next->left) {
                          break;
                      }
                      if (np->left == tp->next->left && np->right > tp->next->right) {
                          break;
                      }
                      tp = tp->next;
                  }
  
                  np->next = tp->next;
                  tp->next = np;
                  context->node_top = top.next;
  
                  left = right - 1;
              }
  
          }
  
          for (x = 0; (np = context->node_top) != NULL;) {
              if (x > np->left) {
                  /* DECGCR Graphics Carriage Return */
                  context->buffer[context->pos] = '$';
                  sixel_advance(context, 1);
                  x = 0;
              }
  
              x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
              sixel_node_del(context, np);
              np = context->node_top;
  
              while (np != NULL) {
                  if (np->left < x) {
                      np = np->next;
                      continue;
                  }
  
                  x = sixel_put_node(context, x, np, (int) ncolors, keycolor);
                  sixel_node_del(context, np);
                  np = context->node_top;
              }
          }
  
          /* DECGNL Graphics Next Line */
          context->buffer[context->pos] = '-';
          sixel_advance(context, 1);
          if (nwrite <= 0) {
              RelinquishNodesAndMap;
              return (MagickFalse);
          }
  
          i = 0;
          (void) ResetMagickMemory(map, 0, len);
      }
  
      if (context->has_8bit_control) {
          context->buffer[context->pos] = 0x9c;
          sixel_advance(context, 1);
      } else {
          context->buffer[context->pos] = 0x1b;
          context->buffer[context->pos + 1] = '\\';
          sixel_advance(context, 2);
      }
      if (nwrite <= 0) {
          RelinquishNodesAndMap;
          return (MagickFalse);
      }
  
      /* flush buffer */
      if (context->pos > 0) {
          (void) WriteBlob(context->image,context->pos,context->buffer);
      }
  
      RelinquishNodesAndMap;
  
      return(MagickTrue);
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   I s S I X E L                                                             %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  IsSIXEL() returns MagickTrue if the image format type, identified by the
  %  magick string, is SIXEL.
  %
  %  The format of the IsSIXEL method is:
  %
  %      MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
  %
  %  A description of each parameter follows:
  %
  %    o magick: compare image format pattern against these bytes. or
  %      blob.
  %
  %    o length: Specifies the length of the magick string.
  %
  */
  static MagickBooleanType IsSIXEL(const unsigned char *magick,const size_t length)
  {
    const unsigned char
      *end = magick + length;
  
    if (length < 3)
      return(MagickFalse);
  
    if (*magick == 0x90 || (*magick == 0x1b && *++magick == 'P')) {
      while (++magick != end) {
        if (*magick == 'q')
          return(MagickTrue);
        if (!(*magick >= '0' && *magick <= '9') && *magick != ';')
          return(MagickFalse);
      }
    }
    return(MagickFalse);
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   R e a d S I X E L I m a g e                                               %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  ReadSIXELImage() reads an X11 pixmap image file and returns it.  It
  %  allocates the memory necessary for the new Image structure and returns a
  %  pointer to the new image.
  %
  %  The format of the ReadSIXELImage method is:
  %
  %      Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
  %
  %  A description of each parameter follows:
  %
  %    o image_info: the image info.
  %
  %    o exception: return any errors or warnings in this structure.
  %
  */
  static Image *ReadSIXELImage(const ImageInfo *image_info,ExceptionInfo *exception)
  {
    char
      *sixel_buffer;
  
    Image
      *image;
  
    MagickBooleanType
      status;
  
    register char
      *p;
  
    register IndexPacket
      *indexes;
  
    register ssize_t
      x;
  
    register PixelPacket
      *r;
  
    size_t
      length;
  
    ssize_t
      i,
      j,
      y;
  
    unsigned char
      *sixel_pixels,
      *sixel_palette;
  
    /*
      Open image file.
    */
    assert(image_info != (const ImageInfo *) NULL);
    assert(image_info->signature == MagickCoreSignature);
    if (image_info->debug != MagickFalse)
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
        image_info->filename);
    assert(exception != (ExceptionInfo *) NULL);
    assert(exception->signature == MagickCoreSignature);
    image=AcquireImage(image_info);
    status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
    if (status == MagickFalse)
      {
        image=DestroyImageList(image);
        return((Image *) NULL);
      }
    /*
      Read SIXEL file.
    */
    length=MaxTextExtent;
    sixel_buffer=(char *) AcquireQuantumMemory((size_t) length+MaxTextExtent,
      sizeof(*sixel_buffer));
    p=sixel_buffer;
    if (sixel_buffer != (char *) NULL)
      while (ReadBlobString(image,p) != (char *) NULL)
      {
        if ((*p == '#') && ((p == sixel_buffer) || (*(p-1) == '\n')))
          continue;
        if ((*p == '}') && (*(p+1) == ';'))
          break;
        p+=strlen(p);
        if ((size_t) (p-sixel_buffer+MaxTextExtent) < length)
          continue;
        length<<=1;
        sixel_buffer=(char *) ResizeQuantumMemory(sixel_buffer,length+
          MaxTextExtent,sizeof(*sixel_buffer));
        if (sixel_buffer == (char *) NULL)
          break;
        p=sixel_buffer+strlen(sixel_buffer);
      }
    if (sixel_buffer == (char *) NULL)
      ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
    sixel_buffer[length]='\0';
    /*
      Decode SIXEL
    */
    if (sixel_decode((unsigned char *)sixel_buffer, &sixel_pixels, &image->columns, &image->rows, &sixel_palette, &image->colors) == MagickFalse)
      {
        sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
        ThrowReaderException(CorruptImageError,"CorruptImage");
      }
    sixel_buffer=(char *) RelinquishMagickMemory(sixel_buffer);
    image->depth=24;
    image->storage_class=PseudoClass;
    status=SetImageExtent(image,image->columns,image->rows);
    if (status == MagickFalse)
      {
        sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
        sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
        InheritException(exception,&image->exception);
        return(DestroyImageList(image));
      }
    if (AcquireImageColormap(image,image->colors) == MagickFalse)
      {
        sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
        sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
        ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
      }
    for (i = 0; i < (ssize_t) image->colors; ++i) {
      image->colormap[i].red   = ScaleCharToQuantum(sixel_palette[i * 4 + 0]);
      image->colormap[i].green = ScaleCharToQuantum(sixel_palette[i * 4 + 1]);
      image->colormap[i].blue  = ScaleCharToQuantum(sixel_palette[i * 4 + 2]);
    }
  
    j=0;
    if (image_info->ping == MagickFalse)
      {
        /*
          Read image pixels.
        */
        for (y=0; y < (ssize_t) image->rows; y++)
        {
          r=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
          if (r == (PixelPacket *) NULL)
            break;
          indexes=GetAuthenticIndexQueue(image);
          for (x=0; x < (ssize_t) image->columns; x++)
          {
            j=(ssize_t) sixel_pixels[y * image->columns + x];
            SetPixelIndex(indexes+x,j);
            r++;
          }
          if (SyncAuthenticPixels(image,exception) == MagickFalse)
            break;
        }
        if (y < (ssize_t) image->rows)
          {
            sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
            sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
            ThrowReaderException(CorruptImageError,"NotEnoughPixelData");
          }
      }
    /*
      Relinquish resources.
    */
    sixel_pixels=(unsigned char *) RelinquishMagickMemory(sixel_pixels);
    sixel_palette=(unsigned char *) RelinquishMagickMemory(sixel_palette);
    (void) CloseBlob(image);
    return(GetFirstImageInList(image));
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   R e g i s t e r S I X E L I m a g e                                       %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  RegisterSIXELImage() adds attributes for the SIXEL image format to
  %  the list of supported formats.  The attributes include the image format
  %  tag, a method to read and/or write the format, whether the format
  %  supports the saving of more than one frame to the same file or blob,
  %  whether the format supports native in-memory I/O, and a brief
  %  description of the format.
  %
  %  The format of the RegisterSIXELImage method is:
  %
  %      size_t RegisterSIXELImage(void)
  %
  */
  ModuleExport size_t RegisterSIXELImage(void)
  {
    MagickInfo
      *entry;
  
    entry=SetMagickInfo("SIXEL");
    entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
    entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
    entry->magick=(IsImageFormatHandler *) IsSIXEL;
    entry->adjoin=MagickFalse;
    entry->description=ConstantString("DEC SIXEL Graphics Format");
    entry->module=ConstantString("SIXEL");
    (void) RegisterMagickInfo(entry);
    entry=SetMagickInfo("SIX");
    entry->decoder=(DecodeImageHandler *) ReadSIXELImage;
    entry->encoder=(EncodeImageHandler *) WriteSIXELImage;
    entry->magick=(IsImageFormatHandler *) IsSIXEL;
    entry->adjoin=MagickFalse;
    entry->description=ConstantString("DEC SIXEL Graphics Format");
    entry->module=ConstantString("SIX");
    (void) RegisterMagickInfo(entry);
    return(MagickImageCoderSignature);
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   U n r e g i s t e r S I X E L I m a g e                                   %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  UnregisterSIXELImage() removes format registrations made by the
  %  SIXEL module from the list of supported formats.
  %
  %  The format of the UnregisterSIXELImage method is:
  %
  %      UnregisterSIXELImage(void)
  %
  */
  ModuleExport void UnregisterSIXELImage(void)
  {
    (void) UnregisterMagickInfo("SIXEL");
    (void) UnregisterMagickInfo("SIX");
  }
  
  /*
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %   W r i t e S I X E L I m a g e                                             %
  %                                                                             %
  %                                                                             %
  %                                                                             %
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  %  WriteSIXELImage() writes an image to a file in the X pixmap format.
  %
  %  The format of the WriteSIXELImage method is:
  %
  %      MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,
  %        Image *image,ExceptionInfo *exception)
  %
  %  A description of each parameter follows.
  %
  %    o image_info: the image info.
  %
  %    o image:  The image.
  %
  %    o exception: return any errors or warnings in this structure.
  %
  */
  static MagickBooleanType WriteSIXELImage(const ImageInfo *image_info,Image *image)
  {
    ExceptionInfo
      *exception;
  
    MagickBooleanType
      status;
  
    register const IndexPacket
      *indexes;
  
    register ssize_t
      i,
      x;
  
    ssize_t
      opacity,
      y;
  
    sixel_output_t
      *output;
  
    unsigned char
      sixel_palette[256 * 3],
      *sixel_pixels;
  
    /*
      Open output image file.
    */
    assert(image_info != (const ImageInfo *) NULL);
    assert(image_info->signature == MagickCoreSignature);
    assert(image != (Image *) NULL);
    assert(image->signature == MagickCoreSignature);
    if (image->debug != MagickFalse)
      (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
    exception=(&image->exception);
    status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
    if (status == MagickFalse)
      return(status);
    if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
      (void) TransformImageColorspace(image,sRGBColorspace);
    opacity=(-1);
    if (image->matte == MagickFalse)
      {
        if ((image->storage_class == DirectClass) || (image->colors > 256))
          (void) SetImageType(image,PaletteType);
      }
    else
      {
        MagickRealType
          alpha,
          beta;
  
        /*
          Identify transparent colormap index.
        */
        if ((image->storage_class == DirectClass) || (image->colors > 256))
          (void) SetImageType(image,PaletteBilevelMatteType);
        for (i=0; i < (ssize_t) image->colors; i++)
          if (image->colormap[i].opacity != OpaqueOpacity)
            {
              if (opacity < 0)
                {
                  opacity=i;
                  continue;
                }
              alpha=(MagickRealType) image->colormap[i].opacity;
              beta=(MagickRealType) image->colormap[opacity].opacity;
              if (alpha > beta)
                opacity=i;
            }
        if (opacity == -1)
          {
            (void) SetImageType(image,PaletteBilevelMatteType);
            for (i=0; i < (ssize_t) image->colors; i++)
              if (image->colormap[i].opacity != OpaqueOpacity)
                {
                  if (opacity < 0)
                    {
                      opacity=i;
                      continue;
                    }
                  alpha=(MagickRealType) image->colormap[i].opacity;
                  beta=(MagickRealType) image->colormap[opacity].opacity;
                  if (alpha > beta)
                    opacity=i;
                }
          }
        if (opacity >= 0)
          {
            image->colormap[opacity].red=image->transparent_color.red;
            image->colormap[opacity].green=image->transparent_color.green;
            image->colormap[opacity].blue=image->transparent_color.blue;
          }
      }
    /*
      SIXEL header.
    */
    for (i=0; i < (ssize_t) image->colors; i++)
    {
      sixel_palette[i * 3 + 0] = ScaleQuantumToChar(image->colormap[i].red);
      sixel_palette[i * 3 + 1] = ScaleQuantumToChar(image->colormap[i].green);
      sixel_palette[i * 3 + 2] = ScaleQuantumToChar(image->colormap[i].blue);
    }
  
    /*
      Define SIXEL pixels.
    */
    output = sixel_output_create(image);
    if (output == (sixel_output_t *) NULL)
      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
    sixel_pixels =(unsigned char *) AcquireQuantumMemory(image->columns,
      image->rows*sizeof(*sixel_pixels));
    if (sixel_pixels == (unsigned char *) NULL)
      {
        output = (sixel_output_t *) RelinquishMagickMemory(output);
        ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
      }
    for (y=0; y < (ssize_t) image->rows; y++)
    {
      register const PixelPacket
        *p;
  
      p=GetVirtualPixels(image,0,y,image->columns,1,exception);
      if (p == (PixelPacket *) NULL)
        break;
      indexes=GetVirtualIndexQueue(image);
      for (x=0; x < (ssize_t) image->columns; x++)
        sixel_pixels[y*image->columns+x]=(unsigned char)
          ((ssize_t) GetPixelIndex(indexes+x));
    }
    status = sixel_encode_impl(sixel_pixels, image->columns, image->rows,
      sixel_palette, image->colors, -1, output);
    sixel_pixels =(unsigned char *) RelinquishMagickMemory(sixel_pixels);
    output = (sixel_output_t *) RelinquishMagickMemory(output);
    (void) CloseBlob(image);
    return(status);
  }
  