From a92b3c605f307fbcfe028094c9c823f75c0d070d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 16 Sep 2017 11:02:42 +0530 Subject: [PATCH] Move the text attributes out of the char type This means that more text attributes can be added in the future (there are 8 bits available) and there is no need to keep bit twiddling when accessing the chars themselves. Also means less data needs to be sent to the GPU. Cell size does not change since there were currently 2 unused bytes because of alignment. --- kitty/cell_vertex.glsl | 10 ++++---- kitty/data-types.h | 16 ++++++------ kitty/line-buf.c | 4 +-- kitty/line.c | 56 ++++++++++++++++++++++-------------------- kitty/lineops.h | 18 ++++++++------ kitty/rewrap.h | 2 +- kitty/screen.c | 2 +- kitty/shaders.c | 3 +-- kitty/sprites.c | 34 +++++++++++++------------ 9 files changed, 75 insertions(+), 70 deletions(-) diff --git a/kitty/cell_vertex.glsl b/kitty/cell_vertex.glsl index 91129ee9f..b353ce17d 100644 --- a/kitty/cell_vertex.glsl +++ b/kitty/cell_vertex.glsl @@ -9,8 +9,7 @@ uniform uvec4 url_range; // The range for the currently highlighted URL (start_x uniform ColorTable { uint color_table[256]; // The color table }; -in uint text_attrs; -in uvec3 sprite_coords; +in uvec4 sprite_coords; in uvec3 colors; in float is_selected; out vec3 sprite_pos; @@ -95,7 +94,8 @@ void main() { gl_Position = vec4(xpos[pos.x], ypos[pos.y], 0, 1); sprite_pos = to_sprite_pos(pos, sprite_coords.x, sprite_coords.y, sprite_coords.z & SHORT_MASK); - uint reverse = (text_attrs >> 30) & STRIKE_MASK; + uint text_attrs = sprite_coords[3]; + uint reverse = (text_attrs >> 6) & STRIKE_MASK; uint fg = colors[color_indices[reverse]]; uint bg = colors[color_indices[ONE - reverse]]; uint resolved_fg = as_color(fg, default_colors[color_indices[0]]); @@ -103,6 +103,6 @@ void main() { background = apply_selection(to_color(bg, default_colors[color_indices[1]]), default_colors[3]); float in_url = in_range(url_range, c, r); decoration_fg = mix_vecs(in_url, color_to_vec(url_color), to_color(colors[2], resolved_fg)); - underline_pos = mix_vecs(in_url, to_sprite_pos(pos, TWO, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> 26) & DECORATION_MASK, ZERO, ZERO)); - strike_pos = to_sprite_pos(pos, ((text_attrs >> 31) & STRIKE_MASK) * THREE, ZERO, ZERO); + underline_pos = mix_vecs(in_url, to_sprite_pos(pos, TWO, ZERO, ZERO), to_sprite_pos(pos, (text_attrs >> 2) & DECORATION_MASK, ZERO, ZERO)); + strike_pos = to_sprite_pos(pos, ((text_attrs >> 7) & STRIKE_MASK) * THREE, ZERO, ZERO); } diff --git a/kitty/data-types.h b/kitty/data-types.h index 95900d98f..635d3089f 100644 --- a/kitty/data-types.h +++ b/kitty/data-types.h @@ -29,6 +29,7 @@ typedef uint32_t color_type; typedef uint32_t combining_type; typedef unsigned int index_type; typedef uint16_t sprite_index; +typedef uint16_t attrs_type; typedef enum CursorShapes { NO_CURSOR_SHAPE, CURSOR_BLOCK, CURSOR_BEAM, CURSOR_UNDERLINE, NUM_OF_CURSOR_SHAPES } CursorShape; #define ERROR_PREFIX "[PARSE ERROR]" @@ -38,15 +39,12 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; #define MAX_CHILDREN 256 #define BLANK_CHAR 0 -#define CHAR_MASK 0xFFFFFF -#define ATTRS_SHIFT 24 -#define ATTRS_MASK_WITHOUT_WIDTH 0xFC000000 +#define ATTRS_MASK_WITHOUT_WIDTH 0xFFC #define WIDTH_MASK 3 #define DECORATION_SHIFT 2 #define DECORATION_MASK 3 #define BOLD_SHIFT 4 #define ITALIC_SHIFT 5 -#define POSCHAR_MASK 0x30FFFFFF #define REVERSE_SHIFT 6 #define STRIKE_SHIFT 7 #define COL_MASK 0xFFFFFFFF @@ -56,14 +54,14 @@ typedef enum MouseShapes { BEAM, HAND, ARROW } MouseShape; #define UTF8_REJECT 1 #define UNDERCURL_CODE 6 #define DECORATION_FG_CODE 58 -#define CHAR_IS_BLANK(ch) ((ch & CHAR_MASK) == 32 || (ch & CHAR_MASK) == 0) +#define CHAR_IS_BLANK(ch) ((ch) == 32 || (ch) == 0) #define FG 1 #define BG 2 #define CURSOR_TO_ATTRS(c, w) \ ((w) | (((c->decoration & 3) << DECORATION_SHIFT) | ((c->bold & 1) << BOLD_SHIFT) | \ - ((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT))) << ATTRS_SHIFT + ((c->italic & 1) << ITALIC_SHIFT) | ((c->reverse & 1) << REVERSE_SHIFT) | ((c->strikethrough & 1) << STRIKE_SHIFT))) #define ATTRS_TO_CURSOR(a, c) \ c->decoration = (a >> DECORATION_SHIFT) & 3; c->bold = (a >> BOLD_SHIFT) & 1; c->italic = (a >> ITALIC_SHIFT) & 1; \ @@ -126,6 +124,7 @@ typedef struct { color_type fg, bg, decoration_fg; combining_type cc; sprite_index sprite_x, sprite_y, sprite_z; + attrs_type attrs; } Cell; typedef struct { @@ -280,8 +279,9 @@ PyTypeObject ChildMonitor_Type; for(index_type __i__ = (at); __i__ < (line)->xnum - (num); __i__++) { \ COPY_CELL(line, __i__ + (num), line, __i__) \ } \ - if ((((line)->cells[(at)].ch >> ATTRS_SHIFT) & WIDTH_MASK) != 1) { \ - (line)->cells[(at)].ch = (1 << ATTRS_SHIFT) | BLANK_CHAR; \ + if ((((line)->cells[(at)].attrs) & WIDTH_MASK) != 1) { \ + (line)->cells[(at)].ch = BLANK_CHAR; \ + (line)->cells[(at)].attrs = BLANK_CHAR ? 1 : 0; \ clear_sprite_position((line)->cells[(at)]); \ } diff --git a/kitty/line-buf.c b/kitty/line-buf.c index 5211b0f16..ff98ca33a 100644 --- a/kitty/line-buf.c +++ b/kitty/line-buf.c @@ -116,7 +116,7 @@ line(LineBuf *self, PyObject *y) { unsigned int linebuf_char_width_at(LineBuf *self, index_type x, index_type y) { - return (lineptr(self, self->line_map[y])[x].ch >> ATTRS_SHIFT) & WIDTH_MASK; + return (lineptr(self, self->line_map[y])[x].attrs) & WIDTH_MASK; } void @@ -453,7 +453,7 @@ linebuf_rewrap(LineBuf *self, LineBuf *other, int *cursor_y_out, HistoryBuf *his for (first = self->ynum - 1; true; first--) { Cell *cells = lineptr(self, first); for(i = 0; i < self->xnum; i++) { - if ((cells[i].ch & CHAR_MASK) != BLANK_CHAR) { is_empty = false; break; } + if ((cells[i].ch) != BLANK_CHAR) { is_empty = false; break; } } if (!is_empty || !first) break; } diff --git a/kitty/line.c b/kitty/line.c index 9dfa3f3de..8184deaa0 100644 --- a/kitty/line.c +++ b/kitty/line.c @@ -27,7 +27,7 @@ unsigned int line_length(Line *self) { index_type last = self->xnum - 1; for (index_type i = 0; i < self->xnum; i++) { - if ((self->cells[last - i].ch & CHAR_MASK) != BLANK_CHAR) return self->xnum - i; + if ((self->cells[last - i].ch) != BLANK_CHAR) return self->xnum - i; } return 0; } @@ -68,7 +68,7 @@ find_colon_slash(Line *self, index_type x, index_type limit) { limit = MAX(2, limit); if (pos < limit) return 0; do { - char_type ch = self->cells[pos].ch & CHAR_MASK; + char_type ch = self->cells[pos].ch; if (!is_url_char(ch)) return false; switch(state) { case ANY: @@ -92,7 +92,7 @@ prefix_matches(Line *self, index_type at, const char* prefix, index_type prefix_ if (prefix_len > at) return false; index_type p, i; for (p = at - prefix_len, i = 0; i < prefix_len && p < self->xnum; i++, p++) { - if ((self->cells[p].ch & CHAR_MASK) != (unsigned char)prefix[i]) return false; + if ((self->cells[p].ch) != (unsigned char)prefix[i]) return false; } return i == prefix_len; } @@ -117,7 +117,7 @@ static inline bool has_url_beyond(Line *self, index_type x) { if (self->xnum <= x + MIN_URL_LEN + 3) return false; for (index_type i = x; i < MIN(x + MIN_URL_LEN + 3, self->xnum); i++) { - if (!is_url_char(self->cells[i].ch & CHAR_MASK)) return false; + if (!is_url_char(self->cells[i].ch)) return false; } return true; } @@ -143,9 +143,9 @@ index_type line_url_end_at(Line *self, index_type x) { index_type ans = x; if (x >= self->xnum || self->xnum <= MIN_URL_LEN + 3) return 0; - while (ans < self->xnum && is_url_char(self->cells[ans].ch & CHAR_MASK)) ans++; + while (ans < self->xnum && is_url_char(self->cells[ans].ch)) ans++; ans--; - while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch & CHAR_MASK)) ans--; + while (ans > x && can_strip_from_end_of_url(self->cells[ans].ch)) ans--; return ans; } @@ -167,7 +167,7 @@ static PyObject* text_at(Line* self, Py_ssize_t xval) { #define text_at_doc "[x] -> Return the text in the specified cell" if (xval >= self->xnum) { PyErr_SetString(PyExc_IndexError, "Column number out of bounds"); return NULL; } - return line_text_at(self->cells[xval].ch & CHAR_MASK, self->cells[xval].cc); + return line_text_at(self->cells[xval].ch, self->cells[xval].cc); } PyObject* @@ -177,7 +177,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc 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++) { - char_type ch = self->cells[i].ch & CHAR_MASK; + char_type ch = self->cells[i].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; }; ch = ' '; @@ -192,7 +192,7 @@ unicode_in_range(Line *self, index_type start, index_type limit, bool include_cc if (cc2) buf[n++] = cc2; } } - previous_width = (self->cells[i].ch >> ATTRS_SHIFT) & WIDTH_MASK; + previous_width = self->cells[i].attrs & WIDTH_MASK; } return PyUnicode_FromKindAndData(PyUnicode_4BYTE_KIND, buf, n); } @@ -247,7 +247,7 @@ line_as_ansi(Line *self, Py_UCS4 *buf, index_type buflen) { WRITE_SGR(0); for (index_type pos=0; pos < limit; pos++) { - char_type attrs = self->cells[pos].ch >> ATTRS_SHIFT, ch = self->cells[pos].ch & CHAR_MASK; + char_type attrs = self->cells[pos].attrs, ch = self->cells[pos].ch; if (ch == 0) { if (previous_width == 2) { previous_width = 0; continue; } ch = ' '; @@ -319,7 +319,7 @@ width(Line *self, PyObject *val) { #define width_doc "width(x) -> the width of the character at x" unsigned long x = PyLong_AsUnsignedLong(val); if (x >= self->xnum) { PyErr_SetString(PyExc_ValueError, "Out of bounds"); return NULL; } - char_type attrs = self->cells[x].ch >> ATTRS_SHIFT; + char_type attrs = self->cells[x].attrs; return PyLong_FromUnsignedLong((unsigned long) (attrs & WIDTH_MASK)); } @@ -375,7 +375,8 @@ set_text(Line* self, PyObject *args) { color_type dfg = cursor->decoration_fg & COL_MASK; for (index_type i = cursor->x; offset < limit && i < self->xnum; i++, offset++) { - self->cells[i].ch = (PyUnicode_READ(kind, buf, offset) & CHAR_MASK) | attrs; + self->cells[i].ch = (PyUnicode_READ(kind, buf, offset)); + self->cells[i].attrs = attrs; self->cells[i].fg = fg; self->cells[i].bg = bg; self->cells[i].decoration_fg = dfg; @@ -399,7 +400,7 @@ cursor_from(Line* self, PyObject *args) { ans = alloc_cursor(); if (ans == NULL) { PyErr_NoMemory(); return NULL; } ans->x = x; ans->y = y; - char_type attrs = self->cells[x].ch >> ATTRS_SHIFT; + char_type attrs = self->cells[x].attrs; ATTRS_TO_CURSOR(attrs, ans); ans->fg = self->cells[x].fg; ans->bg = self->cells[x].bg; ans->decoration_fg = self->cells[x].decoration_fg & COL_MASK; @@ -408,12 +409,12 @@ cursor_from(Line* self, PyObject *args) { } void -line_clear_text(Line *self, unsigned int at, unsigned int num, int ch) { - const char_type repl = ((char_type)ch & CHAR_MASK) | ( (ch ? 1 : 0) << ATTRS_SHIFT); +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 = (self->cells[i].ch & ATTRS_MASK_WITHOUT_WIDTH) | repl; \ - self->cells[i].cc = 0; + self->cells[i].ch = ch; self->cells[i].cc = 0; \ + self->cells[i].attrs = (self->cells[i].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; if (CHAR_IS_BLANK(ch)) { PREFIX clear_sprite_position(self->cells[i]); } @@ -438,16 +439,17 @@ line_apply_cursor(Line *self, Cursor *cursor, unsigned int at, unsigned int num, char_type attrs = CURSOR_TO_ATTRS(cursor, 1); color_type fg = (cursor->fg & COL_MASK), bg = (cursor->bg & COL_MASK); color_type dfg = cursor->decoration_fg & COL_MASK; - if (!clear_char) attrs = ((attrs >> ATTRS_SHIFT) & ~WIDTH_MASK) << ATTRS_SHIFT; + if (!clear_char) attrs = attrs & ATTRS_MASK_WITHOUT_WIDTH; for (index_type i = at; i < self->xnum && i < at + num; i++) { if (clear_char) { - self->cells[i].ch = BLANK_CHAR | attrs; + self->cells[i].ch = BLANK_CHAR; self->cells[i].cc = 0; + self->cells[i].attrs = attrs; clear_sprite_position(self->cells[i]); } else { - char_type w = ((self->cells[i].ch >> ATTRS_SHIFT) & WIDTH_MASK) << ATTRS_SHIFT; - self->cells[i].ch = (self->cells[i].ch & CHAR_MASK) | attrs | w; + attrs_type w = self->cells[i].attrs & WIDTH_MASK; + self->cells[i].attrs = attrs | w; set_sprite_position_at(i); } self->cells[i].fg = fg; self->cells[i].bg = bg; @@ -471,9 +473,10 @@ void line_right_shift(Line *self, unsigned int at, unsigned int num) { COPY_SELF_CELL(i - num, i) } // Check if a wide character was split at the right edge - char_type w = (self->cells[self->xnum - 1].ch >> ATTRS_SHIFT) & WIDTH_MASK; + char_type w = (self->cells[self->xnum - 1].attrs) & WIDTH_MASK; if (w != 1) { - self->cells[self->xnum - 1].ch = ((BLANK_CHAR ? 1 : 0) << ATTRS_SHIFT) | BLANK_CHAR; + self->cells[self->xnum - 1].ch = BLANK_CHAR; + self->cells[self->xnum - 1].attrs = BLANK_CHAR ? 1 : 0; clear_sprite_position(self->cells[self->xnum - 1]); } } @@ -508,16 +511,15 @@ left_shift(Line *self, PyObject *args) { void line_set_char(Line *self, unsigned int at, uint32_t ch, unsigned int width, Cursor *cursor, bool is_second) { - char_type attrs; if (cursor == NULL) { - attrs = (((self->cells[at].ch >> ATTRS_SHIFT) & ~WIDTH_MASK) | (width & WIDTH_MASK)) << ATTRS_SHIFT; + self->cells[at].attrs = (self->cells[at].attrs & ATTRS_MASK_WITHOUT_WIDTH) | width; } else { - attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); + self->cells[at].attrs = CURSOR_TO_ATTRS(cursor, width & WIDTH_MASK); self->cells[at].fg = (cursor->fg & COL_MASK); self->cells[at].bg = (cursor->bg & COL_MASK); self->cells[at].decoration_fg = cursor->decoration_fg & COL_MASK; } - self->cells[at].ch = (ch & CHAR_MASK) | attrs; + self->cells[at].ch = ch; self->cells[at].cc = 0; if (!is_second && CHAR_IS_BLANK(ch)) { clear_sprite_position(self->cells[at]); } else set_sprite_position_at(at); diff --git a/kitty/lineops.h b/kitty/lineops.h index b522b3718..22e3e285b 100644 --- a/kitty/lineops.h +++ b/kitty/lineops.h @@ -22,10 +22,11 @@ update_sprites_in_line(Cell *cells, index_type xnum) { static inline void set_attribute_on_line(Cell *cells, uint32_t shift, uint32_t val, index_type xnum) { - uint32_t mask = shift == DECORATION_SHIFT ? 3 : 1; - uint32_t aval = (val & mask) << (ATTRS_SHIFT + shift); - mask = ~(mask << (ATTRS_SHIFT + shift)); - for (index_type i = 0; i < xnum; i++) cells[i].ch = (cells[i].ch & mask) | aval; + // Set a single attribute on all cells in the line + attrs_type mask = shift == DECORATION_SHIFT ? 3 : 1; + attrs_type aval = (val & mask) << shift; + mask = ~(mask << shift); + for (index_type i = 0; i < xnum; i++) cells[i].attrs = (cells[i].attrs & mask) | aval; if (shift == BOLD_SHIFT || shift == ITALIC_SHIFT) update_sprites_in_line(cells, xnum); } @@ -42,21 +43,22 @@ copy_line(const Line *src, Line *dest) { static inline void clear_chars_in_line(Cell *cells, index_type xnum, char_type ch) { // Clear only the char part of each cell, the rest must have been cleared by a memset or similar - char_type c = (1 << ATTRS_SHIFT) | ch; - for (index_type i = 0; i < xnum; i++) cells[i].ch = c; + if (ch) { + for (index_type i = 0; i < xnum; i++) { cells[i].ch = ch; cells[i].attrs = 1; } + } } static inline index_type xlimit_for_line(Line *line) { index_type xlimit = line->xnum; if (BLANK_CHAR == 0) { - while (xlimit > 0 && (line->cells[xlimit - 1].ch & CHAR_MASK) == BLANK_CHAR) xlimit--; + while (xlimit > 0 && (line->cells[xlimit - 1].ch) == BLANK_CHAR) xlimit--; } return xlimit; } PyObject* line_text_at(char_type, combining_type); -void line_clear_text(Line *self, unsigned int at, unsigned int num, int ch); +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); void line_right_shift(Line *, unsigned int , unsigned int ); diff --git a/kitty/rewrap.h b/kitty/rewrap.h index 716a25588..f151a49d8 100644 --- a/kitty/rewrap.h +++ b/kitty/rewrap.h @@ -59,7 +59,7 @@ rewrap_inner(BufType *src, BufType *dest, const index_type src_limit, HistoryBuf src_x_limit = src->xnum; if (!src_line_is_continued) { // Trim trailing blanks since there is a hard line break at the end of this line - while(src_x_limit && (src->line->cells[src_x_limit - 1].ch & CHAR_MASK) == BLANK_CHAR) src_x_limit--; + while(src_x_limit && (src->line->cells[src_x_limit - 1].ch) == BLANK_CHAR) src_x_limit--; } while (src_x < src_x_limit) { diff --git a/kitty/screen.c b/kitty/screen.c index 1bad5c864..64cfc0257 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -1366,7 +1366,7 @@ screen_selection_range_for_word(Screen *self, index_type x, index_type y, index_ if (y >= self->lines || x >= self->columns) return false; index_type start, end; Line *line = visual_line_(self, y); -#define is_ok(x) (is_word_char((line->cells[x].ch) & CHAR_MASK) || is_opt_word_char(line->cells[x].ch & CHAR_MASK)) +#define is_ok(x) (is_word_char((line->cells[x].ch)) || is_opt_word_char(line->cells[x].ch)) if (!is_ok(x)) { start = x; end = x + 1; } else { diff --git a/kitty/shaders.c b/kitty/shaders.c index dc45e50ff..9a35ce25d 100644 --- a/kitty/shaders.c +++ b/kitty/shaders.c @@ -591,8 +591,7 @@ create_cell_vao() { #define A1(name, size, dtype, offset) A(name, size, dtype, (void*)(offsetof(Cell, offset)), sizeof(Cell)) add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); - A1(text_attrs, 1, GL_UNSIGNED_INT, ch); - A1(sprite_coords, 3, GL_UNSIGNED_SHORT, sprite_x); + A1(sprite_coords, 4, GL_UNSIGNED_SHORT, sprite_x); A1(colors, 3, GL_UNSIGNED_INT, fg); add_buffer_to_vao(vao_idx, GL_ARRAY_BUFFER); A(is_selected, 1, GL_FLOAT, NULL, 0); diff --git a/kitty/sprites.c b/kitty/sprites.c index 9283e84ac..95ff15b0d 100644 --- a/kitty/sprites.c +++ b/kitty/sprites.c @@ -15,6 +15,7 @@ struct SpritePosition { sprite_index x, y, z; char_type ch; combining_type cc; + bool bold, italic; bool is_second; bool filled; bool rendered; @@ -88,15 +89,15 @@ do_increment(int *error) { } SpritePosition* -sprite_map_position_for(char_type ch, combining_type cc, bool is_second, int *error) { - char_type pos_char = ch & POSCHAR_MASK; // Includes only the char and bold and italic bits - unsigned int idx = ((ch >> (ATTRS_SHIFT - 4)) & 0x300) | (ch & 0xFF); // Includes only italic, bold and lowest byte of ch +sprite_map_position_for(char_type ch, attrs_type attrs, combining_type cc, bool is_second, int *error) { + uint8_t bold_italic = (attrs >> BOLD_SHIFT) & 3; + unsigned int idx = (ch & 0xFF) | (bold_italic << 8); // Only bold italic bits and lowest byte of char SpritePosition *s = &(sprite_map.cache[idx]); // Optimize for the common case of an ASCII char already in the cache - if (LIKELY(s->ch == pos_char && s->filled && s->cc == cc && s->is_second == is_second)) return s; // Cache hit + if (LIKELY(s->ch == ch && s->filled && s->cc == cc && s->is_second == is_second)) return s; // Cache hit while(true) { if (s->filled) { - if (s->ch == pos_char && s->cc == cc && s->is_second == is_second) return s; // Cache hit + if (s->ch == ch && s->cc == cc && s->is_second == is_second) return s; // Cache hit } else { break; } @@ -106,11 +107,13 @@ sprite_map_position_for(char_type ch, combining_type cc, bool is_second, int *er } s = s->next; } - s->ch = pos_char; + s->ch = ch; s->cc = cc; s->is_second = is_second; s->filled = true; s->rendered = false; + s->bold = bold_italic & 1; + s->italic = bold_italic >> 1; s->x = sprite_map.x; s->y = sprite_map.y; s->z = sprite_map.z; do_increment(error); sprite_map.dirty = true; @@ -122,10 +125,10 @@ void set_sprite_position(Cell *cell, Cell *previous_cell) { SpritePosition *sp; static int error; - if (UNLIKELY(previous_cell != NULL && ((previous_cell->ch >> ATTRS_SHIFT) & WIDTH_MASK) == 2)) { - sp = sprite_map_position_for(previous_cell->ch, 0, true, &error); + if (UNLIKELY(previous_cell != NULL && ((previous_cell->attrs) & WIDTH_MASK) == 2)) { + sp = sprite_map_position_for(previous_cell->ch, previous_cell->attrs, 0, true, &error); } else { - sp = sprite_map_position_for(cell->ch, cell->cc, false, &error); + sp = sprite_map_position_for(cell->ch, cell->attrs, cell->cc, false, &error); } cell->sprite_x = sp->x; cell->sprite_y = sp->y; @@ -178,12 +181,13 @@ sprite_map_current_layout(unsigned int *x, unsigned int *y, unsigned int *z) { PyObject* sprite_position_for(PyObject UNUSED *self, PyObject *args) { -#define position_for_doc "position_for(ch, cc, is_second) -> x, y, z the sprite position for the specified text" +#define sprite_position_for_doc "sprite_position_for(ch, cc, is_second, attrs) -> x, y, z the sprite position for the specified text" unsigned long ch = 0; unsigned long long cc = 0; + unsigned int attrs = 1; int is_second = 0, error = 0; - if (!PyArg_ParseTuple(args, "|kKp", &ch, &cc, &is_second)) return NULL; - SpritePosition *pos = sprite_map_position_for(ch, cc, is_second, &error); + if (!PyArg_ParseTuple(args, "|kKpI", &ch, &cc, &is_second, &attrs)) return NULL; + SpritePosition *pos = sprite_map_position_for(ch, attrs, cc, is_second, &error); if (pos == NULL) { sprite_map_set_error(error); return NULL; } return Py_BuildValue("III", pos->x, pos->y, pos->z); } @@ -197,10 +201,8 @@ render_dirty_sprites(void (*render)(PyObject*, bool, bool, bool, sprite_index, s SpritePosition *sp = &(sprite_map.cache[i]); do { if (sp->filled && !sp->rendered) { - PyObject *text = line_text_at(sp->ch & CHAR_MASK, sp->cc); - char_type attrs = sp->ch >> ATTRS_SHIFT; - bool bold = (attrs >> BOLD_SHIFT) & 1, italic = (attrs >> ITALIC_SHIFT) & 1; - render(text, bold, italic, sp->is_second, sp->x, sp->y, sp->z); + PyObject *text = line_text_at(sp->ch, sp->cc); + render(text, sp->bold, sp->italic, sp->is_second, sp->x, sp->y, sp->z); Py_CLEAR(text); sp->rendered = true; }