More work on text rendering for CSD
This commit is contained in:
parent
4a3c57054f
commit
d7e1e53450
@ -12,10 +12,6 @@
|
||||
#include <ft2build.h>
|
||||
#include <hb-ft.h>
|
||||
|
||||
#if HB_VERSION_MAJOR > 1 || (HB_VERSION_MAJOR == 1 && (HB_VERSION_MINOR > 6 || (HB_VERSION_MINOR == 6 && HB_VERSION_MICRO >= 3)))
|
||||
#define HARFBUZZ_HAS_CHANGE_FONT
|
||||
#endif
|
||||
|
||||
#if FREETYPE_MAJOR == 2 && FREETYPE_MINOR < 7
|
||||
#define FT_Bitmap_Init FT_Bitmap_New
|
||||
#endif
|
||||
@ -149,17 +145,7 @@ set_font_size(Face *self, FT_F26Dot6 char_width, FT_F26Dot6 char_height, FT_UInt
|
||||
return set_font_size(self, 0, h, xdpi, ydpi, 0, cell_height);
|
||||
}
|
||||
self->char_width = char_width; self->char_height = char_height; self->xdpi = xdpi; self->ydpi = ydpi;
|
||||
if (self->harfbuzz_font != NULL) {
|
||||
#ifdef HARFBUZZ_HAS_CHANGE_FONT
|
||||
hb_ft_font_changed(self->harfbuzz_font);
|
||||
#else
|
||||
hb_font_set_scale(
|
||||
self->harfbuzz_font,
|
||||
(int) (((uint64_t) self->face->size->metrics.x_scale * (uint64_t) self->face->units_per_EM + (1u<<15)) >> 16),
|
||||
(int) (((uint64_t) self->face->size->metrics.y_scale * (uint64_t) self->face->units_per_EM + (1u<<15)) >> 16)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
if (self->harfbuzz_font != NULL) hb_ft_font_changed(self->harfbuzz_font);
|
||||
} else {
|
||||
if (!self->is_scalable && self->face->num_fixed_sizes > 0) {
|
||||
int32_t min_diff = INT32_MAX;
|
||||
|
||||
@ -8,43 +8,189 @@
|
||||
#include "freetype_render_ui_text.h"
|
||||
#include <hb.h>
|
||||
#include <hb-ft.h>
|
||||
#include "charsets.h"
|
||||
|
||||
typedef struct FamilyInformation {
|
||||
char *name;
|
||||
bool bold, italic;
|
||||
} FamilyInformation;
|
||||
|
||||
FT_Face main_face = NULL;
|
||||
typedef struct Face {
|
||||
FT_Face freetype;
|
||||
hb_font_t *hb;
|
||||
FT_UInt pixel_size;
|
||||
struct Face *fallbacks;
|
||||
size_t count, capacity;
|
||||
} Face;
|
||||
|
||||
Face main_face = {0};
|
||||
FontConfigFace main_face_information = {0};
|
||||
FamilyInformation main_face_family = {0};
|
||||
hb_buffer_t *hb_buffer = NULL;
|
||||
|
||||
static inline FT_UInt
|
||||
glyph_id_for_codepoint(FT_Face face, char_type cp) {
|
||||
return FT_Get_Char_Index(face, cp);
|
||||
glyph_id_for_codepoint(Face *face, char_type cp) {
|
||||
return FT_Get_Char_Index(face->freetype, cp);
|
||||
}
|
||||
|
||||
static void
|
||||
free_face(Face *face) {
|
||||
if (face->freetype) FT_Done_Face(face->freetype);
|
||||
if (face->hb) hb_font_destroy(face->hb);
|
||||
for (size_t i = 0; i < face->count; i++) free_face(face->fallbacks + i);
|
||||
memset(face, 0, sizeof(Face));
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup(void) {
|
||||
if (main_face) FT_Done_Face(main_face);
|
||||
main_face = NULL;
|
||||
free_face(&main_face);
|
||||
free(main_face_information.path); main_face_information.path = NULL;
|
||||
free(main_face_family.name);
|
||||
memset(&main_face_family, 0, sizeof(FamilyInformation));
|
||||
if (hb_buffer) hb_buffer_destroy(hb_buffer);
|
||||
hb_buffer = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
set_main_face_family(const char *family, bool bold, bool italic) {
|
||||
if (family == main_face_family.name || (main_face_family.name && strcmp(family, main_face_family.name) == 0)) return;
|
||||
cleanup();
|
||||
main_face_family.name = strdup(family);
|
||||
main_face_family.bold = bold; main_face_family.italic = italic;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
static bool
|
||||
load_font(FontConfigFace *info, Face *ans) {
|
||||
ans->freetype = native_face_from_path(info->path, info->index);
|
||||
if (!ans->freetype) return false;
|
||||
ans->hb = hb_ft_font_create(ans->freetype, NULL);
|
||||
if (!ans->hb) { PyErr_NoMemory(); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
ensure_state(void) {
|
||||
if (main_face) return false;
|
||||
if (main_face.freetype && main_face.hb) return false;
|
||||
if (!information_for_font_family(main_face_family.name, main_face_family.bold, main_face_family.italic, &main_face_information)) return false;
|
||||
main_face = native_face_from_path(main_face_information.path, main_face_information.index);
|
||||
return !!main_face;
|
||||
if (!load_font(&main_face_information, &main_face)) return false;
|
||||
hb_buffer = hb_buffer_create();
|
||||
if (!hb_buffer) { PyErr_NoMemory(); return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_pixel_size(Face *face, FT_UInt sz) {
|
||||
if (sz != face->pixel_size) {
|
||||
FT_Set_Pixel_Sizes(face->freetype, sz, sz); // TODO: check for and handle failures
|
||||
hb_ft_font_changed(face->hb);
|
||||
face->pixel_size = sz;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct RenderState {
|
||||
uint32_t pending_in_buffer, fg, bg;
|
||||
uint8_t *output;
|
||||
bool alpha_first;
|
||||
size_t output_width, output_height;
|
||||
Face *current_face;
|
||||
} RenderState;
|
||||
|
||||
|
||||
bool
|
||||
render_run(RenderState *rs) {
|
||||
hb_buffer_guess_segment_properties(hb_buffer);
|
||||
if (!HB_DIRECTION_IS_HORIZONTAL (hb_buffer_get_direction(hb_buffer))) {
|
||||
PyErr_SetString(PyExc_ValueError, "Vertical text is not supported");
|
||||
return false;
|
||||
}
|
||||
FT_UInt pixel_size = 2 * rs->output_height / 3;
|
||||
set_pixel_size(rs->current_face, pixel_size);
|
||||
hb_shape(rs->current_face->hb, hb_buffer, NULL, 0);
|
||||
unsigned int len = hb_buffer_get_length(hb_buffer);
|
||||
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
|
||||
hb_glyph_position_t *pos = hb_buffer_get_glyph_positions(hb_buffer, NULL);
|
||||
|
||||
(void)len; (void)info; (void)pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
current_font_has_codepoint(RenderState *rs, char_type codep) {
|
||||
if (rs->current_face != &main_face && glyph_id_for_codepoint(&main_face, codep) > 0) {
|
||||
rs->current_face = &main_face;
|
||||
return true;
|
||||
}
|
||||
return glyph_id_for_codepoint(rs->current_face, codep);
|
||||
}
|
||||
|
||||
static bool
|
||||
find_fallback_font_for(RenderState *rs, char_type codep) {
|
||||
if (glyph_id_for_codepoint(&main_face, codep) > 0) {
|
||||
rs->current_face = &main_face;
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < main_face.count; i++) {
|
||||
if (glyph_id_for_codepoint(main_face.fallbacks + i, codep) > 0) {
|
||||
rs->current_face = main_face.fallbacks + i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
FontConfigFace q;
|
||||
if (!fallback_font(codep, main_face_family.name, main_face_family.bold, main_face_family.italic, &q)) return false;
|
||||
ensure_space_for(&main_face, fallbacks, Face, main_face.count + 1, capacity, 8, true);
|
||||
Face *ans = main_face.fallbacks + main_face.count;
|
||||
if (!load_font(&q, ans)) return false;
|
||||
main_face.count++;
|
||||
rs->current_face = ans;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
render_single_line(const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, bool alpha_first) {
|
||||
if (!ensure_state()) return false;
|
||||
for (uint32_t *px = (uint32_t*)output_buf, *end = ((uint32_t*)output_buf) + width * height; px < end; px++) *px = bg;
|
||||
if (!text || !text[0]) return true;
|
||||
(void)fg; (void)alpha_first;
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return false; }
|
||||
RenderState rs = {
|
||||
.current_face = &main_face, .fg = fg, .bg = bg, .output_width = width, .output_height = height,
|
||||
.output = output_buf, .alpha_first = alpha_first
|
||||
};
|
||||
|
||||
for (uint32_t i = 0, codep = 0, state = 0, prev = UTF8_ACCEPT; text[i] > 0; i++) {
|
||||
switch(decode_utf8(&state, &codep, text[i])) {
|
||||
case UTF8_ACCEPT:
|
||||
if (current_font_has_codepoint(&rs, codep)) {
|
||||
hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);
|
||||
rs.pending_in_buffer += 1;
|
||||
} else {
|
||||
if (rs.pending_in_buffer) {
|
||||
if (!render_run(&rs)) return false;
|
||||
rs.pending_in_buffer = 0;
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
}
|
||||
if (!find_fallback_font_for(&rs, codep)) {
|
||||
hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);
|
||||
rs.pending_in_buffer += 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case UTF8_REJECT:
|
||||
state = UTF8_ACCEPT;
|
||||
if (prev != UTF8_ACCEPT && i > 0) i--;
|
||||
break;
|
||||
}
|
||||
prev = state;
|
||||
}
|
||||
if (rs.pending_in_buffer) {
|
||||
if (!render_run(&rs)) return false;
|
||||
rs.pending_in_buffer = 0;
|
||||
hb_buffer_clear_contents(hb_buffer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#include "data-types.h"
|
||||
#include <hb-ft.h>
|
||||
|
||||
bool render_single_line(const char *text, uint32_t fg, uint32_t bg, uint8_t *output_buf, size_t width, size_t height, bool alpha_first);
|
||||
|
||||
typedef struct FontConfigFace {
|
||||
char *path;
|
||||
int index;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user