From aca53d4e2b1f0c787b06882e807c1f82b515c320 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 23 Nov 2016 09:01:07 +0530 Subject: [PATCH] Rendering of underline/strikethrough --- kitty/char_grid.py | 65 ++++++++++++++++++++++++++++++++++----------- kitty/data-types.h | 4 ++- kitty/develop_gl.py | 2 ++ kitty/fonts.py | 6 ++--- kitty/screen.c | 2 +- kitty/shaders.py | 4 +-- kitty/sprites.c | 9 ++++--- 7 files changed, 65 insertions(+), 27 deletions(-) diff --git a/kitty/char_grid.py b/kitty/char_grid.py index f8859d027..5dec5c00d 100644 --- a/kitty/char_grid.py +++ b/kitty/char_grid.py @@ -23,6 +23,9 @@ Size = namedtuple('Size', 'width height') Cursor = namedtuple('Cursor', 'x y hidden shape color blink') ScreenGeometry = namedtuple('ScreenGeometry', 'xstart ystart xnum ynum dx dy') +if DATA_CELL_SIZE % 3: + raise ValueError('Incorrect data cell size, must be a multiple of 3') + # cell shader {{{ cell_shader = ( @@ -32,8 +35,11 @@ uniform vec4 steps; // xstart, ystart, dx, dy uniform vec2 sprite_layout; // dx, dy uniform usamplerBuffer sprite_map; // gl_InstanceID -> x, y, z out vec3 sprite_pos; -out vec4 foreground; -out vec4 background; +out vec3 underline_pos; +out vec3 strike_pos; +out vec3 foreground; +out vec3 background; +out vec3 decoration_fg; const uvec2 pos_map[] = uvec2[4]( uvec2(1, 0), // right, top @@ -42,12 +48,22 @@ const uvec2 pos_map[] = uvec2[4]( uvec2(0, 0) // left, top ); -vec4 to_color(uint c) { +const uint BYTE_MASK = uint(255); +const uint ZERO = uint(0); +const uint SMASK = uint(3); + +vec3 to_color(uint c) { uint r, g, b; - r = (c >> 16) & uint(255); - g = (c >> 8) & uint(255); - b = c & uint(255); - return vec4(r / 255.0, g / 255.0, b / 255.0, 1); + r = (c >> 16) & BYTE_MASK; + g = (c >> 8) & BYTE_MASK; + b = c & BYTE_MASK; + return vec3(r / 255.0, g / 255.0, b / 255.0); +} + +vec3 to_sprite_pos(uvec2 pos, uint x, uint y, uint z) { + vec2 s_xpos = vec2(x, x + 1.0) * sprite_layout[0]; + vec2 s_ypos = vec2(y, y + 1.0) * sprite_layout[1]; + return vec3(s_xpos[pos[0]], s_ypos[pos[1]], z); } void main() { @@ -61,27 +77,44 @@ void main() { uvec2 pos = pos_map[gl_VertexID]; gl_Position = vec4(xpos[pos[0]], ypos[pos[1]], 0, 1); - int sprite_id = int(instance_id) * 3; + int sprite_id = int(instance_id) * STRIDE; uvec4 spos = texelFetch(sprite_map, sprite_id); - vec2 s_xpos = vec2(spos[0], spos[0] + 1.0) * sprite_layout[0]; - vec2 s_ypos = vec2(spos[1], spos[1] + 1.0) * sprite_layout[1]; - sprite_pos = vec3(s_xpos[pos[0]], s_ypos[pos[1]], spos[2]); uvec4 colors = texelFetch(sprite_map, sprite_id + 1); + sprite_pos = to_sprite_pos(pos, spos[0], spos[1], spos[2]); foreground = to_color(colors[0]); background = to_color(colors[1]); + uint decoration = colors[2]; + decoration_fg = to_color(decoration); + underline_pos = to_sprite_pos(pos, (decoration >> 24) & SMASK, ZERO, ZERO); + strike_pos = to_sprite_pos(pos, (decoration >> 26) & SMASK, ZERO, ZERO); } -''', +'''.replace('STRIDE', str(DATA_CELL_SIZE // 3)), '''\ uniform sampler2DArray sprites; in vec3 sprite_pos; -in vec4 foreground; -in vec4 background; +in vec3 underline_pos; +in vec3 strike_pos; +in vec3 foreground; +in vec3 background; +in vec3 decoration_fg; out vec4 final_color; +vec3 blend(float alpha, vec3 over, vec3 under) { + return over + (1 - alpha) * under; +} + void main() { - float alpha = texture(sprites, sprite_pos).r; - final_color = background * (1 - alpha) + foreground * alpha; + float text_alpha = texture(sprites, sprite_pos).r; + float underline_alpha = texture(sprites, underline_pos).r; + float strike_alpha = texture(sprites, strike_pos).r; + vec3 underline = underline_alpha * decoration_fg; + vec3 strike = strike_alpha * foreground; + vec3 fg = text_alpha * foreground; + vec3 decoration = blend(underline_alpha, underline, strike); + vec3 combined_fg = blend(text_alpha, fg, decoration); + float combined_alpha = max(max(underline_alpha, strike_alpha), text_alpha); + final_color = vec4(blend(combined_alpha, combined_fg, background), 1); } ''') # }}} diff --git a/kitty/data-types.h b/kitty/data-types.h index d6160973b..d740bd105 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -22,13 +22,15 @@ typedef uint32_t decoration_type; typedef uint32_t combining_type; typedef unsigned int index_type; #define CELL_SIZE (sizeof(char_type) + sizeof(color_type) + sizeof(decoration_type) + sizeof(combining_type)) -#define DATA_CELL_SIZE 9 +// The data cell size must be a multiple of 3 +#define DATA_CELL_SIZE 2 * 3 #define CHAR_MASK 0xFFFFFF #define ATTRS_SHIFT 24 #define ATTRS_MASK_WITHOUT_WIDTH 0xFC000000 #define WIDTH_MASK 3 #define DECORATION_SHIFT 2 +#define DECORATION_MASK 3 #define BOLD_SHIFT 4 #define ITALIC_SHIFT 5 #define REVERSE_SHIFT 6 diff --git a/kitty/develop_gl.py b/kitty/develop_gl.py index ba72afe3f..21898ca67 100644 --- a/kitty/develop_gl.py +++ b/kitty/develop_gl.py @@ -66,6 +66,8 @@ class Renderer: fg, bg = self.color_pairs[i % 3] c.fg = (fg << 8) | 3 c.bg = (bg << 8) | 3 + c.decoration = 2 + c.strikethrough = True c.x = x line.set_text('%d' % (i % 10), 0, 1, c) self.sprites.backend.update_cell_data(line, 0, sg.xnum - 1, self.color_profile, 0xffffff, 0, ctypes.addressof(data)) diff --git a/kitty/fonts.py b/kitty/fonts.py index 4082e85c9..320f0003e 100644 --- a/kitty/fonts.py +++ b/kitty/fonts.py @@ -249,7 +249,7 @@ def add_curl(buf, position, thickness): buf[offset + x] = 255 -def render_cell(text=' ', bold=False, italic=False, underline=0, strikeout=False): +def render_cell(text=' ', bold=False, italic=False, underline=0, strikethrough=False): # TODO: Handle non-normalizable combining chars. Probably need to use # harfbuzz for that text = unicodedata.normalize('NFC', text)[0] @@ -272,7 +272,7 @@ def render_cell(text=' ', bold=False, italic=False, underline=0, strikeout=False if underline == 2: t = min(cell_height - underline_position - 1, t) dl(add_curl if underline == 2 else add_line, underline_position, t) - if strikeout: + if strikethrough: pos = int(0.65 * baseline) dl(add_line, pos, underline_thickness) return first, second @@ -315,7 +315,7 @@ def test_rendering(text='\'Ping👁a⧽', sz=144, family='Ubuntu Mono for Kov set_font_family(family, sz) cells = [] for c in text: - f, s = render_cell(c, underline=2, strikeout=True) + f, s = render_cell(c, underline=2, strikethrough=True) cells.append(f) if s is not None: cells.append(s) diff --git a/kitty/screen.c b/kitty/screen.c index e6a9a0796..f89483e32 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1045,7 +1045,6 @@ set_scroll_cell_data(Screen *self, PyObject *args) { if (!PyArg_ParseTuple(args, "O!O!O!kkIO", &SpriteMap_Type, &spm, &ColorProfile_Type, &color_profile, &PyLong_Type, &sp, &default_fg, &default_bg, &scrolled_by, &dp)) return NULL; data = PyLong_AsVoidPtr(dp); src = PyLong_AsVoidPtr(sp); - unsigned int line_size = 9 * self->columns; scrolled_by = MIN(self->historybuf->count, scrolled_by); @@ -1056,6 +1055,7 @@ set_scroll_cell_data(Screen *self, PyObject *args) { } if (scrolled_by < self->lines) { // Less than a full screen has been scrolled, copy some lines from the screen buffer to the scroll buffer + unsigned int line_size = DATA_CELL_SIZE * self->columns; index_type num_to_copy = self->lines - scrolled_by; index_type offset = line_size * scrolled_by; memcpy(data + offset, src, line_size * num_to_copy * sizeof(unsigned int)); diff --git a/kitty/shaders.py b/kitty/shaders.py index d92a5cd5f..a01dba753 100644 --- a/kitty/shaders.py +++ b/kitty/shaders.py @@ -69,10 +69,8 @@ class Sprites: send() # blank send(underline=1) - send(strikeout=True) - send(underline=1, strikeout=True) send(underline=2) - if send(underline=2, strikeout=True) != 5: + if send(strikethrough=True) != 3: raise RuntimeError('Available OpenGL texture size is too small') @property diff --git a/kitty/sprites.c b/kitty/sprites.c index 26c2fd971..9c4c21ed5 100644 --- a/kitty/sprites.c +++ b/kitty/sprites.c @@ -139,7 +139,6 @@ position_for(SpriteMap *self, PyObject *args) { return Py_BuildValue("III", pos->x, pos->y, pos->z); } - bool update_cell_range_data(SpriteMap *self, Line *line, unsigned int xstart, unsigned int xmax, ColorProfile *color_profile, const uint32_t default_bg, const uint32_t default_fg, unsigned int *data) { SpritePosition *sp; @@ -153,13 +152,17 @@ update_cell_range_data(SpriteMap *self, Line *line, unsigned int xstart, unsigne if (previous_width == 2) sp = sprite_position_for(self, previous_ch, 0, true, &err); else sp = sprite_position_for(self, ch, line->combining_chars[i], false, &err); if (sp == NULL) { set_sprite_error(err); return false; } + char_type attrs = ch >> ATTRS_SHIFT; + unsigned int decoration = (attrs >> DECORATION_SHIFT) & DECORATION_MASK; + unsigned int strikethrough = ((attrs >> STRIKE_SHIFT) & 1) ? 3 : 0; data[offset] = sp->x; data[offset+1] = sp->y; data[offset+2] = sp->z; data[offset+3] = to_color(color_profile, line->colors[i] & COL_MASK, default_fg); data[offset+4] = to_color(color_profile, line->colors[i] >> COL_SHIFT, default_bg); - data[offset+5] = to_color(color_profile, line->decoration_fg[i] & COL_MASK, default_fg); - previous_ch = ch; previous_width = (ch >> ATTRS_SHIFT) & WIDTH_MASK; + unsigned int decoration_fg = decoration > 1 ? to_color(color_profile, line->decoration_fg[i] & COL_MASK, data[offset+3]) : data[offset+3]; + data[offset+5] = (decoration_fg & COL_MASK) | (decoration << 24) | (strikethrough << 26); + previous_ch = ch; previous_width = (attrs) & WIDTH_MASK; } return true; }