--- src/Makefile.in
+++ src/Makefile.in
@@ -40,7 +40,7 @@ COMMON = \
 	screen.o scrollbar.o scrollbar-next.o scrollbar-rxvt.o \
 	scrollbar-xterm.o scrollbar-plain.o xdefaults.o encoding.o \
 	rxvttoolkit.o rxvtutil.o keyboard.o rxvtimg.o \
-	ev_cpp.o fdpass_wrapper.o ptytty_wrapper.o @PERL_O@
+	ev_cpp.o fdpass_wrapper.o ptytty_wrapper.o sixel.o @PERL_O@
 
 COMMON_DAEMON = rxvtdaemon.o
 
@@ -289,3 +289,5 @@ rxvtperl.o: ../libptytty/src/estl.h emman.h rxvtfont.h rxvttoolkit.h
 rxvtperl.o: callback.h rxvtimg.h scrollbar.h ../libptytty/src/libptytty.h
 rxvtperl.o: rxvtperl.h hookinc.h rsinc.h optinc.h keyboard.h perlxsi.c
 rxvtperl.o: iom_perl.h
+sixel.o: sixel.h
+sixel_hls.o: sixel_hls.h
--- src/command.C
+++ src/command.C
@@ -51,6 +51,7 @@
 #include "rxvtperl.h"
 #include "version.h"
 #include "command.h"
+#include "sixel.h"
 
 #ifdef KEYSYM_RESOURCE
 # include "keyboard.h"
@@ -2865,6 +2866,8 @@ rxvt_term::process_csi_seq ()
           case '?':
             if (ch == 'h' || ch == 'l' || ch == 'r' || ch == 's' || ch == 't')
               process_terminal_mode (ch, priv, nargs, arg);
+            else if (ch == 'S')
+              process_graphics_attributes (nargs, arg);
             break;
 
           case '!':
@@ -3262,15 +3265,215 @@ rxvt_term::get_to_st (unicode_t &ends_how)
 void
 rxvt_term::process_dcs_seq ()
 {
-  /*
-   * Not handled yet
-   */
-
-  unicode_t eh;
-  char *s = get_to_st (eh);
-  if (s)
-    free (s);
+  unicode_t ch;
+  unsigned char c;
+  const int max_params = 16;
+  int params[max_params] = { 0 };
+  int nparams = 0;
+  int cmd = 0;
+  enum {
+    DCS_START,
+    DCS_PARAM,
+    DCS_INTERMEDIATE,
+    DCS_PASSTHROUGH,
+    DCS_IGNORE,
+    DCS_ESC
+  };
+  int st = DCS_START;
+  int x, y;
+  sixel_state_t sixel_st = { PS_GROUND };
+  imagelist_t *new_image;
+  line_t l;
+
+  while (1) {
+    if ((ch = next_char ()) == NOCHAR) {
+        pty_fill ();
+        continue;
+    }
+    c = ch & 0xff;
+    switch (st) {
+    case DCS_START:
+    case DCS_PARAM:
+      switch (c) {
+      case '\030':  /* CAN */
+        goto end;
+      case '\032':  /* SUB */
+        st = DCS_IGNORE;
+        break;
+      case '\033':
+        st = DCS_ESC;
+        break;
+      case ' ' ... '/':
+        cmd = cmd << 8 | c;
+        st = cmd > (0xff << 16) ? DCS_IGNORE : DCS_INTERMEDIATE;
+        break;
+      case '0' ... '9':
+        params[nparams] = params[nparams] * 10 + c - '0';
+        st = params[nparams] > 256 ? DCS_IGNORE : DCS_PARAM;
+        break;
+      case ';':
+        if (++nparams == max_params)
+          st = DCS_IGNORE;
+        else
+          params[nparams] = 0;
+        break;
+      case ':':
+        st = DCS_IGNORE;
+        break;
+      case '<' ... '?':
+        cmd = cmd << 8 | c;
+        st = cmd > (0xff << 16) ? DCS_IGNORE : DCS_PARAM;
+        break;
+      case '@' ... '~':
+        cmd = cmd << 8 | c;
+        st = cmd > (0xff << 16) ? DCS_IGNORE : DCS_PASSTHROUGH;
+        break;
+      default:
+        st = DCS_IGNORE;
+        break;
+      }
+      break;
+    case DCS_INTERMEDIATE:
+      switch (c) {
+      case '\030':  /* CAN */
+        goto end;
+      case '\032':  /* SUB */
+        st = DCS_IGNORE;
+        break;
+      case '\033':
+        st = DCS_ESC;
+        break;
+      case ' ' ... '/':
+        cmd = cmd << 8 | c;
+        st = cmd > (0xff << 16) ? DCS_IGNORE : DCS_INTERMEDIATE;
+        break;
+      case '@' ... '~':
+        cmd = cmd << 8 | c;
+        st = cmd > (0xff << 16) ? DCS_IGNORE : DCS_PASSTHROUGH;
+        break;
+      default:
+        st = DCS_IGNORE;
+        break;
+      }
+      break;
+    case DCS_PASSTHROUGH:
+      switch (c) {
+      case '\030':  /* CAN */
+        goto end;
+      case '\032':  /* SUB */
+        st = DCS_IGNORE;
+        break;
+      case '\033':
+        st = DCS_ESC;
+        break;
+      default:
+        switch (cmd) {
+        case 'q':  /* DECSIXEL */
+          switch (sixel_st.state) {
+            case PS_GROUND:
+              {
+                rgba fg = pix_colors[Color_fg];
+                rgba bg = pix_colors[Color_bg];
+                sixel_parser_init(&sixel_st,
+                                  fg.b >> 8 << 16 | fg.g >> 8 << 8 | fg.r >> 8,
+                                  bg.b >> 8 << 16 | bg.g >> 8 << 8 | bg.r >> 8,
+                                  1, fwidth, fheight);
+              }
+              break;
+            default:
+              sixel_parser_parse(&sixel_st, &c, 1);
+              break;
+          }
+          break;
+        default:
+          break;
+        }
+        break;
+      }
+      break;
+    case DCS_IGNORE:
+      switch (c) {
+      case '\030':  /* CAN */
+        goto end;
+      case '\032':  /* SUB */
+        st = DCS_IGNORE;
+        break;
+      case '\033':
+        st = DCS_ESC;
+        break;
+      default:
+        st = DCS_IGNORE;
+        break;
+      }
+      break;
+    case DCS_ESC:
+      switch (c) {
+      case '\\':
+        switch (cmd) {
+        case 'q':  /* DECSIXEL */
+          new_image = (imagelist_t *)rxvt_calloc (1, sizeof(imagelist_t));
+          new_image->pixels = (unsigned char *)rxvt_malloc (sixel_st.image.width * sixel_st.image.height * 4);
+          (void) sixel_parser_finalize (&sixel_st, new_image->pixels);
+          sixel_parser_deinit(&sixel_st);
+          new_image->col = screen.cur.col;
+          new_image->row = screen.cur.row + virtual_lines;
+          new_image->pxwidth = sixel_st.image.width;
+          new_image->pxheight = sixel_st.image.height;
+          if (this->images) {
+            imagelist_t *im;
+            for (im = this->images; im->next; im = im->next)
+              ;
+            new_image->prev = im;
+            im->next = new_image;
+          } else {
+            this->images = new_image;
+          }
 
+          for (y = 0; y < Pixel2Row (new_image->pxheight + fheight - 1); ++y)
+            {
+              if ((priv_modes & PrivMode_SixelDisplay))
+                l = ROW(screen.cur.row + y);
+              else
+                l = ROW(screen.cur.row);
+              for (x = 0; x < min (ncol - screen.cur.col, Pixel2Col (new_image->pxwidth + fwidth - 1)); ++x)
+                {
+                  l.t[screen.cur.col + x] = CHAR_IMAGE;
+                  l.r[screen.cur.col + x] = RS_None;
+                }
+              if (!(priv_modes & PrivMode_SixelDisplay))
+                {
+                  if (y == Pixel2Row (new_image->pxheight + fheight - 1) - 1)  // on last row
+                    {
+                      if ((priv_modes & PrivMode_SixelScrsRight))
+                        {
+                          screen.cur.col += x;
+                        }
+                      else
+                        {
+                          scr_index (UP);
+                          if ((priv_modes & PrivMode_SixelScrsLeft))
+                            scr_gotorc (0, 0, R_RELATIVE);
+                        }
+                    }
+                  else
+                    {
+                      scr_index (UP);
+                    }
+                }
+            }
+          break;
+        default:
+          break;
+        }
+        goto end;
+      default:
+        goto end;
+      }
+    default:
+      break;
+    }
+  }
+end:
   return;
 }
 
