Implement rendering of emoji for CSD
This commit is contained in:
parent
ada3f9e547
commit
dd2b6119b3
@ -290,7 +290,7 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
fallback_font(char_type ch, const char *family, bool bold, bool italic, FontConfigFace *ans) {
|
fallback_font(char_type ch, const char *family, bool bold, bool italic, bool prefer_color, FontConfigFace *ans) {
|
||||||
memset(ans, 0, sizeof(FontConfigFace));
|
memset(ans, 0, sizeof(FontConfigFace));
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
FcPattern *pat = FcPatternCreate();
|
FcPattern *pat = FcPatternCreate();
|
||||||
@ -298,6 +298,7 @@ fallback_font(char_type ch, const char *family, bool bold, bool italic, FontConf
|
|||||||
if (family) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family");
|
if (family) AP(FcPatternAddString, FC_FAMILY, (const FcChar8*)family, "family");
|
||||||
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
if (bold) { AP(FcPatternAddInteger, FC_WEIGHT, FC_WEIGHT_BOLD, "weight"); }
|
||||||
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
if (italic) { AP(FcPatternAddInteger, FC_SLANT, FC_SLANT_ITALIC, "slant"); }
|
||||||
|
if (prefer_color) { AP(FcPatternAddBool, FC_COLOR, true, "color"); }
|
||||||
char_buf[0] = ch;
|
char_buf[0] = ch;
|
||||||
add_charset(pat, 1);
|
add_charset(pat, 1);
|
||||||
ok = _native_fc_match(pat, ans);
|
ok = _native_fc_match(pat, ans);
|
||||||
|
|||||||
@ -501,8 +501,7 @@ detect_right_edge(ProcessedBitmap *ans) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline) {
|
render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline UNUSED) {
|
||||||
(void)baseline;
|
|
||||||
unsigned short best = 0, diff = USHRT_MAX;
|
unsigned short best = 0, diff = USHRT_MAX;
|
||||||
const short limit = self->face->num_fixed_sizes;
|
const short limit = self->face->num_fixed_sizes;
|
||||||
for (short i = 0; i < limit; i++) {
|
for (short i = 0; i < limit; i++) {
|
||||||
|
|||||||
@ -9,6 +9,9 @@
|
|||||||
#include <hb.h>
|
#include <hb.h>
|
||||||
#include <hb-ft.h>
|
#include <hb-ft.h>
|
||||||
#include "charsets.h"
|
#include "charsets.h"
|
||||||
|
#include "unicode-data.h"
|
||||||
|
#include "wcwidth-std.h"
|
||||||
|
#include "wcswidth.h"
|
||||||
#include FT_BITMAP_H
|
#include FT_BITMAP_H
|
||||||
|
|
||||||
typedef struct FamilyInformation {
|
typedef struct FamilyInformation {
|
||||||
@ -103,10 +106,32 @@ ensure_state(void) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
font_units_to_pixels_y(FT_Face face, int x) {
|
||||||
|
return (int)ceil((double)FT_MulFix(x, face->size->metrics.y_scale) / 64.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_UInt
|
||||||
|
choose_bitmap_size(FT_Face face, FT_UInt desired_height) {
|
||||||
|
unsigned short best = 0, diff = USHRT_MAX;
|
||||||
|
const short limit = face->num_fixed_sizes;
|
||||||
|
for (short i = 0; i < limit; i++) {
|
||||||
|
unsigned short h = face->available_sizes[i].height;
|
||||||
|
unsigned short d = h > (unsigned short)desired_height ? h - (unsigned short)desired_height : (unsigned short)desired_height - h;
|
||||||
|
if (d < diff) {
|
||||||
|
diff = d;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FT_Select_Size(face, best);
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
set_pixel_size(Face *face, FT_UInt sz) {
|
set_pixel_size(Face *face, FT_UInt sz, bool has_color) {
|
||||||
if (sz != face->pixel_size) {
|
if (sz != face->pixel_size) {
|
||||||
FT_Set_Pixel_Sizes(face->freetype, sz, sz); // TODO: check for and handle failures
|
if (has_color) sz = choose_bitmap_size(face->freetype, font_units_to_pixels_y(main_face.freetype, main_face.freetype->height));
|
||||||
|
else FT_Set_Pixel_Sizes(face->freetype, sz, sz);
|
||||||
hb_ft_font_changed(face->hb);
|
hb_ft_font_changed(face->hb);
|
||||||
hb_ft_font_set_load_flags(face->hb, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT));
|
hb_ft_font_set_load_flags(face->hb, get_load_flags(face->hinting, face->hintstyle, FT_LOAD_DEFAULT));
|
||||||
face->pixel_size = sz;
|
face->pixel_size = sz;
|
||||||
@ -123,11 +148,6 @@ typedef struct RenderState {
|
|||||||
Region src, dest;
|
Region src, dest;
|
||||||
} RenderState;
|
} RenderState;
|
||||||
|
|
||||||
static inline int
|
|
||||||
font_units_to_pixels_y(FT_Face face, int x) {
|
|
||||||
return (int)ceil((double)FT_MulFix(x, face->size->metrics.y_scale) / 64.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
setup_regions(ProcessedBitmap *bm, RenderState *rs, int baseline) {
|
setup_regions(ProcessedBitmap *bm, RenderState *rs, int baseline) {
|
||||||
rs->src = (Region){ .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x };
|
rs->src = (Region){ .left = bm->start_x, .bottom = bm->rows, .right = bm->width + bm->start_x };
|
||||||
@ -163,12 +183,24 @@ alpha_blend_premult(pixel over, pixel under) {
|
|||||||
#undef ans
|
#undef ans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
render_color_bitmap(ProcessedBitmap *src, RenderState *rs) {
|
||||||
|
for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) {
|
||||||
|
pixel *dest_row = rs->output + rs->output_width * dr;
|
||||||
|
uint8_t *src_px = src->buf + src->stride * sr;
|
||||||
|
for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++, src_px += 4) {
|
||||||
|
pixel fg = premult_pixel(ARGB(src_px[3], src_px[2], src_px[1], src_px[0]), src_px[3]);
|
||||||
|
dest_row[dc] = alpha_blend_premult(fg, dest_row[dc]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
render_gray_bitmap(ProcessedBitmap *src, RenderState *rs) {
|
render_gray_bitmap(ProcessedBitmap *src, RenderState *rs) {
|
||||||
for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) {
|
for (size_t sr = rs->src.top, dr = rs->dest.top; sr < rs->src.bottom && dr < rs->dest.bottom; sr++, dr++) {
|
||||||
pixel *dest_row = rs->output + rs->output_width * dr;
|
pixel *dest_row = rs->output + rs->output_width * dr;
|
||||||
uint8_t *src_row = src->buf + src->stride * sr;
|
uint8_t *src_row = src->buf + src->stride * sr;
|
||||||
for(size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++) {
|
for (size_t sc = rs->src.left, dc = rs->dest.left; sc < rs->src.right && dc < rs->dest.right; sc++, dc++) {
|
||||||
pixel fg = premult_pixel(rs->fg, src_row[sc]);
|
pixel fg = premult_pixel(rs->fg, src_row[sc]);
|
||||||
dest_row[dc] = alpha_blend_premult(fg, dest_row[dc]);
|
dest_row[dc] = alpha_blend_premult(fg, dest_row[dc]);
|
||||||
}
|
}
|
||||||
@ -192,15 +224,15 @@ render_run(RenderState *rs) {
|
|||||||
PyErr_SetString(PyExc_ValueError, "Vertical text is not supported");
|
PyErr_SetString(PyExc_ValueError, "Vertical text is not supported");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
FT_Face face = rs->current_face->freetype;
|
||||||
|
bool has_color = FT_HAS_COLOR(face);
|
||||||
FT_UInt pixel_size = 3 * rs->output_height / 4;
|
FT_UInt pixel_size = 3 * rs->output_height / 4;
|
||||||
set_pixel_size(rs->current_face, pixel_size);
|
set_pixel_size(rs->current_face, pixel_size, has_color);
|
||||||
hb_shape(rs->current_face->hb, hb_buffer, NULL, 0);
|
hb_shape(rs->current_face->hb, hb_buffer, NULL, 0);
|
||||||
unsigned int len = hb_buffer_get_length(hb_buffer);
|
unsigned int len = hb_buffer_get_length(hb_buffer);
|
||||||
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
|
hb_glyph_info_t *info = hb_buffer_get_glyph_infos(hb_buffer, NULL);
|
||||||
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);
|
hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(hb_buffer, NULL);
|
||||||
FT_Face face = rs->current_face->freetype;
|
|
||||||
int baseline = font_units_to_pixels_y(face, face->ascender);
|
int baseline = font_units_to_pixels_y(face, face->ascender);
|
||||||
bool has_color = FT_HAS_COLOR(face);
|
|
||||||
int load_flags = get_load_flags(rs->current_face->hinting, rs->current_face->hintstyle, has_color ? FT_LOAD_COLOR : FT_LOAD_RENDER);
|
int load_flags = get_load_flags(rs->current_face->hinting, rs->current_face->hintstyle, has_color ? FT_LOAD_COLOR : FT_LOAD_RENDER);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
for (unsigned int i = 0; i < len; i++) {
|
||||||
@ -208,11 +240,18 @@ render_run(RenderState *rs) {
|
|||||||
rs->y += (float)positions[i].y_offset / 64.0f;
|
rs->y += (float)positions[i].y_offset / 64.0f;
|
||||||
if (rs->x > rs->output_width) break;
|
if (rs->x > rs->output_width) break;
|
||||||
int error = FT_Load_Glyph(face, info[i].codepoint, load_flags);
|
int error = FT_Load_Glyph(face, info[i].codepoint, load_flags);
|
||||||
if (error) continue;
|
if (error) {
|
||||||
|
set_freetype_error("Failed loading glyph", error);
|
||||||
|
PyErr_Print();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
ProcessedBitmap pbm = {.factor=1};
|
ProcessedBitmap pbm = {.factor=1};
|
||||||
switch(face->glyph->bitmap.pixel_mode) {
|
switch(face->glyph->bitmap.pixel_mode) {
|
||||||
case FT_PIXEL_MODE_BGRA:
|
case FT_PIXEL_MODE_BGRA: {
|
||||||
// TODO: Implement this
|
populate_processed_bitmap(face->glyph, &face->glyph->bitmap, &pbm);
|
||||||
|
setup_regions(&pbm, rs, baseline);
|
||||||
|
render_color_bitmap(&pbm, rs);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case FT_PIXEL_MODE_MONO: {
|
case FT_PIXEL_MODE_MONO: {
|
||||||
FT_Bitmap bitmap;
|
FT_Bitmap bitmap;
|
||||||
@ -228,6 +267,10 @@ render_run(RenderState *rs) {
|
|||||||
setup_regions(&pbm, rs, baseline);
|
setup_regions(&pbm, rs, baseline);
|
||||||
render_gray_bitmap(&pbm, rs);
|
render_gray_bitmap(&pbm, rs);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_Format(PyExc_TypeError, "Unknown FreeType bitmap type: %x", face->glyph->bitmap.pixel_mode);
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
rs->x += (float)positions[i].x_advance / 64.0f;
|
rs->x += (float)positions[i].x_advance / 64.0f;
|
||||||
}
|
}
|
||||||
@ -235,37 +278,50 @@ render_run(RenderState *rs) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static Face*
|
||||||
current_font_has_codepoint(RenderState *rs, char_type codep) {
|
find_fallback_font_for(char_type codep, char_type next_codep) {
|
||||||
if (rs->current_face != &main_face && glyph_id_for_codepoint(&main_face, codep) > 0) {
|
if (glyph_id_for_codepoint(&main_face, codep) > 0) return &main_face;
|
||||||
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++) {
|
for (size_t i = 0; i < main_face.count; i++) {
|
||||||
if (glyph_id_for_codepoint(main_face.fallbacks + i, codep) > 0) {
|
if (glyph_id_for_codepoint(main_face.fallbacks + i, codep) > 0) return main_face.fallbacks + i;
|
||||||
rs->current_face = main_face.fallbacks + i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FontConfigFace q;
|
FontConfigFace q;
|
||||||
if (!fallback_font(codep, main_face_family.name, main_face_family.bold, main_face_family.italic, &q)) return false;
|
bool prefer_color = false;
|
||||||
|
char_type string[3] = {codep, next_codep, 0};
|
||||||
|
if (wcswidth_string(string) >= 2 && is_emoji_presentation_base(codep)) prefer_color = true;
|
||||||
|
if (!fallback_font(codep, main_face_family.name, main_face_family.bold, main_face_family.italic, prefer_color, &q)) return NULL;
|
||||||
ensure_space_for(&main_face, fallbacks, Face, main_face.count + 1, capacity, 8, true);
|
ensure_space_for(&main_face, fallbacks, Face, main_face.count + 1, capacity, 8, true);
|
||||||
Face *ans = main_face.fallbacks + main_face.count;
|
Face *ans = main_face.fallbacks + main_face.count;
|
||||||
if (!load_font(&q, ans)) return false;
|
if (!load_font(&q, ans)) return NULL;
|
||||||
main_face.count++;
|
main_face.count++;
|
||||||
rs->current_face = ans;
|
return ans;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
process_codepoint(RenderState *rs, char_type codep, char_type next_codep) {
|
||||||
|
bool add_to_current_buffer = false;
|
||||||
|
Face *fallback_font = NULL;
|
||||||
|
if (is_combining_char(codep)) {
|
||||||
|
add_to_current_buffer = true;
|
||||||
|
} if (glyph_id_for_codepoint(&main_face, codep) > 0) {
|
||||||
|
add_to_current_buffer = rs->current_face == &main_face;
|
||||||
|
if (!add_to_current_buffer) fallback_font = &main_face;
|
||||||
|
} else {
|
||||||
|
if (glyph_id_for_codepoint(rs->current_face, codep) > 0) fallback_font = rs->current_face;
|
||||||
|
else fallback_font = find_fallback_font_for(codep, next_codep);
|
||||||
|
add_to_current_buffer = !fallback_font || rs->current_face == fallback_font;
|
||||||
|
}
|
||||||
|
if (!add_to_current_buffer) {
|
||||||
|
if (rs->pending_in_buffer) {
|
||||||
|
if (!render_run(rs)) return false;
|
||||||
|
rs->pending_in_buffer = 0;
|
||||||
|
hb_buffer_clear_contents(hb_buffer);
|
||||||
|
}
|
||||||
|
if (fallback_font) rs->current_face = fallback_font;
|
||||||
|
}
|
||||||
|
hb_buffer_add_utf32(hb_buffer, &codep, 1, 0, 1);
|
||||||
|
rs->pending_in_buffer += 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
render_single_line(const char *text, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset) {
|
render_single_line(const char *text, pixel fg, pixel bg, uint8_t *output_buf, size_t width, size_t height, float x_offset, float y_offset) {
|
||||||
@ -276,35 +332,16 @@ render_single_line(const char *text, pixel fg, pixel bg, uint8_t *output_buf, si
|
|||||||
if (!has_text) return true;
|
if (!has_text) return true;
|
||||||
hb_buffer_clear_contents(hb_buffer);
|
hb_buffer_clear_contents(hb_buffer);
|
||||||
if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return false; }
|
if (!hb_buffer_pre_allocate(hb_buffer, 512)) { PyErr_NoMemory(); return false; }
|
||||||
|
size_t text_len = strlen(text);
|
||||||
|
char_type *unicode = calloc(sizeof(char_type), text_len + 1);
|
||||||
|
text_len = decode_utf8_string(text, text_len, unicode);
|
||||||
RenderState rs = {
|
RenderState rs = {
|
||||||
.current_face = &main_face, .fg = fg, .bg = bg, .output_width = width, .output_height = height,
|
.current_face = &main_face, .fg = fg, .bg = bg, .output_width = width, .output_height = height,
|
||||||
.output = (pixel*)output_buf, .x = x_offset, .y = y_offset
|
.output = (pixel*)output_buf, .x = x_offset, .y = y_offset
|
||||||
};
|
};
|
||||||
|
|
||||||
for (uint32_t i = 0, codep = 0, state = 0, prev = UTF8_ACCEPT; text[i] > 0 && rs.x < rs.output_width; i++) {
|
for (size_t i = 0; i < text_len && rs.x < rs.output_width; i++) {
|
||||||
switch(decode_utf8(&state, &codep, text[i])) {
|
process_codepoint(&rs, unicode[i], unicode[i + 1]);
|
||||||
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 && rs.x < rs.output_width) {
|
if (rs.pending_in_buffer && rs.x < rs.output_width) {
|
||||||
if (!render_run(&rs)) return false;
|
if (!render_run(&rs)) return false;
|
||||||
@ -318,9 +355,9 @@ render_single_line(const char *text, pixel fg, pixel bg, uint8_t *output_buf, si
|
|||||||
static PyObject*
|
static PyObject*
|
||||||
render_line(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
render_line(PyObject *self UNUSED, PyObject *args, PyObject *kw) {
|
||||||
// use for testing as below
|
// use for testing as below
|
||||||
// kitty +runpy "from kitty.fast_data_types import *; open('/tmp/test.rgba', 'wb').write(freetype_render_line('H'))" && convert -size 800x120 -depth 8 /tmp/test.rgba /tmp/test.png && icat /tmp/test.png
|
// kitty +runpy "from kitty.fast_data_types import *; open('/tmp/test.rgba', 'wb').write(freetype_render_line())" && convert -size 800x120 -depth 8 /tmp/test.rgba /tmp/test.png && icat /tmp/test.png
|
||||||
const char *text = "Test rendering with a truncated string", *family = NULL;
|
const char *text = "Test 猫 H🐱H rendering", *family = NULL;
|
||||||
unsigned int width=800, height=120;
|
unsigned int width = 800, height = 120;
|
||||||
int bold = 0, italic = 0;
|
int bold = 0, italic = 0;
|
||||||
unsigned long fg = 0, bg = 0xfffefefe;
|
unsigned long fg = 0, bg = 0xfffefefe;
|
||||||
float x_offset = 0, y_offset = 0;
|
float x_offset = 0, y_offset = 0;
|
||||||
@ -365,7 +402,7 @@ fallback_for_char(PyObject *self UNUSED, PyObject *args) {
|
|||||||
unsigned int ch;
|
unsigned int ch;
|
||||||
if (!PyArg_ParseTuple(args, "I|zpp", &ch, &family, &bold, &italic)) return NULL;
|
if (!PyArg_ParseTuple(args, "I|zpp", &ch, &family, &bold, &italic)) return NULL;
|
||||||
FontConfigFace f;
|
FontConfigFace f;
|
||||||
if (!fallback_font(ch, family, bold, italic, &f)) return NULL;
|
if (!fallback_font(ch, family, bold, italic, false, &f)) return NULL;
|
||||||
PyObject *ret = Py_BuildValue("{ss si si si}", "path", f.path, "index", f.index, "hinting", f.hinting, "hintstyle", f.hintstyle);
|
PyObject *ret = Py_BuildValue("{ss si si si}", "path", f.path, "index", f.index, "hinting", f.hinting, "hintstyle", f.hintstyle);
|
||||||
free(f.path);
|
free(f.path);
|
||||||
return ret;
|
return ret;
|
||||||
|
|||||||
@ -20,8 +20,9 @@ typedef struct FontConfigFace {
|
|||||||
|
|
||||||
bool information_for_font_family(const char *family, bool bold, bool italic, FontConfigFace *ans);
|
bool information_for_font_family(const char *family, bool bold, bool italic, FontConfigFace *ans);
|
||||||
FT_Face native_face_from_path(const char *path, int index);
|
FT_Face native_face_from_path(const char *path, int index);
|
||||||
bool fallback_font(char_type ch, const char *family, bool bold, bool italic, FontConfigFace *ans);
|
bool fallback_font(char_type ch, const char *family, bool bold, bool italic, bool prefer_color, FontConfigFace *ans);
|
||||||
bool freetype_convert_mono_bitmap(FT_Bitmap *src, FT_Bitmap *dest);
|
bool freetype_convert_mono_bitmap(FT_Bitmap *src, FT_Bitmap *dest);
|
||||||
FT_Library freetype_library(void);
|
FT_Library freetype_library(void);
|
||||||
|
void set_freetype_error(const char* prefix, int err_code);
|
||||||
|
|
||||||
void set_main_face_family(const char *family, bool bold, bool italic);
|
void set_main_face_family(const char *family, bool bold, bool italic);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user