mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-03-26 07:57:59 -04:00
sixel: abuse wmemset() when initializing a freshly allocated image buffer
wmemset() is heavily optimized, and in some cases, *much* faster than manually initializing the new image pixels. Furthermore, assume calloc() is better at initializing memory to zero, and use that when initializing new pixels in a transparent image.
This commit is contained in:
parent
dcd4ab4ab8
commit
a99434929c
1 changed files with 77 additions and 37 deletions
114
sixel.c
114
sixel.c
|
|
@ -1385,21 +1385,29 @@ sixel_unhook(struct terminal *term)
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void ALWAYS_INLINE inline
|
||||||
resize_horizontally(struct terminal *term, int new_width)
|
memset_u32(uint32_t *data, uint32_t value, size_t count)
|
||||||
{
|
{
|
||||||
if (unlikely(new_width > term->sixel.max_width)) {
|
static_assert(sizeof(wchar_t) == 4, "wchar_t is not 4 bytes");
|
||||||
|
wmemset((wchar_t *)data, (wchar_t)value, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
resize_horizontally(struct terminal *term, int new_width_mutable)
|
||||||
|
{
|
||||||
|
if (unlikely(new_width_mutable > term->sixel.max_width)) {
|
||||||
LOG_WARN("maximum image dimensions exceeded, truncating");
|
LOG_WARN("maximum image dimensions exceeded, truncating");
|
||||||
new_width = term->sixel.max_width;
|
new_width_mutable = term->sixel.max_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(term->sixel.image.width == new_width))
|
if (unlikely(term->sixel.image.width == new_width_mutable))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int sixel_row_height = 6 * term->sixel.pan;
|
const int sixel_row_height = 6 * term->sixel.pan;
|
||||||
|
|
||||||
uint32_t *old_data = term->sixel.image.data;
|
uint32_t *old_data = term->sixel.image.data;
|
||||||
const int old_width = term->sixel.image.width;
|
const int old_width = term->sixel.image.width;
|
||||||
|
const int new_width = new_width_mutable;
|
||||||
|
|
||||||
int height;
|
int height;
|
||||||
if (unlikely(term->sixel.image.height == 0)) {
|
if (unlikely(term->sixel.image.height == 0)) {
|
||||||
|
|
@ -1424,13 +1432,13 @@ resize_horizontally(struct terminal *term, int new_width)
|
||||||
uint32_t bg = term->sixel.default_bg;
|
uint32_t bg = term->sixel.default_bg;
|
||||||
|
|
||||||
/* Copy old rows, and initialize new columns to background color */
|
/* Copy old rows, and initialize new columns to background color */
|
||||||
for (int r = 0; r < height; r++) {
|
const uint32_t *end = &new_data[alloc_height * new_width];
|
||||||
memcpy(&new_data[r * new_width],
|
for (uint32_t *n = new_data, *o = old_data;
|
||||||
&old_data[r * old_width],
|
n < end;
|
||||||
old_width * sizeof(uint32_t));
|
n += new_width, o += old_width)
|
||||||
|
{
|
||||||
for (int c = old_width; c < new_width; c++)
|
memcpy(n, o, old_width * sizeof(uint32_t));
|
||||||
new_data[r * new_width + c] = bg;
|
memset_u32(&n[old_width], bg, new_width - old_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(old_data);
|
free(old_data);
|
||||||
|
|
@ -1443,7 +1451,7 @@ resize_horizontally(struct terminal *term, int new_width)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
resize_vertically(struct terminal *term, int new_height)
|
resize_vertically(struct terminal *term, const int new_height)
|
||||||
{
|
{
|
||||||
LOG_DBG("resizing image vertically: (%d)x%d -> (%d)x%d",
|
LOG_DBG("resizing image vertically: (%d)x%d -> (%d)x%d",
|
||||||
term->sixel.image.width, term->sixel.image.height,
|
term->sixel.image.width, term->sixel.image.height,
|
||||||
|
|
@ -1477,13 +1485,11 @@ resize_vertically(struct terminal *term, int new_height)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t bg = term->sixel.default_bg;
|
const uint32_t bg = term->sixel.default_bg;
|
||||||
|
|
||||||
/* Initialize new rows to background color */
|
memset_u32(&new_data[old_height * width],
|
||||||
for (int r = old_height; r < new_height; r++) {
|
bg,
|
||||||
for (int c = 0; c < width; c++)
|
(alloc_height - old_height) * width);
|
||||||
new_data[r * width + c] = bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
term->sixel.image.height = new_height;
|
term->sixel.image.height = new_height;
|
||||||
term->sixel.image.alloc_height = alloc_height;
|
term->sixel.image.alloc_height = alloc_height;
|
||||||
|
|
@ -1498,43 +1504,69 @@ resize_vertically(struct terminal *term, int new_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
resize(struct terminal *term, int new_width, int new_height)
|
resize(struct terminal *term, int new_width_mutable, int new_height_mutable)
|
||||||
{
|
{
|
||||||
LOG_DBG("resizing image: %dx%d -> %dx%d",
|
LOG_DBG("resizing image: %dx%d -> %dx%d",
|
||||||
term->sixel.image.width, term->sixel.image.height,
|
term->sixel.image.width, term->sixel.image.height,
|
||||||
new_width, new_height);
|
new_width, new_height);
|
||||||
|
|
||||||
if (unlikely(new_width > term->sixel.max_width)) {
|
if (unlikely(new_width_mutable > term->sixel.max_width)) {
|
||||||
LOG_WARN("maximum image width exceeded, truncating");
|
LOG_WARN("maximum image width exceeded, truncating");
|
||||||
new_width = term->sixel.max_width;
|
new_width_mutable = term->sixel.max_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(new_height > term->sixel.max_height)) {
|
if (unlikely(new_height_mutable > term->sixel.max_height)) {
|
||||||
LOG_WARN("maximum image height exceeded, truncating");
|
LOG_WARN("maximum image height exceeded, truncating");
|
||||||
new_height = term->sixel.max_height;
|
new_height_mutable = term->sixel.max_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint32_t *old_data = term->sixel.image.data;
|
uint32_t *old_data = term->sixel.image.data;
|
||||||
const int old_width = term->sixel.image.width;
|
const int old_width = term->sixel.image.width;
|
||||||
const int old_height = term->sixel.image.height;
|
const int old_height = term->sixel.image.height;
|
||||||
|
const int new_width = new_width_mutable;
|
||||||
|
const int new_height = new_height_mutable;
|
||||||
|
|
||||||
if (unlikely(old_width == new_width && old_height == new_height))
|
if (unlikely(old_width == new_width && old_height == new_height))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const int sixel_row_height = 6 * term->sixel.pan;
|
const int sixel_row_height = 6 * term->sixel.pan;
|
||||||
int alloc_new_width = new_width;
|
const int alloc_new_height =
|
||||||
int alloc_new_height = (new_height + sixel_row_height - 1) / sixel_row_height * sixel_row_height;
|
(new_height + sixel_row_height - 1) / sixel_row_height * sixel_row_height;
|
||||||
|
|
||||||
xassert(alloc_new_height >= new_height);
|
xassert(alloc_new_height >= new_height);
|
||||||
xassert(alloc_new_height - new_height < sixel_row_height);
|
xassert(alloc_new_height - new_height < sixel_row_height);
|
||||||
|
|
||||||
uint32_t *new_data = NULL;
|
uint32_t *new_data = NULL;
|
||||||
uint32_t bg = term->sixel.default_bg;
|
const uint32_t bg = term->sixel.default_bg;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the image is resized horizontally, or if it's opaque, we
|
||||||
|
* need to explicitly initialize the "new" pixels.
|
||||||
|
*
|
||||||
|
* When the image is *not* resized horizontally, we simply do a
|
||||||
|
* realloc(). In this case, there's no need to manually copy the
|
||||||
|
* old pixels. We do however need to initialize the new pixels
|
||||||
|
* since realloc() returns uninitialized memory.
|
||||||
|
*
|
||||||
|
* When the image *is* resized horizontally, we need to allocate
|
||||||
|
* new memory (when the width changes, the stride changes, and
|
||||||
|
* thus we cannot simply realloc())
|
||||||
|
*
|
||||||
|
* If the default background is transparent, the new pixels need
|
||||||
|
* to be initialized to 0x0. We do this by using calloc().
|
||||||
|
*
|
||||||
|
* If the default background is opaque, then we need to manually
|
||||||
|
* initialize the new pixels.
|
||||||
|
*/
|
||||||
|
const bool initialize_bg =
|
||||||
|
!term->sixel.transparent_bg || new_width == old_width;
|
||||||
|
|
||||||
if (new_width == old_width) {
|
if (new_width == old_width) {
|
||||||
/* Width (and thus stride) is the same, so we can simply
|
/* Width (and thus stride) is the same, so we can simply
|
||||||
* re-alloc the existing buffer */
|
* re-alloc the existing buffer */
|
||||||
|
|
||||||
new_data = realloc(old_data, alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
new_data = realloc(old_data, new_width * alloc_new_height * sizeof(uint32_t));
|
||||||
if (new_data == NULL) {
|
if (new_data == NULL) {
|
||||||
LOG_ERRNO("failed to reallocate sixel image buffer");
|
LOG_ERRNO("failed to reallocate sixel image buffer");
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1545,22 +1577,30 @@ resize(struct terminal *term, int new_width, int new_height)
|
||||||
} else {
|
} else {
|
||||||
/* Width (and thus stride) change - need to allocate a new buffer */
|
/* Width (and thus stride) change - need to allocate a new buffer */
|
||||||
xassert(new_width > old_width);
|
xassert(new_width > old_width);
|
||||||
new_data = xmalloc(alloc_new_width * alloc_new_height * sizeof(uint32_t));
|
const size_t pixels = new_width * alloc_new_height;
|
||||||
|
|
||||||
|
new_data = !initialize_bg
|
||||||
|
? xcalloc(pixels, sizeof(uint32_t))
|
||||||
|
: xmalloc(pixels * sizeof(uint32_t));
|
||||||
|
|
||||||
/* Copy old rows, and initialize new columns to background color */
|
/* Copy old rows, and initialize new columns to background color */
|
||||||
for (int r = 0; r < min(old_height, new_height); r++) {
|
const int row_copy_count = min(old_height, alloc_new_height);
|
||||||
memcpy(&new_data[r * new_width], &old_data[r * old_width], old_width * sizeof(uint32_t));
|
const uint32_t *end = &new_data[row_copy_count * new_width];
|
||||||
|
|
||||||
for (int c = old_width; c < new_width; c++)
|
for (uint32_t *n = new_data, *o = old_data;
|
||||||
new_data[r * new_width + c] = bg;
|
n < end;
|
||||||
|
n += new_width, o += old_width)
|
||||||
|
{
|
||||||
|
memcpy(n, o, old_width * sizeof(uint32_t));
|
||||||
|
memset_u32(&n[old_width], bg, new_width - old_width);
|
||||||
}
|
}
|
||||||
free(old_data);
|
free(old_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize new rows to background color */
|
if (initialize_bg) {
|
||||||
for (int r = old_height; r < new_height; r++) {
|
memset_u32(&new_data[old_height * new_width],
|
||||||
for (int c = 0; c < new_width; c++)
|
bg,
|
||||||
new_data[r * new_width + c] = bg;
|
(alloc_new_height - old_height) * new_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
xassert(new_data != NULL);
|
xassert(new_data != NULL);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue