diff --git a/config.def.h b/config.def.h index f79e508..e0b7737 100644 --- a/config.def.h +++ b/config.def.h @@ -54,7 +54,10 @@ char *stty_args = "stty raw pass8 nl -echo -iexten -cstopb 38400"; /* identification sequence returned in DA and DECID */ #if SIXEL_PATCH -char *vtiden = "\033[?12;4c"; +char *vtiden = "\033[?62;4c"; /* VT200 family (62) with sixel (4) */ + +/* sixel rgb byte order: LSBFirst or MSBFirst */ +int const sixelbyteorder = LSBFirst; #else char *vtiden = "\033[?6c"; #endif diff --git a/config.mk b/config.mk index 36e98ce..fb5ddf3 100644 --- a/config.mk +++ b/config.mk @@ -13,10 +13,10 @@ X11LIB = /usr/X11R6/lib PKG_CONFIG = pkg-config # Uncomment this for the alpha patch / ALPHA_PATCH -#XRENDER = -lXrender +#XRENDER = `$(PKG_CONFIG) --libs xrender` # Uncomment this for the themed cursor patch / THEMED_CURSOR_PATCH -#XCURSOR = -lXcursor +#XCURSOR = `$(PKG_CONFIG) --libs xcursor` # Uncomment the lines below for the ligatures patch / LIGATURES_PATCH #LIGATURES_C = hb.c @@ -26,13 +26,14 @@ PKG_CONFIG = pkg-config # Uncomment this for the SIXEL patch / SIXEL_PATCH #SIXEL_C = sixel.c sixel_hls.c +#SIXEL_LIBS = `$(PKG_CONFIG) --libs imlib2` # includes and libs, uncomment harfbuzz for the ligatures patch INCS = -I$(X11INC) \ `$(PKG_CONFIG) --cflags fontconfig` \ `$(PKG_CONFIG) --cflags freetype2` \ $(LIGATURES_INC) -LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${XRENDER} ${XCURSOR}\ +LIBS = -L$(X11LIB) -lm -lrt -lX11 -lutil -lXft ${SIXEL_LIBS} ${XRENDER} ${XCURSOR}\ `$(PKG_CONFIG) --libs fontconfig` \ `$(PKG_CONFIG) --libs freetype2` \ $(LIGATURES_LIBS) diff --git a/patch/sixel_st.c b/patch/sixel_st.c deleted file mode 100644 index 13e1062..0000000 --- a/patch/sixel_st.c +++ /dev/null @@ -1,57 +0,0 @@ -sixel_state_t sixel_st; - -void -dcshandle(void) -{ - int bgcolor; - unsigned char r, g, b, a = 255; - - switch (csiescseq.mode[0]) { - default: - fprintf(stderr, "erresc: unknown csi "); - csidump(); - /* die(""); */ - break; - case 'q': /* DECSIXEL */ - if (IS_TRUECOL(term.c.attr.bg)) { - r = term.c.attr.bg >> 16 & 255; - g = term.c.attr.bg >> 8 & 255; - b = term.c.attr.bg >> 0 & 255; - } else { - xgetcolor(term.c.attr.bg, &r, &g, &b); - #if ALPHA_PATCH - if (term.c.attr.bg == defaultbg) - a = dc.col[defaultbg].pixel >> 24 & 255; - #endif // ALPHA_PATCH - } - bgcolor = a << 24 | b << 16 | g << 8 | r; - if (sixel_parser_init(&sixel_st, 255 << 24, bgcolor, 1, win.cw, win.ch) != 0) - perror("sixel_parser_init() failed"); - term.mode |= MODE_SIXEL; - break; - } -} - -void -scroll_images(int n) { - ImageList *im; - int tmp; - - /* maximum sixel distance in lines from current view before - * deallocation - * TODO: should be in config.h */ - int max_sixel_distance = 10000; - - for (im = term.images; im; im = im->next) { - im->y += n; - - /* check if the current sixel has exceeded the maximum - * draw distance, and should therefore be deleted */ - tmp = im->y; - if (tmp < 0) { tmp = tmp * -1; } - if (tmp > max_sixel_distance) { - fprintf(stderr, "im@0x%08x exceeded maximum distance\n"); - im->should_delete = 1; - } - } -} diff --git a/patch/sixel_st.h b/patch/sixel_st.h deleted file mode 100644 index cf2f0e1..0000000 --- a/patch/sixel_st.h +++ /dev/null @@ -1,2 +0,0 @@ -static void dcshandle(void); -static void scroll_images(int n); \ No newline at end of file diff --git a/patch/sixel_x.c b/patch/sixel_x.c deleted file mode 100644 index 0f74f53..0000000 --- a/patch/sixel_x.c +++ /dev/null @@ -1,14 +0,0 @@ -void -delete_image(ImageList *im) -{ - if (im->prev) - im->prev->next = im->next; - else - term.images = im->next; - if (im->next) - im->next->prev = im->prev; - if (im->pixmap) - XFreePixmap(xw.dpy, (Drawable)im->pixmap); - free(im->pixels); - free(im); -} \ No newline at end of file diff --git a/patch/st_include.c b/patch/st_include.c index 6772a6e..726aad0 100644 --- a/patch/st_include.c +++ b/patch/st_include.c @@ -20,9 +20,6 @@ #if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH #include "scrollback.c" #endif -#if SIXEL_PATCH -#include "sixel_st.c" -#endif #if SYNC_PATCH #include "sync.c" #endif diff --git a/patch/st_include.h b/patch/st_include.h index c30a752..5f339c0 100644 --- a/patch/st_include.h +++ b/patch/st_include.h @@ -23,9 +23,6 @@ #if SCROLLBACK_PATCH || SCROLLBACK_MOUSE_PATCH || SCROLLBACK_MOUSE_ALTSCREEN_PATCH #include "scrollback.h" #endif -#if SIXEL_PATCH -#include "sixel_st.h" -#endif #if SYNC_PATCH #include "sync.h" #endif diff --git a/patch/x_include.c b/patch/x_include.c index cbea418..0395eb5 100644 --- a/patch/x_include.c +++ b/patch/x_include.c @@ -32,9 +32,6 @@ #if RIGHTCLICKTOPLUMB_PATCH #include "rightclicktoplumb_x.c" #endif -#if SIXEL_PATCH -#include "sixel_x.c" -#endif #if ST_EMBEDDER_PATCH #include "st_embedder_x.c" #endif diff --git a/sixel.c b/sixel.c index d11fbf8..b773883 100644 --- a/sixel.c +++ b/sixel.c @@ -5,10 +5,12 @@ #include #include /* memcpy */ +#include "st.h" +#include "win.h" #include "sixel.h" #include "sixel_hls.h" -#define SIXEL_RGB(r, g, b) ((r) + ((g) << 8) + ((b) << 16) + (255 << 24)) +#define SIXEL_RGB(r, g, b) ((255 << 24) + ((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)) @@ -31,6 +33,43 @@ static sixel_color_t const sixel_default_color_table[] = { SIXEL_XRGB(80, 80, 80), /* 15 Gray 75% */ }; +void +scroll_images(int n) { + ImageList *im, *next; + #if SCROLLBACK_PATCH + int top = tisaltscr() ? 0 : term.scr - HISTSIZE; + #else + int top = 0; + #endif // SCROLLBACK_PATCH + + for (im = term.images; im; im = next) { + next = im->next; + im->y += n; + + /* check if the current sixel has exceeded the maximum + * draw distance, and should therefore be deleted */ + if (im->y < top) { + //fprintf(stderr, "im@0x%08x exceeded maximum distance\n"); + delete_image(im); + } + } +} + +void +delete_image(ImageList *im) +{ + if (im->prev) + im->prev->next = im->next; + else + term.images = im->next; + if (im->next) + im->next->prev = im->prev; + if (im->pixmap) + XFreePixmap(xw.dpy, (Drawable)im->pixmap); + free(im->pixels); + free(im); +} + static int set_default_color(sixel_image_t *image) { @@ -171,7 +210,8 @@ end: static void sixel_image_deinit(sixel_image_t *image) { - free(image->data); + if (image->data) + free(image->data); image->data = NULL; } @@ -212,15 +252,19 @@ sixel_parser_set_default_color(sixel_state_t *st) } int -sixel_parser_finalize(sixel_state_t *st, unsigned char **pixels) +sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy, int cw, int ch) { - int status = (-1); sixel_image_t *image = &st->image; int x, y; sixel_color_no_t *src; - unsigned char *dst; + sixel_color_t *dst; int color; int w, h; + int i, j, cols, numimages; + ImageList *im, *next, *tail; + + if (!image->data) + return -1; if (++st->max_x < st->attributed_ph) st->max_x = st->attributed_ph; @@ -229,62 +273,83 @@ sixel_parser_finalize(sixel_state_t *st, unsigned char **pixels) st->max_y = st->attributed_pv; if (image->use_private_register && image->ncolors > 2 && !image->palette_modified) { - status = set_default_color(image); - if (status < 0) - goto end; + if (set_default_color(image) < 0) + return -1; } - w = st->max_x < image->width ? st->max_x : image->width; - h = st->max_y < image->height ? st->max_y : image->height; + w = MIN(st->max_x, image->width); + h = MIN(st->max_y, image->height); - *pixels = malloc(w * h * 4); - if (*pixels == NULL) - goto end; + if ((numimages = (h + ch-1) / ch) <= 0) + return -1; - src = st->image.data; - dst = *pixels; - for (y = 0; y < h; y++) { - src = st->image.data + image->width * y; - for (x = 0; x < w; ++x) { - color = st->image.palette[*src++]; - *dst++ = color >> 16 & 0xff; /* b */ - *dst++ = color >> 8 & 0xff; /* g */ - *dst++ = color >> 0 & 0xff; /* r */ - *dst++ = color >> 24 & 0xff; /* a */ + cols = (w + cw-1) / cw; + + *newimages = NULL, tail = NULL; + for (y = 0, i = 0; i < numimages; i++) { + if ((im = malloc(sizeof(ImageList)))) { + if (!tail) { + *newimages = tail = im; + im->prev = im->next = NULL; + } else { + tail->next = im; + im->prev = tail; + im->next = NULL; + tail = im; + } + im->x = cx; + im->y = cy + i; + im->cols = cols; + im->width = w; + im->height = MIN(h - ch * i, ch); + im->pixels = malloc(im->width * im->height * 4); + im->pixmap = NULL; + im->cw = cw; + im->ch = ch; + } + if (!im || !im->pixels) { + for (im = *newimages; im; im = next) { + next = im->next; + if (im->pixels) + free(im->pixels); + free(im); + } + *newimages = NULL; + return -1; + } + dst = (sixel_color_t *)im->pixels; + for (j = 0; j < im->height && y < h; j++, y++) { + src = st->image.data + image->width * y; + for (x = 0; x < w; x++) + *dst++ = st->image.palette[*src++]; } } - image->width = w; - image->height = h; - - status = (0); - -end: - return status; + return numimages; } /* convert sixel data into indexed pixel bytes and palette data */ int -sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) +sixel_parser_parse(sixel_state_t *st, const unsigned char *p, size_t len) { - int status = (-1); - int n; + int n = 0; int i; int x; int y; int bits; - int sixel_vertical_mask; int sx; int sy; int c; int pos; - unsigned char *p0 = p; + int width; + const unsigned char *p0 = p, *p2 = p + len; sixel_image_t *image = &st->image; + sixel_color_no_t *data, color_index; - if (! image->data) - goto end; + if (!image->data) + st->state = PS_ERROR; - while (p < p0 + len) { + while (p < p2) { switch (st->state) { case PS_ESC: goto end; @@ -293,7 +358,6 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) switch (*p) { case '\x1b': st->state = PS_ESC; - p++; break; case '"': st->param = 0; @@ -338,14 +402,15 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) sy *= 2; } - if (sx > DECSIXEL_WIDTH_MAX) - sx = DECSIXEL_WIDTH_MAX; - if (sy > DECSIXEL_HEIGHT_MAX) - sy = DECSIXEL_HEIGHT_MAX; + sx = MIN(sx, DECSIXEL_WIDTH_MAX); + sy = MIN(sy, DECSIXEL_HEIGHT_MAX); - status = image_buffer_resize(image, sx, sy); - if (status < 0) - goto end; + if (image_buffer_resize(image, sx, sy) < 0) { + perror("sixel_parser_parse() failed"); + st->state = PS_ERROR; + p++; + break; + } } if (st->color_index > image->ncolors) @@ -357,43 +422,44 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) if (st->repeat_count > 0 && st->pos_y + 5 < image->height) { bits = *p - '?'; if (bits != 0) { - sixel_vertical_mask = 0x01; + data = image->data + image->width * st->pos_y + st->pos_x; + width = image->width; + color_index = st->color_index; 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; - } + if (bits & 0x01) + *data = color_index, n = 0; + data += width; + if (bits & 0x02) + *data = color_index, n = 1; + data += width; + if (bits & 0x04) + *data = color_index, n = 2; + data += width; + if (bits & 0x08) + *data = color_index, n = 3; + data += width; + if (bits & 0x10) + *data = color_index, n = 4; + if (bits & 0x20) + data[width] = color_index, n = 5; + if (st->max_x < st->pos_x) + st->max_x = st->pos_x; } 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); + for (i = 0; bits; bits >>= 1, i++, data += width) { + if (bits & 1) { + data[0] = color_index; + data[1] = color_index; + for (x = 2; x < st->repeat_count; x++) + data[x] = color_index; + n = i; } - sixel_vertical_mask <<= 1; } + 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 + n)) + st->max_y = st->pos_y + n; } } if (st->repeat_count > 0) @@ -410,7 +476,6 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) switch (*p) { case '\x1b': st->state = PS_ESC; - p++; break; case '0': case '1': @@ -423,8 +488,7 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) case '8': case '9': st->param = st->param * 10 + *p - '0'; - if (st->param > DECSIXEL_PARAMVALUE_MAX) - st->param = DECSIXEL_PARAMVALUE_MAX; + st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX); p++; break; case ';': @@ -452,27 +516,22 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) 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 = MAX(image->width, st->attributed_ph); + sy = MAX(image->height, st->attributed_pv); /* the height of the image buffer must be divisible by 6 - * to avoid unnecessary resizing of the image buffer in - * sixel_parser_parse() */ + * to avoid unnecessary resizing of the image buffer when + * parsing the last sixel line */ sy = (sy + 5) / 6 * 6; - if (sx > DECSIXEL_WIDTH_MAX) - sx = DECSIXEL_WIDTH_MAX; - if (sy > DECSIXEL_HEIGHT_MAX) - sy = DECSIXEL_HEIGHT_MAX; + sx = MIN(sx, DECSIXEL_WIDTH_MAX); + sy = MIN(sy, DECSIXEL_HEIGHT_MAX); - status = image_buffer_resize(image, sx, sy); - if (status < 0) - goto end; + if (image_buffer_resize(image, sx, sy) < 0) { + perror("sixel_parser_parse() failed"); + st->state = PS_ERROR; + break; + } } st->state = PS_DECSIXEL; st->param = 0; @@ -485,7 +544,6 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) switch (*p) { case '\x1b': st->state = PS_ESC; - p++; break; case '0': case '1': @@ -498,14 +556,11 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) case '8': case '9': st->param = st->param * 10 + *p - '0'; - if (st->param > DECSIXEL_PARAMVALUE_MAX) - st->param = DECSIXEL_PARAMVALUE_MAX; + st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX); p++; break; default: - st->repeat_count = st->param; - if (st->repeat_count == 0) - st->repeat_count = 1; + st->repeat_count = MAX(st->param, 1); st->state = PS_DECSIXEL; st->param = 0; st->nparams = 0; @@ -518,7 +573,6 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) switch (*p) { case '\x1b': st->state = PS_ESC; - p++; break; case '0': case '1': @@ -531,8 +585,7 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) case '8': case '9': st->param = st->param * 10 + *p - '0'; - if (st->param > DECSIXEL_PARAMVALUE_MAX) - st->param = DECSIXEL_PARAMVALUE_MAX; + st->param = MIN(st->param, DECSIXEL_PARAMVALUE_MAX); p++; break; case ';': @@ -559,22 +612,16 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) 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; + st->params[2] = MIN(st->params[2], 360); + st->params[3] = MIN(st->params[3], 100); + st->params[4] = MIN(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; + st->params[2] = MIN(st->params[2], 100); + st->params[3] = MIN(st->params[3], 100); + st->params[4] = MIN(st->params[4], 100); image->palette[st->color_index] = SIXEL_XRGB(st->params[2], st->params[3], st->params[4]); } @@ -582,15 +629,21 @@ sixel_parser_parse(sixel_state_t *st, unsigned char *p, size_t len) break; } break; + + case PS_ERROR: + if (*p == '\x1b') { + st->state = PS_ESC; + goto end; + } + p++; + break; default: break; } } - status = (0); - end: - return status; + return p - p0; } void diff --git a/sixel.h b/sixel.h index a7d403d..7b2266e 100644 --- a/sixel.h +++ b/sixel.h @@ -26,6 +26,7 @@ typedef enum parse_state { 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 */ + PS_ERROR = 6, } parse_state_t; typedef struct parser_context { @@ -49,10 +50,12 @@ typedef struct parser_context { sixel_image_t image; } sixel_state_t; +void scroll_images(int n); +void delete_image(ImageList *im); int sixel_parser_init(sixel_state_t *st, sixel_color_t fgcolor, sixel_color_t 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_parse(sixel_state_t *st, const 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); +int sixel_parser_finalize(sixel_state_t *st, ImageList **newimages, int cx, int cy, int cw, int ch); void sixel_parser_deinit(sixel_state_t *st); #endif diff --git a/st.c b/st.c index e111ac4..0ef00db 100644 --- a/st.c +++ b/st.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -64,15 +65,17 @@ static inline int min(int a, int b) { return a < b ? a : b; } #endif // VIM_BROWSE_PATCH enum term_mode { - MODE_WRAP = 1 << 0, - MODE_INSERT = 1 << 1, - MODE_ALTSCREEN = 1 << 2, - MODE_CRLF = 1 << 3, - MODE_ECHO = 1 << 4, - MODE_PRINT = 1 << 5, - MODE_UTF8 = 1 << 6, + MODE_WRAP = 1 << 0, + MODE_INSERT = 1 << 1, + MODE_ALTSCREEN = 1 << 2, + MODE_CRLF = 1 << 3, + MODE_ECHO = 1 << 4, + MODE_PRINT = 1 << 5, + MODE_UTF8 = 1 << 6, #if SIXEL_PATCH - MODE_SIXEL = 1 << 7, + MODE_SIXEL = 1 << 7, + MODE_SIXEL_CUR_RT = 1 << 8, + MODE_SIXEL_SDM = 1 << 9 #endif // SIXEL_PATCH }; @@ -163,6 +166,9 @@ static void ttywriteraw(const char *, size_t); static void csidump(void); static void csihandle(void); +#if SIXEL_PATCH +static void dcshandle(void); +#endif // SIXEL_PATCH #if UNDERCURL_PATCH static void readcolonargs(char **, int, int[][CAR_PER_ARG]); #endif // UNDERCURL_PATCH @@ -183,6 +189,9 @@ static void tdump(void); static void tclearregion(int, int, int, int); static void tcursor(int); static void tdeletechar(int); +#if SIXEL_PATCH +static void tdeleteimages(void); +#endif // SIXEL_PATCH static void tdeleteline(int); static void tinsertblank(int); static void tinsertblankline(int); @@ -1358,12 +1367,11 @@ treset(void) #else tclearregion(0, 0, term.col-1, term.row-1); #endif // COLUMNS_PATCH + #if SIXEL_PATCH + tdeleteimages(); + #endif // SIXEL_PATCH tswapscreen(); } - #if SIXEL_PATCH - for (im = term.images; im; im = im->next) - im->should_delete = 1; - #endif // SIXEL_PATCH } void @@ -1405,6 +1413,16 @@ tscrolldown(int orig, int n) #endif // VIM_BROWSE_PATCH int i; Line temp; + #if SIXEL_PATCH + int bot = term.bot; + #if SCROLLBACK_PATCH + int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; + #else + int scr = 0; + #endif // SCROLLBACK_PATCH + int itop = orig + scr, ibot = bot + scr; + ImageList *im, *next; + #endif // SIXEL_PATCH LIMIT(n, 0, term.bot-orig+1); @@ -1423,11 +1441,12 @@ tscrolldown(int orig, int n) #if SIXEL_PATCH /* move images, if they are inside the scrolling region */ - ImageList *im; - for (im = term.images; im; im = im->next) { - if (im->y * win.ch + im->height > orig * win.ch && im->y <= term.bot) { + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= itop && im->y <= ibot) { im->y += n; - im->should_delete |= (im->y >= term.row); + if (im->y > ibot) + delete_image(im); } } #endif // SIXEL_PATCH @@ -1457,6 +1476,16 @@ tscrollup(int orig, int n) #endif // VIM_BROWSE_PATCH int i; Line temp; + #if SIXEL_PATCH + int bot = term.bot; + #if SCROLLBACK_PATCH + int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; + #else + int scr = 0; + #endif // SCROLLBACK_PATCH + int itop = orig + scr, ibot = bot + scr; + ImageList *im, *next; + #endif // SIXEL_PATCH LIMIT(n, 0, term.bot-orig+1); @@ -1490,11 +1519,45 @@ tscrollup(int orig, int n) #if SIXEL_PATCH #if SCROLLBACK_PATCH - if (term.scr == 0) - scroll_images(-1 * n); + if (IS_SET(MODE_ALTSCREEN) || !copyhist || orig != 0) { + /* move images, if they are inside the scrolling region */ + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= itop && im->y <= ibot) { + im->y -= n; + if (im->y < itop) + delete_image(im); + } + } + } else { + /* move images, if they are inside the scrolling region or scrollback */ + for (im = term.images; im; im = next) { + next = im->next; + im->y -= scr; + if (im->y < 0) { + im->y -= n; + } else if (im->y >= orig && im->y <= bot) { + im->y -= n; + if (im->y < orig) + im->y -= orig; // move to scrollback + } + if (im->y < -HISTSIZE) + delete_image(im); + else + im->y += term.scr; + } + } #else - scroll_images(-1 * n); - #endif + /* move images, if they are inside the scrolling region */ + for (im = term.images; im; im = next) { + next = im->next; + if (im->y >= itop && im->y <= ibot) { + im->y -= n; + if (im->y < itop) + delete_image(im); + } + } + #endif // SCROLLBACK_PATCH #endif // SIXEL_PATCH #if SCROLLBACK_PATCH @@ -1743,6 +1806,19 @@ tinsertblankline(int n) tscrolldown(term.c.y, n); } +#if SIXEL_PATCH +void +tdeleteimages(void) +{ + ImageList *im, *next; + + for (im = term.images; im; im = next) { + next = im->next; + delete_image(im); + } +} +#endif // SIXEL_PATCH + void tdeleteline(int n) { @@ -2079,6 +2155,14 @@ tsetmode(int priv, int set, const int *args, int narg) and can be mistaken for other control codes. */ break; + #if SIXEL_PATCH + case 80: /* DECSDM -- Sixel Display Mode */ + MODBIT(term.mode, set, MODE_SIXEL_SDM); + break; + case 8452: /* sixel scrolling leaves cursor to right of graphic */ + MODBIT(term.mode, set, MODE_SIXEL_CUR_RT); + break; + #endif // SIXEL_PATCH default: fprintf(stderr, "erresc: unknown private set/reset mode %d\n", @@ -2117,7 +2201,8 @@ csihandle(void) char buffer[40]; int len; #if SIXEL_PATCH - ImageList *im; + ImageList *im, *next; + int n, pi, pa; #endif // SIXEL_PATCH #if COLUMNS_PATCH && !VIM_BROWSE_PATCH int maxcol = term.maxcol; @@ -2259,10 +2344,8 @@ csihandle(void) #endif // SCROLLBACK_PATCH tclearregion(0, 0, maxcol-1, term.row-1); - #if SIXEL_PATCH - for (im = term.images; im; im = im->next) - im->should_delete = 1; + tdeleteimages(); #endif // SIXEL_PATCH break; case 3: /* scrollback */ @@ -2291,16 +2374,17 @@ csihandle(void) } #endif // SCROLLBACK_PATCH #if SIXEL_PATCH - if (!IS_SET(MODE_ALTSCREEN)) { - for (im = term.images; im; im = im->next) - im->should_delete |= (im->y * win.ch + im->height <= 0); + for (im = term.images; im; im = next) { + next = im->next; + if (im->y < 0) + delete_image(im); } #endif // SIXEL_PATCH break; #if SIXEL_PATCH case 6: /* sixels */ - for (im = term.images; im; im = im->next) - im->should_delete = 1; + tdeleteimages(); + tfulldirt(); break; #endif // SIXEL_PATCH default: @@ -2321,9 +2405,35 @@ csihandle(void) break; } break; - case 'S': /* SU -- Scroll line up */ - if (csiescseq.priv) - break; + case 'S': /* SU -- Scroll line up ; XTSMGRAPHICS */ + if (csiescseq.priv) { + #if SIXEL_PATCH + if (csiescseq.narg > 1) { + /* XTSMGRAPHICS */ + pi = csiescseq.arg[0]; + pa = csiescseq.arg[1]; + if (pi == 1 && (pa == 1 || pa == 2 || pa == 4)) { + /* number of sixel color registers */ + /* (read, reset and read the maximum value give the same response) */ + n = snprintf(buffer, sizeof buffer, "\033[?1;0;%dS", DECSIXEL_PALETTE_MAX); + ttywrite(buffer, n, 1); + break; + } else if (pi == 2 && (pa == 1 || pa == 2 || pa == 4)) { + /* sixel graphics geometry (in pixels) */ + /* (read, reset and read the maximum value give the same response) */ + n = snprintf(buffer, sizeof buffer, "\033[?2;0;%d;%dS", + MIN(term.col * win.cw, DECSIXEL_WIDTH_MAX), + MIN(term.row * win.ch, DECSIXEL_HEIGHT_MAX)); + ttywrite(buffer, n, 1); + break; + } + /* the number of color registers and sixel geometry can't be changed */ + n = snprintf(buffer, sizeof buffer, "\033[?%d;3;0S", pi); /* failure */ + ttywrite(buffer, n, 1); + } + #endif // SIXEL_PATCH + goto unknown; + } DEFAULT(csiescseq.arg[0], 1); #if SIXEL_PATCH && SCROLLBACK_PATCH tscrollup(term.top, csiescseq.arg[0], 1); @@ -2343,12 +2453,6 @@ csihandle(void) break; case 'l': /* RM -- Reset Mode */ tsetmode(csiescseq.priv, 0, csiescseq.arg, csiescseq.narg); - #if SIXEL_PATCH - if (IS_SET(MODE_ALTSCREEN)) { - for (im = term.images; im; im = im->next) - im->should_delete = 1; - } - #endif // SIXEL_PATCH break; case 'M': /* DL -- Delete lines */ DEFAULT(csiescseq.arg[0], 1); @@ -2404,9 +2508,27 @@ csihandle(void) case 's': /* DECSC -- Save cursor position (ANSI.SYS) */ tcursor(CURSOR_SAVE); break; - #if CSI_22_23_PATCH - case 't': /* title stack operations */ + #if CSI_22_23_PATCH | SIXEL_PATCH + case 't': /* title stack operations ; XTWINOPS */ switch (csiescseq.arg[0]) { + #if SIXEL_PATCH + case 14: /* text area size in pixels */ + if (csiescseq.narg > 1) + goto unknown; + n = snprintf(buffer, sizeof buffer, "\033[4;%d;%dt", + term.row * win.ch, term.col * win.cw); + ttywrite(buffer, n, 1); + break; + case 16: /* character cell size in pixels */ + n = snprintf(buffer, sizeof buffer, "\033[6;%d;%dt", win.ch, win.cw); + ttywrite(buffer, n, 1); + break; + case 18: /* size of the text area in characters */ + n = snprintf(buffer, sizeof buffer, "\033[8;%d;%dt", term.row, term.col); + ttywrite(buffer, n, 1); + break; + #endif // SIXEL_PATCH + #if CSI_22_23_PATCH case 22: /* pust current title on stack */ switch (csiescseq.arg[1]) { case 0: @@ -2429,11 +2551,12 @@ csihandle(void) goto unknown; } break; + #endif // CSI_22_23_PATCH default: goto unknown; } break; - #endif // CSI_22_23_PATCH + #endif // CSI_22_23_PATCH | SIXEL_PATCH case 'u': /* DECRC -- Restore cursor position (ANSI.SYS) */ tcursor(CURSOR_LOAD); break; @@ -2522,8 +2645,15 @@ strhandle(void) char *p = NULL, *dec; int j, narg, par; #if SIXEL_PATCH - ImageList *new_image; - int i; + ImageList *im, *newimages, *next, *tail; + int i, x, y, x1, y1, x2, y2, numimages; + int cx, cy; + Line line; + #if SCROLLBACK_PATCH + int scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; + #else + int scr = 0; + #endif // SCROLLBACK_PATCH #endif // SIXEL_PATCH term.esc &= ~(ESC_STR_END|ESC_STR); @@ -2650,35 +2780,69 @@ strhandle(void) #if SIXEL_PATCH if (IS_SET(MODE_SIXEL)) { term.mode &= ~MODE_SIXEL; - new_image = malloc(sizeof(ImageList)); - memset(new_image, 0, sizeof(ImageList)); - if (sixel_parser_finalize(&sixel_st, &new_image->pixels) != 0) { - perror("sixel_parser_finalize() failed"); + if (!sixel_st.image.data) { sixel_parser_deinit(&sixel_st); - free(new_image); return; } - new_image->x = term.c.x; - new_image->y = term.c.y; - new_image->width = sixel_st.image.width; - new_image->height = sixel_st.image.height; + cx = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.x; + cy = IS_SET(MODE_SIXEL_SDM) ? 0 : term.c.y; + if ((numimages = sixel_parser_finalize(&sixel_st, &newimages, + cx, cy + scr, win.cw, win.ch)) <= 0) { + sixel_parser_deinit(&sixel_st); + perror("sixel_parser_finalize() failed"); + return; + } sixel_parser_deinit(&sixel_st); - if (term.images) { - ImageList *im; - for (im = term.images; im->next;) - im = im->next; - im->next = new_image; - new_image->prev = im; + x1 = newimages->x; + y1 = newimages->y; + x2 = x1 + newimages->cols; + y2 = y1 + numimages; + for (tail = NULL, im = term.images; im; im = next) { + next = im->next; + if (im->x >= x1 && im->x + im->cols <= x2 && + im->y >= y1 && im->y <= y2) { + delete_image(im); + continue; + } + tail = im; + } + if (tail) { + tail->next = newimages; + newimages->prev = tail; } else { - term.images = new_image; + term.images = newimages; } - for (i = 0; i < (sixel_st.image.height + win.ch-1)/win.ch; ++i) { - int x; - tclearregion(term.c.x, term.c.y, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw-1, term.c.y); - for (x = term.c.x; x < MIN(term.col, term.c.x+(sixel_st.image.width+win.cw-1)/win.cw); x++) - term.line[term.c.y][x].mode |= ATTR_SIXEL; - tnewline(0); + x2 = MIN(x2, term.col); + for (i = 0, im = newimages; im; im = next, i++) { + next = im->next; + #if SCROLLBACK_PATCH + scr = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr; + #endif // SCROLLBACK_PATCH + if (IS_SET(MODE_SIXEL_SDM)) { + if (i >= term.row) { + delete_image(im); + continue; + } + im->y = i + scr; + line = term.line[i]; + } else { + im->y = term.c.y + scr; + line = term.line[term.c.y]; + } + for (x = im->x; x < x2; x++) { + line[x].u = ' '; + line[x].mode = ATTR_SIXEL; + } + term.dirty[MIN(im->y, term.row-1)] = 1; + if (!IS_SET(MODE_SIXEL_SDM) && i < numimages-1) { + im->next = NULL; + tnewline(0); + im->next = next; + } } + /* if mode 8452 is set, sixel scrolling leaves cursor to right of graphic */ + if (!IS_SET(MODE_SIXEL_SDM) && IS_SET(MODE_SIXEL_CUR_RT)) + term.c.x = MIN(term.c.x + newimages->cols, term.col-1); } #endif // SIXEL_PATCH #if SYNC_PATCH @@ -3009,6 +3173,50 @@ tcontrolcode(uchar ascii) term.esc &= ~(ESC_STR_END|ESC_STR); } +#if SIXEL_PATCH +void +dcshandle(void) +{ + int bgcolor; + unsigned char r, g, b, a = 255; + + switch (csiescseq.mode[0]) { + default: + unknown: + fprintf(stderr, "erresc: unknown csi "); + csidump(); + /* die(""); */ + break; + #if SYNC_PATCH + case '=': + /* https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec */ + if (csiescseq.buf[2] == 's' && csiescseq.buf[1] == '1') + tsync_begin(); /* BSU */ + else if (csiescseq.buf[2] == 's' && csiescseq.buf[1] == '2') + tsync_end(); /* ESU */ + else + goto unknown; + break; + #endif // SYNC_PATCH + case 'q': /* DECSIXEL */ + if (IS_TRUECOL(term.c.attr.bg)) { + r = term.c.attr.bg >> 16 & 255; + g = term.c.attr.bg >> 8 & 255; + b = term.c.attr.bg >> 0 & 255; + } else { + xgetcolor(term.c.attr.bg, &r, &g, &b); + if (term.c.attr.bg == defaultbg) + a = dc.col[defaultbg].pixel >> 24 & 255; + } + bgcolor = a << 24 | r << 16 | g << 8 | b; + if (sixel_parser_init(&sixel_st, (255 << 24), bgcolor, 1, win.cw, win.ch) != 0) + perror("sixel_parser_init() failed"); + term.mode |= MODE_SIXEL; + break; + } +} +#endif // SIXEL_PATCH + /* * returns 1 when the sequence is finished and it hasn't to read * more characters for this sequence, otherwise 0 @@ -3123,11 +3331,7 @@ tputc(Rune u) Glyph *gp; control = ISCONTROL(u); - #if SIXEL_PATCH - if (u < 127 || !IS_SET(MODE_UTF8 | MODE_SIXEL)) - #else if (u < 127 || !IS_SET(MODE_UTF8)) - #endif // SIXEL_PATCH { c[0] = u; width = len = 1; @@ -3159,11 +3363,6 @@ tputc(Rune u) } #if SIXEL_PATCH - if (IS_SET(MODE_SIXEL)) { - if (sixel_parser_parse(&sixel_st, (unsigned char *)&u, 1) != 0) - perror("sixel_parser_parse() failed"); - return; - } if (term.esc & ESC_DCS) goto check_control_code; #endif // SIXEL_PATCH @@ -3310,7 +3509,10 @@ twrite(const char *buf, int buflen, int show_ctrl) for (n = 0; n < buflen; n += charsize) { #if SIXEL_PATCH - if (IS_SET(MODE_UTF8) && !IS_SET(MODE_SIXEL)) + if (IS_SET(MODE_SIXEL) && sixel_st.state != PS_ESC) { + charsize = sixel_parser_parse(&sixel_st, (const unsigned char*)buf + n, buflen - n); + continue; + } else if (IS_SET(MODE_UTF8)) #else if (IS_SET(MODE_UTF8)) #endif // SIXEL_PATCH @@ -3344,6 +3546,7 @@ twrite(const char *buf, int buflen, int show_ctrl) return n; } +#if VIM_BROWSE_PATCH void tresize(int col, int row) { @@ -3371,6 +3574,11 @@ tresize(int col, int row) #endif // VIM_BROWSE_PATCH int *bp; TCursor c; + #if SIXEL_PATCH + int x, x2; + Line line; + ImageList *im, *next; + #endif // SIXEL_PATCH #if KEYBOARDSELECT_PATCH if ( row < term.row || col < term.col ) @@ -3514,7 +3722,213 @@ tresize(int col, int row) tcursor(CURSOR_LOAD); } term.c = c; + + #if SIXEL_PATCH + /* expand images into new text cells to prevent them from being deleted in + * xfinishdraw() that draws the images */ + for (i = 0; i < 2; i++) { + for (im = term.images; im; im = next) { + next = im->next; + #if SCROLLBACK_PATCH + if (IS_SET(MODE_ALTSCREEN)) { + if (im->y < 0 || im->y >= term.row) { + delete_image(im); + continue; + } + line = term.line[im->y]; + } else { + if (im->y - term.scr < -HISTSIZE || im->y - term.scr >= term.row) { + delete_image(im); + continue; + } + line = TLINE(im->y); + } + #else + if (im->y < 0 || im->y >= term.row) { + delete_image(im); + continue; + } + line = term.line[im->y]; + #endif // SCROLLBACK_PATCH + x2 = MIN(im->x + im->cols, term.col); + for (x = im->x; x < x2; x++) { + line[x].u = ' '; + line[x].mode = ATTR_SIXEL; + } + } + tswapscreen(); + } + #endif // SIXEL_PATCH } +#else // !VIM_BROWSE_PATCH +void +tresize(int col, int row) +{ + int i, j; + #if COLUMNS_PATCH + int tmp = col; + int minrow, mincol; + + if (!term.maxcol) + term.maxcol = term.col; + col = MAX(col, term.maxcol); + minrow = MIN(row, term.row); + mincol = MIN(col, term.maxcol); + #else + int minrow = MIN(row, term.row); + int mincol = MIN(col, term.col); + #endif // COLUMNS_PATCH + int *bp; + #if SIXEL_PATCH + int x, x2; + Line line; + ImageList *im, *next; + #endif // SIXEL_PATCH + + #if KEYBOARDSELECT_PATCH + if ( row < term.row || col < term.col ) + toggle_winmode(trt_kbdselect(XK_Escape, NULL, 0)); + #endif // KEYBOARDSELECT_PATCH + + if (col < 1 || row < 1) { + fprintf(stderr, + "tresize: error resizing to %dx%d\n", col, row); + return; + } + + #if VIM_BROWSE_PATCH + if (alt) + tswapscreen(); + #endif // VIM_BROWSE_PATCH + + /* scroll both screens independently */ + if (row < term.row) { + tcursor(CURSOR_SAVE); + tsetscroll(0, term.row - 1); + for (i = 0; i < 2; i++) { + if (term.c.y >= row) { + #if SCROLLBACK_PATCH + tscrollup(0, term.c.y - row + 1, !IS_SET(MODE_ALTSCREEN)); + #else + tscrollup(0, term.c.y - row + 1); + #endif // SCROLLBACK_PATCH + } + for (j = row; j < term.row; j++) + free(term.line[j]); + tswapscreen(); + tcursor(CURSOR_LOAD); + } + } + + /* resize to new height */ + term.line = xrealloc(term.line, row * sizeof(Line)); + term.alt = xrealloc(term.alt, row * sizeof(Line)); + term.dirty = xrealloc(term.dirty, row * sizeof(*term.dirty)); + term.tabs = xrealloc(term.tabs, col * sizeof(*term.tabs)); + + #if SCROLLBACK_PATCH + Glyph gc=(Glyph){.bg=term.c.attr.bg, .fg=term.c.attr.fg, .u=' ', .mode=0}; + for (i = 0; i < HISTSIZE; i++) { + term.hist[i] = xrealloc(term.hist[i], col * sizeof(Glyph)); + for (j = mincol; j < col; j++) + term.hist[i][j] = gc; + } + #endif // SCROLLBACK_PATCH + + /* resize each row to new width, zero-pad if needed */ + for (i = 0; i < minrow; i++) { + term.line[i] = xrealloc(term.line[i], col * sizeof(Glyph)); + term.alt[i] = xrealloc(term.alt[i], col * sizeof(Glyph)); + } + + /* allocate any new rows */ + for (/* i = minrow */; i < row; i++) { + term.line[i] = xmalloc(col * sizeof(Glyph)); + term.alt[i] = xmalloc(col * sizeof(Glyph)); + } + #if COLUMNS_PATCH + if (col > term.maxcol) + #else + if (col > term.col) + #endif // COLUMNS_PATCH + { + #if COLUMNS_PATCH + bp = term.tabs + term.maxcol; + memset(bp, 0, sizeof(*term.tabs) * (col - term.maxcol)); + #else + bp = term.tabs + term.col; + memset(bp, 0, sizeof(*term.tabs) * (col - term.col)); + #endif // COLUMNS_PATCH + + while (--bp > term.tabs && !*bp) + /* nothing */ ; + for (bp += tabspaces; bp < term.tabs + col; bp += tabspaces) + *bp = 1; + } + + /* update terminal size */ + #if COLUMNS_PATCH + term.col = tmp; + term.maxcol = col; + #else + term.col = col; + #endif // COLUMNS_PATCH + term.row = row; + + /* reset scrolling region */ + tsetscroll(0, row-1); + /* Clearing both screens (it makes dirty all lines) */ + for (i = 0; i < 2; i++) { + tmoveto(term.c.x, term.c.y); /* make use of the LIMIT in tmoveto */ + tcursor(CURSOR_SAVE); + if (mincol < col && 0 < minrow) { + tclearregion(mincol, 0, col - 1, minrow - 1); + } + if (0 < col && minrow < row) { + tclearregion(0, minrow, col - 1, row - 1); + } + tswapscreen(); + tcursor(CURSOR_LOAD); + } + + #if SIXEL_PATCH + /* expand images into new text cells to prevent them from being deleted in + * xfinishdraw() that draws the images */ + for (i = 0; i < 2; i++) { + for (im = term.images; im; im = next) { + next = im->next; + #if SCROLLBACK_PATCH + if (IS_SET(MODE_ALTSCREEN)) { + if (im->y < 0 || im->y >= term.row) { + delete_image(im); + continue; + } + line = term.line[im->y]; + } else { + if (im->y - term.scr < -HISTSIZE || im->y - term.scr >= term.row) { + delete_image(im); + continue; + } + line = TLINE(im->y); + } + #else + if (im->y < 0 || im->y >= term.row) { + delete_image(im); + continue; + } + line = term.line[im->y]; + #endif // SCROLLBACK_PATCH + x2 = MIN(im->x + im->cols, term.col); + for (x = im->x; x < x2; x++) { + line[x].u = ' '; + line[x].mode = ATTR_SIXEL; + } + } + tswapscreen(); + } + #endif // SIXEL_PATCH +} +#endif // VIM_BROWSE_PATCH void resettitle(void) diff --git a/st.h b/st.h index cea15f2..9f18697 100644 --- a/st.h +++ b/st.h @@ -1,6 +1,7 @@ /* See LICENSE for license details. */ #include +#include #include #include #include @@ -79,7 +80,9 @@ typedef struct _ImageList { int height; int x; int y; - int should_delete; + int cols; + int cw; + int ch; } ImageList; #endif // SIXEL_PATCH diff --git a/x.c b/x.c index e1104ae..38cba1d 100644 --- a/x.c +++ b/x.c @@ -27,6 +27,11 @@ char *argv0; #include #endif // THEMED_CURSOR_PATCH +#if SIXEL_PATCH +#include +#include "sixel.h" +#endif // SIXEL_PATCH + #if UNDERCURL_PATCH /* Undercurl slope types */ enum undercurl_slope_type { @@ -282,17 +287,36 @@ zoom(const Arg *arg) Arg larg; larg.f = usedfontsize + arg->f; + #if SIXEL_PATCH + if (larg.f >= 1.0) + zoomabs(&larg); + #else zoomabs(&larg); + #endif // SIXEL_PATCH } void zoomabs(const Arg *arg) { + #if SIXEL_PATCH + ImageList *im; + #endif // SIXEL_PATCH + xunloadfonts(); xloadfonts(usedfont, arg->f); #if FONT2_PATCH xloadsparefonts(); #endif // FONT2_PATCH + + #if SIXEL_PATCH + /* deleting old pixmaps forces the new scaled pixmaps to be created */ + for (im = term.images; im; im = im->next) { + if (im->pixmap) + XFreePixmap(xw.dpy, (Drawable)im->pixmap); + im->pixmap = NULL; + } + #endif // SIXEL_PATCH + cresize(0, 0); redraw(); xhints(); @@ -850,7 +874,7 @@ cresize(int width, int height) col = (win.w - 2 * borderpx) / win.cw; row = (win.h - 2 * borderpx) / win.ch; - col = MAX(1, col); + col = MAX(2, col); row = MAX(1, row); #if ANYSIZE_PATCH @@ -2981,71 +3005,121 @@ xfinishdraw(void) { #if SIXEL_PATCH ImageList *im, *next; + Imlib_Image origin, scaled; XGCValues gcvalues; GC gc; + int width, height; + int x, x2, del; + Line line; #endif // SIXEL_PATCH #if SIXEL_PATCH for (im = term.images; im; im = next) { - /* get the next image here, because delete_image() will delete the current image */ next = im->next; - if (im->should_delete) { - delete_image(im); + /* do not draw or process the image, if it is not visible */ + if (im->x >= term.col || im->y >= term.row || im->y < 0) continue; - } + /* scale the image */ + width = im->width * win.cw / im->cw; + height = im->height * win.ch / im->ch; if (!im->pixmap) { - im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, im->width, im->height, + im->pixmap = (void *)XCreatePixmap(xw.dpy, xw.win, width, height, #if ALPHA_PATCH xw.depth #else DefaultDepth(xw.dpy, xw.scr) #endif // ALPHA_PATCH ); - XImage ximage = { - .format = ZPixmap, - .data = (char *)im->pixels, - .width = im->width, - .height = im->height, - .xoffset = 0, - .byte_order = LSBFirst, - .bitmap_bit_order = MSBFirst, - .bits_per_pixel = 32, - .bytes_per_line = im->width * 4, - .bitmap_unit = 32, - .bitmap_pad = 32, - #if ALPHA_PATCH - .depth = xw.depth - #else - .depth = 24 - #endif // ALPHA_PATCH - }; - XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, im->width, im->height); - free(im->pixels); - im->pixels = NULL; + if (win.cw == im->cw && win.ch == im->ch) { + XImage ximage = { + .format = ZPixmap, + .data = (char *)im->pixels, + .width = im->width, + .height = im->height, + .xoffset = 0, + .byte_order = sixelbyteorder, + .bitmap_bit_order = MSBFirst, + .bits_per_pixel = 32, + .bytes_per_line = im->width * 4, + .bitmap_unit = 32, + .bitmap_pad = 32, + #if ALPHA_PATCH + .depth = xw.depth + #else + .depth = 24 + #endif // ALPHA_PATCH + }; + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); + } else { + origin = imlib_create_image_using_data(im->width, im->height, (DATA32 *)im->pixels); + if (!origin) + continue; + imlib_context_set_image(origin); + imlib_image_set_has_alpha(1); + scaled = imlib_create_cropped_scaled_image(0, 0, im->width, im->height, width, height); + imlib_free_image_and_decache(); + if (!scaled) + continue; + imlib_context_set_image(scaled); + imlib_image_set_has_alpha(1); + XImage ximage = { + .format = ZPixmap, + .data = (char *)imlib_image_get_data_for_reading_only(), + .width = width, + .height = height, + .xoffset = 0, + .byte_order = sixelbyteorder, + .bitmap_bit_order = MSBFirst, + .bits_per_pixel = 32, + .bytes_per_line = width * 4, + .bitmap_unit = 32, + .bitmap_pad = 32, + #if ALPHA_PATCH + .depth = xw.depth + #else + .depth = 24 + #endif // ALPHA_PATCH + }; + XPutImage(xw.dpy, (Drawable)im->pixmap, dc.gc, &ximage, 0, 0, 0, 0, width, height); + imlib_free_image_and_decache(); + } } + /* clip the image so it does not go over to borders */ + x2 = MIN(im->x + im->cols, term.col); + width = MIN(width, (x2 - im->x) * win.cw); + + /* delete the image if the text cells behind it have been changed */ + #if SCROLLBACK_PATCH + line = TLINE(im->y); + #else + line = term.line[im->y]; + #endif // SCROLLBACK_PATCH + for (del = 0, x = im->x; x < x2; x++) { + if ((del = !(line[x].mode & ATTR_SIXEL))) + break; + } + if (del) { + delete_image(im); + continue; + } + + /* draw the image */ memset(&gcvalues, 0, sizeof(gcvalues)); gcvalues.graphics_exposures = False; gc = XCreateGC(xw.dpy, xw.win, GCGraphicsExposures, &gcvalues); - - #if ANYSIZE_PATCH - XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, im->width, im->height, win.hborderpx + im->x * win.cw, win.vborderpx + im->y * win.ch); - #else - XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, im->width, im->height, borderpx + im->x * win.cw, borderpx + im->y * win.ch); - #endif // ANYSIZE_PATCH + XCopyArea(xw.dpy, (Drawable)im->pixmap, xw.buf, gc, 0, 0, + width, height, borderpx + im->x * win.cw, borderpx + im->y * win.ch); XFreeGC(xw.dpy, gc); - } #endif // SIXEL_PATCH #if !SINGLE_DRAWABLE_BUFFER_PATCH XCopyArea(xw.dpy, xw.buf, xw.win, dc.gc, 0, 0, win.w, win.h, 0, 0); #endif // SINGLE_DRAWABLE_BUFFER_PATCH - XSetForeground(xw.dpy, dc.gc, - dc.col[IS_SET(MODE_REVERSE)? - defaultfg : defaultbg].pixel); + XSetForeground(xw.dpy, dc.gc, dc.col[IS_SET(MODE_REVERSE) ? defaultfg : defaultbg].pixel); } void