@@ -3695,6 +3898,7 @@ rxvt_term::process_terminal_mode (int mode, int priv ecb_unused, unsigned int na
 #ifndef NO_BACKSPACE_KEY
                   { 67, PrivMode_BackSpace },   // DECBKM
 #endif
+                  { 80, PrivMode_SixelDisplay },   // DECSDM sixel display mode
                   { 1000, PrivMode_MouseX11 },
                   { 1002, PrivMode_MouseBtnEvent },
                   { 1003, PrivMode_MouseAnyEvent },
@@ -3715,6 +3919,10 @@ rxvt_term::process_terminal_mode (int mode, int priv ecb_unused, unsigned int na
                   { 1049, PrivMode_Screen }, /* xterm extension, clear screen on ti rather than te */
                  // 1051, 1052, 1060, 1061 keyboard emulation NYI
                   { 2004, PrivMode_BracketPaste },
+                 // 7730 sixel-scrolls-left mode, originally proposed by mintty
+                  { 7730, PrivMode_SixelScrsLeft },
+                 // 8452 sixel-scrolls-right mode, originally proposed by RLogin
+                  { 8452, PrivMode_SixelScrsRight },
                 };
 
   if (nargs == 0)
@@ -4031,6 +4239,62 @@ rxvt_term::process_sgr_mode (unsigned int nargs, const int *arg)
     }
 }
 
