diff --git a/docs/changelog.rst b/docs/changelog.rst index 32a171d8f..21d1e6674 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -38,7 +38,7 @@ Detailed list of changes 0.28.0 [future] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- Text rendering: Use sRGB correct linear gamma blending for nicer font rendering and better color accuracy with transparent windows. See the options :opt:`text_old_gamma`, :opt:`text_gamma_adjustment` and :opt:`text_contrast` (:pull:`5969`) +- Text rendering: Use sRGB correct linear gamma blending for nicer font rendering and better color accuracy with transparent windows. See the option :opt:`text_composition_strategy` for details. (:pull:`5969`) 0.27.1 [2023-02-07] diff --git a/kitty/options/definition.py b/kitty/options/definition.py index 661536c61..1cfa1e471 100644 --- a/kitty/options/definition.py +++ b/kitty/options/definition.py @@ -233,36 +233,30 @@ the curl will peak once per character, with dense twice. ''' ) -opt('text_old_gamma', 'no', - option_type='to_bool', ctype='bool', - long_text=''' -Revert to using the old (pre 0.28) gamma correction algorithm when rendering text. -This will make some text appear like the strokes are uneven. Dark text on bright backgrounds -will also look thicker while lighter text on darker backgrounds will look thinner. -''' - ) +opt('text_composition_strategy', 'platform', ctype='!text_composition_strategy', long_text=''' +Control how kitty composites text glyphs onto the background color. +The default value of :code:`platform` tries for text rendering as +close to "native" for the platform kitty is running on as possible. -opt('text_gamma_adjustment', '1.0', - option_type='positive_float', ctype='float', - long_text=''' -Adjust the thickness of darker text on lighter backgrounds. Increasing the value -setting will make the text appear thicker while decreasing the value will make it thinner. It -can compensate for some fonts looking too-thin when using the gamma-correct alpha blending. +A value of :code:`legacy` uses the old (pre kitty 0.28) strategy for how glyphs +are composited. This will make dark text on light backgrounds look thicker and +light text on dark backgrounds thinner. It might also make some text appear like +the strokes are uneven. +You can fine tune the actual contrast curve used for glyph composition +by specifying two space separated numbers for this setting. + +The first number is the gamma adjustment, which +controls the thickness of dark text on light backgrounds. Increasing the value will make text appear thicker. +The default value for this is 1.0 on Linux and 1.7 on macOS. Valid values are 0.01 and above. The result is scaled based on the luminance difference between the background and the foreground. Dark text on light backgrounds receives the full impact of the curve while light text on dark -backgrounds is affected very little. Valid values are 0.01 and above. For macOS like text rendering, -a value of ~1.7 usually works well. -''' - ) +backgrounds is affected very little. + +The second number is an additional multiplicative contrast. It is percentage ranging from 0 to 100. +The default value is zero On Linux and 30 on macOS. +''') -opt('text_contrast', '0', - option_type='positive_float', ctype='float', - long_text=''' -Increase text contrast further. This will cause jagged edges due to over saturation if set too high. -The value is a percentage from 0 to 100. For macOS like text rendering, a value of 30 usually works well. -''' - ) egr() # }}} diff --git a/kitty/options/parse.py b/kitty/options/parse.py index 237c19fae..65e95aa39 100644 --- a/kitty/options/parse.py +++ b/kitty/options/parse.py @@ -1278,14 +1278,8 @@ class Parser: def term(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['term'] = str(val) - def text_contrast(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['text_contrast'] = positive_float(val) - - def text_gamma_adjustment(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['text_gamma_adjustment'] = positive_float(val) - - def text_old_gamma(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: - ans['text_old_gamma'] = to_bool(val) + def text_composition_strategy(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: + ans['text_composition_strategy'] = str(val) def touch_scroll_multiplier(self, val: str, ans: typing.Dict[str, typing.Any]) -> None: ans['touch_scroll_multiplier'] = float(val) diff --git a/kitty/options/to-c-generated.h b/kitty/options/to-c-generated.h index 633f9ab31..f8ee784c9 100644 --- a/kitty/options/to-c-generated.h +++ b/kitty/options/to-c-generated.h @@ -58,41 +58,15 @@ convert_from_opts_modify_font(PyObject *py_opts, Options *opts) { } static void -convert_from_python_text_old_gamma(PyObject *val, Options *opts) { - opts->text_old_gamma = PyObject_IsTrue(val); +convert_from_python_text_composition_strategy(PyObject *val, Options *opts) { + text_composition_strategy(val, opts); } static void -convert_from_opts_text_old_gamma(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "text_old_gamma"); +convert_from_opts_text_composition_strategy(PyObject *py_opts, Options *opts) { + PyObject *ret = PyObject_GetAttrString(py_opts, "text_composition_strategy"); if (ret == NULL) return; - convert_from_python_text_old_gamma(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_text_gamma_adjustment(PyObject *val, Options *opts) { - opts->text_gamma_adjustment = PyFloat_AsFloat(val); -} - -static void -convert_from_opts_text_gamma_adjustment(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "text_gamma_adjustment"); - if (ret == NULL) return; - convert_from_python_text_gamma_adjustment(ret, opts); - Py_DECREF(ret); -} - -static void -convert_from_python_text_contrast(PyObject *val, Options *opts) { - opts->text_contrast = PyFloat_AsFloat(val); -} - -static void -convert_from_opts_text_contrast(PyObject *py_opts, Options *opts) { - PyObject *ret = PyObject_GetAttrString(py_opts, "text_contrast"); - if (ret == NULL) return; - convert_from_python_text_contrast(ret, opts); + convert_from_python_text_composition_strategy(ret, opts); Py_DECREF(ret); } @@ -1081,11 +1055,7 @@ convert_opts_from_python_opts(PyObject *py_opts, Options *opts) { if (PyErr_Occurred()) return false; convert_from_opts_modify_font(py_opts, opts); if (PyErr_Occurred()) return false; - convert_from_opts_text_old_gamma(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_text_gamma_adjustment(py_opts, opts); - if (PyErr_Occurred()) return false; - convert_from_opts_text_contrast(py_opts, opts); + convert_from_opts_text_composition_strategy(py_opts, opts); if (PyErr_Occurred()) return false; convert_from_opts_cursor_shape(py_opts, opts); if (PyErr_Occurred()) return false; diff --git a/kitty/options/to-c.h b/kitty/options/to-c.h index 44d8f5fd8..abbb8cb49 100644 --- a/kitty/options/to-c.h +++ b/kitty/options/to-c.h @@ -175,6 +175,31 @@ url_prefixes(PyObject *up, Options *opts) { } } +static void +text_composition_strategy(PyObject *val, Options *opts) { + if (!PyUnicode_Check(val)) { PyErr_SetString(PyExc_TypeError, "text_rendering_strategy must be a string"); return; } + opts->text_old_gamma = false; + opts->text_gamma_adjustment = 1.0f; opts->text_contrast = 0.f; + if (PyUnicode_CompareWithASCIIString(val, "platform") == 0) { +#ifdef __APPLE__ + opts->text_gamma_adjustment = 1.7f; opts->text_contrast = 30.f; +#endif + } + else if (PyUnicode_CompareWithASCIIString(val, "legacy") == 0) { + opts->text_old_gamma = true; + } else { + DECREF_AFTER_FUNCTION PyObject *parts = PyUnicode_Split(val, NULL, 1); + if (PyList_GET_SIZE(parts) != 2) { PyErr_SetString(PyExc_ValueError, "text_rendering_strategy must be of the form number:number"); return; } + DECREF_AFTER_FUNCTION PyObject *ga = PyFloat_FromString(PyList_GET_ITEM(parts, 0)); + if (PyErr_Occurred()) return; + opts->text_gamma_adjustment = MAX(0.01f, PyFloat_AsFloat(ga)); + DECREF_AFTER_FUNCTION PyObject *contrast = PyFloat_FromString(PyList_GET_ITEM(parts, 1)); + if (PyErr_Occurred()) return; + opts->text_contrast = MAX(0.0f, PyFloat_AsFloat(contrast)); + opts->text_contrast = MIN(100.0f, opts->text_contrast); + } +} + static char_type* list_of_chars(PyObject *chars) { if (!PyUnicode_Check(chars)) { PyErr_SetString(PyExc_TypeError, "list_of_chars must be a string"); return NULL; } diff --git a/kitty/options/types.py b/kitty/options/types.py index 6e380be1b..65c4c5286 100644 --- a/kitty/options/types.py +++ b/kitty/options/types.py @@ -443,9 +443,7 @@ option_names = ( # {{{ 'tab_title_max_length', 'tab_title_template', 'term', - 'text_contrast', - 'text_gamma_adjustment', - 'text_old_gamma', + 'text_composition_strategy', 'touch_scroll_multiplier', 'undercurl_style', 'update_check_interval', @@ -597,9 +595,7 @@ class Options: tab_title_max_length: int = 0 tab_title_template: str = '{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}' term: str = 'xterm-kitty' - text_contrast: float = 0 - text_gamma_adjustment: float = 1.0 - text_old_gamma: bool = False + text_composition_strategy: str = 'platform' touch_scroll_multiplier: float = 1.0 undercurl_style: choices_for_undercurl_style = 'thin-sparse' update_check_interval: float = 24.0