Store combining chars as an array in the Cell

Makes it easier to increase the number of combining chars if needed,
rather than storing them as a mask.
This commit is contained in:
Kovid Goyal 2018-01-18 14:10:58 +05:30
parent 5fd4bc965f
commit 409bd37db5
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 36 additions and 65 deletions

View File

@ -180,7 +180,7 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
PyObject*
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) {
CTFace *self = (CTFace*)base_face;
char text[128] = {0};
char text[256] = {0};
cell_as_utf8(cell, true, text, ' ');
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
if (str == NULL) return PyErr_NoMemory();

View File

@ -31,7 +31,7 @@
typedef unsigned long long id_type;
typedef uint32_t char_type;
typedef uint32_t color_type;
typedef uint32_t combining_type;
typedef uint16_t combining_type;
typedef uint32_t pixel;
typedef unsigned int index_type;
typedef uint16_t sprite_index;
@ -56,7 +56,6 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape;
#define REVERSE_SHIFT 6
#define STRIKE_SHIFT 7
#define COL_MASK 0xFFFFFFFF
#define CC_MASK 0xFFFF
#define CC_SHIFT 16
#define UTF8_ACCEPT 0
#define UTF8_REJECT 1
@ -139,7 +138,7 @@ typedef struct {
attrs_type attrs;
// The following are only needed on the CPU, not the GPU
char_type ch;
combining_type cc;
combining_type cc[2];
} Cell;
typedef struct {

View File

@ -55,7 +55,7 @@ typedef struct {
static GPUSpriteTracker sprite_tracker = {0};
static hb_buffer_t *harfbuzz_buffer = NULL;
static char_type shape_buffer[2048] = {0};
static char_type shape_buffer[4096] = {0};
typedef struct {
@ -327,10 +327,8 @@ face_has_codepoint(PyObject* face, char_type cp) {
static inline bool
has_cell_text(Font *self, Cell *cell) {
if (!face_has_codepoint(self->face, cell->ch)) return false;
if (cell->cc) {
if (!face_has_codepoint(self->face, cell->cc & CC_MASK)) return false;
char_type cc = cell->cc >> 16;
if (cc && !face_has_codepoint(self->face, cc)) return false;
for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) {
if (!face_has_codepoint(self->face, cell->cc[i])) return false;
}
return true;
}
@ -494,14 +492,12 @@ load_hb_buffer(Cell *first_cell, index_type num_cells) {
hb_buffer_clear_contents(harfbuzz_buffer);
while (num_cells) {
attrs_type prev_width = 0;
for (num = 0; num_cells && num < sizeof(shape_buffer)/sizeof(shape_buffer[0]) - 20; first_cell++, num_cells--) {
for (num = 0; num_cells && num < arraysz(shape_buffer) - 20 - arraysz(first_cell->cc); first_cell++, num_cells--) {
if (prev_width == 2) { prev_width = 0; continue; }
shape_buffer[num++] = first_cell->ch;
prev_width = first_cell->attrs & WIDTH_MASK;
if (first_cell->cc) {
shape_buffer[num++] = first_cell->cc & CC_MASK;
combining_type cc2 = first_cell->cc >> 16;
if (cc2) shape_buffer[num++] = cc2 & CC_MASK;
for (unsigned i = 0; i < arraysz(first_cell->cc) && first_cell->cc[i]; i++) {
shape_buffer[num++] = first_cell->cc[i];
}
}
hb_buffer_add_utf32(harfbuzz_buffer, shape_buffer, num, 0, num);
@ -580,7 +576,7 @@ static GroupState group_state = {0};
static inline unsigned int
num_codepoints_in_cell(Cell *cell) {
unsigned int ans = 1;
if (cell->cc) ans += ((cell->cc >> CC_SHIFT) & CC_MASK) ? 2 : 1;
for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) ans++;
return ans;
}
@ -657,14 +653,8 @@ check_cell_consumed(CellData *cell_data, Cell *last_cell) {
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;
cell_data->current_codepoint = cell_data->cell->cc[cell_data->codepoints_consumed - 1];
break;
}
}
@ -1053,12 +1043,11 @@ get_fallback_font(PyObject UNUSED *self, PyObject *args) {
PyObject *text;
int bold, italic;
if (!PyArg_ParseTuple(args, "Upp", &text, &bold, &italic)) return NULL;
static Py_UCS4 char_buf[16];
if (!PyUnicode_AsUCS4(text, char_buf, sizeof(char_buf)/sizeof(char_buf[0]), 1)) return NULL;
Cell cell = {0};
static Py_UCS4 char_buf[2 + arraysz(cell.cc)];
if (!PyUnicode_AsUCS4(text, char_buf, arraysz(char_buf), 1)) return NULL;
cell.ch = char_buf[0];
if (PyUnicode_GetLength(text) > 1) cell.cc |= char_buf[1] & CC_MASK;
if (PyUnicode_GetLength(text) > 2) cell.cc |= (char_buf[2] & CC_MASK) << 16;
for (unsigned i = 0; i + 1 < PyUnicode_GetLength(text) && i < arraysz(cell.cc); i++) cell.cc[i] = char_buf[i + 1];
if (bold) cell.attrs |= 1 << BOLD_SHIFT;
if (italic) cell.attrs |= 1 << ITALIC_SHIFT;
ssize_t ans = fallback_font(&cell);

View File

@ -35,15 +35,13 @@ line_length(Line *self) {
}
PyObject*
line_text_at(char_type ch, combining_type cc) {
cell_text(Cell *cell) {
PyObject *ans;
if (LIKELY(cc == 0)) {
ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, &ch, 1);
} else {
Py_UCS4 buf[3];
buf[0] = ch; buf[1] = cc & CC_MASK; buf[2] = cc >> 16;
ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, buf[2] ? 3 : 2);
}
unsigned num = 1;
static Py_UCS4 buf[arraysz(cell->cc) + 1];
buf[0] = cell->ch;
for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) buf[num++] = cell->cc[i];
ans = PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, num);
return ans;
}
@ -160,7 +158,7 @@ static PyObject*
text_at(Line* self, Py_ssize_t xval) {
#define text_at_doc "[x] -> Return the text in the specified cell"
if ((unsigned)xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; }
return line_text_at(self->cells[xval].ch, self->cells[xval].cc);
return cell_text(self->cells + xval);
}
size_t
@ -168,13 +166,7 @@ cell_as_unicode(Cell *cell, bool include_cc, Py_UCS4 *buf, char_type zero_char)
size_t n = 1;
buf[0] = cell->ch ? cell->ch : zero_char;
if (include_cc) {
char_type cc = cell->cc;
Py_UCS4 cc1 = cc & CC_MASK, cc2;
if (cc1) {
buf[1] = cc1; n++;
cc2 = cc >> 16;
if (cc2) { buf[2] = cc2; n++; }
}
for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) buf[n++] = cell->cc[i];
}
return n;
}
@ -183,13 +175,7 @@ size_t
cell_as_utf8(Cell *cell, bool include_cc, char *buf, char_type zero_char) {
size_t n = encode_utf8(cell->ch ? cell->ch : zero_char, buf);
if (include_cc) {
char_type cc = cell->cc;
Py_UCS4 cc1 = cc & CC_MASK, cc2;
if (cc1) {
n += encode_utf8(cc1, buf + n);
cc2 = cc >> 16;
if (cc2) { n += encode_utf8(cc2, buf + n); }
}
for (unsigned i = 0; i < arraysz(cell->cc) && cell->cc[i]; i++) n += encode_utf8(cell->cc[i], buf + n);
}
buf[n] = 0;
return n;
@ -202,7 +188,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc
static Py_UCS4 buf[4096];
if (leading_char) buf[n++] = leading_char;
char_type previous_width = 0;
for(index_type i = start; i < limit && n < sizeof(buf)/sizeof(buf[0]) - 4; i++) {
for(index_type i = start; i < limit && n < arraysz(buf) - 2 - arraysz(self->cells->cc); i++) {
char_type ch = self->cells[i].ch;
if (ch == 0) {
if (previous_width == 2) { previous_width = 0; continue; };
@ -263,12 +249,8 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) {
t = prev_cursor; prev_cursor = cursor; cursor = t;
if (*sgr) WRITE_SGR(sgr);
WRITE_CH(ch);
char_type cc = self->cells[pos].cc;
Py_UCS4 cc1 = cc & CC_MASK;
if (cc1) {
WRITE_CH(cc1);
cc1 = cc >> 16;
if (cc1) { WRITE_CH(cc1); }
for(unsigned c = 0; c < arraysz(self->cells[pos].cc) && self->cells[pos].cc[c]; c++) {
WRITE_CH(self->cells[pos].cc[c]);
}
previous_width = attrs & WIDTH_MASK;
}
@ -317,10 +299,12 @@ width(Line *self, PyObject *val) {
void
line_add_combining_char(Line *self, uint32_t ch, unsigned int x) {
if (!self->cells[x].ch) return; // dont allow adding combining chars to a null cell
combining_type c = self->cells[x].cc;
if (c & CC_MASK) self->cells[x].cc = (c & CC_MASK) | ( (ch & CC_MASK) << CC_SHIFT );
else self->cells[x].cc = ch & CC_MASK;
Cell *cell = self->cells + x;
if (!cell->ch) return; // dont allow adding combining chars to a null cell
for (unsigned i = 0; i < arraysz(cell->cc); i++) {
if (!cell->cc[i]) { cell->cc[i] = (combining_type)ch; return; }
}
cell->cc[arraysz(cell->cc) - 1] = (combining_type)ch;
}
static PyObject*
@ -370,7 +354,7 @@ set_text(Line* self, PyObject *args) {
self->cells[i].fg = fg;
self->cells[i].bg = bg;
self->cells[i].decoration_fg = dfg;
self->cells[i].cc = 0;
memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc));
}
Py_RETURN_NONE;
@ -402,7 +386,7 @@ line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch) {
attrs_type width = ch ? 1 : 0;
#define PREFIX \
for (index_type i = at; i < MIN(self->xnum, at + num); i++) { \
self->cells[i].ch = ch; self->cells[i].cc = 0; \
self->cells[i].ch = ch; memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc)); \
self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; \
}
if (CHAR_IS_BLANK(ch)) {
@ -432,7 +416,7 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num,
for (index_type i = at; i < self->xnum && i < at + num; i++) {
if (clear_char) {
self->cells[i].ch = BLANK_CHAR;
self->cells[i].cc = 0;
memset(self->cells[i].cc, 0, sizeof(self->cells[i].cc));
self->cells[i].attrs = attrs;
clear_sprite_position(self->cells[i]);
} else {
@ -507,7 +491,7 @@ line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Curs
self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK;
}
self->cells[at].ch = ch;
self->cells[at].cc = 0;
memset(self->cells[at].cc, 0, sizeof(self->cells[at].cc));
}
static PyObject*

View File

@ -44,7 +44,6 @@ xlimit_for_line(Line *line) {
return xlimit;
}
PyObject* line_text_at(char_type, combining_type);
void line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch);
void line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, bool clear_char);
void line_set_char(Line *, unsigned int , uint32_t , unsigned int , Cursor *, bool);