Start working on actually rendering lines of text
This commit is contained in:
parent
0c4bb6a0d6
commit
7d7d82a68d
6 changed files with 182 additions and 147 deletions
|
|
@ -11,7 +11,7 @@
|
|||
GLFW_KEY_DOWN, GLFW_KEY_UP, ChildMonitor, destroy_global_data,
|
||||
destroy_sprite_map, glfw_post_empty_event, layout_sprite_map
|
||||
)
|
||||
from .fonts.render import render_cell_wrapper, set_font_family, resize_fonts
|
||||
from .fonts.render import set_font_family, resize_fonts
|
||||
from .keys import get_key_map, get_sent_data, get_shortcut
|
||||
from .session import create_session
|
||||
from .tabs import SpecialWindow, TabManager
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
def initialize_renderer():
|
||||
load_shader_programs()
|
||||
layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper)
|
||||
layout_sprite_map(cell_size.width, cell_size.height)
|
||||
|
||||
|
||||
class DumpCommands: # {{{
|
||||
|
|
@ -143,7 +143,7 @@ def change_font_size(self, new_size):
|
|||
w, h = cell_size.width, cell_size.height
|
||||
windows = tuple(filter(None, self.window_id_map.values()))
|
||||
cell_size.width, cell_size.height = resize_fonts(self.current_font_size)
|
||||
layout_sprite_map(cell_size.width, cell_size.height, render_cell_wrapper)
|
||||
layout_sprite_map(cell_size.width, cell_size.height)
|
||||
for window in windows:
|
||||
window.screen.rescale_images(w, h)
|
||||
self.resize_windows_after_font_size_change()
|
||||
|
|
|
|||
124
kitty/fonts.c
124
kitty/fonts.c
|
|
@ -8,7 +8,11 @@
|
|||
#include "fonts.h"
|
||||
#include "state.h"
|
||||
|
||||
#define MISSING_GLYPH 4
|
||||
|
||||
typedef uint16_t glyph_index;
|
||||
typedef void (*send_sprite_to_gpu_func)(unsigned int, unsigned int, unsigned int, uint8_t*);
|
||||
send_sprite_to_gpu_func current_send_sprite_to_gpu = NULL;
|
||||
|
||||
typedef struct SpritePosition SpritePosition;
|
||||
|
||||
|
|
@ -75,7 +79,7 @@ do_increment(int *error) {
|
|||
}
|
||||
|
||||
|
||||
SpritePosition*
|
||||
static SpritePosition*
|
||||
sprite_position_for(Font *font, glyph_index glyph, uint64_t extra_glyphs, bool is_second, int *error) {
|
||||
glyph_index idx = glyph & 0x3ff;
|
||||
SpritePosition *s = font->sprite_map + idx;
|
||||
|
|
@ -108,14 +112,6 @@ sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z)
|
|||
*x = sprite_tracker.xnum; *y = sprite_tracker.ynum; *z = sprite_tracker.z;
|
||||
}
|
||||
|
||||
int
|
||||
sprite_tracker_increment(sprite_index *x, sprite_index *y, sprite_index *z) {
|
||||
int error = 0;
|
||||
*x = sprite_tracker.x; *y = sprite_tracker.y; *z = sprite_tracker.z;
|
||||
do_increment(&error);
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
sprite_map_free(Font *font) {
|
||||
SpritePosition *s, *t;
|
||||
|
|
@ -183,6 +179,8 @@ static Font *symbol_map_fonts = NULL;
|
|||
static size_t symbol_maps_count = 0, symbol_map_fonts_count = 0;
|
||||
|
||||
static unsigned int cell_width = 0, cell_height = 0, baseline = 0, underline_position = 0, underline_thickness = 0;
|
||||
static uint8_t *canvas = NULL;
|
||||
static inline void clear_canvas(void) { memset(canvas, 0, cell_width * cell_height); }
|
||||
|
||||
static inline PyObject*
|
||||
update_cell_metrics(float pt_sz, float xdpi, float ydpi) {
|
||||
|
|
@ -203,6 +201,8 @@ update_cell_metrics(float pt_sz, float xdpi, float ydpi) {
|
|||
if (cell_height > 1000) { PyErr_SetString(PyExc_ValueError, "line height too large after adjustment"); return NULL; }
|
||||
underline_position = MIN(cell_height - 1, underline_position);
|
||||
sprite_tracker_set_layout(cell_width, cell_height);
|
||||
free(canvas); canvas = malloc(cell_width * cell_height);
|
||||
if (canvas == NULL) return PyErr_NoMemory();
|
||||
return Py_BuildValue("IIIII", cell_width, cell_height, baseline, underline_position, underline_thickness);
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +255,7 @@ in_symbol_maps(char_type ch) {
|
|||
}
|
||||
|
||||
|
||||
Font*
|
||||
static Font*
|
||||
font_for_cell(Cell *cell) {
|
||||
Font *ans;
|
||||
START_ALLOW_CASE_RANGE
|
||||
|
|
@ -263,7 +263,7 @@ START_ALLOW_CASE_RANGE
|
|||
case 0:
|
||||
return &blank_font;
|
||||
case 0x2500 ... 0x2570:
|
||||
case 0x2574 ... 0x2577:
|
||||
case 0x2574 ... 0x257f:
|
||||
case 0xe0b0:
|
||||
case 0xe0b2:
|
||||
return &box_font;
|
||||
|
|
@ -291,11 +291,88 @@ START_ALLOW_CASE_RANGE
|
|||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
static inline void
|
||||
set_sprite(Cell *cell, sprite_index x, sprite_index y, sprite_index z) {
|
||||
cell->sprite_x = x; cell->sprite_y = y; cell->sprite_z = z;
|
||||
}
|
||||
|
||||
static inline glyph_index
|
||||
box_glyph_id(char_type ch) {
|
||||
START_ALLOW_CASE_RANGE
|
||||
switch(ch) {
|
||||
case 0x2500 ... 0x257f:
|
||||
return ch - 0x2500;
|
||||
case 0xe0b0:
|
||||
return 0x80;
|
||||
case 0xe0b2:
|
||||
return 0x81;
|
||||
default:
|
||||
return 0x82;
|
||||
}
|
||||
END_ALLOW_CASE_RANGE
|
||||
}
|
||||
|
||||
static PyObject* box_drawing_function = NULL;
|
||||
|
||||
static void
|
||||
render_box_cell(Cell *cell) {
|
||||
int error = 0;
|
||||
glyph_index glyph = box_glyph_id(cell->ch);
|
||||
SpritePosition *sp = sprite_position_for(&box_font, glyph, 0, false, &error);
|
||||
if (sp == NULL) {
|
||||
sprite_map_set_error(error); PyErr_Print();
|
||||
set_sprite(cell, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
set_sprite(cell, sp->x, sp->y, sp->z);
|
||||
if (sp->rendered) return;
|
||||
sp->rendered = true;
|
||||
PyObject *ret = PyObject_CallFunction(box_drawing_function, "I", cell->ch);
|
||||
if (ret == NULL) { PyErr_Print(); return; }
|
||||
current_send_sprite_to_gpu(sp->x, sp->y, sp->z, PyLong_AsVoidPtr(PyTuple_GET_ITEM(ret, 0)));
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
render_run(Cell *first_cell, index_type num_cells, Font *font) {
|
||||
if (font->face) {
|
||||
} else {
|
||||
// special font
|
||||
if (font == &blank_font) {
|
||||
while(num_cells--) set_sprite(first_cell++, 0, 0, 0);
|
||||
} else if (font == &missing_font) {
|
||||
while(num_cells--) set_sprite(first_cell++, MISSING_GLYPH, 0, 0);
|
||||
} else {
|
||||
while(num_cells--) render_box_cell(first_cell++);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
render_line(Line *line) {
|
||||
Font *run_font = NULL;
|
||||
index_type first_cell_in_run = 0, i = 0;
|
||||
attrs_type prev_width = 0;
|
||||
for (; i < line->xnum; i++) {
|
||||
if (prev_width == 2) continue;
|
||||
Cell *cell = line->cells + i;
|
||||
Font *cell_font = font_for_cell(cell);
|
||||
prev_width = cell->attrs & WIDTH_MASK;
|
||||
if (cell_font == run_font) continue;
|
||||
if (run_font != NULL && i > first_cell_in_run) render_run(cell, i - first_cell_in_run, run_font);
|
||||
run_font = cell_font;
|
||||
first_cell_in_run = i;
|
||||
}
|
||||
if (run_font != NULL && i > first_cell_in_run) render_run(line->cells + first_cell_in_run, i - first_cell_in_run, run_font);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
set_font(PyObject UNUSED *m, PyObject *args) {
|
||||
PyObject *sm, *smf, *medium, *bold = NULL, *italic = NULL, *bi = NULL;
|
||||
float xdpi, ydpi, pt_sz;
|
||||
if (!PyArg_ParseTuple(args, "OO!O!fffO|OOO", &get_fallback_font, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL;
|
||||
Py_CLEAR(get_fallback_font); Py_CLEAR(box_drawing_function);
|
||||
if (!PyArg_ParseTuple(args, "OOO!O!fffO|OOO", &get_fallback_font, &box_drawing_function, &PyTuple_Type, &sm, &PyTuple_Type, &smf, &pt_sz, &xdpi, &ydpi, &medium, &bold, &italic, &bi)) return NULL;
|
||||
Py_INCREF(get_fallback_font); Py_INCREF(box_drawing_function);
|
||||
if (!alloc_font(&medium_font, medium, false, false)) return PyErr_NoMemory();
|
||||
if (bold && !alloc_font(&bold_font, bold, false, false)) return PyErr_NoMemory();
|
||||
if (italic && !alloc_font(&italic_font, italic, false, false)) return PyErr_NoMemory();
|
||||
|
|
@ -327,7 +404,9 @@ static hb_buffer_t *harfbuzz_buffer = NULL;
|
|||
|
||||
static void
|
||||
finalize(void) {
|
||||
free(canvas);
|
||||
Py_CLEAR(get_fallback_font);
|
||||
Py_CLEAR(box_drawing_function);
|
||||
free_font(&medium_font); free_font(&bold_font); free_font(&italic_font); free_font(&bi_font);
|
||||
for (size_t i = 0; fallback_fonts[i].face != NULL; i++) free_font(fallback_fonts + i);
|
||||
for (size_t i = 0; symbol_map_fonts_count; i++) free_font(symbol_map_fonts + i);
|
||||
|
|
@ -362,11 +441,31 @@ test_sprite_position_for(PyObject UNUSED *self, PyObject *args) {
|
|||
return Py_BuildValue("HHH", pos->x, pos->y, pos->z);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
send_prerendered_sprites(PyObject UNUSED *s, PyObject *args) {
|
||||
int error = 0;
|
||||
sprite_index x = 0, y = 0, z = 0;
|
||||
// blank cell
|
||||
clear_canvas();
|
||||
current_send_sprite_to_gpu(x, y, z, canvas);
|
||||
do_increment(&error);
|
||||
if (error != 0) { sprite_map_set_error(error); return NULL; }
|
||||
for (ssize_t i = 0; i < PyTuple_GET_SIZE(args); i++) {
|
||||
x = sprite_tracker.x; y = sprite_tracker.y; z = sprite_tracker.z;
|
||||
do_increment(&error);
|
||||
if (error != 0) { sprite_map_set_error(error); return NULL; }
|
||||
current_send_sprite_to_gpu(x, y, z, PyLong_AsVoidPtr(PyTuple_GET_ITEM(args, i)));
|
||||
}
|
||||
return Py_BuildValue("H", x);
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(set_font_size, METH_VARARGS),
|
||||
METHODB(set_font, METH_VARARGS),
|
||||
METHODB(sprite_map_set_limits, METH_VARARGS),
|
||||
METHODB(sprite_map_set_layout, METH_VARARGS),
|
||||
METHODB(send_prerendered_sprites, METH_VARARGS),
|
||||
METHODB(test_sprite_position_for, METH_VARARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
|
@ -380,5 +479,6 @@ init_fonts(PyObject *module) {
|
|||
harfbuzz_buffer = hb_buffer_create();
|
||||
if (harfbuzz_buffer == NULL || !hb_buffer_allocation_successful(harfbuzz_buffer) || !hb_buffer_pre_allocate(harfbuzz_buffer, 2000)) { PyErr_NoMemory(); return false; }
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return false;
|
||||
current_send_sprite_to_gpu = send_sprite_to_gpu;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,4 +19,3 @@ hb_font_t* harfbuzz_font_for_face(PyObject*);
|
|||
bool set_size_for_face(PyObject*, float, float, float);
|
||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||
void sprite_tracker_current_layout(unsigned int *x, unsigned int *y, unsigned int *z);
|
||||
int sprite_tracker_increment(sprite_index *x, sprite_index *y, sprite_index *z);
|
||||
|
|
|
|||
|
|
@ -4,12 +4,15 @@
|
|||
|
||||
import ctypes
|
||||
from collections import namedtuple
|
||||
from math import sin, pi, ceil, floor, sqrt
|
||||
from math import ceil, floor, pi, sin, sqrt
|
||||
|
||||
from kitty.constants import isosx
|
||||
from kitty.fast_data_types import (
|
||||
send_prerendered_sprites, set_font, set_font_size
|
||||
)
|
||||
from kitty.fonts.box_drawing import render_box_char, render_missing_glyph
|
||||
from kitty.utils import get_logical_dpi
|
||||
from kitty.fast_data_types import set_font, set_font_size
|
||||
from .box_drawing import render_box_char, is_renderable_box_char
|
||||
|
||||
if isosx:
|
||||
from .core_text import get_font_files, font_for_text, face_from_font, font_for_family, save_medium_face
|
||||
else:
|
||||
|
|
@ -37,17 +40,27 @@ def create_symbol_map(opts):
|
|||
return sm, tuple(faces)
|
||||
|
||||
|
||||
FontState = namedtuple('FontState', 'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness')
|
||||
FontState = namedtuple(
|
||||
'FontState',
|
||||
'family pt_sz xdpi ydpi cell_width cell_height baseline underline_position underline_thickness'
|
||||
)
|
||||
|
||||
|
||||
def get_fallback_font(text, bold, italic):
|
||||
state = set_font_family.state
|
||||
return create_face(font_for_text(text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold, italic))
|
||||
return create_face(
|
||||
font_for_text(
|
||||
text, state.family, state.pt_sz, state.xdpi, state.ydpi, bold,
|
||||
italic
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def set_font_family(opts, override_font_size=None):
|
||||
if hasattr(set_font_family, 'state'):
|
||||
raise ValueError('Cannot set font family more than once, use resize_fonts() to change size')
|
||||
raise ValueError(
|
||||
'Cannot set font family more than once, use resize_fonts() to change size'
|
||||
)
|
||||
sz = override_font_size or opts.font_size
|
||||
xdpi, ydpi = get_logical_dpi()
|
||||
set_font_family.state = FontState('', sz, xdpi, ydpi, 0, 0, 0, 0, 0)
|
||||
|
|
@ -58,8 +71,14 @@ def set_font_family(opts, override_font_size=None):
|
|||
if k in font_map:
|
||||
faces.append(create_face(font_map[k]))
|
||||
sm, sfaces = create_symbol_map(opts)
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font(get_fallback_font, sm, sfaces, sz, xdpi, ydpi, *faces)
|
||||
set_font_family.state = FontState(opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness)
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font(
|
||||
get_fallback_font, render_box_drawing, sm, sfaces, sz, xdpi, ydpi, *faces
|
||||
)
|
||||
set_font_family.state = FontState(
|
||||
opts.font_family, sz, xdpi, ydpi, cell_width, cell_height, baseline,
|
||||
underline_position, underline_thickness
|
||||
)
|
||||
prerender()
|
||||
return cell_width, cell_height
|
||||
|
||||
|
||||
|
|
@ -67,9 +86,14 @@ def resize_fonts(new_sz, xdpi=None, ydpi=None):
|
|||
s = set_font_family.state
|
||||
xdpi = xdpi or s.xdpi
|
||||
ydpi = ydpi or s.ydpi
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(new_sz, xdpi, ydpi)
|
||||
cell_width, cell_height, baseline, underline_position, underline_thickness = set_font_size(
|
||||
new_sz, xdpi, ydpi
|
||||
)
|
||||
set_font_family.state = FontState(
|
||||
s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline, underline_position, underline_thickness)
|
||||
s.family, new_sz, xdpi, ydpi, cell_width, cell_height, baseline,
|
||||
underline_position, underline_thickness
|
||||
)
|
||||
prerender()
|
||||
|
||||
|
||||
def add_line(buf, cell_width, position, thickness, cell_height):
|
||||
|
|
@ -93,7 +117,9 @@ def clamp_x(x):
|
|||
return max(0, min(int(x), cell_width - 1))
|
||||
|
||||
def add_intensity(x, y, distance):
|
||||
buf[cell_width * y + x] = min(255, buf[cell_width * y + x] + int(255 * (1 - distance)))
|
||||
buf[cell_width * y + x] = min(
|
||||
255, buf[cell_width * y + x] + int(255 * (1 - distance))
|
||||
)
|
||||
|
||||
for x_exact in range(cell_width):
|
||||
y_exact = yfactor * sin(x_exact * xfactor) + position
|
||||
|
|
@ -106,103 +132,47 @@ def add_intensity(x, y, distance):
|
|||
add_intensity(x, y, dist)
|
||||
|
||||
|
||||
def render_cell(text=' ', bold=False, italic=False, underline=0, strikethrough=False):
|
||||
def render_special(underline=0, strikethrough=False, missing=False):
|
||||
s = set_font_family.state
|
||||
cell_width, cell_height, baseline = s.cell_width, s.cell_height, s.baseline
|
||||
underline_thickness, underline_position = s.underline_thickness, s.underline_position
|
||||
underline_position, underline_thickness = s.underline_position, s.underline_thickness
|
||||
CharTexture = ctypes.c_ubyte * cell_width * cell_height
|
||||
if is_renderable_box_char(text):
|
||||
first, second = render_box_char(text, CharTexture(), cell_width, cell_height), None
|
||||
else:
|
||||
first = CharTexture()
|
||||
second = None
|
||||
ans = CharTexture if missing else CharTexture()
|
||||
|
||||
def dl(f, *a):
|
||||
f(first, cell_width, *a)
|
||||
if second is not None:
|
||||
f(second, cell_width, *a)
|
||||
f(ans, cell_width, *a)
|
||||
|
||||
if underline:
|
||||
t = underline_thickness
|
||||
if underline == 2:
|
||||
t = max(1, min(cell_height - underline_position - 1, t))
|
||||
dl(add_curl if underline == 2 else add_line, underline_position, t, cell_height)
|
||||
dl(
|
||||
add_curl
|
||||
if underline == 2 else add_line, underline_position, t, cell_height
|
||||
)
|
||||
if strikethrough:
|
||||
pos = int(0.65 * baseline)
|
||||
dl(add_line, pos, underline_thickness, cell_height)
|
||||
|
||||
return first, second
|
||||
|
||||
|
||||
class Buf:
|
||||
|
||||
def __init__(self, buf):
|
||||
self.buf = buf
|
||||
|
||||
def __call__(self):
|
||||
return ctypes.addressof(self.buf)
|
||||
|
||||
|
||||
def render_cell_wrapper(text, bold, italic, underline, strikethrough, is_second):
|
||||
first, second = render_cell(text, bold, italic, underline, strikethrough)
|
||||
ans = second if is_second else first
|
||||
ans = ans or render_cell()[0]
|
||||
return Buf(ans)
|
||||
|
||||
|
||||
def join_cells(cell_width, cell_height, *cells):
|
||||
dstride = len(cells) * cell_width
|
||||
ans = (ctypes.c_ubyte * (cell_height * dstride))()
|
||||
for r in range(cell_height):
|
||||
soff = r * cell_width
|
||||
doff = r * dstride
|
||||
for cnum, cell in enumerate(cells):
|
||||
doff2 = doff + (cnum * cell_width)
|
||||
ans[doff2:doff2 + cell_width] = cell[soff:soff + cell_width]
|
||||
if missing:
|
||||
buf = bytearray(cell_width * cell_height)
|
||||
render_missing_glyph(buf, cell_width, cell_height)
|
||||
ans = CharTexture.from_buffer(buf)
|
||||
return ans
|
||||
|
||||
|
||||
def display_bitmap(data, w, h):
|
||||
from PIL import Image
|
||||
img = Image.new('L', (w, h))
|
||||
img.putdata(data)
|
||||
img.show()
|
||||
def prerender():
|
||||
# Pre-render the special blank, underline and strikethrough cells
|
||||
cells = render_special(1), render_special(2), render_special(0, True), render_special(missing=True)
|
||||
if send_prerendered_sprites(*map(ctypes.addressof, cells)) != len(cells):
|
||||
raise RuntimeError('Your GPU has too small a max texture size')
|
||||
|
||||
|
||||
def render_string(text='\'Qing👁a⧽', underline=2, strikethrough=True):
|
||||
import unicodedata
|
||||
cells = []
|
||||
current_text = ''
|
||||
|
||||
def render_one(c):
|
||||
f, s = render_cell(c, underline=underline, strikethrough=strikethrough)
|
||||
cells.append(f)
|
||||
if s is not None:
|
||||
cells.append(s)
|
||||
|
||||
for c in text:
|
||||
if unicodedata.combining(c):
|
||||
current_text += c
|
||||
else:
|
||||
if current_text:
|
||||
render_one(current_text)
|
||||
current_text = c
|
||||
if current_text:
|
||||
render_one(current_text)
|
||||
def render_box_drawing(codepoint):
|
||||
s = set_font_family.state
|
||||
cell_width, cell_height = s.cell_width, s.cell_height
|
||||
char_data = join_cells(cell_width, cell_height, *cells)
|
||||
return char_data, cell_width * len(cells), cell_height
|
||||
|
||||
|
||||
def test_rendering(text='\'Ping👁a⧽', sz=144, family='monospace', underline=2, strikethrough=True):
|
||||
from kitty.config import defaults
|
||||
from kitty.fast_data_types import glfw_init, glfw_terminate
|
||||
if not glfw_init():
|
||||
raise SystemExit('Failed to initialize glfw')
|
||||
try:
|
||||
opts = defaults._replace(font_family=family, font_size=sz)
|
||||
set_font_family(opts)
|
||||
display_bitmap(*render_string(text, underline=underline, strikethrough=strikethrough))
|
||||
finally:
|
||||
glfw_terminate()
|
||||
CharTexture = ctypes.c_ubyte * cell_width * cell_height
|
||||
buf = render_box_char(
|
||||
chr(codepoint), CharTexture(), cell_width, cell_height
|
||||
)
|
||||
return ctypes.addressof(buf), buf
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ typedef struct {
|
|||
GLuint texture_id;
|
||||
GLenum texture_unit;
|
||||
GLint max_texture_size, max_array_texture_layers;
|
||||
PyObject *render_cell;
|
||||
} SpriteMap;
|
||||
|
||||
static SpriteMap sprite_map = { .xnum = 1, .ynum = 1, .last_num_of_layers = 1, .last_ynum = -1, .texture_unit = GL_TEXTURE0 };
|
||||
|
|
@ -78,15 +77,6 @@ realloc_sprite_texture() {
|
|||
sprite_map.texture_id = tex;
|
||||
}
|
||||
|
||||
static inline PyObject*
|
||||
render_cell(PyObject *text, bool bold, bool italic, unsigned int underline, bool strikethrough, bool is_second) {
|
||||
#define B(x) (x ? Py_True : Py_False)
|
||||
PyObject *ret = PyObject_CallFunction(sprite_map.render_cell, "OOOIOO", text, B(bold), B(italic), underline, B(strikethrough), B(is_second));
|
||||
if (ret == NULL) { PyErr_Print(); fatal("Rendering of a cell failed, aborting"); }
|
||||
return ret;
|
||||
#undef B
|
||||
}
|
||||
|
||||
static inline void
|
||||
ensure_sprite_map() {
|
||||
static GLuint bound_texture_id = 0;
|
||||
|
|
@ -98,36 +88,20 @@ ensure_sprite_map() {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
sprite_send_to_gpu(unsigned int x, unsigned int y, unsigned int z, PyObject *buf) {
|
||||
void
|
||||
send_sprite_to_gpu(unsigned int x, unsigned int y, unsigned int z, uint8_t *buf) {
|
||||
unsigned int xnum, ynum, znum;
|
||||
sprite_tracker_current_layout(&xnum, &ynum, &znum);
|
||||
if ((int)znum >= sprite_map.last_num_of_layers || (znum == 0 && (int)ynum > sprite_map.last_ynum)) realloc_sprite_texture();
|
||||
glBindTexture(GL_TEXTURE_2D_ARRAY, sprite_map.texture_id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
x *= sprite_map.cell_width; y *= sprite_map.cell_height;
|
||||
PyObject *ret = PyObject_CallObject(buf, NULL);
|
||||
if (ret == NULL) { PyErr_Print(); fatal("Failed to get address of rendered cell buffer"); }
|
||||
void *address = PyLong_AsVoidPtr(ret);
|
||||
Py_DECREF(ret);
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, sprite_map.cell_width, sprite_map.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, address);
|
||||
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, x, y, z, sprite_map.cell_width, sprite_map.cell_height, 1, GL_RED, GL_UNSIGNED_BYTE, buf);
|
||||
Py_DECREF(buf);
|
||||
}
|
||||
|
||||
static inline sprite_index
|
||||
send_prerendered(unsigned int underline, bool strikethrough) {
|
||||
sprite_index x, y, z;
|
||||
PyObject *blank = PyUnicode_FromString(" ");
|
||||
if (blank == NULL) { fatal("Out of memory"); }
|
||||
PyObject *buf = render_cell(blank, false, false, underline, strikethrough, false);
|
||||
Py_CLEAR(blank);
|
||||
if (sprite_tracker_increment(&x, &y, &z) != 0) { fatal("Failed to increment sprite map for prerendering"); }
|
||||
sprite_send_to_gpu(x, y, z, buf);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
layout_sprite_map(unsigned int cell_width, unsigned int cell_height, PyObject *render_cell) {
|
||||
layout_sprite_map(unsigned int cell_width, unsigned int cell_height) {
|
||||
sprite_map.cell_width = MAX(1, cell_width);
|
||||
sprite_map.cell_height = MAX(1, cell_height);
|
||||
global_state.cell_width = sprite_map.cell_width;
|
||||
|
|
@ -138,21 +112,13 @@ layout_sprite_map(unsigned int cell_width, unsigned int cell_height, PyObject *r
|
|||
/* sprite_map_set_limits(sprite_map.max_texture_size, sprite_map.max_array_texture_layers); */
|
||||
}
|
||||
/* sprite_map_set_layout(sprite_map.cell_width, sprite_map.cell_height); */
|
||||
Py_CLEAR(sprite_map.render_cell);
|
||||
sprite_map.render_cell = render_cell; Py_INCREF(sprite_map.render_cell);
|
||||
if (sprite_map.texture_id) { glDeleteTextures(1, &(sprite_map.texture_id)); sprite_map.texture_id = 0; }
|
||||
realloc_sprite_texture();
|
||||
// Pre-render the basic cells to ensure they have known sprite numbers
|
||||
send_prerendered(0, false);
|
||||
send_prerendered(1, false);
|
||||
send_prerendered(2, false);
|
||||
if (send_prerendered(0, true) != 3) { fatal("Available OpenGL texture size is too small"); }
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_sprite_map() {
|
||||
/* sprite_map_free(); */
|
||||
Py_CLEAR(sprite_map.render_cell);
|
||||
if (sprite_map.texture_id) {
|
||||
glDeleteTextures(1, &(sprite_map.texture_id));
|
||||
sprite_map.texture_id = 0;
|
||||
|
|
@ -535,9 +501,8 @@ NO_ARG_INT(create_graphics_vao)
|
|||
NO_ARG(destroy_sprite_map)
|
||||
PYWRAP1(layout_sprite_map) {
|
||||
unsigned int cell_width, cell_height;
|
||||
PyObject *render_cell;
|
||||
PA("IIO", &cell_width, &cell_height, &render_cell);
|
||||
layout_sprite_map(cell_width, cell_height, render_cell);
|
||||
PA("II", &cell_width, &cell_height);
|
||||
layout_sprite_map(cell_width, cell_height);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,3 +104,4 @@ void draw_cursor(CursorRenderInfo *);
|
|||
void update_viewport_size(int, int);
|
||||
void free_texture(uint32_t*);
|
||||
void send_image_to_gpu(uint32_t*, const void*, int32_t, int32_t, bool, bool);
|
||||
void send_sprite_to_gpu(unsigned int, unsigned int, unsigned int, uint8_t*);
|
||||
|
|
|
|||
Loading…
Reference in a new issue