Better fix for not using the unsafe to break flag

Recognize special glyphs by comparing the index to the glyph for the
codepoint in the font. Cannot rely on glyph width as many glyphs have
zero width. For example the glyph for space characters.
This commit is contained in:
Kovid Goyal 2017-11-13 07:03:58 +05:30
parent 339c0d2980
commit 9fccc38382
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 68 additions and 32 deletions

View File

@ -44,7 +44,7 @@ typedef struct {
hb_font_t *hb_font; hb_font_t *hb_font;
// Map glyphs to sprite map co-ords // Map glyphs to sprite map co-ords
SpritePosition sprite_map[1024]; SpritePosition sprite_map[1024];
uint8_t dummy_glyph_cache[1 << (8 * sizeof(glyph_index))]; uint8_t special_glyph_cache[1 << (8 * sizeof(glyph_index))];
bool bold, italic; bool bold, italic;
} Font; } Font;
@ -303,6 +303,7 @@ font_for_cell(Cell *cell, Font** font) {
START_ALLOW_CASE_RANGE START_ALLOW_CASE_RANGE
switch(cell->ch) { switch(cell->ch) {
case 0: case 0:
case ' ':
return BLANK_FONT; return BLANK_FONT;
case 0x2500 ... 0x2570: case 0x2500 ... 0x2570:
case 0x2574 ... 0x257f: case 0x2574 ... 0x257f:
@ -414,6 +415,7 @@ static inline void
render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, uint64_t extra_glyphs) { render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_glyph_info_t *info, hb_glyph_position_t *positions, Font *font, glyph_index glyph, uint64_t extra_glyphs) {
static SpritePosition* sprite_position[16]; static SpritePosition* sprite_position[16];
int error = 0; int error = 0;
num_cells = MIN(sizeof(sprite_position)/sizeof(sprite_position[0]), num_cells);
for (unsigned int i = 0; i < num_cells; i++) { for (unsigned int i = 0; i < num_cells; i++) {
sprite_position[i] = sprite_position_for(font, glyph, extra_glyphs, (uint8_t)i, &error); sprite_position[i] = sprite_position_for(font, glyph, extra_glyphs, (uint8_t)i, &error);
if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; } if (error != 0) { sprite_map_set_error(error); PyErr_Print(); return; }
@ -435,15 +437,25 @@ render_group(unsigned int num_cells, unsigned int num_glyphs, Cell *cells, hb_gl
} }
typedef struct {
Cell *cell;
unsigned int num_codepoints;
unsigned int codepoints_consumed;
char_type current_codepoint;
} CellData;
static inline bool static inline bool
is_dummy_glyph(glyph_index glyph_id, Font *font) { is_special_glyph(glyph_index glyph_id, Font *font, CellData* cell_data) {
// we assume glyphs with no width are dummy glyphs used for a contextual ligature, so skip it // A glyph is special if the codepoint it corresponds to matches a
if (!font->dummy_glyph_cache[glyph_id]) { // different glyph in the font
static hb_glyph_extents_t extents; if (font->special_glyph_cache[glyph_id] == 0) {
hb_font_get_glyph_extents(font->hb_font, glyph_id, &extents); font->special_glyph_cache[glyph_id] = cell_data->current_codepoint ? (
font->dummy_glyph_cache[glyph_id] = extents.width == 0 ? 1 : 2; glyph_id != glyph_id_for_codepoint(font->face, cell_data->current_codepoint) ? 1 : 2)
:
2;
} }
return font->dummy_glyph_cache[glyph_id] & 1; return font->special_glyph_cache[glyph_id] & 1;
} }
static inline unsigned int static inline unsigned int
@ -453,12 +465,6 @@ num_codepoints_in_cell(Cell *cell) {
return ans; return ans;
} }
typedef struct {
Cell *cell;
unsigned int num_codepoints;
unsigned int codepoints_consumed;
} CellData;
static inline unsigned int static inline unsigned int
check_cell_consumed(CellData *cell_data, Cell *last_cell) { check_cell_consumed(CellData *cell_data, Cell *last_cell) {
cell_data->codepoints_consumed++; cell_data->codepoints_consumed++;
@ -466,8 +472,26 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
attrs_type width = cell_data->cell->attrs & WIDTH_MASK; attrs_type width = cell_data->cell->attrs & WIDTH_MASK;
cell_data->cell += MAX(1, width); cell_data->cell += MAX(1, width);
cell_data->codepoints_consumed = 0; cell_data->codepoints_consumed = 0;
if (cell_data->cell <= last_cell) cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell); if (cell_data->cell <= last_cell) {
cell_data->num_codepoints = num_codepoints_in_cell(cell_data->cell);
cell_data->current_codepoint = cell_data->cell->ch;
} else cell_data->current_codepoint = 0;
return width; return width;
} else {
switch(cell_data->codepoints_consumed) {
case 0:
cell_data->current_codepoint = cell_data->cell->ch;
break;
case 1:
cell_data->current_codepoint = cell_data->cell->cc & CC_MASK;
break;
case 2:
cell_data->current_codepoint = (cell_data->cell->cc >> CC_SHIFT) & CC_MASK;
break;
default:
cell_data->current_codepoint = 0;
break;
}
} }
return 0; return 0;
} }
@ -480,32 +504,36 @@ next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_gl
// their ligatures. // their ligatures.
CellData cell_data; CellData cell_data;
cell_data.cell = cells; cell_data.num_codepoints = num_codepoints_in_cell(cells); cell_data.codepoints_consumed = 0; cell_data.cell = cells; cell_data.num_codepoints = num_codepoints_in_cell(cells); cell_data.codepoints_consumed = 0; cell_data.current_codepoint = cells->ch;
glyph_index significant_glyphs[5]; #define LIMIT 5
significant_glyphs[0] = 0; glyph_index glyphs_in_group[LIMIT];
unsigned int num_significant_glyphs = 0;
unsigned int ncells = 0, nglyphs = 0, n; unsigned int ncells = 0, nglyphs = 0, n;
uint32_t previous_cluster = UINT32_MAX, cluster; uint32_t previous_cluster = UINT32_MAX, cluster;
Cell *last_cell = cells + max_num_cells; Cell *last_cell = cells + max_num_cells;
while(num_significant_glyphs < sizeof(significant_glyphs)/sizeof(significant_glyphs[0]) && ncells < max_num_cells && nglyphs < max_num_glyphs) { unsigned int cell_limit = MIN(max_num_cells, LIMIT + 1);
bool is_special, prev_was_special = false;
while(nglyphs < LIMIT && ncells < cell_limit && nglyphs < max_num_glyphs) {
glyph_index glyph_id = info[nglyphs].codepoint; glyph_index glyph_id = info[nglyphs].codepoint;
cluster = info[nglyphs].cluster; cluster = info[nglyphs].cluster;
nglyphs += 1; is_special = is_special_glyph(glyph_id, font, &cell_data);
bool is_dummy = is_dummy_glyph(glyph_id, font); if (prev_was_special && !is_special) break;
if (!is_dummy) significant_glyphs[num_significant_glyphs++] = glyph_id; glyphs_in_group[nglyphs++] = glyph_id;
// Soak up a number of codepoints indicated by the difference in cluster numbers.
if (cluster > previous_cluster || nglyphs == 1) { if (cluster > previous_cluster || nglyphs == 1) {
n = nglyphs == 1 ? 1 : cluster - previous_cluster; n = nglyphs == 1 ? 1 : cluster - previous_cluster;
unsigned int before = ncells; unsigned int before = ncells;
while(n-- && ncells < max_num_cells) ncells += check_cell_consumed(&cell_data, last_cell); while(n-- && ncells < max_num_cells) ncells += check_cell_consumed(&cell_data, last_cell);
if (ncells > before && !is_dummy) break; if (ncells > before && !is_special) break;
} }
previous_cluster = cluster; previous_cluster = cluster;
prev_was_special = is_special;
} }
*num_group_cells = MAX(1, MIN(ncells, max_num_cells)); *num_group_cells = MAX(1, MIN(ncells, cell_limit));
*num_group_glyphs = MAX(1, MIN(nglyphs, max_num_glyphs)); *num_group_glyphs = MAX(1, MIN(nglyphs, max_num_glyphs));
#define G(n) ((uint64_t)(significant_glyphs[n] & 0xffff)) #define G(n) ((uint64_t)(glyphs_in_group[n] & 0xffff))
switch(num_significant_glyphs) { switch(nglyphs) {
case 0: case 0:
case 1: case 1:
*extra_glyphs = 0; *extra_glyphs = 0;
@ -524,7 +552,7 @@ next_group(Font *font, unsigned int *num_group_cells, unsigned int *num_group_gl
break; break;
} }
#undef G #undef G
return significant_glyphs[0]; return glyphs_in_group[0];
} }
static inline unsigned int static inline unsigned int
@ -577,6 +605,7 @@ test_shape(PyObject UNUSED *self, PyObject *args) {
Font f = {0}; Font f = {0};
font = &f; font = &f;
font->hb_font = harfbuzz_font_for_face(face); font->hb_font = harfbuzz_font_for_face(face);
font->face = face;
if (!font->hb_font) return NULL; if (!font->hb_font) return NULL;
} }
hb_shape(font->hb_font, harfbuzz_buffer, NULL, 0); hb_shape(font->hb_font, harfbuzz_buffer, NULL, 0);

