parent
0c1ca14782
commit
fe0ecbd9ca
4 changed files with 62 additions and 23 deletions
|
|
@ -15,6 +15,7 @@
|
|||
#endif
|
||||
|
||||
static const color_type NULL_COLOR_VALUE = 0xffffffff;
|
||||
static const color_type GENERATED_COLOR_MASK = 0xff000000;
|
||||
|
||||
static uint32_t FG_BG_256[256] = {
|
||||
0x000000, // 0
|
||||
|
|
@ -122,6 +123,10 @@ set_mark_colors(ColorProfile *self, PyObject *opts) {
|
|||
return true;
|
||||
}
|
||||
|
||||
// Generating 256 color palette {{{
|
||||
// For more information, see
|
||||
// https://gist.github.com/jake-stewart/0a8ea46159a7da2c808e5be2177e1783
|
||||
|
||||
static void
|
||||
color_type_to_lab(color_type color, float *lab) {
|
||||
float r = ((color >> 16) & 0xff) / 255.0f;
|
||||
|
|
@ -168,7 +173,7 @@ lab_to_color_type(float *lab) {
|
|||
uint8_t gb = (uint8_t)(fminf(fmaxf(g, 0.0f), 1.0f) * 255.0f + 0.5f);
|
||||
uint8_t bb = (uint8_t)(fminf(fmaxf(b, 0.0f), 1.0f) * 255.0f + 0.5f);
|
||||
|
||||
return (rb << 16) | (gb << 8) | bb;
|
||||
return GENERATED_COLOR_MASK | (rb << 16) | (gb << 8) | bb;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -178,8 +183,6 @@ lerp_lab(float t, float *a, float *b, float *out) {
|
|||
out[2] = a[2] + t * (b[2] - a[2]);
|
||||
}
|
||||
|
||||
// For more information, see
|
||||
// https://gist.github.com/jake-stewart/0a8ea46159a7da2c808e5be2177e1783
|
||||
static void
|
||||
setup_256_palette_lab(ColorProfile *self, color_type *color_table, bool semantic, float base8_lab[8][3]) {
|
||||
const color_type bg = colorprofile_to_color(self, self->overridden.default_bg, self->configured.default_bg).rgb;
|
||||
|
|
@ -198,6 +201,9 @@ setup_256_palette_lab(ColorProfile *self, color_type *color_table, bool semantic
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
is_generated_color(const color_type c) { return (c & GENERATED_COLOR_MASK) == GENERATED_COLOR_MASK; }
|
||||
|
||||
static void
|
||||
generate_256_palette(ColorProfile *self, color_type *color_table, bool semantic) {
|
||||
float base8_lab[8][3];
|
||||
|
|
@ -217,7 +223,7 @@ generate_256_palette(ColorProfile *self, color_type *color_table, bool semantic)
|
|||
lerp_lab(tg, c0, c1, c4);
|
||||
lerp_lab(tg, c2, c3, c5);
|
||||
for (int b = 0; b < 6; b++) {
|
||||
if (color_table[idx] == NULL_COLOR_VALUE) {
|
||||
if (is_generated_color(color_table[idx])) {
|
||||
float c6[3];
|
||||
lerp_lab(b / 5.0f, c4, c5, c6);
|
||||
color_table[idx] = lab_to_color_type(c6);
|
||||
|
|
@ -231,9 +237,7 @@ generate_256_palette(ColorProfile *self, color_type *color_table, bool semantic)
|
|||
float t = (i + 1) / 25.0f;
|
||||
float lab[3];
|
||||
lerp_lab(t, base8_lab[0], base8_lab[7], lab);
|
||||
if (color_table[idx] == NULL_COLOR_VALUE) {
|
||||
color_table[idx] = lab_to_color_type(lab);
|
||||
}
|
||||
if (is_generated_color(color_table[idx])) color_table[idx] = lab_to_color_type(lab);
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
|
@ -267,7 +271,7 @@ static void
|
|||
fixed_color_palette(color_type *color_table) {
|
||||
init_FG_BG_table();
|
||||
for (unsigned i = 16; i < arraysz(FG_BG_256); i++) {
|
||||
if (color_table[i] == NULL_COLOR_VALUE) color_table[i] = FG_BG_256[i];
|
||||
if (is_generated_color(color_table[i])) color_table[i] = GENERATED_COLOR_MASK | FG_BG_256[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -276,11 +280,15 @@ palette_generation_is_dynamic(PyObject *opts, bool *semantic) {
|
|||
bool ans = false; *semantic = false;
|
||||
if (opts) {
|
||||
RAII_PyObject(policy, PyObject_GetAttrString(opts, "palette_generate")); if (!policy) return ans;
|
||||
ans = PyUnicode_CompareWithASCIIString(policy, "fixed") != 0;
|
||||
*semantic = ans && PyUnicode_CompareWithASCIIString(policy, "semantic") == 0;
|
||||
switch(PyUnicode_AsUTF8(policy)[0]) {
|
||||
case 'f': break;
|
||||
case 's': *semantic = true; /* fallthrough */
|
||||
default: ans = true;
|
||||
}
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
// }}}
|
||||
|
||||
static bool
|
||||
set_colortable(ColorProfile *self, PyObject *opts) {
|
||||
|
|
@ -296,7 +304,7 @@ set_colortable(ColorProfile *self, PyObject *opts) {
|
|||
for (size_t i = 0; i < arraysz(FG_BG_256); i++) self->color_table[i] = color_table[i];
|
||||
if (dynamic_palette) generate_256_palette(self, self->color_table, semantic_generation);
|
||||
else fixed_color_palette(self->color_table);
|
||||
memcpy(self->orig_color_table, self->color_table, arraysz(self->color_table) * sizeof(self->color_table[0]));
|
||||
memcpy(self->orig_color_table, self->color_table, sizeof(self->color_table));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -462,7 +470,7 @@ colorprofile_to_color(const ColorProfile *self, DynamicColor entry, DynamicColor
|
|||
return defval;
|
||||
case COLOR_IS_INDEX: {
|
||||
DynamicColor ans;
|
||||
ans.rgb = self->color_table[entry.rgb & 0xff] & 0xffffff;
|
||||
ans.rgb = self->color_table[entry.rgb & 0xff] & (~GENERATED_COLOR_MASK);
|
||||
ans.type = COLOR_IS_RGB;
|
||||
return ans;
|
||||
}
|
||||
|
|
@ -483,7 +491,7 @@ colorprofile_to_color_with_fallback(ColorProfile *self, DynamicColor entry, Dyna
|
|||
case COLOR_IS_RGB:
|
||||
return entry.rgb;
|
||||
case COLOR_IS_INDEX:
|
||||
return self->color_table[entry.rgb & 0xff] & 0xffffff;
|
||||
return self->color_table[entry.rgb & 0xff] & (~GENERATED_COLOR_MASK);
|
||||
}
|
||||
return entry.rgb;
|
||||
}
|
||||
|
|
@ -494,7 +502,7 @@ colortable_colors_into_dict(ColorProfile *self, unsigned start, unsigned limit,
|
|||
static char buf[32] = {'c', 'o', 'l', 'o', 'r', 0};
|
||||
for (unsigned i = start; i < limit; i++) {
|
||||
snprintf(buf + 5, sizeof(buf) - 6, "%u", i);
|
||||
PyObject *val = PyLong_FromUnsignedLong(self->color_table[i]);
|
||||
PyObject *val = PyLong_FromUnsignedLong(self->color_table[i] &(~GENERATED_COLOR_MASK));
|
||||
if (!val) return false;
|
||||
int ret = PyDict_SetItemString(ans, buf, val);
|
||||
Py_DECREF(val);
|
||||
|
|
@ -575,7 +583,7 @@ as_color(ColorProfile *self, PyObject *val) {
|
|||
switch(t) {
|
||||
case 1:
|
||||
r = (entry >> 8) & 0xff;
|
||||
col = self->color_table[r];
|
||||
col = self->color_table[r] & (~GENERATED_COLOR_MASK);
|
||||
break;
|
||||
case 2:
|
||||
col = entry >> 8;
|
||||
|
|
@ -617,7 +625,7 @@ set_color(ColorProfile *self, PyObject *args) {
|
|||
self->dirty = true;
|
||||
if (val == NULL_COLOR_VALUE && i >= 16) {
|
||||
bool semantic, dynamic = palette_generation_is_dynamic(global_state.options_object, &semantic);
|
||||
self->color_table[i] = dynamic ? generate_256_palette_color(self, self->color_table, i, semantic) : FG_BG_256[i];
|
||||
self->color_table[i] = dynamic ? generate_256_palette_color(self, self->color_table, i, semantic) : GENERATED_COLOR_MASK | FG_BG_256[i];
|
||||
} else self->color_table[i] = val;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
|
@ -626,7 +634,7 @@ void
|
|||
copy_color_table_to_buffer(ColorProfile *self, color_type *buf, int offset, size_t stride) {
|
||||
size_t i;
|
||||
stride = MAX(1u, stride);
|
||||
for (i = 0, buf = buf + offset; i < arraysz(self->color_table); i++, buf += stride) *buf = self->color_table[i];
|
||||
for (i = 0, buf = buf + offset; i < arraysz(self->color_table); i++, buf += stride) *buf = self->color_table[i] & (~GENERATED_COLOR_MASK);
|
||||
// Copy the mark colors
|
||||
for (i = 0; i < arraysz(self->mark_backgrounds); i++) {
|
||||
*buf = self->mark_backgrounds[i]; buf += stride;
|
||||
|
|
@ -778,11 +786,26 @@ static PyMemberDef cp_members[] = {
|
|||
};
|
||||
|
||||
static PyObject*
|
||||
reload_from_opts(ColorProfile *self, PyObject *args UNUSED) {
|
||||
PyObject *opts = global_state.options_object;
|
||||
if (!PyArg_ParseTuple(args, "|O", &opts)) return NULL;
|
||||
palette_color_is_generated(ColorProfile *self, PyObject *x) {
|
||||
if (!PyLong_Check(x)) { PyErr_SetString(PyExc_TypeError, "idx must be an integer"); return NULL; }
|
||||
size_t idx = PyLong_AsUnsignedLong(x);
|
||||
if (idx >= arraysz(FG_BG_256)) { PyErr_SetString(PyExc_IndexError, "idx out of bounds"); return NULL; }
|
||||
return Py_NewRef(is_generated_color(self->color_table[idx]) ? Py_True : Py_False);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
reload_from_opts(ColorProfile *self, PyObject *args, PyObject *kw) {
|
||||
PyObject *opts = NULL;
|
||||
int reset_overriden_colors = 1;
|
||||
static const char* kwlist[] = {"opts", "reset_overriden_colors", NULL};
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kw, "|Op", (char**)kwlist, &opts, &reset_overriden_colors)) return NULL;
|
||||
if (opts == NULL) opts = global_state.options_object;
|
||||
self->dirty = true;
|
||||
if (!set_configured_colors(self, opts)) return NULL;
|
||||
if (reset_overriden_colors) {
|
||||
zero_at_ptr(&self->overridden);
|
||||
memset(self->overriden_transparent_colors, 0, sizeof(self->overriden_transparent_colors));
|
||||
}
|
||||
if (!set_mark_colors(self, opts)) return NULL;
|
||||
if (!set_colortable(self, opts)) return NULL;
|
||||
Py_RETURN_NONE;
|
||||
|
|
@ -826,8 +849,9 @@ static PyMethodDef cp_methods[] = {
|
|||
METHOD(as_color, METH_O)
|
||||
METHOD(reset_color, METH_O)
|
||||
METHOD(set_color, METH_VARARGS)
|
||||
METHODB(palette_color_is_generated, METH_O),
|
||||
METHODB(get_transparent_background_color, METH_O),
|
||||
METHODB(reload_from_opts, METH_VARARGS),
|
||||
{"reload_from_opts", (PyCFunction)(void (*) (void))(reload_from_opts), METH_VARARGS | METH_KEYWORDS, NULL},
|
||||
{"set_transparent_background_color", (PyCFunction)(void(*)(void))set_transparent_background_color, METH_FASTCALL, ""},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ def patch_options_with_color_spec(
|
|||
|
||||
|
||||
def patch_colors(
|
||||
spec: ColorsSpec, transparent_background_colors: TransparentBackgroundColors, configured: bool = False,
|
||||
spec: ColorsSpec, transparent_background_colors: TransparentBackgroundColors = (), configured: bool = False,
|
||||
windows: Sequence[WindowType] | None = None, notify_on_bg_change: bool = True,
|
||||
background_image_options: BackgroundImageOptions | None = None
|
||||
) -> None:
|
||||
|
|
|
|||
|
|
@ -870,7 +870,8 @@ class ColorProfile:
|
|||
def reset_color(self, num: int) -> None:
|
||||
pass
|
||||
|
||||
def reload_from_opts(self, opts: Optional[Options] = None) -> None: ...
|
||||
def reload_from_opts(self, opts: Optional[Options] = None, reset_overriden_colors: bool = True) -> None: ...
|
||||
def palette_color_is_generated(self, idx: int) -> None: ...
|
||||
|
||||
def get_transparent_background_color(self, index: int) -> Color | None: ...
|
||||
def set_transparent_background_color(self, index: int, color: Color | None = None, opacity: float | None = None) -> None: ...
|
||||
|
|
|
|||
|
|
@ -1593,6 +1593,7 @@ def t(q, e=None):
|
|||
t('=fleur', 'move')
|
||||
|
||||
def test_color_profile(self):
|
||||
from kitty.fast_data_types import patch_color_profiles
|
||||
opts = self.set_options({'palette_generate': 'fixed'})
|
||||
c = ColorProfile(opts)
|
||||
for i in range(8):
|
||||
|
|
@ -1616,9 +1617,11 @@ def q(send, expected=None):
|
|||
q({'selection_background': ''})
|
||||
self.assertIsNone(s.color_profile.highlight_bg)
|
||||
q({'selection_background': '?'}, {'selection_background': ''})
|
||||
self.assertTrue(s.color_profile.palette_color_is_generated(213))
|
||||
opts = self.set_options({'palette_generate': 'semantic'})
|
||||
q({'213': ''})
|
||||
q({'213': '?'}, {'213': Color(216, 125, 215)})
|
||||
self.assertTrue(s.color_profile.palette_color_is_generated(213))
|
||||
s.color_profile.reload_from_opts(opts)
|
||||
q({'transparent_background_color9': '?'}, {'transparent_background_color9': '?'})
|
||||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': ''})
|
||||
|
|
@ -1626,6 +1629,17 @@ def q(send, expected=None):
|
|||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 0, 0), 126)})
|
||||
q({'transparent_background_color2': '#ffffff@-1'})
|
||||
q({'transparent_background_color2': '?'}, {'transparent_background_color2': (Color(255, 255, 255), 255)})
|
||||
opts.color114 = Color(1, 1, 4)
|
||||
s.color_profile.reload_from_opts(opts)
|
||||
self.assertTrue(s.color_profile.palette_color_is_generated(213))
|
||||
self.assertFalse(s.color_profile.palette_color_is_generated(114))
|
||||
q({'213': '?'}, {'213': Color(216, 125, 215)})
|
||||
patch_color_profiles(
|
||||
{'background': Color(255, 255, 255), 'foreground': Color(0, 0, 0)}, (), (s.color_profile,), True)
|
||||
self.assertTrue(s.color_profile.palette_color_is_generated(213))
|
||||
self.assertFalse(s.color_profile.palette_color_is_generated(114))
|
||||
q({'213': '?'}, {'213': Color(216, 125, 215)})
|
||||
|
||||
|
||||
def test_multi_cursors(self):
|
||||
s = self.create_screen()
|
||||
|
|
|
|||
Loading…
Reference in a new issue