macOS: Fix a crash when rendering ligatures larger than 128 characters

Fixes #3724
This commit is contained in:
Kovid Goyal 2021-06-14 07:15:52 +05:30
parent ebff343a55
commit 1b35708d89
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 54 additions and 36 deletions

View File

@ -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`)

View File

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

View File

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

View File

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