diff --git a/dcs.c b/dcs.c index fb4a14b6..7ce1a868 100644 --- a/dcs.c +++ b/dcs.c @@ -427,8 +427,7 @@ dcs_hook(struct terminal *term, uint8_t final) int p2 = vt_param_get(term, 1,0); int p3 = vt_param_get(term, 2, 0); - sixel_init(term, p1, p2, p3); - term->vt.dcs.put_handler = &sixel_put; + term->vt.dcs.put_handler = sixel_init(term, p1, p2, p3); term->vt.dcs.unhook_handler = &sixel_unhook; break; } diff --git a/sixel.c b/sixel.c index b0746a9a..e454032c 100644 --- a/sixel.c +++ b/sixel.c @@ -16,6 +16,9 @@ static size_t count; +static void sixel_put_generic(struct terminal *term, uint8_t c); +static void sixel_put_ar_11(struct terminal *term, uint8_t c); + void sixel_fini(struct terminal *term) { @@ -24,7 +27,7 @@ sixel_fini(struct terminal *term) free(term->sixel.shared_palette); } -void +sixel_put sixel_init(struct terminal *term, int p1, int p2, int p3) { /* @@ -119,6 +122,7 @@ sixel_init(struct terminal *term, int p1, int p2, int p3) : bg; count = 0; + return pan == 1 && pad == 1 ? &sixel_put_ar_11 : &sixel_put_generic; } void @@ -1327,7 +1331,8 @@ resize(struct terminal *term, int new_width, int new_height) } static void -sixel_add(struct terminal *term, int col, int width, uint32_t color, uint8_t sixel) +sixel_add_generic(struct terminal *term, int col, int width, uint32_t color, + uint8_t sixel) { xassert(term->sixel.pos.col < term->sixel.image.width); xassert(term->sixel.pos.row < term->sixel.image.height); @@ -1348,7 +1353,26 @@ sixel_add(struct terminal *term, int col, int width, uint32_t color, uint8_t six } static void -sixel_add_many(struct terminal *term, uint8_t c, unsigned count) +sixel_add_ar_11(struct terminal *term, int col, int width, uint32_t color, + uint8_t sixel) +{ + xassert(term->sixel.pos.col < term->sixel.image.width); + xassert(term->sixel.pos.row < term->sixel.image.height); + xassert(term->sixel.pan == 1); + + size_t ofs = term->sixel.row_byte_ofs + col; + uint32_t *data = &term->sixel.image.data[ofs]; + + for (int i = 0; i < 6; i++, sixel >>= 1, data += width) { + if (sixel & 1) + *data = color; + } + + xassert(sixel == 0); +} + +static void +sixel_add_many_generic(struct terminal *term, uint8_t c, unsigned count) { int col = term->sixel.pos.col; int width = term->sixel.image.width; @@ -1362,14 +1386,38 @@ sixel_add_many(struct terminal *term, uint8_t c, unsigned count) } uint32_t color = term->sixel.color; - for (unsigned i = 0; i < count; i++, col++) - sixel_add(term, col, width, color, c); + for (unsigned i = 0; i < count; i++, col++) { + /* TODO: is it worth dynamically dispatching to either generic or AR-11? */ + sixel_add_generic(term, col, width, color, c); + } term->sixel.pos.col = col; } static void -decsixel(struct terminal *term, uint8_t c) +sixel_add_many_ar_11(struct terminal *term, uint8_t c, unsigned count) +{ + xassert(term->sixel.pan == 1); + xassert(term->sixel.pad == 1); + + int col = term->sixel.pos.col; + int width = term->sixel.image.width; + + if (unlikely(col + count - 1 >= width)) { + resize_horizontally(term, col + count); + width = term->sixel.image.width; + count = min(count, max(width - col, 0)); + } + + uint32_t color = term->sixel.color; + for (unsigned i = 0; i < count; i++, col++) + sixel_add_ar_11(term, col, width, color, c); + + term->sixel.pos.col = col; +} + +static void +decsixel_generic(struct terminal *term, uint8_t c) { switch (c) { case '"': @@ -1382,6 +1430,7 @@ decsixel(struct terminal *term, uint8_t c) term->sixel.state = SIXEL_DECGRI; term->sixel.param = 0; term->sixel.param_idx = 0; + term->sixel.repeat_count = 1; break; case '#': @@ -1424,7 +1473,7 @@ decsixel(struct terminal *term, uint8_t c) case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': - sixel_add_many(term, c - 63, 1); + sixel_add_many_generic(term, c - 63, 1); break; case ' ': @@ -1438,6 +1487,29 @@ decsixel(struct terminal *term, uint8_t c) } } +static void +decsixel_ar_11(struct terminal *term, uint8_t c) +{ + switch (c) { + case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': + case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': + case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': + case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': + case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': + case '~': + sixel_add_many_ar_11(term, c - 63, 1); + break; + + default: + decsixel_generic(term, c); + break; + } +} + static void decgra(struct terminal *term, uint8_t c) { @@ -1483,21 +1555,34 @@ decgra(struct terminal *term, uint8_t c) } term->sixel.state = SIXEL_DECSIXEL; - decsixel(term, c); + + /* Update DCS put handler, since pan/pad may have changed */ + term->vt.dcs.put_handler = pan == 1 && pad == 1 + ? &sixel_put_ar_11 + : &sixel_put_generic; + + if (likely(pan == 1 && pad == 1)) + decsixel_ar_11(term, c); + else + decsixel_generic(term, c); + break; } } } static void -decgri(struct terminal *term, uint8_t c) +decgri_generic(struct terminal *term, uint8_t c) { switch (c) { case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - term->sixel.param *= 10; - term->sixel.param += c - '0'; + case '5': case '6': case '7': case '8': case '9': { + unsigned param = term->sixel.param; + param *= 10; + param += c - '0'; + term->sixel.repeat_count = term->sixel.param = param; break; + } case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': @@ -1509,18 +1594,41 @@ decgri(struct terminal *term, uint8_t c) case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': case '~': { - unsigned count = term->sixel.param; - if (likely(count > 0)) - sixel_add_many(term, c - 63, count); - else if (unlikely(count == 0)) - sixel_add_many(term, c - 63, 1); + const unsigned count = term->sixel.repeat_count; + sixel_add_many_generic(term, c - 63, count); term->sixel.state = SIXEL_DECSIXEL; break; } default: term->sixel.state = SIXEL_DECSIXEL; - sixel_put(term, c); + term->vt.dcs.put_handler(term, c); + break; + } +} + +static void +decgri_ar_11(struct terminal *term, uint8_t c) +{ + switch (c) { + case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E': + case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': + case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': + case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': + case '[': case '\\': case ']': case '^': case '_': case '`': case 'a': + case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': + case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': + case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}': + case '~': { + const unsigned count = term->sixel.repeat_count; + sixel_add_many_ar_11(term, c - 63, count); + term->sixel.state = SIXEL_DECSIXEL; + break; + } + + default: + decgri_generic(term, c); break; } } @@ -1601,19 +1709,36 @@ decgci(struct terminal *term, uint8_t c) term->sixel.color = term->sixel.palette[term->sixel.color_idx]; term->sixel.state = SIXEL_DECSIXEL; - decsixel(term, c); + + if (likely(term->sixel.pan == 1 && term->sixel.pad == 1)) + decsixel_ar_11(term, c); + else + decsixel_generic(term, c); break; } } } -void -sixel_put(struct terminal *term, uint8_t c) +static void +sixel_put_generic(struct terminal *term, uint8_t c) { switch (term->sixel.state) { - case SIXEL_DECSIXEL: decsixel(term, c); break; + case SIXEL_DECSIXEL: decsixel_generic(term, c); break; case SIXEL_DECGRA: decgra(term, c); break; - case SIXEL_DECGRI: decgri(term, c); break; + case SIXEL_DECGRI: decgri_generic(term, c); break; + case SIXEL_DECGCI: decgci(term, c); break; + } + + count++; +} + +static void +sixel_put_ar_11(struct terminal *term, uint8_t c) +{ + switch (term->sixel.state) { + case SIXEL_DECSIXEL: decsixel_ar_11(term, c); break; + case SIXEL_DECGRA: decgra(term, c); break; + case SIXEL_DECGRI: decgri_ar_11(term, c); break; case SIXEL_DECGCI: decgci(term, c); break; } diff --git a/sixel.h b/sixel.h index f72b4dc4..efd64908 100644 --- a/sixel.h +++ b/sixel.h @@ -6,10 +6,11 @@ #define SIXEL_MAX_WIDTH 10000u #define SIXEL_MAX_HEIGHT 10000u +typedef void (*sixel_put)(struct terminal *term, uint8_t c); + void sixel_fini(struct terminal *term); -void sixel_init(struct terminal *term, int p1, int p2, int p3); -void sixel_put(struct terminal *term, uint8_t c); +sixel_put sixel_init(struct terminal *term, int p1, int p2, int p3); void sixel_unhook(struct terminal *term); void sixel_destroy(struct sixel *sixel); diff --git a/terminal.h b/terminal.h index 220070e1..1cb62686 100644 --- a/terminal.h +++ b/terminal.h @@ -649,6 +649,7 @@ struct terminal { unsigned params[5]; /* Collected parameters, for RASTER, COLOR_SPEC */ unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */ unsigned param_idx; /* Parameters seen */ + unsigned repeat_count; bool transparent_bg; uint32_t default_bg;