+void
+rxvt_term::process_graphics_attributes (unsigned int nargs, const int *arg)
+{
+  if (nargs != 3)
+    return;
+  switch (arg[0])
+    {
+      case 1:  /* number of sixel color palette */
+        switch (arg[1])
+          {
+            case 1:  /* read */
+              tt_printf ("\033[?%d;%d;%dS", arg[0], 0, DECSIXEL_PALETTE_MAX);
+              break;
+            case 2:  /* reset to default */
+              tt_printf ("\033[?%d;%d;%dS", arg[0], 0, DECSIXEL_PALETTE_MAX);
+              break;
+            case 3:  /* set */
+              if (arg[2] == DECSIXEL_PALETTE_MAX)
+                tt_printf ("\033[?%d;%d;%dS", arg[0], 0, DECSIXEL_PALETTE_MAX);
+              else
+                tt_printf ("\033[?%d;%d;%dS", arg[0], 3, 0);
+              break;
+            case 4:  /* read the maximum value */
+              tt_printf ("\033[?%d;%d;%dS", arg[0], 0, DECSIXEL_PALETTE_MAX);
+              break;
+            default:
+              tt_printf ("\033[?%d;%d;%dS", arg[0], 2, 0);
+              break;
+          }
+        break;
+      case 2:  /* geometory of sixel graphics */
+        switch (arg[1])
+          {
+            case 1:  /* read */
+              tt_printf ("\033[?%d;%d;%d;%dS", arg[0], 0, ncol * fwidth, nrow * fheight);
+              break;
+            case 2:  /* reset to default */
+              tt_printf ("\033[?%d;%d;%d;%dS", arg[0], 3, 0, 0);
+              break;
+            case 3:  /* set */
+              tt_printf ("\033[?%d;%d;%d;%dS", arg[0], 3, 0, 0);
+              break;
+            case 4:  /* read the maximum value */
+              tt_printf ("\033[?%d;%d;%d;%dS", arg[0], 3, 0, 0);
+              break;
+            default:
+              tt_printf ("\033[?%d;%d;%d;%dS", arg[0], 2, 0, 0);
+              break;
+          }
+        break;
+      default:
+        tt_printf ("\033[?%d;%d;%dS", arg[0], 1, 0);
+        break;
+    }
+}
+
 void
 rxvt_term::set_cursor_style (int style)
 {
--- src/command.h
+++ src/command.h
@@ -58,7 +58,7 @@
  * two strings should be the same so that identical read (2) calls may be
  * used.
  */
-#define VT100_ANS	"\033[?1;2c"	/* vt100 answerback */
+#define VT100_ANS	"\033[?1;2;4c"	/* vt100 answerback(1;2) + sixel graphics(4) */
 #ifndef ESCZ_ANSWER
 # define ESCZ_ANSWER	VT100_ANS	/* obsolete ANSI ESC[c */
 #endif
--- src/rxvt.h
+++ src/rxvt.h
@@ -389,6 +389,7 @@ enum {
   C0_CAN, C0_EM , C0_SUB, C0_ESC, C0_IS4, C0_IS3, C0_IS2, C0_IS1,
 };
 #define CHAR_ST                 0x9c    /* 0234 */
+#define CHAR_IMAGE              0x01    /* special character for image area */
 
 /*
  * XTerm Operating System Commands: ESC ] Ps;Pt (ST|BEL)
@@ -575,6 +576,16 @@ enum {
 #define PrivMode_ExtMouseLeft   (1UL<<23)
 #define PrivMode_ExtMouseRight  (1UL<<24) // xterm pseudo-utf-8, but works in non-utf-8-locales
 #define PrivMode_BlinkingCursor (1UL<<25)
+#define PrivMode_ExtMouseSgr    (1UL<<26) // sgr mouse extension
+#define PrivMode_SixelDisplay   (1UL<<27) // sixel display mode
+/*  DECSET 7730: sixel scrolling end position
+ *  on: sixel scrolling moves cursor to beginning of the line
+ *  off(default): sixel scrolling moves cursor to left of graphics */
+#define PrivMode_SixelScrsLeft  (1UL<<28)
+/*  DECSET 8452: sixel scrolling end position right
+ *  on: sixel scrolling leaves cursor to right of graphic
+ *  off(default): position after sixel depends on sixel_scrolls_left */
+#define PrivMode_SixelScrsRight (1UL<<29)
 
 #define PrivMode_mouse_report   (PrivMode_MouseX10|PrivMode_MouseX11|PrivMode_MouseBtnEvent|PrivMode_MouseAnyEvent)
 
@@ -901,6 +911,7 @@ struct TermWin_t
   int            term_start;    /* term lines start here                    */
   int            view_start;    /* scrollback view starts here              */
   int            top_row;       /* topmost row index of scrollback          */
+  int            virtual_lines;
   Window         parent;        /* parent identifier                        */
   Window         vt;            /* vt100 window                             */
   GC             gc;            /* GC for drawing                           */
@@ -984,6 +995,18 @@ enum {
   Opt_count
 };
 
+struct imagelist_t
+{
+  imagelist_t *prev, *next;
+  unsigned char *pixels;
+  Drawable drawable;
+  void *storage;
+  int col;
+  int row;
+  int pxwidth;
+  int pxheight;
+};
+
 /* ------------------------------------------------------------------------- */
 
 struct rxvt_vars : TermWin_t
@@ -1005,6 +1028,7 @@ struct rxvt_vars : TermWin_t
 #ifdef OFF_FOCUS_FADING
   rxvt_color      pix_colors_unfocused[TOTAL_COLORS];
 #endif
+  imagelist_t     *images;
 };
 
 struct rxvt_term : zero_initialized, rxvt_vars, rxvt_screen
@@ -1297,6 +1321,7 @@ struct rxvt_term : zero_initialized, rxvt_vars, rxvt_screen
   int privcases (int mode, unsigned long bit);
   void process_terminal_mode (int mode, int priv, unsigned int nargs, const int *arg);
   void process_sgr_mode (unsigned int nargs, const int *arg);
+  void process_graphics_attributes (unsigned int nargs, const int *arg);
   void set_cursor_style (int style);
   // init.C
   void init (stringvec *argv, stringvec *envv);
--- src/screen.C
+++ src/screen.C
@@ -30,6 +30,174 @@
 #include "rxvtperl.h"           /* NECESSARY */
 
 #include <inttypes.h>
+#include <stdio.h>
+
+// tempfile_t manipulation
+
+typedef struct {
+  FILE *fp;
+  unsigned int ref_counter;
+} tempfile_t;
+
+typedef struct {
+  tempfile_t *tempfile;
+  size_t position;
+} temp_storage_t;
+
+static tempfile_t *tempfile_current = NULL;
+static size_t tempfile_num = 0;
+static size_t const TEMPFILE_MAX_SIZE = 1024 * 1024 * 16;  /* 16MB */
+static size_t const TEMPFILE_MAX_NUM = 16;
+
+static tempfile_t *
+tempfile_new (void)
+{
+  tempfile_t *tempfile;
+  FILE *fp;
+
+  fp = tmpfile();
+  if (!fp)
+    return NULL;
+
+  tempfile = (tempfile_t *)rxvt_malloc (sizeof(tempfile_t));
+  if (!tempfile)
+    return NULL;
+
+  tempfile->fp = fp;
+  tempfile->ref_counter = 1;
+
+  tempfile_num++;
+
+  return tempfile;
+}
+
+static void
+tempfile_destroy(tempfile_t *tempfile)
+{
+  if (tempfile == tempfile_current)
+    tempfile_current = NULL;
+  fclose((FILE *)tempfile->fp);
+  free (tempfile);
+  tempfile_num--;
+}
+
+static void
+tempfile_ref(tempfile_t *tempfile)
+{
+  tempfile->ref_counter++;
+}
+
+static void
+tempfile_deref(tempfile_t *tempfile)
+{
+  if (--tempfile->ref_counter == 0)
+    tempfile_destroy(tempfile);
+}
+
+static size_t
+tempfile_size(tempfile_t *tempfile)
+{
+  struct stat info;
+
+  fstat(fileno(tempfile->fp), &info);
+
+  return info.st_size;
+}
+
+static tempfile_t *
+tempfile_get(void)
+{
+  size_t size;
+
+  if (!tempfile_current)
+    {
+      tempfile_current = tempfile_new();
+      return tempfile_current;
+    }
+
+  /* get file size */
+  size = tempfile_size(tempfile_current);
+
+  /* if the file size reaches TEMPFILE_MAX_SIZE, return new temporary file */
+  if (size > TEMPFILE_MAX_SIZE)
+    {
+      tempfile_current = tempfile_new();
+      return tempfile_current;
+    }
+
+  /* increment reference counter */
+  tempfile_ref (tempfile_current);
+
+  return tempfile_current;
+}
+
+static bool
+tempfile_write(tempfile_t *tempfile, void *p, size_t pos, size_t size)
+{
+  size_t nbytes;
+
+  fseek ((FILE *)tempfile->fp, pos, SEEK_SET);
+  nbytes = fwrite(p, 1, size, tempfile->fp);
+  if (nbytes != size)
+    return false;
+
+  return true;
+}
+
+static bool
+tempfile_read(tempfile_t *tempfile, void *p, size_t pos, size_t size)
+{
+  size_t nbytes;
+
+  fflush((FILE *)tempfile->fp);
+  fseek((FILE *)tempfile->fp, pos, SEEK_SET);
+  nbytes = fread (p, 1, size, (FILE *)tempfile->fp);
+  if (nbytes != size)
+    return false;
+
+  return true;
+}
+
+// temp_storage_t implementation
+
+static temp_storage_t *
+storage_create(void)
+{
+  temp_storage_t *storage;
+  tempfile_t *tempfile;
+
+  tempfile = tempfile_get ();
+  if (!tempfile)
+    return NULL;
+
+  storage = (temp_storage_t *)rxvt_malloc (sizeof(temp_storage_t));
+  if (!storage)
+    return NULL;
+
+  storage->tempfile = tempfile;
+  storage->position = tempfile_size(storage->tempfile);
+
+  return storage;
+}
+
+static void
+storage_destroy (temp_storage_t *storage)
+{
+  tempfile_deref (storage->tempfile);
+  free (storage);
+}
+
+static bool
+storage_write(temp_storage_t *storage, void *p, size_t size)
+{
+  return tempfile_write (storage->tempfile, p, storage->position, size);
+}
+
+static bool
+storage_read(temp_storage_t *storage, void *p, size_t size)
+{
+  return tempfile_read (storage->tempfile, p, storage->position, size);
+}
 
 static inline void
 fill_text (text_t *start, text_t value, int len)
