2020-02-21 21:53:23 +01:00
|
|
|
#include "sixel.h"
|
|
|
|
|
|
|
|
|
|
#include <string.h>
|
2020-06-06 13:59:46 +02:00
|
|
|
#include <limits.h>
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
|
|
|
#define LOG_MODULE "sixel"
|
|
|
|
|
#define LOG_ENABLE_DBG 0
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "render.h"
|
2020-02-22 21:18:55 +01:00
|
|
|
#include "sixel-hls.h"
|
2020-05-01 11:46:24 +02:00
|
|
|
#include "util.h"
|
2020-02-21 21:53:23 +01:00
|
|
|
|
|
|
|
|
static size_t count;
|
|
|
|
|
|
2020-06-10 18:36:54 +02:00
|
|
|
void
|
|
|
|
|
sixel_fini(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
free(term->sixel.palette);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-10 18:52:53 +02:00
|
|
|
static uint32_t
|
|
|
|
|
color_with_alpha(const struct terminal *term, uint32_t color)
|
|
|
|
|
{
|
|
|
|
|
uint16_t alpha = color == term->colors.bg ? term->colors.alpha : 0xffff;
|
|
|
|
|
return (alpha / 256u) << 24 | color;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
void
|
|
|
|
|
sixel_init(struct terminal *term)
|
|
|
|
|
{
|
2020-02-22 10:46:35 +01:00
|
|
|
assert(term->sixel.image.data == NULL);
|
2020-02-22 14:02:00 +01:00
|
|
|
assert(term->sixel.palette_size <= SIXEL_MAX_COLORS);
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
2020-02-22 10:54:52 +01:00
|
|
|
term->sixel.pos = (struct coord){0, 0};
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.color_idx = 0;
|
|
|
|
|
term->sixel.max_col = 0;
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
memset(term->sixel.params, 0, sizeof(term->sixel.params));
|
2020-02-22 11:52:22 +01:00
|
|
|
term->sixel.image.data = malloc(1 * 6 * sizeof(term->sixel.image.data[0]));
|
2020-02-22 10:46:35 +01:00
|
|
|
term->sixel.image.width = 1;
|
|
|
|
|
term->sixel.image.height = 6;
|
2020-06-11 18:40:52 +02:00
|
|
|
term->sixel.image.autosize = true;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-06-10 18:36:54 +02:00
|
|
|
if (term->sixel.palette == NULL) {
|
|
|
|
|
term->sixel.palette = calloc(
|
|
|
|
|
term->sixel.palette_size, sizeof(term->sixel.palette[0]));
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
for (size_t i = 0; i < 1 * 6; i++)
|
2020-06-10 18:52:53 +02:00
|
|
|
term->sixel.image.data[i] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
count = 0;
|
|
|
|
|
|
|
|
|
|
/* TODO: default palette */
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 00:23:19 +01:00
|
|
|
void
|
|
|
|
|
sixel_destroy(struct sixel *sixel)
|
|
|
|
|
{
|
|
|
|
|
pixman_image_unref(sixel->pix);
|
|
|
|
|
free(sixel->data);
|
|
|
|
|
|
|
|
|
|
sixel->pix = NULL;
|
|
|
|
|
sixel->data = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 18:39:14 +01:00
|
|
|
static void
|
|
|
|
|
sixel_erase(struct terminal *term, struct sixel *sixel)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < sixel->rows; i++) {
|
|
|
|
|
int r = (sixel->pos.row + i) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
struct row *row = term->grid->rows[r];
|
2020-03-06 19:11:57 +01:00
|
|
|
if (row == NULL) {
|
|
|
|
|
/* A resize/reflow may cause row to now be unallocated */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-24 18:39:14 +01:00
|
|
|
row->dirty = true;
|
|
|
|
|
|
|
|
|
|
for (int c = 0; c < term->grid->num_cols; c++)
|
|
|
|
|
row->cells[c].attrs.clean = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sixel_destroy(sixel);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-28 08:37:25 +02:00
|
|
|
static void
|
|
|
|
|
sixel_insert(struct terminal *term, struct sixel sixel)
|
|
|
|
|
{
|
2020-06-28 09:16:43 +02:00
|
|
|
const int scrollback_end
|
|
|
|
|
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
2020-06-28 10:45:05 +02:00
|
|
|
const int end_row
|
|
|
|
|
= (sixel.pos.row + sixel.rows
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
assert(end_row >= 0 && end_row < term->grid->num_rows);
|
2020-06-28 09:16:43 +02:00
|
|
|
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
2020-06-28 10:45:05 +02:00
|
|
|
const int e
|
|
|
|
|
= (it->item.pos.row + it->item.rows
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
assert(e >= 0 && e < term->grid->num_rows);
|
2020-06-28 09:16:43 +02:00
|
|
|
|
|
|
|
|
if (e < end_row) {
|
|
|
|
|
tll_insert_before(term->grid->sixel_images, it, sixel);
|
2020-06-28 10:45:05 +02:00
|
|
|
goto out;
|
2020-06-28 09:16:43 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-28 08:37:25 +02:00
|
|
|
tll_push_back(term->grid->sixel_images, sixel);
|
2020-06-28 10:45:05 +02:00
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
|
|
|
|
LOG_DBG("sixel list after insertion:");
|
|
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
LOG_DBG(" rows=%d+%d", it->item.pos.row, it->item.rows);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
;
|
|
|
|
|
#endif
|
2020-06-28 08:37:25 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
/* Row numbers are absolute */
|
2020-06-06 13:59:46 +02:00
|
|
|
static void
|
2020-06-27 13:56:13 +02:00
|
|
|
sixel_delete_at_point(struct terminal *term, int row, int col)
|
2020-02-22 21:35:45 +01:00
|
|
|
{
|
2020-06-27 14:19:08 +02:00
|
|
|
assert(row >= 0);
|
|
|
|
|
assert(row < term->grid->num_rows);
|
|
|
|
|
assert(col < term->grid->num_cols);
|
|
|
|
|
|
2020-03-13 18:44:23 +01:00
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
2020-02-24 18:39:14 +01:00
|
|
|
return;
|
2020-02-22 21:35:45 +01:00
|
|
|
|
2020-03-13 18:44:23 +01:00
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
2020-02-24 18:39:14 +01:00
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
const int six_start = six->pos.row;
|
2020-06-27 11:00:28 +02:00
|
|
|
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
|
2020-02-22 21:35:45 +01:00
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
/* We should never generate scrollback wrapping sixels */
|
|
|
|
|
assert(six_end >= six_start);
|
2020-06-27 11:00:28 +02:00
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
if (row >= six_start && row <= six_end) {
|
2020-06-10 18:43:42 +02:00
|
|
|
const int col_start = six->pos.col;
|
2020-06-06 13:59:46 +02:00
|
|
|
const int col_end = six->pos.col + six->cols;
|
|
|
|
|
|
2020-06-27 11:00:28 +02:00
|
|
|
if (col < 0 || (col >= col_start && col < col_end)) {
|
2020-06-06 13:59:46 +02:00
|
|
|
sixel_erase(term, six);
|
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
|
|
|
|
}
|
2020-02-22 21:35:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 14:43:29 +02:00
|
|
|
/* TODO: remove */
|
2020-06-06 13:59:46 +02:00
|
|
|
void
|
2020-06-27 13:56:13 +02:00
|
|
|
sixel_delete_at_row(struct terminal *term, int row)
|
2020-06-06 13:59:46 +02:00
|
|
|
{
|
2020-06-27 14:43:29 +02:00
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
return;
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
sixel_delete_at_point(
|
|
|
|
|
term, (term->grid->offset + row) & (term->grid->num_rows - 1), -1);
|
2020-06-06 13:59:46 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
/* Row numbers are absolute */
|
|
|
|
|
static void
|
|
|
|
|
_sixel_delete_in_range(struct terminal *term, int start, int end)
|
2020-02-24 18:39:14 +01:00
|
|
|
{
|
2020-06-27 13:56:13 +02:00
|
|
|
assert(end >= start);
|
2020-06-27 14:19:08 +02:00
|
|
|
assert(start >= 0);
|
|
|
|
|
assert(start < term->grid->num_rows);
|
|
|
|
|
assert(end >= 0);
|
|
|
|
|
assert(end < term->grid->num_rows);
|
2020-02-24 18:39:14 +01:00
|
|
|
|
2020-03-13 18:44:23 +01:00
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
2020-02-24 18:39:14 +01:00
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
|
|
|
|
const int six_start = six->pos.row;
|
2020-06-27 11:00:28 +02:00
|
|
|
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
|
|
|
/* We should never generate scrollback wrapping sixels */
|
|
|
|
|
assert(six_end >= six_start);
|
|
|
|
|
|
|
|
|
|
if ((start <= six_start && end >= six_start) || /* Crosses sixel start boundary */
|
|
|
|
|
(start <= six_end && end >= six_end) || /* Crosses sixel end boundary */
|
|
|
|
|
(start >= six_start && end <= six_end)) /* Fully within sixel range */
|
2020-02-24 18:39:14 +01:00
|
|
|
{
|
|
|
|
|
sixel_erase(term, six);
|
2020-03-13 18:44:23 +01:00
|
|
|
tll_remove(term->grid->sixel_images, it);
|
2020-02-24 18:39:14 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
void
|
|
|
|
|
sixel_delete_in_range(struct terminal *term, int _start, int _end)
|
|
|
|
|
{
|
2020-06-27 14:43:29 +02:00
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (_start == _end) {
|
|
|
|
|
/* Avoid expensive wrap calculation */
|
|
|
|
|
return sixel_delete_at_point(
|
|
|
|
|
term, (term->grid->offset + _start) & (term->grid->num_rows - 1), -1);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
assert(_end >= _start);
|
|
|
|
|
const int lines = _end - _start + 1;
|
|
|
|
|
const int start = (term->grid->offset + _start) & (term->grid->num_rows - 1);
|
|
|
|
|
const int end = (start + lines - 1) & (term->grid->num_rows - 1);
|
|
|
|
|
const bool wraps = end < start;
|
|
|
|
|
|
|
|
|
|
if (wraps) {
|
|
|
|
|
int rows_to_wrap_around = term->grid->num_rows - start;
|
|
|
|
|
assert(lines - rows_to_wrap_around > 0);
|
|
|
|
|
_sixel_delete_in_range(term, start, term->grid->num_rows);
|
|
|
|
|
_sixel_delete_in_range(term, 0, lines - rows_to_wrap_around);
|
|
|
|
|
} else
|
|
|
|
|
_sixel_delete_in_range(term, start, end);
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 21:07:12 +02:00
|
|
|
static void
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite(struct terminal *term, struct sixel *six,
|
|
|
|
|
int row, int col, int height, int width)
|
2020-06-23 21:07:12 +02:00
|
|
|
{
|
2020-06-27 14:19:08 +02:00
|
|
|
assert(row >= 0);
|
|
|
|
|
assert(row + height <= term->grid->num_rows);
|
|
|
|
|
assert(col >= 0);
|
|
|
|
|
assert(col + width <= term->grid->num_cols);
|
|
|
|
|
|
2020-06-27 13:18:54 +02:00
|
|
|
int rel_above = min(max(row - six->pos.row, 0), six->rows);
|
|
|
|
|
int rel_below = max(min(row + height - six->pos.row, six->rows), 0);
|
|
|
|
|
int rel_left = min(max(col - six->pos.col, 0), six->cols);
|
|
|
|
|
int rel_right = max(min(col + width - six->pos.col, six->cols), 0);
|
2020-06-27 12:45:22 +02:00
|
|
|
|
|
|
|
|
assert(rel_above >= 0);
|
|
|
|
|
assert(rel_below >= 0);
|
|
|
|
|
assert(rel_left >= 0);
|
|
|
|
|
assert(rel_right >= 0);
|
|
|
|
|
|
|
|
|
|
LOG_DBG("SPLIT: six (%p): %dx%d-%dx%d, %dx%d-%dx%d, rel: above=%d, below=%d, left=%d, right=%d",
|
|
|
|
|
six, six->pos.row, six->pos.col, six->rows, six->cols,
|
|
|
|
|
row, col, height, width,
|
|
|
|
|
rel_above, rel_below, rel_left, rel_right);
|
|
|
|
|
|
2020-06-27 14:26:13 +02:00
|
|
|
struct sixel imgs[4] = {};
|
|
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
if (rel_above > 0) {
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[0] = (struct sixel){
|
2020-06-23 21:07:12 +02:00
|
|
|
.width = six->width,
|
2020-06-27 12:45:22 +02:00
|
|
|
.height = rel_above * term->cell_height,
|
|
|
|
|
.rows = rel_above,
|
2020-06-23 21:07:12 +02:00
|
|
|
.cols = six->cols,
|
|
|
|
|
.pos = six->pos,
|
|
|
|
|
};
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[0].data = malloc(imgs[0].width * imgs[0].height * sizeof(uint32_t));
|
|
|
|
|
memcpy(imgs[0].data, six->data, imgs[0].width * imgs[0].height * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
if (rel_below < six->rows) {
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[1] = (struct sixel){
|
2020-06-23 21:07:12 +02:00
|
|
|
.width = six->width,
|
2020-06-27 12:45:22 +02:00
|
|
|
.height = six->height - rel_below * term->cell_height,
|
|
|
|
|
.rows = six->rows - rel_below,
|
2020-06-23 21:07:12 +02:00
|
|
|
.cols = six->cols,
|
|
|
|
|
.pos = (struct coord){
|
|
|
|
|
six->pos.col,
|
2020-06-27 12:45:22 +02:00
|
|
|
(six->pos.row + rel_below) & (term->grid->num_rows - 1)},
|
2020-06-23 21:07:12 +02:00
|
|
|
};
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[1].data = malloc(imgs[1].width * imgs[1].height * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
memcpy(
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[1].data,
|
2020-06-27 12:45:22 +02:00
|
|
|
&((const uint32_t *)six->data)[rel_below * term->cell_height * six->width],
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[1].width * imgs[1].height * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
if (rel_left > 0) {
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[2] = (struct sixel){
|
2020-06-27 12:45:22 +02:00
|
|
|
.width = rel_left * term->cell_width,
|
|
|
|
|
.height = min(term->cell_height, six->height - rel_above * term->cell_height),
|
2020-06-23 21:07:12 +02:00
|
|
|
.rows = 1,
|
2020-06-27 12:45:22 +02:00
|
|
|
.cols = rel_left,
|
|
|
|
|
.pos = (struct coord){
|
|
|
|
|
six->pos.col,
|
|
|
|
|
(six->pos.row + rel_above) & (term->grid->num_rows - 1)},
|
2020-06-23 21:07:12 +02:00
|
|
|
};
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[2].data = malloc(imgs[2].width * imgs[2].height * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
for (size_t i = 0; i < term->cell_height; i++)
|
|
|
|
|
memcpy(
|
2020-06-27 14:26:13 +02:00
|
|
|
&((uint32_t *)imgs[2].data)[i * imgs[2].width],
|
2020-06-27 12:45:22 +02:00
|
|
|
&((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width],
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[2].width * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
if (rel_right < six->cols) {
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[3] = (struct sixel){
|
2020-06-27 12:45:22 +02:00
|
|
|
.width = six->width - rel_right * term->cell_width,
|
|
|
|
|
.height = min(term->cell_height, six->height - rel_above * term->cell_height),
|
2020-06-23 21:07:12 +02:00
|
|
|
.rows = 1,
|
2020-06-27 12:45:22 +02:00
|
|
|
.cols = six->cols - rel_right,
|
|
|
|
|
.pos = (struct coord){
|
|
|
|
|
six->pos.col + rel_right,
|
|
|
|
|
(six->pos.row + rel_above) & (term->grid->num_rows - 1)},
|
2020-06-23 21:07:12 +02:00
|
|
|
};
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[3].data = malloc(imgs[3].width * imgs[3].height * sizeof(uint32_t));
|
2020-06-23 21:07:12 +02:00
|
|
|
for (size_t i = 0; i < term->cell_height; i++)
|
|
|
|
|
memcpy(
|
2020-06-27 14:26:13 +02:00
|
|
|
&((uint32_t *)imgs[3].data)[i * imgs[3].width],
|
2020-06-27 12:45:22 +02:00
|
|
|
&((const uint32_t *)six->data)[(rel_above * term->cell_height + i) * six->width + rel_right * term->cell_width],
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[3].width * sizeof(uint32_t));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < sizeof(imgs) / sizeof(imgs[0]); i++) {
|
|
|
|
|
if (imgs[i].data == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
imgs[i].pix = pixman_image_create_bits_no_clear(
|
2020-06-23 21:07:12 +02:00
|
|
|
PIXMAN_a8r8g8b8,
|
2020-06-27 14:26:13 +02:00
|
|
|
imgs[i].width, imgs[i].height,
|
|
|
|
|
imgs[i].data, imgs[i].width * sizeof(uint32_t));
|
2020-06-28 08:37:25 +02:00
|
|
|
sixel_insert(term, imgs[i]);
|
2020-06-23 21:07:12 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
/* Row numbers are absolute */
|
2020-06-27 12:45:22 +02:00
|
|
|
static void
|
2020-06-27 15:29:47 +02:00
|
|
|
_sixel_overwrite_by_rectangle(
|
2020-06-27 13:56:13 +02:00
|
|
|
struct terminal *term, int row, int col, int height, int width)
|
2020-06-23 21:07:12 +02:00
|
|
|
{
|
2020-06-27 13:56:13 +02:00
|
|
|
assert(row + height <= term->grid->num_rows);
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
assert(row >= 0);
|
|
|
|
|
assert(row + height <= term->grid->num_rows);
|
|
|
|
|
assert(col >= 0);
|
|
|
|
|
assert(col + width <= term->grid->num_cols);
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
/* We don't handle rectangle wrapping around */
|
|
|
|
|
assert(row + height <= term->grid->num_rows);
|
|
|
|
|
|
|
|
|
|
const int start = row;
|
|
|
|
|
const int end = row + height - 1;
|
2020-06-23 21:07:12 +02:00
|
|
|
|
2020-06-28 19:22:23 +02:00
|
|
|
const int scrollback_end
|
|
|
|
|
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
const int grid_relative_start
|
|
|
|
|
= (start
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
2020-06-23 21:07:12 +02:00
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
|
|
|
|
|
const int six_start = six->pos.row;
|
2020-06-27 12:45:22 +02:00
|
|
|
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
2020-06-28 19:22:23 +02:00
|
|
|
const int six_grid_relative_end =
|
|
|
|
|
(six_end
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ + term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
if (six_grid_relative_end < grid_relative_start) {
|
|
|
|
|
/* All remaining sixels are *before* our rectangle */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
/* We should never generate scrollback wrapping sixels */
|
|
|
|
|
assert(six_end >= six_start);
|
|
|
|
|
|
|
|
|
|
if ((start <= six_start && end >= six_start) || /* Crosses sixel start boundary */
|
|
|
|
|
(start <= six_end && end >= six_end) || /* Crosses sixel end boundary */
|
|
|
|
|
(start >= six_start && end <= six_end)) /* Fully within sixel range */
|
2020-06-27 12:45:22 +02:00
|
|
|
{
|
2020-06-23 21:07:12 +02:00
|
|
|
const int col_start = six->pos.col;
|
2020-06-27 12:45:22 +02:00
|
|
|
const int col_end = six->pos.col + six->cols - 1;
|
2020-06-23 21:07:12 +02:00
|
|
|
|
2020-06-27 12:45:22 +02:00
|
|
|
if ((col <= col_start && col + width - 1 >= col_start) ||
|
|
|
|
|
(col <= col_end && col + width - 1 >= col_end) ||
|
|
|
|
|
(col >= col_start && col + width - 1 <= col_end))
|
|
|
|
|
{
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite(term, six, start, col, height, width);
|
2020-06-23 21:07:12 +02:00
|
|
|
sixel_erase(term, six);
|
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
void
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite_by_rectangle(
|
2020-06-27 15:22:31 +02:00
|
|
|
struct terminal *term, int row, int col, int height, int width)
|
2020-06-27 13:56:13 +02:00
|
|
|
{
|
2020-06-27 14:43:29 +02:00
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
return;
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
const int start = (term->grid->offset + row) & (term->grid->num_rows - 1);
|
2020-06-27 13:56:13 +02:00
|
|
|
const int end = (start + height - 1) & (term->grid->num_rows - 1);
|
|
|
|
|
const bool wraps = end < start;
|
|
|
|
|
|
|
|
|
|
if (wraps) {
|
|
|
|
|
int rows_to_wrap_around = term->grid->num_rows - start;
|
|
|
|
|
assert(height - rows_to_wrap_around > 0);
|
2020-06-27 15:29:47 +02:00
|
|
|
_sixel_overwrite_by_rectangle(term, start, col, rows_to_wrap_around, width);
|
|
|
|
|
_sixel_overwrite_by_rectangle(term, 0, col, height - rows_to_wrap_around, width);
|
2020-06-27 13:56:13 +02:00
|
|
|
} else
|
2020-06-27 15:29:47 +02:00
|
|
|
_sixel_overwrite_by_rectangle(term, start, col, height, width);
|
2020-06-27 13:56:13 +02:00
|
|
|
}
|
|
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
/* Row numbers are relative to grid offset */
|
|
|
|
|
void
|
|
|
|
|
sixel_overwrite_by_row(struct terminal *term, int _row, int col, int width)
|
2020-06-27 13:56:13 +02:00
|
|
|
{
|
|
|
|
|
assert(col >= 0);
|
|
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
assert(_row >= 0);
|
|
|
|
|
assert(_row < term->rows);
|
2020-06-27 14:19:08 +02:00
|
|
|
assert(col >= 0);
|
2020-06-27 15:22:31 +02:00
|
|
|
assert(col + width <= term->grid->num_cols);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
if (likely(tll_length(term->grid->sixel_images) == 0))
|
|
|
|
|
return;
|
|
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
const int scrollback_end
|
|
|
|
|
= (term->grid->offset + term->rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
const int row = (term->grid->offset + _row) & (term->grid->num_rows - 1);
|
|
|
|
|
const int grid_relative_row
|
|
|
|
|
= (term->grid->offset + row
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
2020-06-27 13:56:13 +02:00
|
|
|
tll_foreach(term->grid->sixel_images, it) {
|
|
|
|
|
struct sixel *six = &it->item;
|
|
|
|
|
const int six_start = six->pos.row;
|
|
|
|
|
const int six_end = (six_start + six->rows - 1) & (term->grid->num_rows - 1);
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
/* We should never generate scrollback wrapping sixels */
|
|
|
|
|
assert(six_end >= six_start);
|
2020-06-27 13:56:13 +02:00
|
|
|
|
2020-06-28 14:24:30 +02:00
|
|
|
const int six_grid_relative_end
|
|
|
|
|
= (six->pos.row + six->rows - 1
|
|
|
|
|
- scrollback_end
|
|
|
|
|
+ term->grid->num_rows) & (term->grid->num_rows - 1);
|
|
|
|
|
|
|
|
|
|
if (six_grid_relative_end < grid_relative_row) {
|
|
|
|
|
/* All remaining sixels are *before* "our" row */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 14:19:08 +02:00
|
|
|
if (row >= six_start && row <= six_end) {
|
2020-06-27 13:56:13 +02:00
|
|
|
const int col_start = six->pos.col;
|
2020-06-27 15:22:31 +02:00
|
|
|
const int col_end = six->pos.col + six->cols - 1;
|
2020-06-27 13:56:13 +02:00
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
if ((col <= col_start && col + width - 1 >= col_start) ||
|
|
|
|
|
(col <= col_end && col + width - 1 >= col_end) ||
|
|
|
|
|
(col >= col_start && col + width - 1 <= col_end))
|
|
|
|
|
{
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite(term, six, row, col, 1, width);
|
2020-06-27 13:56:13 +02:00
|
|
|
sixel_erase(term, six);
|
|
|
|
|
tll_remove(term->grid->sixel_images, it);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-27 15:22:31 +02:00
|
|
|
void
|
2020-06-28 11:01:19 +02:00
|
|
|
sixel_overwrite_at_cursor(struct terminal *term, int width)
|
2020-06-27 15:22:31 +02:00
|
|
|
{
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite_by_row(
|
2020-06-28 11:01:19 +02:00
|
|
|
term, term->grid->cursor.point.row, term->grid->cursor.point.col, width);
|
2020-06-23 21:07:12 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
void
|
|
|
|
|
sixel_unhook(struct terminal *term)
|
|
|
|
|
{
|
2020-06-27 14:19:08 +02:00
|
|
|
int pixel_row_idx = 0;
|
|
|
|
|
int pixel_rows_left = term->sixel.image.height;
|
|
|
|
|
const int stride = term->sixel.image.width * sizeof(uint32_t);
|
|
|
|
|
|
|
|
|
|
/* We do not allow sixels to cross the scrollback wrap-around, as
|
|
|
|
|
* this makes intersection calculations much more complicated */
|
|
|
|
|
while (pixel_rows_left > 0) {
|
|
|
|
|
const struct coord *cursor = &term->grid->cursor.point;
|
|
|
|
|
|
|
|
|
|
const int cur_row = (term->grid->offset + cursor->row) & (term->grid->num_rows - 1);
|
|
|
|
|
const int rows_avail = term->grid->num_rows - cur_row;
|
|
|
|
|
|
|
|
|
|
const int pixel_rows_avail = rows_avail * term->cell_height;
|
|
|
|
|
|
|
|
|
|
const int width = term->sixel.image.width;
|
|
|
|
|
const int height = min(pixel_rows_left, pixel_rows_avail);
|
|
|
|
|
|
|
|
|
|
uint32_t *img_data;
|
|
|
|
|
if (pixel_row_idx == 0)
|
|
|
|
|
img_data = term->sixel.image.data;
|
|
|
|
|
else {
|
|
|
|
|
img_data = malloc(height * stride);
|
|
|
|
|
memcpy(
|
|
|
|
|
img_data,
|
|
|
|
|
&((uint8_t *)term->sixel.image.data)[pixel_row_idx * stride],
|
|
|
|
|
height * stride);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct sixel image = {
|
|
|
|
|
.data = img_data,
|
|
|
|
|
.width = width,
|
|
|
|
|
.height = height,
|
|
|
|
|
.rows = (height + term->cell_height - 1) / term->cell_height,
|
|
|
|
|
.cols = (width + term->cell_width - 1) / term->cell_width,
|
|
|
|
|
.pos = (struct coord){cursor->col, cur_row},
|
|
|
|
|
};
|
|
|
|
|
|
2020-06-27 15:29:47 +02:00
|
|
|
sixel_overwrite_by_rectangle(
|
2020-06-27 14:19:08 +02:00
|
|
|
term, cursor->row, image.pos.col, image.rows, image.cols);
|
|
|
|
|
|
|
|
|
|
LOG_DBG("generating %dx%d pixman image at %d-%d", image.width, image.height, image.pos.row, image.pos.row + image.rows);
|
|
|
|
|
|
|
|
|
|
image.pix = pixman_image_create_bits_no_clear(
|
|
|
|
|
PIXMAN_a8r8g8b8,
|
|
|
|
|
image.width, image.height,
|
|
|
|
|
img_data, stride);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < image.rows; i++)
|
|
|
|
|
term_linefeed(term);
|
|
|
|
|
term_formfeed(term);
|
|
|
|
|
render_refresh(term);
|
|
|
|
|
|
2020-06-28 08:37:25 +02:00
|
|
|
sixel_insert(term, image);
|
2020-06-27 14:19:08 +02:00
|
|
|
|
|
|
|
|
pixel_row_idx += height;
|
|
|
|
|
pixel_rows_left -= height;
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
term->sixel.image.data = NULL;
|
|
|
|
|
term->sixel.image.width = 0;
|
|
|
|
|
term->sixel.image.height = 0;
|
2020-02-21 23:40:35 +01:00
|
|
|
term->sixel.max_col = 0;
|
2020-02-22 10:54:52 +01:00
|
|
|
term->sixel.pos = (struct coord){0, 0};
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-22 21:03:24 +01:00
|
|
|
static unsigned
|
|
|
|
|
max_width(const struct terminal *term)
|
|
|
|
|
{
|
2020-02-22 21:04:24 +01:00
|
|
|
/* foot extension - treat 0 to mean current terminal size */
|
2020-02-22 21:03:24 +01:00
|
|
|
return term->sixel.max_width == 0
|
|
|
|
|
? term->cols * term->cell_width
|
|
|
|
|
: term->sixel.max_width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
|
max_height(const struct terminal *term)
|
|
|
|
|
{
|
2020-02-22 21:04:24 +01:00
|
|
|
/* foot extension - treat 0 to mean current terminal size */
|
2020-02-22 21:03:24 +01:00
|
|
|
return term->sixel.max_height == 0
|
|
|
|
|
? term->rows * term->cell_height
|
|
|
|
|
: term->sixel.max_height;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
static bool
|
|
|
|
|
resize(struct terminal *term, int new_width, int new_height)
|
|
|
|
|
{
|
2020-06-11 18:40:52 +02:00
|
|
|
if (!term->sixel.image.autosize)
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
LOG_DBG("resizing image: %dx%d -> %dx%d",
|
|
|
|
|
term->sixel.image.width, term->sixel.image.height,
|
|
|
|
|
new_width, new_height);
|
|
|
|
|
|
|
|
|
|
uint32_t *old_data = term->sixel.image.data;
|
|
|
|
|
const int old_width = term->sixel.image.width;
|
|
|
|
|
const int old_height = term->sixel.image.height;
|
|
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
int alloc_new_width = new_width;
|
|
|
|
|
int alloc_new_height = (new_height + 6 - 1) / 6 * 6;
|
|
|
|
|
assert(alloc_new_height >= new_height);
|
|
|
|
|
assert(alloc_new_height - new_height < 6);
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
assert(new_width >= old_width);
|
|
|
|
|
assert(new_height >= old_height);
|
|
|
|
|
|
|
|
|
|
uint32_t *new_data = NULL;
|
|
|
|
|
|
|
|
|
|
if (new_width == old_width) {
|
|
|
|
|
/* Width (and thus stride) is the same, so we can simply
|
|
|
|
|
* re-alloc the existing buffer */
|
|
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
new_data = realloc(old_data, alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
2020-02-22 10:46:35 +01:00
|
|
|
if (new_data == NULL) {
|
|
|
|
|
LOG_ERRNO("failed to reallocate sixel image buffer");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(new_height > old_height);
|
2020-02-22 11:52:22 +01:00
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
} else {
|
|
|
|
|
/* Width (and thus stride) change - need to allocate a new buffer */
|
|
|
|
|
assert(new_width > old_width);
|
2020-06-11 18:40:52 +02:00
|
|
|
new_data = malloc(alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
2020-02-22 10:46:35 +01:00
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
/* Copy old rows, and initialize new columns to background color */
|
2020-02-22 10:46:35 +01:00
|
|
|
for (int r = 0; r < old_height; r++) {
|
|
|
|
|
memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t));
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
for (int c = old_width; c < new_width; c++)
|
2020-06-10 18:52:53 +02:00
|
|
|
new_data[r * new_width + c] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
}
|
2020-02-22 10:46:35 +01:00
|
|
|
free(old_data);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 11:52:22 +01:00
|
|
|
/* Initialize new rows to background color */
|
|
|
|
|
for (int r = old_height; r < new_height; r++) {
|
|
|
|
|
for (int c = 0; c < new_width; c++)
|
2020-06-10 18:52:53 +02:00
|
|
|
new_data[r * new_width + c] = color_with_alpha(term, term->colors.bg);
|
2020-02-22 11:52:22 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
assert(new_data != NULL);
|
|
|
|
|
term->sixel.image.data = new_data;
|
|
|
|
|
term->sixel.image.width = new_width;
|
|
|
|
|
term->sixel.image.height = new_height;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-21 21:53:23 +01:00
|
|
|
static void
|
|
|
|
|
sixel_add(struct terminal *term, uint32_t color, uint8_t sixel)
|
|
|
|
|
{
|
2020-02-22 10:46:35 +01:00
|
|
|
//LOG_DBG("adding sixel %02hhx using color 0x%06x", sixel, color);
|
|
|
|
|
|
2020-02-22 21:03:24 +01:00
|
|
|
if (term->sixel.pos.col >= max_width(term) ||
|
|
|
|
|
term->sixel.pos.row * 6 + 5 >= max_height(term))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 10:54:52 +01:00
|
|
|
if (term->sixel.pos.col >= term->sixel.image.width ||
|
2020-06-11 18:40:52 +02:00
|
|
|
term->sixel.pos.row * 6 + 5 >= (term->sixel.image.height + 6 - 1) / 6 * 6)
|
2020-02-22 10:46:35 +01:00
|
|
|
{
|
2020-02-22 21:20:22 +01:00
|
|
|
int width = max(
|
|
|
|
|
term->sixel.image.width,
|
|
|
|
|
max(term->sixel.max_col, term->sixel.pos.col + 1));
|
|
|
|
|
|
|
|
|
|
int height = max(
|
|
|
|
|
term->sixel.image.height,
|
2020-02-22 21:24:21 +01:00
|
|
|
(term->sixel.pos.row + 1) * 6);
|
2020-02-22 21:20:22 +01:00
|
|
|
|
2020-06-11 18:40:52 +02:00
|
|
|
if (!resize(term, width, height))
|
|
|
|
|
return;
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
|
2020-02-22 10:46:35 +01:00
|
|
|
for (int i = 0; i < 6; i++, sixel >>= 1) {
|
|
|
|
|
if (sixel & 1) {
|
2020-02-22 10:54:52 +01:00
|
|
|
size_t pixel_row = term->sixel.pos.row * 6 + i;
|
2020-02-22 10:46:35 +01:00
|
|
|
size_t stride = term->sixel.image.width;
|
2020-02-22 10:54:52 +01:00
|
|
|
size_t idx = pixel_row * stride + term->sixel.pos.col;
|
2020-06-10 18:52:53 +02:00
|
|
|
term->sixel.image.data[idx] = color_with_alpha(term, color);
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(sixel == 0);
|
2020-02-22 10:54:52 +01:00
|
|
|
term->sixel.pos.col++;
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
decsixel(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
{
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '"':
|
2020-02-22 11:30:30 +01:00
|
|
|
term->sixel.state = SIXEL_DECGRA;
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
|
term->sixel.state = SIXEL_DECGRI;
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.param = 0;
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '#':
|
2020-02-22 11:30:30 +01:00
|
|
|
term->sixel.state = SIXEL_DECGCI;
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.color_idx = 0;
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
term->sixel.param_idx = 0;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '$':
|
2020-02-22 10:54:52 +01:00
|
|
|
if (term->sixel.pos.col > term->sixel.max_col)
|
|
|
|
|
term->sixel.max_col = term->sixel.pos.col;
|
|
|
|
|
term->sixel.pos.col = 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '-':
|
2020-02-22 10:54:52 +01:00
|
|
|
if (term->sixel.pos.col > term->sixel.max_col)
|
|
|
|
|
term->sixel.max_col = term->sixel.pos.col;
|
|
|
|
|
term->sixel.pos.row++;
|
|
|
|
|
term->sixel.pos.col = 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
case '?'...'~':
|
|
|
|
|
sixel_add(term, term->sixel.palette[term->sixel.color_idx], c - 63);
|
2020-02-21 21:53:23 +01:00
|
|
|
break;
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
case ' ':
|
|
|
|
|
case '\n':
|
|
|
|
|
case '\r':
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
default:
|
2020-06-09 17:34:04 +02:00
|
|
|
LOG_WARN("invalid sixel character: '%c' at idx=%zu", c, count);
|
2020-02-21 21:53:23 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
decgra(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
switch (c) {
|
|
|
|
|
case '0'...'9':
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
2020-02-22 10:46:35 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
case ';':
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
term->sixel.param = 0;
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
default: {
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
int nparams = term->sixel.param_idx;
|
|
|
|
|
unsigned pan = nparams > 0 ? term->sixel.params[0] : 0;
|
|
|
|
|
unsigned pad = nparams > 1 ? term->sixel.params[1] : 0;
|
|
|
|
|
unsigned ph = nparams > 2 ? term->sixel.params[2] : 0;
|
|
|
|
|
unsigned pv = nparams > 3 ? term->sixel.params[3] : 0;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
pan = pan > 0 ? pan : 1;
|
|
|
|
|
pad = pad > 0 ? pad : 1;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u",
|
|
|
|
|
pan, pad, pan / pad, ph, pv);
|
2020-02-22 10:49:00 +01:00
|
|
|
|
2020-02-22 21:03:24 +01:00
|
|
|
if (ph >= term->sixel.image.height && pv >= term->sixel.image.width &&
|
|
|
|
|
ph <= max_height(term) && pv <= max_width(term))
|
|
|
|
|
{
|
2020-06-11 18:40:52 +02:00
|
|
|
if (resize(term, ph, pv))
|
|
|
|
|
term->sixel.image.autosize = false;
|
2020-02-22 21:03:24 +01:00
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
sixel_put(term, c);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
decgri(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
switch (c) {
|
|
|
|
|
case '0'...'9':
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
default:
|
|
|
|
|
//LOG_DBG("repeating '%c' %u times", c, term->sixel.param);
|
|
|
|
|
for (unsigned i = 0; i < term->sixel.param; i++)
|
|
|
|
|
decsixel(term, c);
|
|
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-02-22 11:30:30 +01:00
|
|
|
decgci(struct terminal *term, uint8_t c)
|
2020-02-21 21:53:23 +01:00
|
|
|
{
|
2020-02-22 11:30:30 +01:00
|
|
|
switch (c) {
|
|
|
|
|
case '0'...'9':
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.param *= 10;
|
|
|
|
|
term->sixel.param += c - '0';
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
case ';':
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
2020-02-21 21:53:23 +01:00
|
|
|
term->sixel.param = 0;
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
default: {
|
|
|
|
|
if (term->sixel.param_idx < ALEN(term->sixel.params))
|
|
|
|
|
term->sixel.params[term->sixel.param_idx++] = term->sixel.param;
|
|
|
|
|
|
|
|
|
|
int nparams = term->sixel.param_idx;
|
|
|
|
|
|
2020-02-22 14:02:00 +01:00
|
|
|
if (nparams > 0)
|
|
|
|
|
term->sixel.color_idx = min(term->sixel.params[0], term->sixel.palette_size - 1);
|
2020-02-22 11:30:30 +01:00
|
|
|
|
|
|
|
|
if (nparams > 4) {
|
|
|
|
|
unsigned format = term->sixel.params[1];
|
|
|
|
|
unsigned c1 = term->sixel.params[2];
|
|
|
|
|
unsigned c2 = term->sixel.params[3];
|
|
|
|
|
unsigned c3 = term->sixel.params[4];
|
2020-02-21 21:53:23 +01:00
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
switch (format) {
|
|
|
|
|
case 1: { /* HLS */
|
2020-02-22 21:18:55 +01:00
|
|
|
uint32_t rgb = hls_to_rgb(c1, c2, c3);
|
|
|
|
|
LOG_DBG("setting palette #%d = HLS %hhu/%hhu/%hhu (0x%06x)",
|
|
|
|
|
term->sixel.color_idx, c1, c2, c3, rgb);
|
|
|
|
|
term->sixel.palette[term->sixel.color_idx] = rgb;
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 2: { /* RGB */
|
2020-02-21 21:53:23 +01:00
|
|
|
uint8_t r = 255 * c1 / 100;
|
|
|
|
|
uint8_t g = 255 * c2 / 100;
|
|
|
|
|
uint8_t b = 255 * c3 / 100;
|
|
|
|
|
|
|
|
|
|
LOG_DBG("setting palette #%d = RGB %hhu/%hhu/%hhu",
|
|
|
|
|
term->sixel.color_idx, r, g, b);
|
|
|
|
|
|
|
|
|
|
term->sixel.palette[term->sixel.color_idx] = r << 16 | g << 8 | b;
|
2020-02-22 11:30:30 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 11:30:30 +01:00
|
|
|
term->sixel.state = SIXEL_DECSIXEL;
|
|
|
|
|
sixel_put(term, c);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_put(struct terminal *term, uint8_t c)
|
|
|
|
|
{
|
|
|
|
|
switch (term->sixel.state) {
|
2020-02-22 11:30:30 +01:00
|
|
|
case SIXEL_DECSIXEL: decsixel(term, c); break;
|
|
|
|
|
case SIXEL_DECGRA: decgra(term, c); break;
|
|
|
|
|
case SIXEL_DECGRI: decgri(term, c); break;
|
|
|
|
|
case SIXEL_DECGCI: decgci(term, c); break;
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
2020-02-22 11:30:30 +01:00
|
|
|
|
|
|
|
|
count++;
|
2020-02-21 21:53:23 +01:00
|
|
|
}
|
2020-02-22 14:02:00 +01:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_colors_report_current(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
char reply[24];
|
|
|
|
|
snprintf(reply, sizeof(reply), "\033[?1;0;%uS", term->sixel.palette_size);
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
2020-02-22 21:03:24 +01:00
|
|
|
LOG_DBG("query response for current color count: %u", term->sixel.palette_size);
|
2020-02-22 14:02:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_colors_reset(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
LOG_DBG("sixel palette size reset to %u", SIXEL_MAX_COLORS);
|
2020-06-10 18:36:54 +02:00
|
|
|
|
|
|
|
|
free(term->sixel.palette);
|
|
|
|
|
term->sixel.palette = NULL;
|
|
|
|
|
|
2020-02-24 18:42:04 +01:00
|
|
|
term->sixel.palette_size = SIXEL_MAX_COLORS;
|
|
|
|
|
sixel_colors_report_current(term);
|
2020-02-22 14:02:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_colors_set(struct terminal *term, unsigned count)
|
|
|
|
|
{
|
|
|
|
|
unsigned new_palette_size = min(max(2, count), SIXEL_MAX_COLORS);
|
|
|
|
|
LOG_DBG("sixel palette size set to %u", new_palette_size);
|
2020-02-24 18:42:04 +01:00
|
|
|
|
2020-06-10 18:36:54 +02:00
|
|
|
free(term->sixel.palette);
|
|
|
|
|
term->sixel.palette = NULL;
|
|
|
|
|
|
2020-02-24 18:42:04 +01:00
|
|
|
term->sixel.palette_size = new_palette_size;
|
|
|
|
|
sixel_colors_report_current(term);
|
2020-02-22 14:02:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_colors_report_max(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
char reply[24];
|
|
|
|
|
snprintf(reply, sizeof(reply), "\033[?1;0;%uS", SIXEL_MAX_COLORS);
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
2020-02-22 21:03:24 +01:00
|
|
|
LOG_DBG("query response for max color count: %u", SIXEL_MAX_COLORS);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_geometry_report_current(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
char reply[64];
|
|
|
|
|
snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS",
|
|
|
|
|
max_width(term), max_height(term));
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
|
|
|
|
|
|
|
|
|
LOG_DBG("query response for current sixel geometry: %ux%u",
|
|
|
|
|
max_width(term), max_height(term));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_geometry_reset(struct terminal *term)
|
|
|
|
|
{
|
2020-02-24 18:42:04 +01:00
|
|
|
LOG_DBG("sixel geometry reset to %ux%u", max_width(term), max_height(term));
|
2020-02-22 21:03:24 +01:00
|
|
|
term->sixel.max_width = 0;
|
|
|
|
|
term->sixel.max_height = 0;
|
2020-02-24 18:42:04 +01:00
|
|
|
sixel_geometry_report_current(term);
|
2020-02-22 21:03:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_geometry_set(struct terminal *term, unsigned width, unsigned height)
|
|
|
|
|
{
|
2020-02-24 18:42:04 +01:00
|
|
|
LOG_DBG("sixel geometry set to %ux%u", width, height);
|
2020-02-22 21:03:24 +01:00
|
|
|
term->sixel.max_width = width;
|
|
|
|
|
term->sixel.max_height = height;
|
2020-02-24 18:42:04 +01:00
|
|
|
sixel_geometry_report_current(term);
|
2020-02-22 21:03:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
sixel_geometry_report_max(struct terminal *term)
|
|
|
|
|
{
|
|
|
|
|
unsigned max_width = term->cols * term->cell_width;
|
|
|
|
|
unsigned max_height = term->rows * term->cell_height;
|
|
|
|
|
|
|
|
|
|
char reply[64];
|
|
|
|
|
snprintf(reply, sizeof(reply), "\033[?2;0;%u;%uS", max_width, max_height);
|
|
|
|
|
term_to_slave(term, reply, strlen(reply));
|
|
|
|
|
|
|
|
|
|
LOG_DBG("query response for max sixel geometry: %ux%u",
|
|
|
|
|
max_width, max_height);
|
2020-02-22 14:02:00 +01:00
|
|
|
}
|