macOS: Fix a crash when rendering ligatures larger than 128 characters
Fixes #3724
This commit is contained in:
parent
ebff343a55
commit
1b35708d89
@ -10,6 +10,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
|||||||
- macOS: Fix a regression in the previous release that broke rendering of
|
- macOS: Fix a regression in the previous release that broke rendering of
|
||||||
strikeout (:iss:`3717`)
|
strikeout (:iss:`3717`)
|
||||||
|
|
||||||
|
- macOS: Fix a crash when rendering ligatures larger than 128 characters
|
||||||
|
(:iss:`3724`)
|
||||||
|
|
||||||
- Fix a regression in the previous release that could cause a crash when
|
- Fix a regression in the previous release that could cause a crash when
|
||||||
changing layouts and mousing (:iss:`3713`)
|
changing layouts and mousing (:iss:`3713`)
|
||||||
|
|
||||||
|
|||||||
@ -415,16 +415,20 @@ specialize_font_descriptor(PyObject *base_descriptor, FONTS_DATA_HANDLE fg UNUSE
|
|||||||
return base_descriptor;
|
return base_descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t *render_buf = NULL;
|
struct RenderBuffers {
|
||||||
static size_t render_buf_sz = 0;
|
uint8_t *render_buf;
|
||||||
static CGGlyph glyphs[128];
|
size_t render_buf_sz, sz;
|
||||||
static CGRect boxes[128];
|
CGGlyph *glyphs;
|
||||||
static CGPoint positions[128];
|
CGRect *boxes;
|
||||||
static CGSize advances[128];
|
CGPoint *positions;
|
||||||
|
CGSize *advances;
|
||||||
|
};
|
||||||
|
static struct RenderBuffers buffers = {0};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
finalize(void) {
|
finalize(void) {
|
||||||
free(render_buf);
|
free(buffers.render_buf); free(buffers.glyphs); free(buffers.boxes); free(buffers.positions); free(buffers.advances);
|
||||||
|
memset(&buffers, 0, sizeof(struct RenderBuffers));
|
||||||
if (all_fonts_collection_data) CFRelease(all_fonts_collection_data);
|
if (all_fonts_collection_data) CFRelease(all_fonts_collection_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -442,7 +446,7 @@ render_color_glyph(CTFontRef font, uint8_t *buf, int glyph_id, unsigned int widt
|
|||||||
CGContextSetTextDrawingMode(ctx, kCGTextFill);
|
CGContextSetTextDrawingMode(ctx, kCGTextFill);
|
||||||
CGGlyph glyph = glyph_id;
|
CGGlyph glyph = glyph_id;
|
||||||
CGContextSetTextMatrix(ctx, transform);
|
CGContextSetTextMatrix(ctx, transform);
|
||||||
CGContextSetTextPosition(ctx, -boxes[0].origin.x, MAX(2, height - baseline));
|
CGContextSetTextPosition(ctx, -buffers.boxes[0].origin.x, MAX(2, height - baseline));
|
||||||
CGPoint p = CGPointMake(0, 0);
|
CGPoint p = CGPointMake(0, 0);
|
||||||
CTFontDrawGlyphs(font, &glyph, &p, 1, ctx);
|
CTFontDrawGlyphs(font, &glyph, &p, 1, ctx);
|
||||||
CGContextRelease(ctx);
|
CGContextRelease(ctx);
|
||||||
@ -455,21 +459,30 @@ render_color_glyph(CTFontRef font, uint8_t *buf, int glyph_id, unsigned int widt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static void
|
||||||
ensure_render_space(size_t width, size_t height) {
|
ensure_render_space(size_t width, size_t height, size_t num_glyphs) {
|
||||||
if (render_buf_sz >= width * height) return;
|
if (buffers.render_buf_sz < width * height) {
|
||||||
free(render_buf);
|
free(buffers.render_buf); buffers.render_buf = NULL;
|
||||||
render_buf_sz = width * height;
|
buffers.render_buf_sz = width * height;
|
||||||
render_buf = malloc(render_buf_sz);
|
buffers.render_buf = malloc(buffers.render_buf_sz);
|
||||||
if (render_buf == NULL) fatal("Out of memory");
|
if (buffers.render_buf == NULL) fatal("Out of memory");
|
||||||
|
}
|
||||||
|
if (buffers.sz < num_glyphs) {
|
||||||
|
buffers.sz = MAX(128, num_glyphs * 2);
|
||||||
|
buffers.advances = calloc(sizeof(buffers.advances[0]), buffers.sz);
|
||||||
|
buffers.boxes = calloc(sizeof(buffers.boxes[0]), buffers.sz);
|
||||||
|
buffers.glyphs = calloc(sizeof(buffers.glyphs[0]), buffers.sz);
|
||||||
|
buffers.positions = calloc(sizeof(buffers.positions[0]), buffers.sz);
|
||||||
|
if (!buffers.advances || !buffers.boxes || !buffers.glyphs || !buffers.positions) fatal("Out of memory");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned int baseline, unsigned int num_glyphs) {
|
render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned int baseline, unsigned int num_glyphs) {
|
||||||
memset(render_buf, 0, width * height);
|
memset(buffers.render_buf, 0, width * height);
|
||||||
CGColorSpaceRef gray_color_space = CGColorSpaceCreateDeviceGray();
|
CGColorSpaceRef gray_color_space = CGColorSpaceCreateDeviceGray();
|
||||||
if (gray_color_space == NULL) fatal("Out of memory");
|
if (gray_color_space == NULL) fatal("Out of memory");
|
||||||
CGContextRef render_ctx = CGBitmapContextCreate(render_buf, width, height, 8, width, gray_color_space, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));
|
CGContextRef render_ctx = CGBitmapContextCreate(buffers.render_buf, width, height, 8, width, gray_color_space, (kCGBitmapAlphaInfoMask & kCGImageAlphaNone));
|
||||||
if (render_ctx == NULL) fatal("Out of memory");
|
if (render_ctx == NULL) fatal("Out of memory");
|
||||||
CGContextSetShouldAntialias(render_ctx, true);
|
CGContextSetShouldAntialias(render_ctx, true);
|
||||||
CGContextSetShouldSmoothFonts(render_ctx, true);
|
CGContextSetShouldSmoothFonts(render_ctx, true);
|
||||||
@ -479,7 +492,7 @@ render_glyphs(CTFontRef font, unsigned int width, unsigned int height, unsigned
|
|||||||
CGContextSetTextDrawingMode(render_ctx, kCGTextFillStroke);
|
CGContextSetTextDrawingMode(render_ctx, kCGTextFillStroke);
|
||||||
CGContextSetTextMatrix(render_ctx, CGAffineTransformIdentity);
|
CGContextSetTextMatrix(render_ctx, CGAffineTransformIdentity);
|
||||||
CGContextSetTextPosition(render_ctx, 0, height - baseline);
|
CGContextSetTextPosition(render_ctx, 0, height - baseline);
|
||||||
CTFontDrawGlyphs(font, glyphs, positions, num_glyphs, render_ctx);
|
CTFontDrawGlyphs(font, buffers.glyphs, buffers.positions, num_glyphs, render_ctx);
|
||||||
CGContextRelease(render_ctx);
|
CGContextRelease(render_ctx);
|
||||||
CGColorSpaceRelease(gray_color_space);
|
CGColorSpaceRelease(gray_color_space);
|
||||||
}
|
}
|
||||||
@ -492,19 +505,20 @@ render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
|
|||||||
unichar chars[num_chars];
|
unichar chars[num_chars];
|
||||||
CGSize local_advances[num_chars];
|
CGSize local_advances[num_chars];
|
||||||
for (size_t i = 0; i < num_chars; i++) chars[i] = text[i];
|
for (size_t i = 0; i < num_chars; i++) chars[i] = text[i];
|
||||||
CTFontGetGlyphsForCharacters(font, chars, glyphs, num_chars);
|
ensure_render_space(0, 0, num_chars);
|
||||||
CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, glyphs, local_advances, num_chars);
|
CTFontGetGlyphsForCharacters(font, chars, buffers.glyphs, num_chars);
|
||||||
CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, glyphs, boxes, num_chars);
|
CTFontGetAdvancesForGlyphs(font, kCTFontOrientationDefault, buffers.glyphs, local_advances, num_chars);
|
||||||
|
CGRect bounding_box = CTFontGetBoundingRectsForGlyphs(font, kCTFontOrientationDefault, buffers.glyphs, buffers.boxes, num_chars);
|
||||||
CGFloat x = 0, y = 0;
|
CGFloat x = 0, y = 0;
|
||||||
for (size_t i = 0; i < num_chars; i++) {
|
for (size_t i = 0; i < num_chars; i++) {
|
||||||
positions[i] = CGPointMake(x, y);
|
buffers.positions[i] = CGPointMake(x, y);
|
||||||
x += local_advances[i].width; y += local_advances[i].height;
|
x += local_advances[i].width; y += local_advances[i].height;
|
||||||
}
|
}
|
||||||
StringCanvas ans = { .width = (size_t)ceil(x), .height = (size_t)(2 * bounding_box.size.height) };
|
StringCanvas ans = { .width = (size_t)ceil(x), .height = (size_t)(2 * bounding_box.size.height) };
|
||||||
ensure_render_space(ans.width, ans.height);
|
ensure_render_space(ans.width, ans.height, num_chars);
|
||||||
render_glyphs(font, ans.width, ans.height, baseline, num_chars);
|
render_glyphs(font, ans.width, ans.height, baseline, num_chars);
|
||||||
ans.canvas = malloc(ans.width * ans.height);
|
ans.canvas = malloc(ans.width * ans.height);
|
||||||
if (ans.canvas) memcpy(ans.canvas, render_buf, ans.width * ans.height);
|
if (ans.canvas) memcpy(ans.canvas, buffers.render_buf, ans.width * ans.height);
|
||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,12 +526,13 @@ render_simple_text_impl(PyObject *s, const char *text, unsigned int baseline) {
|
|||||||
static inline bool
|
static inline bool
|
||||||
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, bool allow_resize, FONTS_DATA_HANDLE fg, bool center_glyph) {
|
do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, bool allow_resize, FONTS_DATA_HANDLE fg, bool center_glyph) {
|
||||||
unsigned int canvas_width = cell_width * num_cells;
|
unsigned int canvas_width = cell_width * num_cells;
|
||||||
CGRect br = CTFontGetBoundingRectsForGlyphs(ct_font, kCTFontOrientationHorizontal, glyphs, boxes, num_glyphs);
|
ensure_render_space(canvas_width, cell_height, num_glyphs);
|
||||||
|
CGRect br = CTFontGetBoundingRectsForGlyphs(ct_font, kCTFontOrientationHorizontal, buffers.glyphs, buffers.boxes, num_glyphs);
|
||||||
const bool debug_rendering = false;
|
const bool debug_rendering = false;
|
||||||
if (allow_resize) {
|
if (allow_resize) {
|
||||||
// Resize glyphs that would bleed into neighboring cells, by scaling the font size
|
// Resize glyphs that would bleed into neighboring cells, by scaling the font size
|
||||||
float right = 0;
|
float right = 0;
|
||||||
for (unsigned i=0; i < num_glyphs; i++) right = MAX(right, boxes[i].origin.x + boxes[i].size.width);
|
for (unsigned i=0; i < num_glyphs; i++) right = MAX(right, buffers.boxes[i].origin.x + buffers.boxes[i].size.width);
|
||||||
if (!bold && !italic && right > canvas_width + 1) {
|
if (!bold && !italic && right > canvas_width + 1) {
|
||||||
if (debug_rendering) printf("resizing glyphs, right: %f canvas_width: %u\n", right, canvas_width);
|
if (debug_rendering) printf("resizing glyphs, right: %f canvas_width: %u\n", right, canvas_width);
|
||||||
CGFloat sz = CTFontGetSize(ct_font);
|
CGFloat sz = CTFontGetSize(ct_font);
|
||||||
@ -529,19 +544,18 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
CGFloat x = 0, y = 0;
|
CGFloat x = 0, y = 0;
|
||||||
CTFontGetAdvancesForGlyphs(ct_font, kCTFontOrientationDefault, glyphs, advances, num_glyphs);
|
CTFontGetAdvancesForGlyphs(ct_font, kCTFontOrientationDefault, buffers.glyphs, buffers.advances, num_glyphs);
|
||||||
for (unsigned i=0; i < num_glyphs; i++) {
|
for (unsigned i=0; i < num_glyphs; i++) {
|
||||||
positions[i].x = x; positions[i].y = y;
|
buffers.positions[i].x = x; buffers.positions[i].y = y;
|
||||||
if (debug_rendering) printf("x=%f origin=%f width=%f advance=%f\n", x, boxes[i].origin.x, boxes[i].size.width, advances[i].width);
|
if (debug_rendering) printf("x=%f origin=%f width=%f advance=%f\n", x, buffers.boxes[i].origin.x, buffers.boxes[i].size.width, buffers.advances[i].width);
|
||||||
x += advances[i].width; y += advances[i].height;
|
x += buffers.advances[i].width; y += buffers.advances[i].height;
|
||||||
}
|
}
|
||||||
if (*was_colored) {
|
if (*was_colored) {
|
||||||
render_color_glyph(ct_font, (uint8_t*)canvas, info[0].codepoint, cell_width * num_cells, cell_height, baseline);
|
render_color_glyph(ct_font, (uint8_t*)canvas, info[0].codepoint, cell_width * num_cells, cell_height, baseline);
|
||||||
} else {
|
} else {
|
||||||
ensure_render_space(canvas_width, cell_height);
|
|
||||||
render_glyphs(ct_font, canvas_width, cell_height, baseline, num_glyphs);
|
render_glyphs(ct_font, canvas_width, cell_height, baseline, num_glyphs);
|
||||||
Region src = {.bottom=cell_height, .right=canvas_width}, dest = {.bottom=cell_height, .right=canvas_width};
|
Region src = {.bottom=cell_height, .right=canvas_width}, dest = {.bottom=cell_height, .right=canvas_width};
|
||||||
render_alpha_mask(render_buf, canvas, &src, &dest, canvas_width, canvas_width);
|
render_alpha_mask(buffers.render_buf, canvas, &src, &dest, canvas_width, canvas_width);
|
||||||
}
|
}
|
||||||
if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) {
|
if (num_cells && (center_glyph || (num_cells == 2 && *was_colored))) {
|
||||||
if (debug_rendering) printf("centering glyphs: center_glyph: %d\n", center_glyph);
|
if (debug_rendering) printf("centering glyphs: center_glyph: %d\n", center_glyph);
|
||||||
@ -557,7 +571,8 @@ do_render(CTFontRef ct_font, bool bold, bool italic, hb_glyph_info_t *info, hb_g
|
|||||||
bool
|
bool
|
||||||
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, FONTS_DATA_HANDLE fg, bool center_glyph) {
|
render_glyphs_in_cells(PyObject *s, bool bold, bool italic, hb_glyph_info_t *info, hb_glyph_position_t *hb_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, FONTS_DATA_HANDLE fg, bool center_glyph) {
|
||||||
CTFace *self = (CTFace*)s;
|
CTFace *self = (CTFace*)s;
|
||||||
for (unsigned i=0; i < num_glyphs; i++) glyphs[i] = info[i].codepoint;
|
ensure_render_space(128, 128, num_glyphs);
|
||||||
|
for (unsigned i=0; i < num_glyphs; i++) buffers.glyphs[i] = info[i].codepoint;
|
||||||
return do_render(self->ct_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg, center_glyph);
|
return do_render(self->ct_font, bold, italic, info, hb_positions, num_glyphs, canvas, cell_width, cell_height, num_cells, baseline, was_colored, true, fg, center_glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -560,10 +560,10 @@ END_ALLOW_CASE_RANGE
|
|||||||
static PyObject* box_drawing_function = NULL, *prerender_function = NULL, *descriptor_for_idx = NULL;
|
static PyObject* box_drawing_function = NULL, *prerender_function = NULL, *descriptor_for_idx = NULL;
|
||||||
|
|
||||||
void
|
void
|
||||||
render_alpha_mask(uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
|
render_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride) {
|
||||||
for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
|
for (size_t sr = src_rect->top, dr = dest_rect->top; sr < src_rect->bottom && dr < dest_rect->bottom; sr++, dr++) {
|
||||||
pixel *d = dest + dest_stride * dr;
|
pixel *d = dest + dest_stride * dr;
|
||||||
uint8_t *s = alpha_mask + src_stride * sr;
|
const uint8_t *s = alpha_mask + src_stride * sr;
|
||||||
for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
|
for(size_t sc = src_rect->left, dc = dest_rect->left; sc < src_rect->right && dc < dest_rect->right; sc++, dc++) {
|
||||||
uint8_t src_alpha = d[dc] & 0xff;
|
uint8_t src_alpha = d[dc] & 0xff;
|
||||||
uint8_t alpha = s[sc];
|
uint8_t alpha = s[sc];
|
||||||
|
|||||||
@ -33,7 +33,7 @@ PyObject* face_from_descriptor(PyObject*, FONTS_DATA_HANDLE);
|
|||||||
const char* postscript_name_for_face(const PyObject*);
|
const char* postscript_name_for_face(const PyObject*);
|
||||||
|
|
||||||
void sprite_tracker_current_layout(FONTS_DATA_HANDLE data, unsigned int *x, unsigned int *y, unsigned int *z);
|
void sprite_tracker_current_layout(FONTS_DATA_HANDLE data, 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_alpha_mask(const uint8_t *alpha_mask, pixel* dest, Region *src_rect, Region *dest_rect, size_t src_stride, size_t dest_stride);
|
||||||
void render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature);
|
void render_line(FONTS_DATA_HANDLE, Line *line, index_type lnum, Cursor *cursor, DisableLigature);
|
||||||
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
void sprite_tracker_set_limits(size_t max_texture_size, size_t max_array_len);
|
||||||
typedef void (*free_extra_data_func)(void*);
|
typedef void (*free_extra_data_func)(void*);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user