@@ -673,6 +841,7 @@ rxvt_term::scr_scroll_text (int row1, int row2, int count) NOTHROW
 
       // scroll everything up 'count' lines
       term_start = (term_start + count) % total_rows;
+      virtual_lines += count;
 
       // now copy lines below the scroll region bottom to the
       // bottom of the screen again, so they look as if they
@@ -2446,6 +2615,168 @@ rxvt_term::scr_refresh () NOTHROW
         }                     /* for (col....) */
     }                         /* for (row....) */
 
+  /*
+   * F: draw sixel images
+   */
+  if (images)
+    {
+      imagelist_t *im;
+      GC clipgc;
+      int trow, brow;  // top, bottom
+      int nlimit = 256;  //  num of allocated rects
+      XRectangle *rects = NULL, *itrect = NULL, *p = NULL;
+      int n, x, y;
+      XImage xi;
+
+      // for each images
+      for (im = images; im; im = im->next)
+        {
+          trow = im->row - virtual_lines;
+          brow = trow + (im->pxheight + fheight - 1) / fheight;
+
+          // if the image is out of scrollback, delete it.
+          if (brow <= top_row)
+            {
+              if (im->prev)
+                im->prev->next = im->next;
+              else
+                images = im->next;
+              if (im->next)
+                im->next->prev = im->prev;
+              if (im->drawable)
+                XFreePixmap (dpy, im->drawable);
+              if (im->storage)
+                storage_destroy ((temp_storage_t *)im->storage);
+              free (im->pixels);
+              free (im);
+              continue;
+            }
+
+          // if the image is out of view, serialize into the storage object(im->storage).
+          if (trow >= view_start + nrow || brow <= view_start)
+            {
+              if (! im->storage)
+                {
+                  if (im->drawable)
+                    {
+                      XFreePixmap (dpy, im->drawable);
+                      im->drawable = NULL;
+                    }
+                  if (! im->storage)
+                      im->storage = storage_create();
+                  if (! storage_write ((temp_storage_t *)im->storage, im->pixels, im->pxwidth * im->pxheight * 4))
+                    break;
+                  free (im->pixels);
+                  im->pixels = NULL;
+                }
+              continue;
+            }
+
+          // ensure the pixmap(im->drawable) is available
+          if (! im->drawable)
+            {
+              // ensure the in-memory pixel buffer(im->pixels) is available
+              if (! im->pixels)
+                {
+                  im->pixels = (unsigned char *)rxvt_malloc (im->pxwidth * im->pxheight * 4);
+                  if (! storage_read ((temp_storage_t *)im->storage, im->pixels, im->pxwidth * im->pxheight * 4))
+                    break;
+                  storage_destroy ((temp_storage_t *)im->storage);
+                  im->storage = NULL;
+                  assert (im->pixels);
+                }
+
+              // create the pixmap object(im->drawable).
+              XInitImage (&xi);
+              xi.format = ZPixmap;
+              xi.data = (char *)im->pixels;
+              xi.width = im->pxwidth;
+              xi.height = im->pxheight;
+              xi.xoffset = 0;
+              xi.byte_order = LSBFirst;
+              xi.bitmap_bit_order = MSBFirst;
+              xi.bits_per_pixel = 32;
+              xi.bytes_per_line = im->pxwidth * 4;
+              xi.bitmap_unit = 32;
+              xi.bitmap_pad = 32;
+              xi.depth = 24;
+              im->drawable = XCreatePixmap(dpy, vt, im->pxwidth, im->pxheight, DefaultDepth (dpy, 0));
+              XPutImage (dpy, im->drawable, gc, &xi, 0, 0, 0, 0, im->pxwidth, im->pxheight);
+            }
+
+          // XXX: this is shoddy work!!
+          // construct clipping rectangle array
+          itrect = rects = NULL;
+          for (y = trow - view_start; y < brow - view_start; y++)  // for rows
+            {
+              if (y >= 0 && y < nrow)
+                {
+                  // compare recent two clip rectangles, combine them if they has same (left, width).
+                  if (itrect - rects > 0 && itrect->x == (itrect - 1)->x && itrect->width == (itrect - 1)->width)
+                    {
+                      // test whether itrect adjacent to itrect - 1
+                      if ((itrect - 1)->y + (itrect - 1)->height == itrect->y)
+                        {
+                          itrect--;
+                          itrect->height += fheight;
+                        }
+                    }
+
+                  for (x = im->col; x < im->col + Pixel2Col (im->pxwidth + fwidth - 1) && x < ncol; x++)  // for cols
+                    {
+                      // lazy initialization for rectangle array
+                      if (!rects)
+                        itrect = rects = (XRectangle *)rxvt_malloc (sizeof (XRectangle) * nlimit);
+                      if (rects == NULL)
+                        break;
+
+                      // resize rectangle array
+                      if (itrect - rects == nlimit)
+                        {
+                          p = (XRectangle *)rxvt_realloc (rects, sizeof (XRectangle) * (nlimit *= 2));
+                          if (p == NULL)
+                            break;
+                          rects = p;
+                        }
+
+                      if (ROW(view_start + y).t[x] == CHAR_IMAGE)
+                        {
+                          // check if current cell is combinable with last clip rectangle
+                          if (itrect - rects > 0 && (itrect - 1)->x + (itrect - 1)->width == Col2Pixel (x) && (itrect - 1)->y == Row2Pixel (y))
+                            {
+                              (itrect - 1)->width += fwidth;
+                            }
+                          else
+                            {
+                              // add new clip rectangle
+                              itrect->x = Col2Pixel (x);
+                              itrect->y = Row2Pixel (y);
+                              itrect->width = fwidth;
+                              itrect->height = fheight;
+                              itrect++;
+                            }
+                        }
+                    }
+                }
+            }
+
+          if (itrect - rects > 0)  // if it should be drawn
+            {
+              clipgc = XCreateGC (dpy, vt, 0, 0);
+              // set clipping region
+              XSetClipRectangles (dpy, clipgc, 0, 0, rects, itrect - rects, YXSorted);
+              XCopyArea (dpy, im->drawable, vt,
+                         clipgc, 0, 0,
+                         im->pxwidth, im->pxheight,
+                         (unsigned int)Width2Pixel (im->col),
+                         (unsigned int)Height2Pixel (im->row - virtual_lines - view_start));
+              XFreeGC (dpy, clipgc);
+            }
+
+          free (rects);
+      }
+    }
+
   /*
    * G: cleanup cursor and display outline cursor if necessary
    */