View File

@ -180,11 +180,14 @@ def render_string(text, family='monospace', size=11.0, dpi=96.0):
finally: finally:
set_send_sprite_to_gpu(None) set_send_sprite_to_gpu(None)
cells = [] cells = []
for i in range(s.columns): found_content = False
for i in reversed(range(s.columns)):
sp = line.sprite_at(i) sp = line.sprite_at(i)
if sp != (0, 0, 0): if sp == (0, 0, 0) and not found_content:
cells.append(sprites[sp]) continue
return cell_width, cell_height, cells found_content = True
cells.append(sprites[sp])
return cell_width, cell_height, list(reversed(cells))
def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None): def shape_string(text="abcd", family='monospace', size=11.0, dpi=96.0, path=None):

View File

@ -81,6 +81,10 @@ class Rendering(BaseTest):
self.ae(groups('abcd'), [(1, 1) for i in range(4)]) self.ae(groups('abcd'), [(1, 1) for i in range(4)])
self.ae(groups('A=>>B!=C', path='kitty_tests/FiraCode-Medium.otf'), [(1, 1), (3, 3), (1, 1), (2, 2), (1, 1)]) self.ae(groups('A=>>B!=C', path='kitty_tests/FiraCode-Medium.otf'), [(1, 1), (3, 3), (1, 1), (2, 2), (1, 1)])
colon_glyph = shape_string('9:30', path='kitty_tests/FiraCode-Medium.otf')[1][2]
self.assertNotEqual(colon_glyph, shape_string(':', path='kitty_tests/FiraCode-Medium.otf')[0][2])
self.ae(colon_glyph, 998)
self.ae(groups('9:30', path='kitty_tests/FiraCode-Medium.otf'), [(1, 1), (1, 1), (1, 1), (1, 1)])
self.ae(groups('|\U0001F601|\U0001F64f|\U0001F63a|'), [(1, 1), (2, 1), (1, 1), (2, 1), (1, 1), (2, 1), (1, 1)]) self.ae(groups('|\U0001F601|\U0001F64f|\U0001F63a|'), [(1, 1), (2, 1), (1, 1), (2, 1), (1, 1), (2, 1), (1, 1)])
self.ae(groups('He\u0347\u0305llo\u0337,', path='kitty_tests/LiberationMono-Regular.ttf'), self.ae(groups('He\u0347\u0305llo\u0337,', path='kitty_tests/LiberationMono-Regular.ttf'),
[(1, 1), (1, 3), (1, 1), (1, 1), (1, 2), (1, 1)]) [(1, 1), (1, 3), (1, 1), (1, 1), (1, 2), (1, 1)])