diff --git a/src/alloc.c b/src/alloc.c index ebc55857ea..1395f647f4 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -322,7 +322,7 @@ #define PUREBEG (char *) pure /* If positive, garbage collection is inhibited. Otherwise, zero. */ -static intptr_t garbage_collection_inhibited; +static intptr_t garbage_collection_inhibited = 3; /* The GC threshold in bytes, the last time it was calculated from gc-cons-threshold and gc-cons-percentage. */ diff --git a/src/composite.c b/src/composite.c index 518502be49..e2bece40c8 100644 --- a/src/composite.c +++ b/src/composite.c @@ -836,7 +836,7 @@ fill_gstring_body (Lisp_Object gstring) LGLYPH_SET_CHAR (g, c); if (font != NULL) - code = font->driver->encode_char (font, LGLYPH_CHAR (g)); + code = font->driver->encode_char (font, LGLYPH_CHAR (g), NULL); else code = FONT_INVALID_CODE; if (code != FONT_INVALID_CODE) diff --git a/src/dispextern.h b/src/dispextern.h index 0b1f3d14ae..2f6b33e74c 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -397,6 +397,15 @@ #define SET_GLYPH_FROM_GLYPH_CODE(glyph, gc) \ }; +struct glyph_context +{ + union vectorlike_header header; + Lisp_Object string; + Lisp_Object position; + int i; + int n; +}; + /* Glyphs. Be extra careful when changing this structure! Esp. make sure that @@ -567,6 +576,8 @@ #define FACE_ID_BITS 20 /* Used to compare all bit-fields above in one step. */ unsigned val; } u; + + struct glyph_context *context; }; diff --git a/src/font.c b/src/font.c index ab00402b40..8de3c969b9 100644 --- a/src/font.c +++ b/src/font.c @@ -3010,7 +3010,7 @@ font_has_char (struct frame *f, Lisp_Object font, int c) if (result >= 0) return result; } - return (fontp->driver->encode_char (fontp, c) != FONT_INVALID_CODE); + return (fontp->driver->encode_char (fontp, c, NULL) != FONT_INVALID_CODE); } @@ -3023,7 +3023,7 @@ font_encode_char (Lisp_Object font_object, int c) eassert (FONT_OBJECT_P (font_object)); font = XFONT_OBJECT (font_object); - return font->driver->encode_char (font, c); + return font->driver->encode_char (font, c, NULL); } @@ -4418,7 +4418,7 @@ font_fill_lglyph_metrics (Lisp_Object glyph, struct font *font, unsigned int cod struct font_metrics metrics; LGLYPH_SET_CODE (glyph, code); - font->driver->text_extents (font, &code, 1, &metrics); + font->driver->text_extents (font, &code, 1, &metrics, NULL); LGLYPH_SET_LBEARING (glyph, metrics.lbearing); LGLYPH_SET_RBEARING (glyph, metrics.rbearing); LGLYPH_SET_WIDTH (glyph, metrics.width); @@ -4638,7 +4638,7 @@ DEFUN ("internal-char-font", Finternal_char_font, Sinternal_char_font, 1, 2, 0, struct face *face = FACE_FROM_ID (f, face_id); if (! face->font) return Qnil; - unsigned code = face->font->driver->encode_char (face->font, c); + unsigned code = face->font->driver->encode_char (face->font, c, NULL); if (code == FONT_INVALID_CODE) return Qnil; Lisp_Object font_object; @@ -4965,7 +4965,7 @@ DEFUN ("font-get-glyphs", Ffont_get_glyphs, Sfont_get_glyphs, 3, 4, 0, unsigned code; struct font_metrics metrics; - code = font->driver->encode_char (font, c); + code = font->driver->encode_char (font, c, NULL); if (code == FONT_INVALID_CODE) { ASET (vec, i, Qnil); @@ -4976,7 +4976,7 @@ DEFUN ("font-get-glyphs", Ffont_get_glyphs, Sfont_get_glyphs, 3, 4, 0, LGLYPH_SET_TO (g, i); LGLYPH_SET_CHAR (g, c); LGLYPH_SET_CODE (g, code); - font->driver->text_extents (font, &code, 1, &metrics); + font->driver->text_extents (font, &code, 1, &metrics, NULL); LGLYPH_SET_WIDTH (g, metrics.width); LGLYPH_SET_LBEARING (g, metrics.lbearing); LGLYPH_SET_RBEARING (g, metrics.rbearing); diff --git a/src/font.h b/src/font.h index 8614e7fa10..952a9fa4c3 100644 --- a/src/font.h +++ b/src/font.h @@ -565,6 +565,8 @@ #define FONT_PIXEL_SIZE_QUANTUM 1 #define FONT_INVALID_CODE 0xFFFFFFFF +struct glyph_context; + /* Font driver. Members specified as "optional" can be NULL. */ struct font_driver @@ -645,14 +647,15 @@ #define FONT_INVALID_CODE 0xFFFFFFFF /* Return a glyph code of FONT for character C (Unicode code point). If FONT doesn't have such a glyph, return FONT_INVALID_CODE. */ - unsigned (*encode_char) (struct font *font, int c); + unsigned (*encode_char) (struct font *font, int c, struct glyph_context *context); /* Compute the total metrics of the NGLYPHS glyphs specified by the font FONT and the sequence of glyph codes CODE, and store the result in METRICS. */ void (*text_extents) (struct font *font, const unsigned *code, int nglyphs, - struct font_metrics *metrics); + struct font_metrics *metrics, + struct glyph_context *context); #ifdef HAVE_WINDOW_SYSTEM diff --git a/src/ftcrfont.c b/src/ftcrfont.c index 7832d4f5ce..19c2644285 100644 --- a/src/ftcrfont.c +++ b/src/ftcrfont.c @@ -323,7 +323,7 @@ ftcrfont_has_char (Lisp_Object font, int c) } static unsigned -ftcrfont_encode_char (struct font *font, int c) +ftcrfont_encode_char (struct font *font, int c, struct glyph_context *context) { struct font_info *ftcrfont_info = (struct font_info *) font; unsigned code = FONT_INVALID_CODE; @@ -331,20 +331,53 @@ ftcrfont_encode_char (struct font *font, int c) int utf8len = CHAR_STRING (c, utf8); cairo_glyph_t stack_glyph; cairo_glyph_t *glyphs = &stack_glyph; - int num_glyphs = 1; - if (cairo_scaled_font_text_to_glyphs (ftcrfont_info->cr_scaled_font, 0, 0, - (char *) utf8, utf8len, - &glyphs, &num_glyphs, - NULL, NULL, NULL) - == CAIRO_STATUS_SUCCESS) + if (context == NULL) { - if (glyphs != &stack_glyph) - cairo_glyph_free (glyphs); - else if (stack_glyph.index) - code = stack_glyph.index; + context = xmalloc (sizeof *context); + context->string = CALLN (Fstring, make_fixnum (c)); + context->position = make_fixnum (0); } + unsigned int num_glyphs = 0; + unsigned int num_clusters = 0; + hb_buffer_t *hb_buf = hb_buffer_create (); + hb_buffer_set_cluster_level (hb_buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); + hb_buffer_add_utf8 (hb_buf, SDATA (context->string), -1, 0, -1); + hb_buffer_set_direction (hb_buf, HB_DIRECTION_LTR); + hb_font_t *hb_font = hb_ft_font_create_referenced + (cairo_ft_scaled_font_lock_face (ftcrfont_info->cr_scaled_font)); + hb_shape (hb_font, hb_buf, NULL, 0); + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos + (hb_buf, &num_glyphs); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions + (hb_buf, &num_glyphs); + int i0, i1; + int c0, c1; + i0 = 0; + for (int i = num_glyphs - 1; i >= 0; i--) + { + if (glyph_info[i].cluster <= XFIXNUM (context->position)) + { + i0 = i; + c0 = glyph_info[i].cluster; + break; + } + } + i1 = num_glyphs; + for (int i = 0; i < num_glyphs; i++) + { + if (glyph_info[i].cluster > c0) + { + i1 = i; + c1 = glyph_info[i].cluster; + break; + } + } + context->i = XFIXNUM (context->position) - c0; + context->n = c1 - c0; + code = glyph_info[i0].codepoint; + return code; } @@ -352,30 +385,65 @@ ftcrfont_encode_char (struct font *font, int c) ftcrfont_text_extents (struct font *font, const unsigned *code, int nglyphs, - struct font_metrics *metrics) + struct font_metrics *metrics, + struct glyph_context *context) { + struct font_info *ftcrfont_info = (struct font_info *) font; int width, i; block_input (); - width = ftcrfont_glyph_extents (font, code[0], metrics); - for (i = 1; i < nglyphs; i++) + + if (context == NULL) { - struct font_metrics m; - int w = ftcrfont_glyph_extents (font, code[i], metrics ? &m : NULL); + context = xmalloc (sizeof *context); + context->string = CALLN (Fstring, make_fixnum (code[0])); + context->position = make_fixnum (0); + } - if (metrics) + unsigned int num_glyphs = 0; + unsigned int num_clusters = 0; + hb_buffer_t *hb_buf = hb_buffer_create (); + hb_buffer_set_cluster_level (hb_buf, HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); + hb_buffer_set_direction (hb_buf, HB_DIRECTION_LTR); + hb_buffer_set_content_type (hb_buf, HB_BUFFER_CONTENT_TYPE_UNICODE); + int n = 0; + for (const char *p = SDATA (context->string); p <= SDATA (context->string) + SBYTES (context->string);) + { + int c = string_char_advance (&p); + hb_buffer_add (hb_buf, c, n++); + } + hb_font_t *hb_font = hb_ft_font_create_referenced + (cairo_ft_scaled_font_lock_face (ftcrfont_info->cr_scaled_font)); + hb_shape (hb_font, hb_buf, NULL, 0); + hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos + (hb_buf, &num_glyphs); + hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions + (hb_buf, &num_glyphs); + int i0, i1; + int c0, c1; + i0 = 0; + for (int i = num_glyphs - 1; i >= 0; i--) + { + if (glyph_info[i].cluster <= XFIXNUM (context->position)) + { + i0 = i; + c0 = glyph_info[i].cluster; + break; + } + } + i1 = num_glyphs; + for (int i = 0; i < num_glyphs; i++) + { + if (glyph_info[i].cluster > c0) { - if (width + m.lbearing < metrics->lbearing) - metrics->lbearing = width + m.lbearing; - if (width + m.rbearing > metrics->rbearing) - metrics->rbearing = width + m.rbearing; - if (m.ascent > metrics->ascent) - metrics->ascent = m.ascent; - if (m.descent > metrics->descent) - metrics->descent = m.descent; + i1 = i; + c1 = glyph_info[i].cluster; + break; } - width += w; } + context->i = XFIXNUM (context->position) - c0; + context->n = c1 - c0; + width = glyph_pos[i0].x_advance / (c1 - c0) / 64; unblock_input (); if (metrics) @@ -508,6 +576,8 @@ ftcrfont_draw (struct glyph_string *s, glyphs[i].index = s->char2b[from + i]; glyphs[i].x = x; glyphs[i].y = y; + struct glyph_context *context = s->first_glyph->context; + glyphs[i].x -= (context->i * s->width); x += (s->padding_p ? 1 : ftcrfont_glyph_extents (s->font, glyphs[i].index, NULL)); diff --git a/src/hbfont.c b/src/hbfont.c index 576c5fe7f6..5c3c690281 100644 --- a/src/hbfont.c +++ b/src/hbfont.c @@ -578,7 +578,7 @@ hbfont_shape (Lisp_Object lgstring, Lisp_Object direction) LGLYPH_SET_CODE (lglyph, info[i].codepoint); unsigned code = info[i].codepoint; - font->driver->text_extents (font, &code, 1, &metrics); + font->driver->text_extents (font, &code, 1, &metrics, NULL); LGLYPH_SET_WIDTH (lglyph, metrics.width); LGLYPH_SET_LBEARING (lglyph, metrics.lbearing); LGLYPH_SET_RBEARING (lglyph, metrics.rbearing); diff --git a/src/lisp.h b/src/lisp.h index ad7d67ae69..c4ae954999 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -1103,6 +1103,7 @@ DEFINE_GDB_SYMBOL_END (PSEUDOVECTOR_FLAG) PVEC_MUTEX, PVEC_CONDVAR, PVEC_MODULE_FUNCTION, + PVEC_GLYPH_CONTEXT, /* These should be last, for internal_equal and sxhash_obj. */ PVEC_COMPILED, diff --git a/src/xdisp.c b/src/xdisp.c index cf15f579b5..41a7b4235a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -27499,14 +27499,15 @@ append_glyph_string (struct glyph_string **head, struct glyph_string **tail, static struct face * get_char_face_and_encoding (struct frame *f, int c, int face_id, - unsigned *char2b, bool display_p) + unsigned *char2b, bool display_p, + struct glyph_context *context) { struct face *face = FACE_FROM_ID (f, face_id); unsigned code = 0; if (face->font) { - code = face->font->driver->encode_char (face->font, c); + code = face->font->driver->encode_char (face->font, c, context); if (code == FONT_INVALID_CODE) code = 0; @@ -27533,7 +27534,7 @@ get_char_face_and_encoding (struct frame *f, int c, int face_id, static struct face * get_glyph_face_and_encoding (struct frame *f, struct glyph *glyph, - unsigned *char2b) + unsigned *char2b, struct glyph_context *context) { struct face *face; unsigned code = 0; @@ -27549,7 +27550,8 @@ get_glyph_face_and_encoding (struct frame *f, struct glyph *glyph, if (CHAR_BYTE8_P (glyph->u.ch)) code = CHAR_TO_BYTE8 (glyph->u.ch); else - code = face->font->driver->encode_char (face->font, glyph->u.ch); + code = face->font->driver->encode_char (face->font, glyph->u.ch, + context); if (code == FONT_INVALID_CODE) code = 0; @@ -27565,14 +27567,15 @@ get_glyph_face_and_encoding (struct frame *f, struct glyph *glyph, Return true iff FONT has a glyph for C. */ static bool -get_char_glyph_code (int c, struct font *font, unsigned *char2b) +get_char_glyph_code (int c, struct font *font, unsigned *char2b, + struct glyph_context *context) { unsigned code; if (CHAR_BYTE8_P (c)) code = CHAR_TO_BYTE8 (c); else - code = font->driver->encode_char (font, c); + code = font->driver->encode_char (font, c, context); if (code == FONT_INVALID_CODE) return false; @@ -27620,7 +27623,8 @@ fill_composite_glyph_string (struct glyph_string *s, struct face *base_face, -1, Qnil); face = get_char_face_and_encoding (s->f, c, face_id, - s->char2b + i, true); + s->char2b + i, true, + NULL); if (face) { if (! s->face) @@ -27777,12 +27781,13 @@ fill_glyph_string (struct glyph_string *s, int face_id, && glyph->glyph_not_available_p == glyph_not_available_p) { s->face = get_glyph_face_and_encoding (s->f, glyph, - s->char2b + s->nchars); + s->char2b + s->nchars, + glyph->context); ++s->nchars; eassert (s->nchars <= end - start); s->width += glyph->pixel_width; - if (glyph++->padding_p != s->padding_p) - break; + glyph++; + break; } s->font = s->face->font; @@ -27877,7 +27882,8 @@ fill_stretch_glyph_string (struct glyph_string *s, int start, int end) } static struct font_metrics * -get_per_char_metric (struct font *font, const unsigned *char2b) +get_per_char_metric (struct font *font, const unsigned *char2b, + struct glyph_context *context) { static struct font_metrics metrics; @@ -27886,7 +27892,7 @@ get_per_char_metric (struct font *font, const unsigned *char2b) if (*char2b == FONT_INVALID_CODE) return NULL; - font->driver->text_extents (font, char2b, 1, &metrics); + font->driver->text_extents (font, char2b, 1, &metrics, context); return &metrics; } @@ -27908,9 +27914,10 @@ normal_char_ascent_descent (struct font *font, int c, int *ascent, int *descent) /* Get metrics of C, defaulting to a reasonably sized ASCII character. */ - if (get_char_glyph_code (c >= 0 ? c : '{', font, &char2b)) + if (get_char_glyph_code (c >= 0 ? c : '{', font, &char2b, NULL)) { - struct font_metrics *pcm = get_per_char_metric (font, &char2b); + struct font_metrics *pcm = get_per_char_metric (font, &char2b, + NULL); if (!(pcm->width == 0 && pcm->rbearing == 0 && pcm->lbearing == 0)) { @@ -27952,10 +27959,12 @@ gui_get_glyph_overhangs (struct glyph *glyph, struct frame *f, int *left, int *r if (glyph->type == CHAR_GLYPH) { unsigned char2b; - struct face *face = get_glyph_face_and_encoding (f, glyph, &char2b); + struct face *face = get_glyph_face_and_encoding (f, glyph, &char2b, + NULL); if (face->font) { - struct font_metrics *pcm = get_per_char_metric (face->font, &char2b); + struct font_metrics *pcm = get_per_char_metric (face->font, &char2b, + NULL); if (pcm) { if (pcm->rbearing > pcm->width) @@ -29841,12 +29850,12 @@ produce_glyphless_glyph (struct it *it, bool for_no_font, Lisp_Object acronym) str = buf; } for (len = 0; str[len] && ASCII_CHAR_P (str[len]) && len < 6; len++) - code[len] = font->driver->encode_char (font, str[len]); + code[len] = font->driver->encode_char (font, str[len], NULL); upper_len = (len + 1) / 2; font->driver->text_extents (font, code, upper_len, - &metrics_upper); + &metrics_upper, NULL); font->driver->text_extents (font, code + upper_len, len - upper_len, - &metrics_lower); + &metrics_lower, NULL); @@ -29936,6 +29945,40 @@ #define IT_APPLY_FACE_BOX(it, face) \ } \ } while (false) +static struct glyph_context * +make_context (struct it *it) +{ + struct glyph_context *context = xmalloc (sizeof *context); // XXX GC + char *string = xmalloc (128); + char *p = string; + ptrdiff_t bytepos = it->current.pos.bytepos; + ptrdiff_t charpos = it->current.pos.charpos; + ptrdiff_t bp5 = bytepos; + ptrdiff_t bp0 = bp5; + ptrdiff_t bp1 = bp5; + while (bytepos > BEG_BYTE && bp5 - bytepos < 32) + dec_both (&charpos, &bytepos); + bp0 = bytepos; + int i = 0; + Lisp_Object pos = make_fixnum (0); + while (bytepos >= BEG_BYTE && bytepos < Z_BYTE && bytepos - bp0 < 32) + { + inc_both (&charpos, &bytepos); + memcpy (p, BUF_BYTE_ADDRESS (current_buffer, bytepos - prev_char_len (bytepos)), prev_char_len (bytepos)); + p += prev_char_len (bytepos); + ++i; + if (bytepos == bp5) + pos = make_fixnum (i); + } + bp1 = bytepos; + eassert (strlen (p) == bp1 - bp0); + *p++ = it->c; + *p++ = 0; + context->string = build_string (string); + context->position = pos; + return context; +} + /* RIF: Produce glyphs/get display metrics for the display element IT is loaded with. See the description of struct it in dispextern.h @@ -29973,6 +30016,7 @@ gui_produce_glyphs (struct it *it) if (font->vertical_centering) boff = VCENTER_BASELINE_OFFSET (font, it->f) - boff; + struct glyph_context *context = NULL; if (it->char_to_display != '\n' && it->char_to_display != '\t') { it->nglyphs = 1; @@ -29989,9 +30033,11 @@ gui_produce_glyphs (struct it *it) it->descent = FONT_DESCENT (font) - boff; } - if (get_char_glyph_code (it->char_to_display, font, &char2b)) + context = make_context (it); + if (get_char_glyph_code (it->char_to_display, font, &char2b, + context)) { - pcm = get_per_char_metric (font, &char2b); + pcm = get_per_char_metric (font, &char2b, context); if (pcm->width == 0 && pcm->rbearing == 0 && pcm->lbearing == 0) pcm = NULL; @@ -30079,9 +30125,13 @@ gui_produce_glyphs (struct it *it) / FONT_HEIGHT (font)); append_stretch_glyph (it, it->object, it->pixel_width, it->ascent + it->descent, ascent); + it->glyph_row->glyphs[it->area][it->glyph_row->used[it->area] - 1].context = NULL; } else - append_glyph (it); + { + append_glyph (it); + it->glyph_row->glyphs[it->area][it->glyph_row->used[it->area] - 1].context = context; + } /* If characters with lbearing or rbearing are displayed in this line, record that fact in a flag of the @@ -30233,9 +30283,9 @@ gui_produce_glyphs (struct it *it) it->nglyphs = 1; if (FONT_TOO_HIGH (font)) { - if (get_char_glyph_code (' ', font, &char2b)) + if (get_char_glyph_code (' ', font, &char2b, NULL)) { - pcm = get_per_char_metric (font, &char2b); + pcm = get_per_char_metric (font, &char2b, NULL); if (pcm->width == 0 && pcm->rbearing == 0 && pcm->lbearing == 0) pcm = NULL; @@ -30372,8 +30422,8 @@ gui_produce_glyphs (struct it *it) if (! font_not_found_p) { get_char_face_and_encoding (it->f, c, it->face_id, - &char2b, false); - pcm = get_per_char_metric (font, &char2b); + &char2b, false, NULL); + pcm = get_per_char_metric (font, &char2b, NULL); } /* Initialize the bounding box. */ @@ -30433,8 +30483,9 @@ gui_produce_glyphs (struct it *it) else { get_char_face_and_encoding (it->f, ch, face_id, - &char2b, false); - pcm = get_per_char_metric (font, &char2b); + &char2b, false, + make_context (it)); + pcm = get_per_char_metric (font, &char2b, make_context (it)); } if (! pcm) cmp->offsets[i * 2] = cmp->offsets[i * 2 + 1] = 0; diff --git a/src/xterm.c b/src/xterm.c index 7989cecec7..3b5f0d3524 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -1703,7 +1703,8 @@ x_compute_glyph_string_overhangs (struct glyph_string *s) if (s->first_glyph->type == CHAR_GLYPH) { struct font *font = s->font; - font->driver->text_extents (font, s->char2b, s->nchars, &metrics); + font->driver->text_extents (font, s->char2b, s->nchars, &metrics, + NULL); } else { @@ -2047,7 +2048,7 @@ x_draw_glyphless_glyph_string_foreground (struct glyph_string *s) /* It is assured that all LEN characters in STR is ASCII. */ for (j = 0; j < len; j++) - char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF; + char2b[j] = s->font->driver->encode_char (s->font, str[j], NULL) & 0xFFFF; s->font->driver->draw (s, 0, upper_len, x + glyph->slice.glyphless.upper_xoff, s->ybase + glyph->slice.glyphless.upper_yoff,