termptyesc: cache true color approximations

On my stupid benchmark ( https://github.com/billiob/fire ), this goes from
about 45 fps to about 125fps.
This commit is contained in:
Boris Faure 2020-04-22 23:38:53 +02:00
parent f497b9446c
commit f9b6f88be1
Signed by: borisfaure
GPG Key ID: 35C0410516166BE8
1 changed files with 86 additions and 8 deletions

View File

@ -654,12 +654,77 @@ _csi_truecolor_arg_get(Termpty *ty, Eina_Unicode **ptr)
return sum;
}
#if !defined(ENABLE_FUZZING)
/*********************************
* cache true color approximations
*********************************
* Approximating true colors is costly since it needs to compare 256 colors.
* The following considers that a few colors are used a lot. For example, one
* can consider that a text editor with syntax highlighting would only use a
* small palette.
* Given that, using the cache needs to be efficient: it needs to speed up the
* approximation when the color is already in the cache but not knowing the
* color requested is not in the cache needs to be efficient too.
*
* The cache is an array of @TCC_LEN uint32_t.
* Each entry has on the MSB the color as 3 uint8_t (R,G,B) and the LSB is
* the approximated color.
* Searching a color is simply going through the array and testing the mask on
* the 3 most significant bytes.
* When a color is found, it is bubbled up towards the start of the array by
* swapping this entry with the one above. This way the array is ordered to
* get most used colors as fast as possible.
* When a color is not found, the slow process of comparing it with the 256
* colors is used. Then the result is inserted in the lower half of the
* array. This ensures that new entries can live a bit and not be removed by
* the next new color. Using a modulo with a prime number makes for a
* nice-enough random generator to figure out where to insert.
*/
#define TCC_LEN 32
#define TCC_PRIME 17 /* smallest prime number larger than @TCC_LEN/2 */
static struct {
uint32_t colors[TCC_LEN];
} _truecolor_cache;
static int _tcc_random_pos = 0;
static int
static Eina_Bool
_tcc_find(const uint32_t color_msb, uint8_t *chosen_color)
{
int i;
for (i = 0; i < TCC_LEN; i++)
{
if ((_truecolor_cache.colors[i] & 0xffffff00) == color_msb)
{
*chosen_color = _truecolor_cache.colors[i] & 0xff;
/* bubble up this result */
if (i > 0)
{
uint32_t prev = _truecolor_cache.colors[i - 1];
_truecolor_cache.colors[i - 1] = _truecolor_cache.colors[i];
_truecolor_cache.colors[i] = prev;
}
return EINA_TRUE;
}
}
return EINA_FALSE;
}
static void
_tcc_insert(const uint32_t color_msb, const uint8_t approximated)
{
uint32_t c = color_msb | approximated;
int i;
i = (TCC_LEN / 2) + ((_tcc_random_pos + TCC_PRIME) % (TCC_LEN / 2));
_truecolor_cache.colors[i] = c;
}
#endif
static uint8_t
_approximate_truecolor_rgb(Termpty *ty, int r0, int g0, int b0)
{
int chosen_color = COL_DEF;
/* TODO: use the function in tests */
uint8_t chosen_color = COL_DEF;
#if defined(ENABLE_FUZZING)
(void) ty;
(void) r0;
@ -669,14 +734,16 @@ _approximate_truecolor_rgb(Termpty *ty, int r0, int g0, int b0)
int c;
int distance_min = INT_MAX;
Evas_Object *textgrid;
const uint32_t color_msb = (r0 << 24) | (g0 << 16) | (b0 << 8);
DBG("approximating r:%d g:%d b:%d", r0, g0, b0);
if (_tcc_find(color_msb, &chosen_color))
return chosen_color;
textgrid = termio_textgrid_get(ty->obj);
for (c = 0; c < 256; c++)
{
int r1 = 0, g1 = 0, b1 = 0, a1 = 0;
int delta_red_sq, delta_green_sq, delta_blue_sq, middle_red;
int delta_red_sq, delta_green_sq, delta_blue_sq, red_mean;
int distance;
evas_object_textgrid_palette_get(textgrid,
@ -684,23 +751,34 @@ _approximate_truecolor_rgb(Termpty *ty, int r0, int g0, int b0)
c, &r1, &g1, &b1, &a1);
/* Compute the color distance
* XXX: this is inacurate but should give good enough results.
* See * https://en.wikipedia.org/wiki/Color_difference
* See https://en.wikipedia.org/wiki/Color_difference
*/
middle_red = (r0 + r1) / 2;
red_mean = (r0 + r1) / 2;
delta_red_sq = (r0 - r1) * (r0 - r1);
delta_green_sq = (g0 - g1) * (g0 - g1);
delta_blue_sq = (b0 - b1) * (b0 - b1);
#if 1
distance = 2 * delta_red_sq
+ 4 * delta_green_sq
+ 3 * delta_blue_sq
+ ((middle_red) * (delta_red_sq - delta_blue_sq) / 256);
+ ((red_mean) * (delta_red_sq - delta_blue_sq) / 256);
#else
/* from https://www.compuphase.com/cmetric.htm */
distance = (((512 + red_mean) * delta_red_sq) >> 8)
+ 4 * delta_green_sq
+ (((767 - red_mean) * delta_blue_sq) >> 8);
/* euclidian distance */
distance = delta_red_sq + delta_green_sq + delta_blue_sq;
(void)red_mean;
#endif
if (distance < distance_min)
{
distance_min = distance;
chosen_color = c;
}
}
_tcc_insert(color_msb, chosen_color);
#endif
return chosen_color;
}