--- /dev/null
+++ src/sixel.C
@@ -0,0 +1,681 @@
+// sixel.c (part of mintty)
+// originally written by kmiya@cluti (https://github.com/saitoha/sixel/blob/master/fromsixel.c)
+// Licensed under the terms of the GNU General Public License v3 or later.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>   /* isdigit */
+#include <string.h>  /* memcpy */
+
+#include "sixel.h"
+
+#define SIXEL_XRGB(r,g,b) (((r) * 255 + 50) / 100) << 0 | \
+                          (((g) * 255 + 50) / 100) << 8 | \
+                          (((b) * 255 + 50) / 100) << 16
+
+static colour 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% */
+};
+
+/*
+ * Primary color hues:
+ *  blue:    0 degrees
+ *  red:   120 degrees
+ *  green: 240 degrees
+ */
+int
+hls_to_rgb(int hue, int lum, int sat)
+{
+	double min, max;
+	int r, g, b;
+
+	if (sat == 0) {
+		r = g = b = lum;
+	}
+
+	/* https://wikimedia.org/api/rest_v1/media/math/render/svg/17e876f7e3260ea7fed73f69e19c71eb715dd09d */
+	max = lum + sat * (1.0 - (lum > 50 ? (((lum << 2) / 100.0) - 1.0): - (2 * (lum / 100.0) - 1.0))) / 2.0;
+
+	/* https://wikimedia.org/api/rest_v1/media/math/render/svg/f6721b57985ad83db3d5b800dc38c9980eedde1d */
+	min = lum - sat * (1.0 - (lum > 50 ? (((lum << 2) / 100.0) - 1.0): - (2 * (lum / 100.0) - 1.0))) / 2.0;
+
+	/* sixel hue color ring is roteted -120 degree from nowdays general one. */
+	hue = (hue + 240) % 360;
+
+	/* https://wikimedia.org/api/rest_v1/media/math/render/svg/937e8abdab308a22ff99de24d645ec9e70f1e384 */
+	switch (hue / 60) {
+	case 0:  /* 0 <= hue < 60 */
+		r = max;
+		g = (min + (max - min) * (hue / 60.0));
+		b = min;
+		break;
+	case 1:  /* 60 <= hue < 120 */
+		r = min + (max - min) * ((120 - hue) / 60.0);
+		g = max;
+		b = min;
+		break;
+	case 2:  /* 120 <= hue < 180 */
+		r = min;
+		g = max;
+		b = (min + (max - min) * ((hue - 120) / 60.0));
+		break;
+	case 3:  /* 180 <= hue < 240 */
+		r = min;
+		g = (min + (max - min) * ((240 - hue) / 60.0));
+		b = max;
+		break;
+	case 4:  /* 240 <= hue < 300 */
+		r = (min + (max - min) * ((hue - 240) / 60.0));
+		g = min;
+		b = max;
+		break;
+	case 5:  /* 300 <= hue < 360 */
+		r = max;
+		g = min;
+		b = (min + (max - min) * ((360 - hue) / 60.0));
+		break;
+	default:
+		break;
+	}
+
+	return SIXEL_XRGB(r, g, b);
+}
+
+static int
+set_default_color(sixel_image_t *image)
+{
+	int i;
+	int n;
+	int r;
+	int g;
+	int b;
+
+	/* palette initialization */
+	for (n = 1; n < 17; n++) {
+		image->palette[n] = sixel_default_color_table[n - 1];
+	}
+
+	/* colors 17-232 are a 6x6x6 color cube */
+	for (r = 0; r < 6; r++) {
+		for (g = 0; g < 6; g++) {
+			for (b = 0; b < 6; b++) {
+				image->palette[n++] = SIXEL_XRGB(r * 51, g * 51, b * 51);
+			}
+		}
+	}
+
+	/* colors 233-256 are a grayscale ramp, intentionally leaving out */
+	for (i = 0; i < 24; i++) {
+		image->palette[n++] = SIXEL_XRGB(i * 11, i * 11, i * 11);
+	}
+
+	for (; n < DECSIXEL_PALETTE_MAX; n++) {
+		image->palette[n] = SIXEL_XRGB(255, 255, 255);
+	}
+
+	return (0);
+}
+
+static int
+sixel_image_init(
+    sixel_image_t    *image,
+    int              width,
+    int              height,
+    int              fgcolor,
+    int              bgcolor,
+    int              use_private_register)
+{
+	int status = (-1);
+	size_t size;
+
+	size = (size_t)(width * height) * sizeof(sixel_color_no_t);
+	image->width = width;
+	image->height = height;
+	image->data = (sixel_color_no_t *)malloc(size);
+	image->ncolors = 2;
+	image->use_private_register = use_private_register;
+
+	if (image->data == NULL) {
+		status = (-1);
+		goto end;
+	}
+	memset(image->data, 0, size);
+
+	image->palette[0] = bgcolor;
+
+	if (image->use_private_register)
+		image->palette[1] = fgcolor;
+
+	image->palette_modified = 0;
+
+	status = (0);
+
+end:
+	return status;
+}
+
+
+static int
+image_buffer_resize(
+    sixel_image_t   *image,
+    int              width,
+    int              height)
+{
+	int status = (-1);
+	size_t size;
+	sixel_color_no_t *alt_buffer;
+	int n;
+	int min_height;
+
+	size = (size_t)(width * height) * sizeof(sixel_color_no_t);
+	alt_buffer = (sixel_color_no_t *)malloc(size);
+	if (alt_buffer == NULL) {
+		/* free source image */
+		free(image->data);
+		image->data = NULL;
+		status = (-1);
+		goto end;
+	}
+
+	min_height = height > image->height ? image->height: height;
+	if (width > image->width) {  /* if width is extended */
+		for (n = 0; n < min_height; ++n) {
+			/* copy from source image */
+			memcpy(alt_buffer + width * n,
+			       image->data + image->width * n,
+			       (size_t)image->width * sizeof(sixel_color_no_t));
+			/* fill extended area with background color */
+			memset(alt_buffer + width * n + image->width,
+			       0,
+			       (size_t)(width - image->width) * sizeof(sixel_color_no_t));
+		}
+	} else {
+		for (n = 0; n < min_height; ++n) {
+			/* copy from source image */
+			memcpy(alt_buffer + width * n,
+			       image->data + image->width * n,
+			       (size_t)width * sizeof(sixel_color_no_t));
+		}
+	}
+
+	if (height > image->height) {  /* if height is extended */
+		/* fill extended area with background color */
+		memset(alt_buffer + width * image->height,
+		       0,
+		       (size_t)(width * (height - image->height)) * sizeof(sixel_color_no_t));
+	}
+
+	/* free source image */
+	free(image->data);
+
+	image->data = alt_buffer;
+	image->width = width;
+	image->height = height;
+
+	status = (0);
+
+end:
+	return status;
+}
+
+static void
+sixel_image_deinit(sixel_image_t *image)
+{
+	free(image->data);
+	image->data = NULL;
+}
+
+int
+sixel_parser_init(sixel_state_t *st,
+                  colour fgcolor, colour bgcolor,
+                  unsigned char use_private_register,
+                  int cell_width, int cell_height)
+{
+	int status = (-1);
+
+	st->state = PS_DECSIXEL;
+	st->pos_x = 0;
+	st->pos_y = 0;
+	st->max_x = 0;
+	st->max_y = 0;
+	st->attributed_pan = 2;
+	st->attributed_pad = 1;
+	st->attributed_ph = 0;
+	st->attributed_pv = 0;
+	st->repeat_count = 1;
+	st->color_index = 16;
+	st->grid_width = cell_width;
+	st->grid_height = cell_height;
+	st->nparams = 0;
+	st->param = 0;
+
+	/* buffer initialization */
+	status = sixel_image_init(&st->image, 1, 1, fgcolor, bgcolor, use_private_register);
+
+	return status;
+}
+
+int
+sixel_parser_set_default_color(sixel_state_t *st)
+{
+	return set_default_color(&st->image);
+}
+
+int
+sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels)
+{
+	int status = (-1);
+	int sx;
+	int sy;
+	sixel_image_t *image = &st->image;
+	int x, y;
+	sixel_color_no_t *src;
+	unsigned char *dst;
+	int color;
+
+	if (++st->max_x < st->attributed_ph)
+		st->max_x = st->attributed_ph;
+
+	if (++st->max_y < st->attributed_pv)
+		st->max_y = st->attributed_pv;
+
+	sx = (st->max_x + st->grid_width - 1) / st->grid_width * st->grid_width;
+	sy = (st->max_y + st->grid_height - 1) / st->grid_height * st->grid_height;
+
+	if (image->width > sx || image->height > sy) {
+		status = image_buffer_resize(image, sx, sy);
+		if (status < 0)
+			goto end;
+	}
+
+	if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) {
+		status = set_default_color(image);
+		if (status < 0)
+			goto end;
+	}
+
+	src = st->image.data;
+	dst = pixels;
+	for (y = 0; y < st->image.height; ++y) {
+		for (x = 0; x < st->image.width; ++x) {
+			color = st->image.palette[*src++];
+			*dst++ = color >> 16 & 0xff;   /* b */
+			*dst++ = color >> 8 & 0xff;    /* g */
+			*dst++ = color >> 0 & 0xff;    /* r */
+			dst++;                         /* a */
+		}
+		/* fill right padding with bgcolor */
+		for (; x < st->image.width; ++x) {
+			color = st->image.palette[0];  /* bgcolor */
+			*dst++ = color >> 16 & 0xff;   /* b */
+			*dst++ = color >> 8 & 0xff;    /* g */
+			*dst++ = color >> 0 & 0xff;    /* r */
+			dst++;                         /* a */
+		}
+	}
+	/* fill bottom padding with bgcolor */
+	for (; y < st->image.height; ++y) {
+		for (x = 0; x < st->image.width; ++x) {
+			color = st->image.palette[0];  /* bgcolor */
+			*dst++ = color >> 16 & 0xff;   /* b */
+			*dst++ = color >> 8 & 0xff;    /* g */
+			*dst++ = color >> 0 & 0xff;    /* r */
+			dst++;                         /* a */
+		}
+	}
+
+	status = (0);
+
+end:
+	return status;
+}
+
+/* convert sixel data into indexed pixel bytes and palette data */
+int
+sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len)
+{
+	int status = (-1);
+	int n;
+	int i;
+	int x;
+	int y;
+	int bits;
+	int sixel_vertical_mask;
+	int sx;
+	int sy;
+	int c;
+	int pos;
+	unsigned char *p0 = p;
+	sixel_image_t *image = &st->image;
+
+	if (! image->data)
+		goto end;
+
+	while (p < p0 + len) {
+		switch (st->state) {
+		case PS_ESC:
+			goto end;
+
+		case PS_DECSIXEL:
+			switch (*p) {
+			case '\x1b':
+				st->state = PS_ESC;
+				p++;
+				break;
+			case '"':
+				st->param = 0;
+				st->nparams = 0;
+				st->state = PS_DECGRA;
+				p++;
+				break;
+			case '!':
+				st->param = 0;
+				st->nparams = 0;
+				st->state = PS_DECGRI;
+				p++;
+				break;
+			case '#':
+				st->param = 0;
+				st->nparams = 0;
+				st->state = PS_DECGCI;
+				p++;
+				break;
+			case '$':
+				/* DECGCR Graphics Carriage Return */
+				st->pos_x = 0;
+				p++;
+				break;
+			case '-':
+				/* DECGNL Graphics Next Line */
+				st->pos_x = 0;
+				if (st->pos_y < DECSIXEL_HEIGHT_MAX - 5 - 6)
+					st->pos_y += 6;
+				else
+					st->pos_y = DECSIXEL_HEIGHT_MAX + 1;
+				p++;
+				break;
+			default:
+				if (*p >= '?' && *p <= '~') {  /* sixel characters */
+					if ((image->width < (st->pos_x + st->repeat_count) || image->height < (st->pos_y + 6))
+					        && image->width < DECSIXEL_WIDTH_MAX && image->height < DECSIXEL_HEIGHT_MAX) {
+						sx = image->width * 2;
+						sy = image->height * 2;
+						while (sx < (st->pos_x + st->repeat_count) || sy < (st->pos_y + 6)) {
+							sx *= 2;
+							sy *= 2;
+						}
+
+						if (sx > DECSIXEL_WIDTH_MAX)
+							sx = DECSIXEL_WIDTH_MAX;
+						if (sy > DECSIXEL_HEIGHT_MAX)
+							sy = DECSIXEL_HEIGHT_MAX;
+
+						status = image_buffer_resize(image, sx, sy);
+						if (status < 0)
+							goto end;
+					}
+
+					if (st->color_index > image->ncolors)
+						image->ncolors = st->color_index;
+
+					if (st->pos_x + st->repeat_count > image->width)
+						st->repeat_count = image->width - st->pos_x;
+
+					if (st->repeat_count > 0 && st->pos_y - 5 < image->height) {
+						bits = *p - '?';
+						if (bits != 0) {
+							sixel_vertical_mask = 0x01;
+							if (st->repeat_count <= 1) {
+								for (i = 0; i < 6; i++) {
+									if ((bits & sixel_vertical_mask) != 0) {
+										pos = image->width * (st->pos_y + i) + st->pos_x;
+										image->data[pos] = st->color_index;
+										if (st->max_x < st->pos_x)
+											st->max_x = st->pos_x;
+										if (st->max_y < (st->pos_y + i))
+											st->max_y = st->pos_y + i;
+									}
+									sixel_vertical_mask <<= 1;
+								}
+							} else {
+								/* st->repeat_count > 1 */
+								for (i = 0; i < 6; i++) {
+									if ((bits & sixel_vertical_mask) != 0) {
+										c = sixel_vertical_mask << 1;
+										for (n = 1; (i + n) < 6; n++) {
+											if ((bits & c) == 0)
+												break;
+											c <<= 1;
+										}
+										for (y = st->pos_y + i; y < st->pos_y + i + n; ++y) {
+											for (x = st->pos_x; x < st->pos_x + st->repeat_count; ++x)
+												image->data[image->width * y + x] = st->color_index;
+										}
+										if (st->max_x < (st->pos_x + st->repeat_count - 1))
+											st->max_x = st->pos_x + st->repeat_count - 1;
+										if (st->max_y < (st->pos_y + i + n - 1))
+											st->max_y = st->pos_y + i + n - 1;
+										i += (n - 1);
+										sixel_vertical_mask <<= (n - 1);
+									}
+									sixel_vertical_mask <<= 1;
+								}
+							}
+						}
+					}
+					if (st->repeat_count > 0)
+						st->pos_x += st->repeat_count;
+					st->repeat_count = 1;
+				}
+				p++;
+				break;
+			}
+			break;
+
+		case PS_DECGRA:
+			/* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
+			switch (*p) {
+			case '\x1b':
+				st->state = PS_ESC;
+				p++;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				st->param = st->param * 10 + *p - '0';
+				if (st->param > DECSIXEL_PARAMVALUE_MAX)
+					st->param = DECSIXEL_PARAMVALUE_MAX;
+				p++;
+				break;
+			case ';':
+				if (st->nparams < DECSIXEL_PARAMS_MAX)
+					st->params[st->nparams++] = st->param;
+				st->param = 0;
+				p++;
+				break;
+			default:
+				if (st->nparams < DECSIXEL_PARAMS_MAX)
+					st->params[st->nparams++] = st->param;
+				if (st->nparams > 0)
+					st->attributed_pad = st->params[0];
+				if (st->nparams > 1)
+					st->attributed_pan = st->params[1];
+				if (st->nparams > 2 && st->params[2] > 0)
+					st->attributed_ph = st->params[2];
+				if (st->nparams > 3 && st->params[3] > 0)
+					st->attributed_pv = st->params[3];
+
+				if (st->attributed_pan <= 0)
+					st->attributed_pan = 1;
+				if (st->attributed_pad <= 0)
+					st->attributed_pad = 1;
+
+				if (image->width < st->attributed_ph ||
+					image->height < st->attributed_pv) {
+					sx = st->attributed_ph;
+					if (image->width > st->attributed_ph)
+						sx = image->width;
+
+					sy = st->attributed_pv;
+					if (image->height > st->attributed_pv)
+						sy = image->height;
+
+					sx = (sx + st->grid_width - 1) / st->grid_width * st->grid_width;
+					sy = (sy + st->grid_height - 1) / st->grid_height * st->grid_height;
+
+					if (sx > DECSIXEL_WIDTH_MAX)
+						sx = DECSIXEL_WIDTH_MAX;
+					if (sy > DECSIXEL_HEIGHT_MAX)
+						sy = DECSIXEL_HEIGHT_MAX;
+
+					status = image_buffer_resize(image, sx, sy);
+					if (status < 0)
+						goto end;
+				}
+				st->state = PS_DECSIXEL;
+				st->param = 0;
+				st->nparams = 0;
+			}
+			break;
+
+		case PS_DECGRI:
+			/* DECGRI Graphics Repeat Introducer ! Pn Ch */
+			switch (*p) {
+			case '\x1b':
+				st->state = PS_ESC;
+				p++;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				st->param = st->param * 10 + *p - '0';
+				if (st->param > DECSIXEL_PARAMVALUE_MAX)
+					st->param = DECSIXEL_PARAMVALUE_MAX;
+				p++;
+				break;
+			default:
+				st->repeat_count = st->param;
+				if (st->repeat_count == 0)
+					st->repeat_count = 1;
+				st->state = PS_DECSIXEL;
+				st->param = 0;
+				st->nparams = 0;
+				break;
+			}
+			break;
+
+		case PS_DECGCI:
+			/* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
+			switch (*p) {
+			case '\x1b':
+				st->state = PS_ESC;
+				p++;
+				break;
+			case '0':
+			case '1':
+			case '2':
+			case '3':
+			case '4':
+			case '5':
+			case '6':
+			case '7':
+			case '8':
+			case '9':
+				st->param = st->param * 10 + *p - '0';
+				if (st->param > DECSIXEL_PARAMVALUE_MAX)
+					st->param = DECSIXEL_PARAMVALUE_MAX;
+				p++;
+				break;
+			case ';':
+				if (st->nparams < DECSIXEL_PARAMS_MAX)
+					st->params[st->nparams++] = st->param;
+				st->param = 0;
+				p++;
+				break;
+			default:
+				st->state = PS_DECSIXEL;
+				if (st->nparams < DECSIXEL_PARAMS_MAX)
+					st->params[st->nparams++] = st->param;
+				st->param = 0;
+
+				if (st->nparams > 0) {
+					st->color_index = 1 + st->params[0];  /* offset 1(background color) added */
+					if (st->color_index < 0)
+						st->color_index = 0;
+					else if (st->color_index >= DECSIXEL_PALETTE_MAX)
+						st->color_index = DECSIXEL_PALETTE_MAX - 1;
+				}
+
+				if (st->nparams > 4) {
+					st->image.palette_modified = 1;
+					if (st->params[1] == 1) {
+						/* HLS */
+						if (st->params[2] > 360)
+							st->params[2] = 360;
+						if (st->params[3] > 100)
+							st->params[3] = 100;
+						if (st->params[4] > 100)
+							st->params[4] = 100;
+						image->palette[st->color_index]
+						    = hls_to_rgb(st->params[2], st->params[3], st->params[4]);
+					} else if (st->params[1] == 2) {
+						/* RGB */
+						if (st->params[2] > 100)
+							st->params[2] = 100;
+						if (st->params[3] > 100)
+							st->params[3] = 100;
+						if (st->params[4] > 100)
+							st->params[4] = 100;
+						image->palette[st->color_index]
+						    = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]);
+					}
+				}
+				break;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	status = (0);
+
+end:
+	return status;
+}
+
+void
+sixel_parser_deinit(sixel_state_t *st)
+{
+	if (st)
+		sixel_image_deinit(&st->image);
+}
--- /dev/null
+++ src/sixel.h
@@ -0,0 +1,61 @@
+#ifndef SIXEL_H
+#define SIXEL_H
+
+#include "config.h"
+
+#define DECSIXEL_PARAMS_MAX 16
+#define DECSIXEL_PALETTE_MAX 65535
+#define DECSIXEL_PARAMVALUE_MAX 65535
+#define DECSIXEL_WIDTH_MAX 4096
+#define DECSIXEL_HEIGHT_MAX 4096
+
+typedef unsigned short sixel_color_no_t;
+typedef unsigned int colour;
+
+typedef struct sixel_image_buffer {
+	sixel_color_no_t *data;
+	int width;
+	int height;
+	colour palette[DECSIXEL_PALETTE_MAX];
+	sixel_color_no_t ncolors;
+	int palette_modified;
+	int use_private_register;
+} sixel_image_t;
+
+typedef enum parse_state {
+	PS_GROUND     = 0,
+	PS_ESC        = 1,  /* ESC */
+	PS_DECSIXEL   = 2,  /* DECSIXEL body part ", $, -, ? ... ~ */
+	PS_DECGRA     = 3,  /* DECGRA Set Raster Attributes " Pan; Pad; Ph; Pv */
+	PS_DECGRI     = 4,  /* DECGRI Graphics Repeat Introducer ! Pn Ch */
+	PS_DECGCI     = 5,  /* DECGCI Graphics Color Introducer # Pc; Pu; Px; Py; Pz */
+} parse_state_t;
+
+typedef struct parser_context {
+	parse_state_t state;
+	int pos_x;
+	int pos_y;
+	int max_x;
+	int max_y;
+	int attributed_pan;
+	int attributed_pad;
+	int attributed_ph;
+	int attributed_pv;
+	int repeat_count;
+	int color_index;
+	int bgindex;
+	int grid_width;
+	int grid_height;
+	int param;
+	int nparams;
+	int params[DECSIXEL_PARAMS_MAX];
+	sixel_image_t image;
+} sixel_state_t;
+
+int sixel_parser_init(sixel_state_t *st, colour fgcolor, colour bgcolor, unsigned char use_private_register, int cell_width, int cell_height);
+int sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len);
+int sixel_parser_set_default_color(sixel_state_t *st);
+int sixel_parser_finalize(sixel_state_t *st, unsigned char *pixels);
+void sixel_parser_deinit(sixel_state_t *st);
+
+#endif
