Start work on rendering with CoreText
This commit is contained in:
parent
e332522a61
commit
5407fa72b4
@ -10,6 +10,7 @@
|
||||
#include <structmember.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <hb-coretext.h>
|
||||
#import <CoreGraphics/CGBitmapContext.h>
|
||||
#import <CoreText/CTFont.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
@ -17,10 +18,20 @@
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
unsigned int units_per_em;
|
||||
float ascent, descent, leading, underline_position, underline_thickness, point_sz, scaled_point_sz;
|
||||
CTFontRef ct_font;
|
||||
hb_font_t *hb_font;
|
||||
PyObject *family_name, *full_name, *postscript_name, *path;
|
||||
} CTFace;
|
||||
PyTypeObject CTFace_Type;
|
||||
|
||||
static inline char*
|
||||
convert_cfstring(CFStringRef src, int free_src) {
|
||||
#define SZ 2048
|
||||
#define SZ 4094
|
||||
static char buf[SZ+2] = {0};
|
||||
bool ok = false;
|
||||
if(!CFStringGetCString(src, buf, SZ, kCFStringEncodingUTF8)) PyErr_SetString(PyExc_ValueError, "Failed to convert CFString");
|
||||
@ -30,6 +41,46 @@ convert_cfstring(CFStringRef src, int free_src) {
|
||||
#undef SZ
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_face(CTFace *self, CTFontRef font) {
|
||||
if (self->hb_font) hb_font_destroy(self->hb_font);
|
||||
self->hb_font = NULL;
|
||||
if (self->ct_font) CFRelease(self->ct_font);
|
||||
self->ct_font = font;
|
||||
self->units_per_em = CTFontGetUnitsPerEm(self->ct_font);
|
||||
self->ascent = CTFontGetAscent(self->ct_font);
|
||||
self->descent = CTFontGetDescent(self->ct_font);
|
||||
self->leading = CTFontGetLeading(self->ct_font);
|
||||
self->underline_position = CTFontGetUnderlinePosition(self->ct_font);
|
||||
self->underline_thickness = CTFontGetUnderlineThickness(self->ct_font);
|
||||
self->scaled_point_sz = CTFontGetSize(self->ct_font);
|
||||
}
|
||||
|
||||
static inline CTFace*
|
||||
ct_face(CTFontRef font) {
|
||||
CTFace *self = (CTFace *)CTFace_Type.tp_alloc(&CTFace_Type, 0);
|
||||
if (self) {
|
||||
init_face(self, font);
|
||||
self->family_name = Py_BuildValue("s", convert_cfstring(CTFontCopyFamilyName(self->ct_font), true));
|
||||
self->full_name = Py_BuildValue("s", convert_cfstring(CTFontCopyFullName(self->ct_font), true));
|
||||
self->postscript_name = Py_BuildValue("s", convert_cfstring(CTFontCopyPostScriptName(self->ct_font), true));
|
||||
NSURL *url = (NSURL*)CTFontCopyAttribute(self->ct_font, kCTFontURLAttribute);
|
||||
self->path = Py_BuildValue("s", [[url path] UTF8String]);
|
||||
[url release];
|
||||
if (self->family_name == NULL || self->full_name == NULL || self->postscript_name == NULL || self->path == NULL) { Py_CLEAR(self); }
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
dealloc(CTFace* self) {
|
||||
if (self->ct_font) CFRelease(self->ct_font);
|
||||
if (self->hb_font) hb_font_destroy(self->hb_font);
|
||||
self->hb_font = NULL;
|
||||
self->ct_font = NULL;
|
||||
Py_TYPE(self)->tp_free((PyObject*)self);
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
font_descriptor_to_python(CTFontDescriptorRef descriptor) {
|
||||
NSURL *url = (NSURL *) CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
|
||||
@ -106,24 +157,6 @@ coretext_all_fonts(PyObject UNUSED *_self) {
|
||||
return ans;
|
||||
}
|
||||
|
||||
static void
|
||||
free_font(void *f) {
|
||||
CFRelease((CTFontRef)f);
|
||||
}
|
||||
|
||||
static inline PyObject*
|
||||
ft_face(CTFontRef font) {
|
||||
const char *psname = convert_cfstring(CTFontCopyPostScriptName(font), 1);
|
||||
NSURL *url = (NSURL*)CTFontCopyAttribute(font, kCTFontURLAttribute);
|
||||
PyObject *path = PyUnicode_FromString([[url path] UTF8String]);
|
||||
[url release];
|
||||
if (path == NULL) { CFRelease(font); return NULL; }
|
||||
PyObject *ans = ft_face_from_path_and_psname(path, psname, (void*)font, free_font, true, 3, CTFontGetLeading(font));
|
||||
Py_DECREF(path);
|
||||
if (ans == NULL) { CFRelease(font); }
|
||||
return ans;
|
||||
}
|
||||
|
||||
static inline CTFontRef
|
||||
find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
||||
// CTFontCreateForString returns the original font when there are combining
|
||||
@ -144,27 +177,109 @@ find_substitute_face(CFStringRef str, CTFontRef old_font) {
|
||||
|
||||
PyObject*
|
||||
create_fallback_face(PyObject *base_face, Cell* cell, bool UNUSED bold, bool UNUSED italic) {
|
||||
PyObject *lp = PyObject_CallMethod(base_face, "extra_data", NULL);
|
||||
if (lp == NULL) return NULL;
|
||||
CTFontRef font = PyLong_AsVoidPtr(lp);
|
||||
Py_CLEAR(lp);
|
||||
CTFace *self = (CTFace*)base_face;
|
||||
char text[128] = {0};
|
||||
cell_as_utf8(cell, true, text, ' ');
|
||||
CFStringRef str = CFStringCreateWithCString(NULL, text, kCFStringEncodingUTF8);
|
||||
if (str == NULL) return PyErr_NoMemory();
|
||||
CTFontRef new_font = find_substitute_face(str, font);
|
||||
CTFontRef new_font = find_substitute_face(str, self->ct_font);
|
||||
CFRelease(str);
|
||||
if (new_font == NULL) return NULL;
|
||||
return ft_face(new_font);
|
||||
return (PyObject*)ct_face(new_font);
|
||||
}
|
||||
|
||||
uint8_t*
|
||||
coretext_render_color_glyph(void *f, int glyph_id, unsigned int width, unsigned int height, unsigned int baseline) {
|
||||
CTFontRef font = f;
|
||||
unsigned int
|
||||
glyph_id_for_codepoint(PyObject *s, char_type ch) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
unichar chars[2] = {0};
|
||||
CGGlyph glyphs[2] = {0};
|
||||
int count = CFStringGetSurrogatePairForLongCharacter(ch, chars) ? 2 : 1;
|
||||
CTFontGetGlyphsForCharacters(self->ct_font, chars, glyphs, count);
|
||||
return glyphs[0];
|
||||
}
|
||||
|
||||
static inline float
|
||||
scaled_point_sz() {
|
||||
return ((global_state.logical_dpi_x + global_state.logical_dpi_y) / 144.0) * global_state.font_sz_in_pts;
|
||||
}
|
||||
|
||||
bool
|
||||
set_size_for_face(PyObject *s, unsigned int UNUSED desired_height, bool force) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
float sz = scaled_point_sz();
|
||||
if (!force && self->scaled_point_sz == sz) return true;
|
||||
CTFontRef new_font = CTFontCreateCopyWithAttributes(self->ct_font, sz, NULL, NULL);
|
||||
if (new_font == NULL) fatal("Out of memory");
|
||||
init_face(self, new_font);
|
||||
return true;
|
||||
}
|
||||
|
||||
hb_font_t*
|
||||
harfbuzz_font_for_face(PyObject* s) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
if (!self->hb_font) self->hb_font = hb_coretext_font_create (self->ct_font);
|
||||
return self->hb_font;
|
||||
}
|
||||
|
||||
void
|
||||
cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, unsigned int* baseline, unsigned int* underline_position, unsigned int* underline_thickness) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
#define count (128 - 32)
|
||||
unichar chars[count+1] = {0};
|
||||
CGGlyph glyphs[count+1] = {0};
|
||||
unsigned int width = 0, w, i;
|
||||
for (i = 0; i < count; i++) chars[i] = 32 + i;
|
||||
CTFontGetGlyphsForCharacters(self->ct_font, chars, glyphs, count);
|
||||
for (i = 0; i < count; i++) {
|
||||
if (glyphs[i]) {
|
||||
w = (unsigned int)(ceilf(
|
||||
CTFontGetAdvancesForGlyphs(self->ct_font, kCTFontOrientationHorizontal, glyphs+i, NULL, 1)));
|
||||
if (w > width) width = w;
|
||||
}
|
||||
}
|
||||
// See https://stackoverflow.com/questions/5511830/how-does-line-spacing-work-in-core-text-and-why-is-it-different-from-nslayoutm
|
||||
CGFloat leading = MAX(0, self->leading);
|
||||
leading = floor(leading + 0.5);
|
||||
CGFloat line_height = floor(self->ascent + 0.5) + floor(self->descent + 0.5) + leading;
|
||||
CGFloat ascender_delta = (leading > 0) ? 0 : floor(0.2 * line_height + 0.5);
|
||||
*cell_width = width; *cell_height = (unsigned int)(line_height + ascender_delta);
|
||||
*underline_position = (unsigned int)self->underline_position;
|
||||
*underline_thickness = (unsigned int)self->underline_thickness;
|
||||
// See https://developer.apple.com/library/content/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/TypoFeatures/TextSystemFeatures.html
|
||||
*baseline = (unsigned int)self->ascent;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor) {
|
||||
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
|
||||
if (!desc) return NULL;
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz(), NULL);
|
||||
CFRelease(desc); desc = NULL;
|
||||
if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; }
|
||||
return (PyObject*) ct_face(font);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
face_from_path(const char *path, int UNUSED index) {
|
||||
CFStringRef s = CFStringCreateWithCString(NULL, path, kCFStringEncodingUTF8);
|
||||
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, s, kCFURLPOSIXPathStyle, false);
|
||||
CGDataProviderRef dp = CGDataProviderCreateWithURL(url);
|
||||
CGFontRef cg_font = CGFontCreateWithDataProvider(dp);
|
||||
CTFontRef ct_font = CTFontCreateWithGraphicsFont(cg_font, 0.0, NULL, NULL);
|
||||
CFRelease(cg_font); CFRelease(dp); CFRelease(url); CFRelease(s);
|
||||
return (PyObject*) ct_face(ct_font);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
specialize_font_descriptor(PyObject *base_descriptor) {
|
||||
Py_INCREF(base_descriptor);
|
||||
return base_descriptor;
|
||||
}
|
||||
|
||||
static inline void
|
||||
coretext_render_color_glyph(CTFontRef font, uint8_t *buf, int glyph_id, unsigned int width, unsigned int height, unsigned int baseline) {
|
||||
CGColorSpaceRef color_space = CGColorSpaceCreateDeviceRGB();
|
||||
if (color_space == NULL) fatal("Out of memory");
|
||||
uint8_t* buf = calloc(4, width * height);
|
||||
if (buf == NULL) fatal("Out of memory");
|
||||
CGContextRef ctx = CGBitmapContextCreate(buf, width, height, 8, 4 * width, color_space, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault);
|
||||
if (ctx == NULL) fatal("Out of memory");
|
||||
CGContextSetShouldAntialias(ctx, true);
|
||||
@ -181,38 +296,141 @@ coretext_render_color_glyph(void *f, int glyph_id, unsigned int width, unsigned
|
||||
CTFontDrawGlyphs(font, &glyph, &CGPointZero, 1, ctx);
|
||||
CGContextRelease(ctx);
|
||||
CGColorSpaceRelease(color_space);
|
||||
return buf;
|
||||
}
|
||||
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor) {
|
||||
CTFontDescriptorRef desc = font_descriptor_from_python(descriptor);
|
||||
if (!desc) return NULL;
|
||||
float scaled_point_sz = ((global_state.logical_dpi_x + global_state.logical_dpi_y) / 144.0) * global_state.font_sz_in_pts;
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(desc, scaled_point_sz, NULL);
|
||||
CFRelease(desc); desc = NULL;
|
||||
if (!font) { PyErr_SetString(PyExc_ValueError, "Failed to create CTFont object"); return NULL; }
|
||||
return ft_face(font);
|
||||
|
||||
static uint8_t *render_buf = NULL;
|
||||
static size_t render_buf_sz = 0;
|
||||
|
||||
static void
|
||||
finalize(void) {
|
||||
free(render_buf);
|
||||
}
|
||||
|
||||
PyObject*
|
||||
specialize_font_descriptor(PyObject *base_descriptor) {
|
||||
Py_INCREF(base_descriptor);
|
||||
return base_descriptor;
|
||||
static inline void
|
||||
ensure_render_space(size_t width, size_t height) {
|
||||
if (render_buf_sz >= width * height) return;
|
||||
free(render_buf);
|
||||
render_buf_sz = width * height;
|
||||
render_buf = malloc(render_buf_sz);
|
||||
if (render_buf == NULL) fatal("Out of memory");
|
||||
}
|
||||
|
||||
static inline void
|
||||
render_glyph(CTFontRef font, CGGlyph glyph, unsigned int width, unsigned int height, unsigned int y_origin, CGFloat x, CGFloat y) {
|
||||
memset(render_buf, 0, render_buf_sz);
|
||||
CGColorSpaceRef gray_color_space = CGColorSpaceCreateDeviceGray();
|
||||
CGContextRef render_ctx = CGBitmapContextCreate(render_buf, width, height, 8, width, gray_color_space, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));
|
||||
if (render_ctx == NULL || gray_color_space == NULL) fatal("Out of memory");
|
||||
CGContextSetShouldAntialias(render_ctx, true);
|
||||
CGContextSetShouldSmoothFonts(render_ctx, true); // sub-pixel antialias
|
||||
CGContextSetRGBFillColor(render_ctx, 1, 1, 1, 1); // white glyphs
|
||||
CGContextSetTextDrawingMode(render_ctx, kCGTextFill);
|
||||
CGContextSetTextMatrix(render_ctx, CGAffineTransformIdentity);
|
||||
CGContextSetTextPosition(render_ctx, 0, y_origin);
|
||||
CGPoint p = CGPointMake(x, y);
|
||||
CTFontDrawGlyphs(font, &glyph, &p, 1, render_ctx);
|
||||
}
|
||||
|
||||
static inline void
|
||||
place_glyph_in_canvas(float x, pixel *canvas, unsigned int canvas_width, unsigned int cell_height) {
|
||||
Region src = {.bottom=cell_height, .right=canvas_width}, dest = {.left=(unsigned int)ceil(x), .right=canvas_width, .bottom=cell_height};
|
||||
render_alpha_mask(render_buf, canvas, &src, &dest, canvas_width, canvas_width);
|
||||
}
|
||||
|
||||
bool
|
||||
render_glyphs_in_cells(PyObject *s, bool UNUSED bold, bool UNUSED italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored) {
|
||||
CTFace *self = (CTFace*)s;
|
||||
printf("was_colored: %d num_glyphs: %u\n", *was_colored, num_glyphs);
|
||||
if (*was_colored) {
|
||||
coretext_render_color_glyph(self->ct_font, (uint8_t*)canvas, info[0].codepoint, cell_width * num_cells, cell_height, baseline);
|
||||
return true;
|
||||
}
|
||||
unsigned int canvas_width = cell_width * num_cells;
|
||||
ensure_render_space(canvas_width, cell_height);
|
||||
float x = 0.f, y_offset = 0.f, x_offset = 0.f;
|
||||
for (unsigned int i = 0; i < num_glyphs; i++) {
|
||||
x_offset = (float)positions[i].x_offset / 64.0f;
|
||||
y_offset = (float)positions[i].y_offset / 64.0f;
|
||||
render_glyph(self->ct_font, info[i].codepoint, canvas_width, cell_height, cell_height - baseline, x_offset, y_offset);
|
||||
place_glyph_in_canvas(x, canvas, canvas_width, cell_height);
|
||||
x += (float)positions[i].x_advance / 64.0f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Boilerplate {{{
|
||||
|
||||
static PyObject*
|
||||
display_name(CTFace *self) {
|
||||
CFStringRef dn = CTFontCopyDisplayName(self->ct_font);
|
||||
const char *d = convert_cfstring(dn, true);
|
||||
return Py_BuildValue("s", d);
|
||||
}
|
||||
|
||||
static PyMethodDef methods[] = {
|
||||
METHODB(display_name, METH_NOARGS),
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyObject *
|
||||
repr(CTFace *self) {
|
||||
char buf[1024] = {0};
|
||||
snprintf(buf, sizeof(buf)/sizeof(buf[0]), "ascent=%.1f, descent=%.1f, leading=%.1f, point_sz=%.1f, scaled_point_sz=%.1f, underline_position=%.1f underline_thickness=%.1f",
|
||||
(self->ascent), (self->descent), (self->leading), (self->point_sz), (self->scaled_point_sz), (self->underline_position), (self->underline_thickness));
|
||||
return PyUnicode_FromFormat(
|
||||
"Face(family=%U, full_name=%U, postscript_name=%U, path=%U, units_per_em=%u, %s)",
|
||||
self->family_name, self->full_name, self->postscript_name, self->path, self->units_per_em, buf
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
METHODB(coretext_all_fonts, METH_NOARGS),
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef members[] = {
|
||||
#define MEM(name, type) {#name, type, offsetof(CTFace, name), READONLY, #name}
|
||||
MEM(units_per_em, T_UINT),
|
||||
MEM(point_sz, T_FLOAT),
|
||||
MEM(scaled_point_sz, T_FLOAT),
|
||||
MEM(ascent, T_FLOAT),
|
||||
MEM(descent, T_FLOAT),
|
||||
MEM(leading, T_FLOAT),
|
||||
MEM(underline_position, T_FLOAT),
|
||||
MEM(underline_thickness, T_FLOAT),
|
||||
MEM(family_name, T_OBJECT),
|
||||
MEM(path, T_OBJECT),
|
||||
MEM(full_name, T_OBJECT),
|
||||
MEM(postscript_name, T_OBJECT),
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject CTFace_Type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
.tp_name = "fast_data_types.CTFace",
|
||||
.tp_basicsize = sizeof(CTFace),
|
||||
.tp_dealloc = (destructor)dealloc,
|
||||
.tp_flags = Py_TPFLAGS_DEFAULT,
|
||||
.tp_doc = "CoreText Font face",
|
||||
.tp_methods = methods,
|
||||
.tp_members = members,
|
||||
.tp_repr = (reprfunc)repr,
|
||||
};
|
||||
|
||||
|
||||
|
||||
int
|
||||
init_CoreText(PyObject *module) {
|
||||
if (PyType_Ready(&CTFace_Type) < 0) return 0;
|
||||
if (PyModule_AddObject(module, "CTFace", (PyObject *)&CTFace_Type) != 0) return 0;
|
||||
if (PyModule_AddFunctions(module, module_methods) != 0) return 0;
|
||||
if (Py_AtExit(finalize) != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Failed to register the CoreText at exit handler");
|
||||
return false;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// }}}
|
||||
|
||||
@ -173,7 +173,6 @@ extern bool init_child_monitor(PyObject *);
|
||||
extern int init_Line(PyObject *);
|
||||
extern int init_ColorProfile(PyObject *);
|
||||
extern int init_Screen(PyObject *);
|
||||
extern bool init_freetype_library(PyObject*);
|
||||
extern bool init_fontconfig_library(PyObject*);
|
||||
extern bool init_desktop(PyObject*);
|
||||
extern bool init_fonts(PyObject*);
|
||||
@ -185,6 +184,8 @@ extern bool init_shaders(PyObject *module);
|
||||
#ifdef __APPLE__
|
||||
extern int init_CoreText(PyObject *);
|
||||
extern bool init_cocoa(PyObject *module);
|
||||
#else
|
||||
extern bool init_freetype_library(PyObject*);
|
||||
#endif
|
||||
|
||||
|
||||
@ -211,11 +212,11 @@ PyInit_fast_data_types(void) {
|
||||
if (!init_keys(m)) return NULL;
|
||||
if (!init_graphics(m)) return NULL;
|
||||
if (!init_shaders(m)) return NULL;
|
||||
if (!init_freetype_library(m)) return NULL;
|
||||
#ifdef __APPLE__
|
||||
if (!init_CoreText(m)) return NULL;
|
||||
if (!init_cocoa(m)) return NULL;
|
||||
#else
|
||||
if (!init_freetype_library(m)) return NULL;
|
||||
if (!init_fontconfig_library(m)) return NULL;
|
||||
if (!init_desktop(m)) return NULL;
|
||||
#endif
|
||||
|
||||
@ -17,11 +17,13 @@
|
||||
unsigned int glyph_id_for_codepoint(PyObject *, char_type);
|
||||
hb_font_t* harfbuzz_font_for_face(PyObject*);
|
||||
bool set_size_for_face(PyObject*, unsigned int, bool);
|
||||
void cell_metrics(PyObject*, unsigned int*, unsigned int*, unsigned int*, unsigned int*, unsigned int*);
|
||||
bool render_glyphs_in_cells(PyObject *f, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *positions, unsigned int num_glyphs, pixel *canvas, unsigned int cell_width, unsigned int cell_height, unsigned int num_cells, unsigned int baseline, bool *was_colored);
|
||||
PyObject* create_fallback_face(PyObject *base_face, Cell* cell, bool bold, bool italic);
|
||||
PyObject* specialize_font_descriptor(PyObject *base_descriptor);
|
||||
PyObject* face_from_path(const char *path, int index);
|
||||
PyObject* face_from_descriptor(PyObject*);
|
||||
|
||||
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);
|
||||
void render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride);
|
||||
void render_line(Line *line);
|
||||
@ -30,8 +32,3 @@ void sprite_tracker_set_layout(unsigned int cell_width, unsigned int cell_height
|
||||
typedef void (*free_extra_data_func)(void*);
|
||||
PyObject* ft_face_from_data(const uint8_t* data, size_t sz, void *extra_data, free_extra_data_func fed, PyObject *path, int hinting, int hintstyle, float);
|
||||
PyObject* ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_data, free_extra_data_func fed, int hinting, int hintstyle, float);
|
||||
PyObject* face_from_descriptor(PyObject*);
|
||||
PyObject* face_from_path(const char *path, int index);
|
||||
#ifdef __APPLE__
|
||||
uint8_t* coretext_render_color_glyph(void *f, int glyph_id, unsigned int width, unsigned int height, unsigned int);
|
||||
#endif
|
||||
|
||||
@ -212,7 +212,6 @@ ft_face_from_path_and_psname(PyObject* path, const char* psname, void *extra_dat
|
||||
return (PyObject*)ans;
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
PyObject*
|
||||
face_from_descriptor(PyObject *descriptor) {
|
||||
#define D(key, conv) { PyObject *t = PyDict_GetItemString(descriptor, #key); if (t == NULL) return NULL; key = conv(t); t = NULL; }
|
||||
@ -233,7 +232,6 @@ face_from_descriptor(PyObject *descriptor) {
|
||||
}
|
||||
return (PyObject*)self;
|
||||
}
|
||||
#endif
|
||||
|
||||
PyObject*
|
||||
face_from_path(const char *path, int index) {
|
||||
@ -292,12 +290,6 @@ cell_metrics(PyObject *s, unsigned int* cell_width, unsigned int* cell_height, u
|
||||
Face *self = (Face*)s;
|
||||
*cell_width = calc_cell_width(self);
|
||||
*cell_height = CALC_CELL_HEIGHT(self);
|
||||
#ifdef __APPLE__
|
||||
// See https://stackoverflow.com/questions/5511830/how-does-line-spacing-work-in-core-text-and-why-is-it-different-from-nslayoutm
|
||||
if (self->apple_leading <= 0) {
|
||||
*cell_height += floor(0.2 * (double)(*cell_height) + 0.5);
|
||||
}
|
||||
#endif
|
||||
*baseline = font_units_to_pixels(self, self->ascender);
|
||||
*underline_position = MIN(*cell_height - 1, (unsigned int)font_units_to_pixels(self, MAX(0, self->ascender - self->underline_position)));
|
||||
*underline_thickness = MAX(1, font_units_to_pixels(self, self->underline_thickness));
|
||||
@ -317,7 +309,7 @@ typedef struct {
|
||||
size_t start_x, width, stride;
|
||||
size_t rows;
|
||||
FT_Pixel_Mode pixel_mode;
|
||||
bool needs_free, ignore_bearing_y;
|
||||
bool needs_free;
|
||||
unsigned int factor, right_edge;
|
||||
} ProcessedBitmap;
|
||||
|
||||
@ -365,7 +357,6 @@ render_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int cell_
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef __APPLE__
|
||||
static void
|
||||
downsample_bitmap(ProcessedBitmap *bm, unsigned int width, unsigned int cell_height) {
|
||||
// Downsample using a simple area averaging algorithm. Could probably do
|
||||
@ -397,7 +388,6 @@ downsample_bitmap(ProcessedBitmap *bm, unsigned int width, unsigned int cell_hei
|
||||
bm->buf = dest; bm->needs_free = true; bm->stride = 4 * width; bm->width = width; bm->rows = cell_height;
|
||||
bm->factor = factor;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void
|
||||
detect_right_edge(ProcessedBitmap *ans) {
|
||||
@ -412,17 +402,6 @@ detect_right_edge(ProcessedBitmap *ans) {
|
||||
|
||||
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) {
|
||||
#ifdef __APPLE__
|
||||
ans->width = cell_width * num_cells;
|
||||
ans->buf = coretext_render_color_glyph(self->extra_data, glyph_id, ans->width, cell_height, baseline);
|
||||
ans->start_x = 0; ans->stride = ans->width * 4;
|
||||
ans->rows = cell_height;
|
||||
ans->pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||
ans->needs_free = true;
|
||||
ans->ignore_bearing_y = true;
|
||||
detect_right_edge(ans);
|
||||
return true;
|
||||
#else
|
||||
(void)baseline;
|
||||
unsigned short best = 0, diff = USHRT_MAX;
|
||||
for (short i = 0; i < self->face->num_fixed_sizes; i++) {
|
||||
@ -447,7 +426,6 @@ render_color_bitmap(Face *self, int glyph_id, ProcessedBitmap *ans, unsigned int
|
||||
if (ans->width > num_cells * cell_width + 2) downsample_bitmap(ans, num_cells * cell_width, cell_height);
|
||||
detect_right_edge(ans);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -460,11 +438,7 @@ copy_color_bitmap(uint8_t *src, pixel* dest, Region *src_rect, Region *dest_rect
|
||||
uint8_t *bgra = s + 4 * sc;
|
||||
if (bgra[3]) {
|
||||
#define C(idx, shift) ( (uint8_t)(((float)bgra[idx] / (float)bgra[3]) * 255) << shift)
|
||||
#ifdef __APPLE__
|
||||
d[dc] = C(0, 24) | C(1, 16) | C(2, 8) | bgra[3];
|
||||
#else
|
||||
d[dc] = C(2, 24) | C(1, 16) | C(0, 8) | bgra[3];
|
||||
#endif
|
||||
#undef C
|
||||
} else d[dc] = 0;
|
||||
}
|
||||
@ -495,7 +469,7 @@ place_bitmap_in_canvas(pixel *cell, ProcessedBitmap *bm, size_t cell_width, size
|
||||
float bearing_y = (float)metrics->horiBearingY / 64.f;
|
||||
bearing_y /= bm->factor;
|
||||
int32_t yoff = (ssize_t)(y_offset + bearing_y);
|
||||
if ((yoff > 0 && (size_t)yoff > baseline) || bm->ignore_bearing_y) {
|
||||
if ((yoff > 0 && (size_t)yoff > baseline)) {
|
||||
dest.top = 0;
|
||||
} else {
|
||||
dest.top = baseline - yoff;
|
||||
|
||||
4
setup.py
4
setup.py
@ -211,8 +211,6 @@ def kitty_env():
|
||||
cflags.extend(pkg_config('libpng', '--cflags-only-I'))
|
||||
if is_macos:
|
||||
font_libs = ['-framework', 'CoreText', '-framework', 'CoreGraphics']
|
||||
cflags.extend(pkg_config('freetype2', '--cflags-only-I'))
|
||||
font_libs += pkg_config('freetype2', '--libs')
|
||||
else:
|
||||
cflags.extend(pkg_config('fontconfig', '--cflags-only-I'))
|
||||
font_libs = pkg_config('fontconfig', '--libs')
|
||||
@ -375,7 +373,7 @@ def compile_c_extension(kenv, module, incremental, compilation_database, all_key
|
||||
def find_c_files():
|
||||
ans, headers = [], []
|
||||
d = os.path.join(base, 'kitty')
|
||||
exclude = {'fontconfig.c', 'desktop.c'} if is_macos else {'core_text.m', 'cocoa_window.m'}
|
||||
exclude = {'fontconfig.c', 'freetype.c', 'desktop.c'} if is_macos else {'core_text.m', 'cocoa_window.m'}
|
||||
for x in os.listdir(d):
|
||||
ext = os.path.splitext(x)[1]
|
||||
if ext in ('.c', '.m') and os.path.basename(x) not in exclude:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user