From faf10bd8eb3272880b774fe220fa9916ed1f00c0 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Mitsuharu Date: Sat, 8 Jun 2019 14:05:49 +0900 Subject: [PATCH] Support X core font driver on cairo (Bug#28236) * configure.ac (HAVE_X_WINDOWS): Add xfont.o to FONT_OBJ if HAVE_CAIRO. * doc/lispref/frames.texi (Font and Color Parameters): Mention X core font driver with Cairo drawing. * src/font.c (syms_of_font) [HAVE_X_WINDOWS && USE_CAIRO]: Call syms_of_xfont. * src/xfns.c (x_create_tip_frame) [USE_CAIRO]: Register xfont_driver. * src/xterm.c (x_cr_gc_clip) [USE_CAIRO]: New function extracted from x_begin_cr_clip. (x_begin_cr_clip) [USE_CAIRO]: Use it. (xlib_surface_key, saved_drawable_key) [USE_CAIRO]: New variables. (x_cr_destroy_xlib_surface, x_try_cr_xlib_drawable) (x_end_cr_xlib_drawable) [USE_CAIRO]: New functions. (x_draw_composite_glyph_string_foreground) (x_draw_glyph_string_foreground) [USE_CAIRO]: Get Xlib surface when drawing text with X core fonts into bitmap surfaces. Add fallback code for drawing into outline surfaces. --- configure.ac | 2 +- doc/lispref/frames.texi | 10 +- src/font.c | 2 +- src/xfns.c | 2 +- src/xterm.c | 341 ++++++++++++++++++++++++++++++---------- 5 files changed, 267 insertions(+), 90 deletions(-) diff --git a/configure.ac b/configure.ac index bd51f54212c..5c96f4231ee 100644 --- a/configure.ac +++ b/configure.ac @@ -5261,7 +5261,7 @@ if test "${HAVE_X_WINDOWS}" = "yes" ; then XOBJ="xterm.o xfns.o xselect.o xrdb.o xsmfns.o xsettings.o" FONT_OBJ=xfont.o if test "$HAVE_CAIRO" = "yes"; then - FONT_OBJ="ftfont.o ftcrfont.o" + FONT_OBJ="$FONT_OBJ ftfont.o ftcrfont.o" elif test "$HAVE_XFT" = "yes"; then FONT_OBJ="$FONT_OBJ ftfont.o xftfont.o ftxfont.o" elif test "$HAVE_FREETYPE" = "yes"; then diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 336075a1ca2..629cec3c5fe 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -2282,11 +2282,11 @@ drawing characters on the frame, in order of priority. In Emacs built without Cairo drawing on X, there are currently three available font backends: @code{x} (the X core font driver), @code{xft} (the Xft font driver), and @code{xfthb} (the Xft font driver with HarfBuzz text -shaping). If built with the Cairo drawing, there are two available -font backends on X: @code{ftcr} (the FreeType font driver on Cairo) -and @code{ftcrhb} (the FreeType font driver on Cairo with HarfBuzz -text shaping). On MS-Windows, there are currently three available -font backends: @code{gdi} (the core MS-Windows font driver), +shaping). If built with the Cairo drawing, there are also three +available font backends on X: @code{x}, @code{ftcr} (the FreeType font +driver on Cairo), and @code{ftcrhb} (the FreeType font driver on Cairo +with HarfBuzz text shaping). On MS-Windows, there are currently three +available font backends: @code{gdi} (the core MS-Windows font driver), @code{uniscribe} (font driver for OTF and TTF fonts with text shaping by the Uniscribe engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, diff --git a/src/font.c b/src/font.c index 6ab4923c3d2..5705758b99f 100644 --- a/src/font.c +++ b/src/font.c @@ -5497,10 +5497,10 @@ cause Xft crashes. Only has an effect in Xft builds. */); #ifdef HAVE_FREETYPE syms_of_ftfont (); #ifdef HAVE_X_WINDOWS + syms_of_xfont (); #ifdef USE_CAIRO syms_of_ftcrfont (); #else - syms_of_xfont (); syms_of_ftxfont (); #ifdef HAVE_XFT syms_of_xftfont (); diff --git a/src/xfns.c b/src/xfns.c index 460dd1316e6..46f19ff82ac 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -3794,8 +3794,8 @@ This function is an internal primitive--use `make-frame' instead. */) register_font_driver (&ftxfont_driver, f); #endif /* not HAVE_XFT */ #endif /* HAVE_FREETYPE */ - register_font_driver (&xfont_driver, f); #endif /* not USE_CAIRO */ + register_font_driver (&xfont_driver, f); image_cache_refcount = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0; diff --git a/src/xterm.c b/src/xterm.c index e0edd9c1a40..3cd95b7a65d 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -357,6 +357,25 @@ x_cr_update_surface_desired_size (struct frame *f, int width, int height) } } +static void +x_cr_gc_clip (cairo_t *cr, struct frame *f, GC gc) +{ + if (gc) + { + struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 0); + + if (gc_ext && gc_ext->n_clip_rects) + { + for (int i = 0; i < gc_ext->n_clip_rects; i++) + cairo_rectangle (cr, gc_ext->clip_rects[i].x, + gc_ext->clip_rects[i].y, + gc_ext->clip_rects[i].width, + gc_ext->clip_rects[i].height); + cairo_clip (cr); + } + } +} + cairo_t * x_begin_cr_clip (struct frame *f, GC gc) { @@ -379,23 +398,7 @@ x_begin_cr_clip (struct frame *f, GC gc) cairo_surface_destroy (surface); } cairo_save (cr); - - if (gc) - { - struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 0); - - if (gc_ext && gc_ext->n_clip_rects) - { - int i; - - for (i = 0; i < gc_ext->n_clip_rects; i++) - cairo_rectangle (cr, gc_ext->clip_rects[i].x, - gc_ext->clip_rects[i].y, - gc_ext->clip_rects[i].width, - gc_ext->clip_rects[i].height); - cairo_clip (cr); - } - } + x_cr_gc_clip (cr, f, gc); return cr; } @@ -434,6 +437,116 @@ x_set_cr_source_with_gc_background (struct frame *f, GC gc) color.green / 65535.0, color.blue / 65535.0); } +static const cairo_user_data_key_t xlib_surface_key, saved_drawable_key; + +static void +x_cr_destroy_xlib_surface (cairo_surface_t *xlib_surface) +{ + if (xlib_surface) + { + XFreePixmap (cairo_xlib_surface_get_display (xlib_surface), + cairo_xlib_surface_get_drawable (xlib_surface)); + cairo_surface_destroy (xlib_surface); + } +} + +static bool +x_try_cr_xlib_drawable (struct frame *f, GC gc) +{ + cairo_t *cr = FRAME_CR_CONTEXT (f); + if (!cr) + return true; + + cairo_surface_t *surface = cairo_get_target (cr); + switch (cairo_surface_get_type (surface)) + { + case CAIRO_SURFACE_TYPE_XLIB: + cairo_surface_flush (surface); + return true; + + case CAIRO_SURFACE_TYPE_IMAGE: + break; + + default: + return false; + } + + /* FRAME_CR_CONTEXT (f) is an image surface we can not draw into + directly with Xlib. Set up a Pixmap so we can copy back the + result later in x_end_cr_xlib_drawable. */ + cairo_surface_t *xlib_surface = cairo_get_user_data (cr, &xlib_surface_key); + int width = FRAME_CR_SURFACE_DESIRED_WIDTH (f); + int height = FRAME_CR_SURFACE_DESIRED_HEIGHT (f); + Pixmap pixmap; + if (xlib_surface + && cairo_xlib_surface_get_width (xlib_surface) == width + && cairo_xlib_surface_get_height (xlib_surface) == height) + pixmap = cairo_xlib_surface_get_drawable (xlib_surface); + else + { + pixmap = XCreatePixmap (FRAME_X_DISPLAY (f), FRAME_X_RAW_DRAWABLE (f), + width, height, + DefaultDepthOfScreen (FRAME_X_SCREEN (f))); + xlib_surface = cairo_xlib_surface_create (FRAME_X_DISPLAY (f), + pixmap, FRAME_X_VISUAL (f), + width, height); + cairo_set_user_data (cr, &xlib_surface_key, xlib_surface, + (cairo_destroy_func_t) x_cr_destroy_xlib_surface); + } + + cairo_t *buf = cairo_create (xlib_surface); + cairo_set_source_surface (buf, surface, 0, 0); + cairo_matrix_t matrix; + cairo_get_matrix (cr, &matrix); + cairo_pattern_set_matrix (cairo_get_source (cr), &matrix); + cairo_set_operator (buf, CAIRO_OPERATOR_SOURCE); + x_cr_gc_clip (buf, f, gc); + cairo_paint (buf); + cairo_destroy (buf); + + cairo_set_user_data (cr, &saved_drawable_key, + (void *) (uintptr_t) FRAME_X_RAW_DRAWABLE (f), NULL); + FRAME_X_RAW_DRAWABLE (f) = pixmap; + cairo_surface_flush (xlib_surface); + + return true; +} + +static void +x_end_cr_xlib_drawable (struct frame *f, GC gc) +{ + cairo_t *cr = FRAME_CR_CONTEXT (f); + if (!cr) + return; + + Drawable saved_drawable + = (uintptr_t) cairo_get_user_data (cr, &saved_drawable_key); + cairo_surface_t *surface = (saved_drawable + ? cairo_get_user_data (cr, &xlib_surface_key) + : cairo_get_target (cr)); + struct x_gc_ext_data *gc_ext = x_gc_get_ext_data (f, gc, 0); + if (gc_ext && gc_ext->n_clip_rects) + for (int i = 0; i < gc_ext->n_clip_rects; i++) + cairo_surface_mark_dirty_rectangle (surface, gc_ext->clip_rects[i].x, + gc_ext->clip_rects[i].y, + gc_ext->clip_rects[i].width, + gc_ext->clip_rects[i].height); + else + cairo_surface_mark_dirty (surface); + if (!saved_drawable) + return; + + cairo_save (cr); + cairo_set_source_surface (cr, surface, 0, 0); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + x_cr_gc_clip (cr, f, gc); + cairo_paint (cr); + cairo_restore (cr); + + FRAME_X_RAW_DRAWABLE (f) = saved_drawable; + cairo_set_user_data (cr, &saved_drawable_key, NULL, NULL); +} + /* Fringe bitmaps. */ static int max_fringe_bmp = 0; @@ -1732,20 +1845,65 @@ x_draw_glyph_string_foreground (struct glyph_string *s) else { struct font *font = s->font; - int boff = font->baseline_offset; - int y; +#ifdef USE_CAIRO + if (!EQ (font->driver->type, Qx) + || x_try_cr_xlib_drawable (s->f, s->gc)) + { +#endif /* USE_CAIRO */ + int boff = font->baseline_offset; + int y; - if (font->vertical_centering) - boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff; + if (font->vertical_centering) + boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff; - y = s->ybase - boff; - if (s->for_overlaps - || (s->background_filled_p && s->hl != DRAW_CURSOR)) - font->driver->draw (s, 0, s->nchars, x, y, false); + y = s->ybase - boff; + if (s->for_overlaps + || (s->background_filled_p && s->hl != DRAW_CURSOR)) + font->driver->draw (s, 0, s->nchars, x, y, false); + else + font->driver->draw (s, 0, s->nchars, x, y, true); + if (s->face->overstrike) + font->driver->draw (s, 0, s->nchars, x + 1, y, false); +#ifdef USE_CAIRO + if (EQ (font->driver->type, Qx)) + x_end_cr_xlib_drawable (s->f, s->gc); + } else - font->driver->draw (s, 0, s->nchars, x, y, true); - if (s->face->overstrike) - font->driver->draw (s, 0, s->nchars, x + 1, y, false); + { + /* Fallback for the case that no Xlib Drawable is available + for drawing text with X core fonts. */ + if (!(s->for_overlaps + || (s->background_filled_p && s->hl != DRAW_CURSOR))) + { + int box_line_width = max (s->face->box_line_width, 0); + + if (s->stippled_p) + { + Display *display = FRAME_X_DISPLAY (s->f); + + /* Fill background with a stipple pattern. */ + XSetFillStyle (display, s->gc, FillOpaqueStippled); + x_fill_rectangle (s->f, s->gc, s->x, + s->y + box_line_width, + s->background_width, + s->height - 2 * box_line_width); + XSetFillStyle (display, s->gc, FillSolid); + } + else + x_clear_glyph_string_rect (s, s->x, s->y + box_line_width, + s->background_width, + s->height - 2 * box_line_width); + } + for (i = 0; i < s->nchars; ++i) + { + struct glyph *g = s->first_glyph + i; + x_draw_rectangle (s->f, + s->gc, x, s->y, g->pixel_width - 1, + s->height - 1); + x += g->pixel_width; + } + } +#endif /* USE_CAIRO */ } } @@ -1778,65 +1936,84 @@ x_draw_composite_glyph_string_foreground (struct glyph_string *s) x_draw_rectangle (s->f, s->gc, x, s->y, s->width - 1, s->height - 1); } - else if (! s->first_glyph->u.cmp.automatic) - { - int y = s->ybase; - - for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++) - /* TAB in a composition means display glyphs with padding - space on the left or right. */ - if (COMPOSITION_GLYPH (s->cmp, j) != '\t') - { - int xx = x + s->cmp->offsets[j * 2]; - int yy = y - s->cmp->offsets[j * 2 + 1]; - - font->driver->draw (s, j, j + 1, xx, yy, false); - if (s->face->overstrike) - font->driver->draw (s, j, j + 1, xx + 1, yy, false); - } - } else - { - Lisp_Object gstring = composition_gstring_from_id (s->cmp_id); - Lisp_Object glyph; - int y = s->ybase; - int width = 0; - - for (i = j = s->cmp_from; i < s->cmp_to; i++) - { - glyph = LGSTRING_GLYPH (gstring, i); - if (NILP (LGLYPH_ADJUSTMENT (glyph))) - width += LGLYPH_WIDTH (glyph); - else - { - int xoff, yoff, wadjust; +#ifdef USE_CAIRO + if (!EQ (font->driver->type, Qx) + || x_try_cr_xlib_drawable (s->f, s->gc)) + { +#endif /* USE_CAIRO */ + if (! s->first_glyph->u.cmp.automatic) + { + int y = s->ybase; - if (j < i) + for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++) + /* TAB in a composition means display glyphs with + padding space on the left or right. */ + if (COMPOSITION_GLYPH (s->cmp, j) != '\t') { - font->driver->draw (s, j, i, x, y, false); + int xx = x + s->cmp->offsets[j * 2]; + int yy = y - s->cmp->offsets[j * 2 + 1]; + + font->driver->draw (s, j, j + 1, xx, yy, false); if (s->face->overstrike) - font->driver->draw (s, j, i, x + 1, y, false); - x += width; + font->driver->draw (s, j, j + 1, xx + 1, yy, false); } - xoff = LGLYPH_XOFF (glyph); - yoff = LGLYPH_YOFF (glyph); - wadjust = LGLYPH_WADJUST (glyph); - font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false); - if (s->face->overstrike) - font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff, - false); - x += wadjust; - j = i + 1; - width = 0; - } - } - if (j < i) - { - font->driver->draw (s, j, i, x, y, false); - if (s->face->overstrike) - font->driver->draw (s, j, i, x + 1, y, false); - } - } + } + else + { + Lisp_Object gstring = composition_gstring_from_id (s->cmp_id); + Lisp_Object glyph; + int y = s->ybase; + int width = 0; + + for (i = j = s->cmp_from; i < s->cmp_to; i++) + { + glyph = LGSTRING_GLYPH (gstring, i); + if (NILP (LGLYPH_ADJUSTMENT (glyph))) + width += LGLYPH_WIDTH (glyph); + else + { + int xoff, yoff, wadjust; + + if (j < i) + { + font->driver->draw (s, j, i, x, y, false); + if (s->face->overstrike) + font->driver->draw (s, j, i, x + 1, y, false); + x += width; + } + xoff = LGLYPH_XOFF (glyph); + yoff = LGLYPH_YOFF (glyph); + wadjust = LGLYPH_WADJUST (glyph); + font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false); + if (s->face->overstrike) + font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff, + false); + x += wadjust; + j = i + 1; + width = 0; + } + } + if (j < i) + { + font->driver->draw (s, j, i, x, y, false); + if (s->face->overstrike) + font->driver->draw (s, j, i, x + 1, y, false); + } + } +#ifdef USE_CAIRO + if (EQ (font->driver->type, Qx)) + x_end_cr_xlib_drawable (s->f, s->gc); + } + else + { + /* Fallback for the case that no Xlib Drawable is available + for drawing text with X core fonts. */ + if (s->cmp_from == 0) + x_draw_rectangle (s->f, s->gc, x, s->y, + s->width - 1, s->height - 1); + } +#endif /* USE_CAIRO */ } -- 2.39.2