Last active
May 2, 2019 10:17
-
-
Save saitoha/be56f3b58c5212abe3f76a13578a548d to your computer and use it in GitHub Desktop.
Add SIXEL graphics support for rxvt-unicode.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/src/Makefile.in b/src/Makefile.in | |
index 18acb39e..ef2e7a40 100644 | |
--- a/src/Makefile.in | |
+++ b/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 | |
diff --git a/src/command.C b/src/command.C | |
index ed376ed1..e9a9072c 100644 | |
--- a/src/command.C | |
+++ b/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) | |
{ | |
diff --git a/src/command.h b/src/command.h | |
index cf356c4d..319eaba0 100644 | |
--- a/src/command.h | |
+++ b/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 | |
diff --git a/src/rxvt.h b/src/rxvt.h | |
index 5e5ee2df..267ba923 100644 | |
--- a/src/rxvt.h | |
+++ b/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) | |
@@ -576,6 +577,15 @@ enum { | |
#define PrivMode_ExtMouseRight (1UL<<24) // xterm pseudo-utf-8, but works in non-utf-8-locales | |
#define PrivMode_BlinkingCursor (1UL<<25) | |
#define PrivMode_FocusEvent (1UL<<26) | |
+#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); | |
diff --git a/src/screen.C b/src/screen.C | |
index f3c6d576..160fc28d 100644 | |
--- a/src/screen.C | |
+++ b/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 | |
*/ | |
diff --git a/src/sixel.C b/src/sixel.C | |
new file mode 100644 | |
index 00000000..3e21c54a | |
--- /dev/null | |
+++ b/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); | |
+} | |
diff --git a/src/sixel.h b/src/sixel.h | |
new file mode 100644 | |
index 00000000..1877b2bb | |
--- /dev/null | |
+++ b/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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
In urxvt with 32-bit color depth displays foreground-color rectangles instead of images