More work on text rendering for CSD

This commit is contained in:
Kovid Goyal 2021-03-30 08:25:01 +05:30
parent 4a3c57054f
commit d7e1e53450
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 158 additions and 24 deletions

View File

@ -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;

View File

@ -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*

View File

@ -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;