Rendering of underline/strikethrough

This commit is contained in:
Kovid Goyal 2016-11-23 09:01:07 +05:30
parent e53c3076ef
commit aca53d4e2b
7 changed files with 65 additions and 27 deletions

View File

@ -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);
}
''')
# }}}

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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));

View File

@ -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

View File

@ -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;
}