* bug#73730: 31.0.50; Support for color fonts on MS-Windows
@ 2024-10-10 11:16 Cecilio Pardo
2024-10-10 13:08 ` Eli Zaretskii
2024-10-10 21:50 ` Cecilio Pardo
0 siblings, 2 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-10 11:16 UTC (permalink / raw)
To: 73730
[-- Attachment #1: Type: text/plain, Size: 821 bytes --]
The attached patch is a preliminar implementation of a DirectWrite font
driver that allows for color fonts, tested only on Windows 11.
There is much to be refined about quality, performance (caching), OS
version conditionals, etc.
Before doing all that, I need to know that this is the right (or at
least good enough) way to do it.
The DirectWrite font driver mounts on top of a copy of the harfbuzz one,
and then replaces some of the functions, but some of the hb functions
include eassert to check that the driver for a font is harfbuzz. I don't
know how to deal with that.
I suppose that if we skip harfbuzz completely, then we would have to
reimplement a lot of stuff with DirectWrite?
Also uniscribe_open has been modified to change the driver assigned to
the font unconditionally to dwrite.
--
Cecilio Pardo
[-- Attachment #2: 0001-Font-driver-for-DirectWrite-MS-Windows-supporting-co.patch --]
[-- Type: text/plain, Size: 16794 bytes --]
From d004845fe6a9495af7c03ddd9d30d3ba71fc3e5d Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Font driver for DirectWrite (MS-Windows) supporting color
fonts.
---
configure.ac | 4 +-
src/font.h | 1 +
src/w32dwrite.c | 470 +++++++++++++++++++++++++++++++++++++++++++++
src/w32fns.c | 7 +
src/w32uniscribe.c | 2 +
5 files changed, 482 insertions(+), 2 deletions(-)
create mode 100644 src/w32dwrite.c
diff --git a/configure.ac b/configure.ac
index 8a5ba7db3d1..8a06d9d5027 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
AC_CHECK_TOOL([WINDRES], [windres],
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
- W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+ W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
@@ -3202,7 +3202,7 @@ AC_DEFUN
W32_OBJ="$W32_OBJ w32image.o"
fi
W32_LIBS="$W32_LIBS -lwinmm -lgdi32 -lcomdlg32"
- W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32"
+ W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32 -ldwrite"
W32_RES_LINK="\$(EMACSRES)"
CLIENTRES="emacsclient.res"
CLIENTW="emacsclientw\$(EXEEXT)"
diff --git a/src/font.h b/src/font.h
index 8ee1940be0a..7e0e96d9bfa 100644
--- a/src/font.h
+++ b/src/font.h
@@ -978,6 +978,7 @@ valid_font_driver (struct font_driver const *d)
extern struct font_driver uniscribe_font_driver;
#ifdef HAVE_HARFBUZZ
extern struct font_driver harfbuzz_font_driver;
+extern struct font_driver dwrite_font_driver;
#endif
extern void syms_of_w32font (void);
#endif /* HAVE_NTGUI */
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..32c9c179778
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,470 @@
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#include "lisp.h"
+#include "coding.h"
+#include "w32term.h"
+#include "frame.h"
+#include "composite.h"
+#include "font.h"
+#include "w32font.h"
+#include "pdumper.h"
+#include "w32common.h"
+
+#include "initguid.h"
+#include <dwrite_3.h>
+
+void syms_of_w32dwrite (void);
+
+struct font_driver dwrite_font_driver;
+
+/* Initialized on syms_of_w32dwrite_for_pdumper */
+IDWriteFactory *dwrite_factory = NULL;
+IDWriteFactory2 *dwrite_factory2 = NULL;
+IDWriteGdiInterop *gdi_interop = NULL;
+IDWriteRenderingParams *rendering_params = NULL;
+
+static void
+verify_hr (HRESULT hr, const char *msg)
+{
+ if (FAILED (hr))
+ {
+ printf ("Error: %s\n", msg);
+ abort ();
+ }
+}
+
+static float
+get_font_and_face (HFONT hfont, LOGFONTW *logfont_out,
+ IDWriteFont **font, IDWriteFontFace **face)
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+
+ GetObjectW (hfont, sizeof (LOGFONTW), &logfont);
+ hr = gdi_interop->lpVtbl->
+ CreateFontFromLOGFONT (gdi_interop, (const LOGFONTW *) &logfont, font);
+ verify_hr (hr, "Failed to create DWriteFont");
+ hr = (*font)->lpVtbl->
+ CreateFontFace (*font, face);
+ verify_hr (hr, "Failed to create DWriteFontFace");
+
+ if (logfont_out)
+ *logfont_out = logfont;
+
+ return abs (logfont.lfHeight);
+}
+
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+ HRESULT hr;
+ IDWriteBitmapRenderTarget *brt;
+ hr = gdi_interop->lpVtbl->
+ CreateBitmapRenderTarget (gdi_interop, hdc, width, height, &brt);
+ verify_hr (hr, "Failed to create DWriteBitmapRenderTarget");
+
+ IDWriteBitmapRenderTarget1 *brt1;
+ hr = brt->lpVtbl->
+ QueryInterface (brt, &IID_IDWriteBitmapRenderTarget1, (void **)&brt1);
+ verify_hr (hr, "Failed to create DWriteBitmapRenderTarget1");
+ brt1->lpVtbl->
+ SetTextAntialiasMode ( brt1, DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE );
+ brt1->lpVtbl->Release(brt1);
+
+ return brt;
+}
+
+
+/* This function does not fill in the ascent and descent fields of
+ metrics. */
+static void text_extents_internal(IDWriteFont *dwrite_font,
+ IDWriteFontFace *dwrite_font_face,
+ float font_size,
+ const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ HRESULT hr;
+
+ DWRITE_FONT_METRICS dwrite_font_metrics;
+ dwrite_font->lpVtbl->
+ GetMetrics (dwrite_font, &dwrite_font_metrics);
+
+ UINT16 *indices = alloca (nglyphs * sizeof (UINT16));
+ for (int i = 0; i < nglyphs; i++)
+ indices[i] = code[i];
+
+ DWRITE_GLYPH_METRICS* gmetrics =
+ alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+ hr = dwrite_font_face->lpVtbl->
+ GetGdiCompatibleGlyphMetrics (dwrite_font_face, font_size, 1.0, NULL,
+ TRUE, indices, nglyphs, gmetrics, false );
+ verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics");
+
+ float width = 0;
+ int units_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+ for (int i = 0; i < nglyphs; i++)
+ {
+ width += (float)gmetrics[i].advanceWidth * font_size / units_per_em;
+ float lbearing = round ((float) gmetrics[i].leftSideBearing * font_size
+ / units_per_em);
+ float rbearing = round ((float) gmetrics[i].rightSideBearing * font_size
+ / units_per_em);
+ if (i==0)
+ {
+ metrics->lbearing = lbearing;
+ metrics->rbearing = rbearing;
+ }
+ if (metrics->lbearing > lbearing)
+ metrics->lbearing = lbearing;
+ if (metrics->rbearing < rbearing)
+ metrics->rbearing = rbearing;
+ }
+ metrics->width = round(width);
+}
+
+static Lisp_Object
+dwrite_list (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object fonts = w32font_list_internal (f, font_spec, true);
+ for (Lisp_Object tail = fonts; CONSP (tail); tail = XCDR (tail))
+ ASET (XCAR (tail), FONT_TYPE_INDEX, Qdwrite);
+
+ FONT_ADD_LOG ("dwrite-list", font_spec, fonts);
+ return fonts;
+}
+
+static Lisp_Object
+dwrite_match (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object entity = w32font_match_internal (f, font_spec, true);
+ FONT_ADD_LOG ("dwrite-match", font_spec, entity);
+
+ if (! NILP (entity))
+ ASET (entity, FONT_TYPE_INDEX, Qdwrite);
+ return entity;
+}
+
+static unsigned
+dwrite_encode_char (struct font *font, int c)
+{
+ HRESULT hr;
+ IDWriteFont *dwrite_font;
+ IDWriteFontFace *dwrite_font_face;
+
+ get_font_and_face (((struct w32font_info *) font)->hfont,
+ NULL, &dwrite_font, &dwrite_font_face );
+ UINT16 index;
+
+ hr = dwrite_font_face->lpVtbl->
+ GetGlyphIndices (dwrite_font_face, &c, 1, &index);
+ verify_hr (hr, "Failed to GetGlyphIndices");
+
+ dwrite_font->lpVtbl->Release (dwrite_font);
+ dwrite_font_face->lpVtbl->Release (dwrite_font_face);
+
+ if (index == 0)
+ return FONT_INVALID_CODE;
+
+ return index;
+}
+
+static void
+dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ IDWriteFont *dwrite_font;
+ IDWriteFontFace *dwrite_font_face;
+
+ HFONT hfont = ((struct w32font_info *) font)->hfont;
+ float font_size = get_font_and_face (hfont,
+ NULL, &dwrite_font,
+ &dwrite_font_face );
+
+ text_extents_internal (dwrite_font, dwrite_font_face, font_size, code,
+ nglyphs, metrics);
+
+ dwrite_font->lpVtbl->Release (dwrite_font);
+ dwrite_font_face->lpVtbl->Release (dwrite_font_face);
+
+ metrics->ascent = font->ascent;
+ metrics->descent = font->descent;
+}
+
+static
+void dwrite_draw_internal (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ int baseline_y, COLORREF color, struct font *font )
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+ IDWriteFont *dwrite_font;
+ IDWriteFontFace *dwrite_font_face;
+
+ float font_size = get_font_and_face (GetCurrentObject (hdc, OBJ_FONT),
+ &logfont, &dwrite_font,
+ &dwrite_font_face );
+
+ IDWriteTextFormat *text_format;
+ hr = dwrite_factory->lpVtbl->
+ CreateTextFormat (dwrite_factory,
+ logfont.lfFaceName,
+ NULL,
+ DWRITE_FONT_WEIGHT_NORMAL,
+ DWRITE_FONT_STYLE_NORMAL,
+ DWRITE_FONT_STRETCH_NORMAL,
+ font_size, L"",
+ &text_format);
+ verify_hr (hr, "Failed to create DWriteTextFormat");
+
+ struct font_metrics metrics;
+ text_extents_internal (dwrite_font, dwrite_font_face,
+ font_size, glyphs, len, &metrics);
+
+ int bitmap_width = metrics.width;
+ int bitmap_height = font->ascent + font->descent;
+
+ IDWriteBitmapRenderTarget *bitmap_render_target =
+ get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+ HDC text_dc = bitmap_render_target->lpVtbl->
+ GetMemoryDC (bitmap_render_target);
+
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+ DWRITE_GLYPH_RUN glyph_run;
+
+ UINT16 *indices = alloca (len * sizeof (UINT16));
+
+ for (int i = 0; i < len; i++)
+ indices[i] = glyphs[i];
+
+ FLOAT *advances = alloca (len * sizeof (FLOAT));
+
+ for (int i = 0; i < len; i++)
+ {
+ text_extents_internal (dwrite_font, dwrite_font_face,
+ font_size, glyphs + i, 1, &metrics);
+ advances[i] = metrics.width;
+ }
+
+ glyph_run.fontFace = dwrite_font_face;
+ glyph_run.fontEmSize = font_size;
+ glyph_run.glyphIndices = indices;
+ glyph_run.glyphCount = len;
+ glyph_run.isSideways = false;
+ glyph_run.bidiLevel = 0;
+ glyph_run.glyphOffsets = NULL;
+ glyph_run.glyphAdvances = advances;
+
+ IDWriteColorGlyphRunEnumerator *layers;
+
+ hr = dwrite_factory2->lpVtbl->
+ TranslateColorGlyphRun (dwrite_factory2,
+ 0, baseline_y,
+ &glyph_run,
+ NULL,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC,
+ NULL,
+ 0,
+ &layers );
+ if (hr == DWRITE_E_NOCOLOR)
+ {
+ bitmap_render_target->lpVtbl->
+ DrawGlyphRun (bitmap_render_target,
+ 0, baseline_y,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC,
+ &glyph_run,
+ rendering_params,
+ color,
+ NULL);
+ }
+ else
+ {
+ verify_hr (hr, "Failed at TranslateColorGlyphRun");
+ for (;;) {
+ HRESULT hr;
+ BOOL more_layers;
+ const DWRITE_COLOR_GLYPH_RUN *layer;
+
+ hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+ verify_hr (hr, "Failed at MoveNext");
+ if (!more_layers)
+ break;
+ hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+ verify_hr (hr, "Failed at GetCurrentRun");
+ bitmap_render_target->lpVtbl->
+ DrawGlyphRun (bitmap_render_target,
+ layer->baselineOriginX, layer->baselineOriginY,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC,
+ &layer->glyphRun,
+ rendering_params,
+ RGB( layer->runColor.r * 255,
+ layer->runColor.g * 255,
+ layer->runColor.b * 255 ),
+ NULL);
+ }
+
+ layers->lpVtbl->Release (layers);
+ }
+ BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+
+ text_format->lpVtbl->Release (text_format);
+ dwrite_font->lpVtbl->Release (dwrite_font);
+ bitmap_render_target->lpVtbl->Release (bitmap_render_target);
+ dwrite_font_face->lpVtbl->Release (dwrite_font_face);
+}
+
+static int
+dwrite_draw (struct glyph_string *s, int from, int to,
+ int x, int y, bool with_background)
+{
+ HRGN orig_clip = NULL;
+ int len = to - from;
+
+ if (s->num_clips > 0)
+ {
+ HRGN new_clip = CreateRectRgnIndirect (s->clip);
+
+ /* Save clip region for later restoration. */
+ orig_clip = CreateRectRgn (0, 0, 0, 0);
+ if (!GetClipRgn (s->hdc, orig_clip))
+ {
+ DeleteObject (orig_clip);
+ orig_clip = NULL;
+ }
+
+ if (s->num_clips > 1)
+ {
+ HRGN clip2 = CreateRectRgnIndirect (s->clip + 1);
+
+ CombineRgn (new_clip, new_clip, clip2, RGN_OR);
+ DeleteObject (clip2);
+ }
+
+ SelectClipRgn (s->hdc, new_clip);
+ DeleteObject (new_clip);
+ }
+
+ /* Using OPAQUE background mode can clear more background than expected
+ when Cleartype is used. Draw the background manually to avoid this. */
+ SetBkMode (s->hdc, TRANSPARENT);
+ if (with_background)
+ {
+ HBRUSH brush;
+ RECT rect;
+ struct font *font = s->font;
+ int ascent = font->ascent, descent = font->descent;
+
+ /* Font's global ascent and descent values might be
+ preposterously large for some fonts. We fix here the case
+ when those fonts are used for display of glyphless
+ characters, because drawing background with font dimensions
+ in those cases makes the display illegible. There's only one
+ more call to the draw method with with_background set to
+ true, and that's in w32_draw_glyph_string_foreground, when
+ drawing the cursor, where we have no such heuristics
+ available. FIXME. */
+ if (s->first_glyph->type == GLYPHLESS_GLYPH
+ && (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE
+ || s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM))
+ {
+ ascent =
+ s->first_glyph->slice.glyphless.lower_yoff
+ - s->first_glyph->slice.glyphless.upper_yoff;
+ descent = 0;
+ }
+ brush = CreateSolidBrush (s->gc->background);
+ rect.left = x;
+ rect.top = y - ascent;
+ rect.right = x + s->width;
+ rect.bottom = y + descent;
+ FillRect (s->hdc, &rect, brush);
+ DeleteObject (brush);
+ }
+
+ if (s->padding_p)
+ {
+ int i;
+ for (i = 0; i < len; i++)
+ dwrite_draw_internal (s->hdc, x, y - s->font->ascent,
+ s->char2b + from, 1, s->font->ascent,
+ GetTextColor(s->hdc), s->font);
+ }
+ else
+ {
+ dwrite_draw_internal( s->hdc, x, y - s->font->ascent,
+ s->char2b + from, len, s->font->ascent,
+ GetTextColor(s->hdc), s->font );
+
+ }
+
+ /* Restore clip region. */
+ if (s->num_clips > 0)
+ SelectClipRgn (s->hdc, orig_clip);
+
+ if (orig_clip)
+ DeleteObject (orig_clip);
+
+ return len;
+}
+
+static void
+syms_of_w32dwrite_for_pdumper (void)
+{
+ HRESULT hr;
+
+ if (!initialized)
+ return;
+
+ DEFSYM (Qdwrite, "dwrite");
+
+ dwrite_font_driver = harfbuzz_font_driver;
+ dwrite_font_driver.type = Qdwrite;
+ dwrite_font_driver.draw = dwrite_draw;
+ dwrite_font_driver.list = dwrite_list;
+ dwrite_font_driver.match = dwrite_match;
+ dwrite_font_driver.encode_char = dwrite_encode_char;
+ dwrite_font_driver.text_extents = dwrite_text_extents;
+ register_font_driver (&dwrite_font_driver, NULL);
+
+ hr = DWriteCreateFactory (DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown**)&dwrite_factory);
+ verify_hr (hr, "Failed to create DWriteFactory");
+
+ hr = dwrite_factory->lpVtbl->
+ QueryInterface (dwrite_factory, &IID_IDWriteFactory2,
+ (void **)&dwrite_factory2);
+ verify_hr (hr, "Failed to create DWriteFactory2");
+
+ hr = dwrite_factory->lpVtbl->
+ GetGdiInterop (dwrite_factory, &gdi_interop);
+ verify_hr (hr, "Failed to get DWriteGgiInterop");
+
+ IDWriteRenderingParams *def;
+
+ hr = dwrite_factory->lpVtbl->
+ CreateRenderingParams (dwrite_factory, &def);
+ verify_hr (hr, "Failed to create DWriteRenderingParams");
+
+ hr = dwrite_factory->lpVtbl->
+ CreateCustomRenderingParams (dwrite_factory,
+ def->lpVtbl->GetGamma(def),
+ def->lpVtbl->GetEnhancedContrast(def),
+ def->lpVtbl->GetClearTypeLevel(def),
+ def->lpVtbl->GetPixelGeometry(def),
+ DWRITE_RENDERING_MODE_GDI_CLASSIC,
+ &rendering_params);
+ verify_hr (hr, "Failed to create DWriteRenderingParams");
+
+ def->lpVtbl->Release (def);
+}
+
+void
+syms_of_w32dwrite (void)
+{
+ pdumper_do_now_and_after_load (syms_of_w32dwrite_for_pdumper);
+}
diff --git a/src/w32fns.c b/src/w32fns.c
index 3ee13dcbbdd..aba91c1d41c 100644
--- a/src/w32fns.c
+++ b/src/w32fns.c
@@ -293,6 +293,10 @@ #define MENU_FREE_DELAY 1000
extern int uniscribe_available;
extern int harfbuzz_available;
+/* From w32dwrite.c */
+extern void syms_of_w32dwrite (void);
+
+
#ifdef WINDOWSNT
/* From w32inevt.c */
extern int faked_key;
@@ -6356,6 +6360,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
specbind (Qx_resource_name, name);
}
+ register_font_driver (&dwrite_font_driver, f);
+
#ifdef HAVE_HARFBUZZ
if (harfbuzz_available)
register_font_driver (&harfbuzz_font_driver, f);
@@ -11811,6 +11817,7 @@ globals_of_w32fns (void)
InitCommonControls ();
syms_of_w32uniscribe ();
+ syms_of_w32dwrite ();
}
#ifdef WINDOWSNT
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..cbe57d8490a 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -211,6 +211,8 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
#endif /* HAVE_HARFBUZZ */
uniscribe_font->w32_font.font.driver = &uniscribe_font_driver;
+ uniscribe_font->w32_font.font.driver = &dwrite_font_driver;
+
return font_object;
}
--
2.35.1.windows.2
[-- Attachment #3: emacs_3xjaQ7etE9.png --]
[-- Type: image/png, Size: 30997 bytes --]
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 11:16 bug#73730: 31.0.50; Support for color fonts on MS-Windows Cecilio Pardo
@ 2024-10-10 13:08 ` Eli Zaretskii
2024-10-10 15:14 ` Cecilio Pardo
2024-10-15 22:18 ` Cecilio Pardo
2024-10-10 21:50 ` Cecilio Pardo
1 sibling, 2 replies; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-10 13:08 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Thu, 10 Oct 2024 13:16:23 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> The attached patch is a preliminar implementation of a DirectWrite font
> driver that allows for color fonts, tested only on Windows 11.
Thanks!
> There is much to be refined about quality, performance (caching), OS
> version conditionals, etc.
>
> Before doing all that, I need to know that this is the right (or at
> least good enough) way to do it.
>
> The DirectWrite font driver mounts on top of a copy of the harfbuzz one,
> and then replaces some of the functions, but some of the hb functions
> include eassert to check that the driver for a font is harfbuzz. I don't
> know how to deal with that.
If we use a (slightly modified) HarfBuzz font driver (see below), this
won't be a problem, right?
> I suppose that if we skip harfbuzz completely, then we would have to
> reimplement a lot of stuff with DirectWrite?
Yes, I don't want to skip HarfBuzz, since we want HarfBuzz to be our
primary text-shaping engine. We don't want to rely on
Windows-specific shapers because MS has a habit of deprecating them at
will (which happened with Uniscribe). The experts on text shaping are
hard to get on board, so we don't want to have to rewrite our display
routines when MS decide to come up with a shining new shaper.
> Also uniscribe_open has been modified to change the driver assigned to
> the font unconditionally to dwrite.
That's temporary, I presume?
Some comments about significant issues below.
> @@ -3202,7 +3202,7 @@ AC_DEFUN
> W32_OBJ="$W32_OBJ w32image.o"
> fi
> W32_LIBS="$W32_LIBS -lwinmm -lgdi32 -lcomdlg32"
> - W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32"
> + W32_LIBS="$W32_LIBS -lmpr -lwinspool -lole32 -lcomctl32 -ldwrite"
This we cannot do, for two reasons: (1) mingw.org's MinGW doesn't have
the libdwrite.dll.a import library, and (2) linking against -ldwrite
will make Emacs refuse to start on systems without DWrite.dll.
Instead, we need to use LoadLibrary at startup time to load the DLL,
like we do with HarfBuzz, and if the load succeeds, use get_proc_addr
to assign the addresses of APIs we need to use to function pointers;
then we call the functions through those pointers. See w32image.c for
how we do that with GdiPlus.dll and w32uniscribe.c for how we do that
with libharfbuzz-0.dll.
> --- a/src/font.h
> +++ b/src/font.h
> @@ -978,6 +978,7 @@ valid_font_driver (struct font_driver const *d)
> extern struct font_driver uniscribe_font_driver;
> #ifdef HAVE_HARFBUZZ
> extern struct font_driver harfbuzz_font_driver;
> +extern struct font_driver dwrite_font_driver;
Do we really need a whole new font driver? I hoped that we'd only
need to replace the functions we call to actually draw the font glyphs
in w32font_draw (ExtTextOutW etc.) with the appropriate replacements
from Direct2D. You see, HarfBuzz already does the job of finding the
appropriate font glyphs when we need some non-trivial font processing
(like character compositions), so I thought all we needed was to be
able to actually draw the font glyphs of color fonts.
IOW, how about just modifying the few methods of the HarfBuzz font
driver when DirectWrite is available, and otherwise leaving the
HarfBuzz font driver be the one which supports this? With Uniscribe
we had legacy support issues (Windows 9X etc.), but there's no such
problem with HarfBuzz vs DirectWrite, so adding yet another font
driver which needs to coexist with the others is a complexity I'd like
to avoid. In my mental model, we just use DirectWrite for low-level
drawing of font glyphs, and otherwise we still keep the HarfBuzz font
driver. Does that make sense?
> +/* Initialized on syms_of_w32dwrite_for_pdumper */
> +IDWriteFactory *dwrite_factory = NULL;
> +IDWriteFactory2 *dwrite_factory2 = NULL;
> +IDWriteGdiInterop *gdi_interop = NULL;
> +IDWriteRenderingParams *rendering_params = NULL;
These should probably be static, if they are only used in this one
file.
> +static void
> +verify_hr (HRESULT hr, const char *msg)
> +{
> + if (FAILED (hr))
> + {
> + printf ("Error: %s\n", msg);
> + abort ();
> + }
> +}
This will need to be replaced with something more suitable.
> +static Lisp_Object
> +dwrite_list (struct frame *f, Lisp_Object font_spec)
> +{
> + Lisp_Object fonts = w32font_list_internal (f, font_spec, true);
> + for (Lisp_Object tail = fonts; CONSP (tail); tail = XCDR (tail))
> + ASET (XCAR (tail), FONT_TYPE_INDEX, Qdwrite);
> +
> + FONT_ADD_LOG ("dwrite-list", font_spec, fonts);
> + return fonts;
> +}
> +
> +static Lisp_Object
> +dwrite_match (struct frame *f, Lisp_Object font_spec)
> +{
> + Lisp_Object entity = w32font_match_internal (f, font_spec, true);
> + FONT_ADD_LOG ("dwrite-match", font_spec, entity);
> +
> + if (! NILP (entity))
> + ASET (entity, FONT_TYPE_INDEX, Qdwrite);
> + return entity;
> +}
If we avoid defining a new font driver, the above two methods are not
needed, as they do the same as the HarfBuzz driver does.
> +static int
> +dwrite_draw (struct glyph_string *s, int from, int to,
> + int x, int y, bool with_background)
> +{
> + HRGN orig_clip = NULL;
> + int len = to - from;
> +
> + if (s->num_clips > 0)
> + {
> + HRGN new_clip = CreateRectRgnIndirect (s->clip);
> +
> + /* Save clip region for later restoration. */
> + orig_clip = CreateRectRgn (0, 0, 0, 0);
> + if (!GetClipRgn (s->hdc, orig_clip))
> + {
> + DeleteObject (orig_clip);
> + orig_clip = NULL;
> + }
> +
> + if (s->num_clips > 1)
> + {
> + HRGN clip2 = CreateRectRgnIndirect (s->clip + 1);
> +
> + CombineRgn (new_clip, new_clip, clip2, RGN_OR);
> + DeleteObject (clip2);
> + }
> +
> + SelectClipRgn (s->hdc, new_clip);
> + DeleteObject (new_clip);
> + }
> +
> + /* Using OPAQUE background mode can clear more background than expected
> + when Cleartype is used. Draw the background manually to avoid this. */
> + SetBkMode (s->hdc, TRANSPARENT);
> + if (with_background)
> + {
> + HBRUSH brush;
> + RECT rect;
> + struct font *font = s->font;
> + int ascent = font->ascent, descent = font->descent;
> +
> + /* Font's global ascent and descent values might be
> + preposterously large for some fonts. We fix here the case
> + when those fonts are used for display of glyphless
> + characters, because drawing background with font dimensions
> + in those cases makes the display illegible. There's only one
> + more call to the draw method with with_background set to
> + true, and that's in w32_draw_glyph_string_foreground, when
> + drawing the cursor, where we have no such heuristics
> + available. FIXME. */
> + if (s->first_glyph->type == GLYPHLESS_GLYPH
> + && (s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE
> + || s->first_glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM))
> + {
> + ascent =
> + s->first_glyph->slice.glyphless.lower_yoff
> + - s->first_glyph->slice.glyphless.upper_yoff;
> + descent = 0;
> + }
> + brush = CreateSolidBrush (s->gc->background);
> + rect.left = x;
> + rect.top = y - ascent;
> + rect.right = x + s->width;
> + rect.bottom = y + descent;
> + FillRect (s->hdc, &rect, brush);
> + DeleteObject (brush);
> + }
> +
> + if (s->padding_p)
> + {
> + int i;
> + for (i = 0; i < len; i++)
> + dwrite_draw_internal (s->hdc, x, y - s->font->ascent,
> + s->char2b + from, 1, s->font->ascent,
> + GetTextColor(s->hdc), s->font);
> + }
> + else
> + {
> + dwrite_draw_internal( s->hdc, x, y - s->font->ascent,
> + s->char2b + from, len, s->font->ascent,
> + GetTextColor(s->hdc), s->font );
> +
> + }
> +
> + /* Restore clip region. */
> + if (s->num_clips > 0)
> + SelectClipRgn (s->hdc, orig_clip);
> +
> + if (orig_clip)
> + DeleteObject (orig_clip);
> +
> + return len;
> +}
This method looks almost identical to w32font_draw, except that it
calls DirectWrite specific functions instead of ExtTextOutW. It would
be better to reuse the code in w32font.c instead of copying it; a
single test for DirectWrite's availability should not be an issue, I
think (if you are worried about performance).
> + hr = dwrite_factory->lpVtbl->
> + CreateCustomRenderingParams (dwrite_factory,
> + def->lpVtbl->GetGamma(def),
> + def->lpVtbl->GetEnhancedContrast(def),
> + def->lpVtbl->GetClearTypeLevel(def),
> + def->lpVtbl->GetPixelGeometry(def),
> + DWRITE_RENDERING_MODE_GDI_CLASSIC,
> + &rendering_params);
Are there some options for the rendering here that we perhaps need to
discuss? E.g., is DWRITE_RENDERING_MODE_GDI_CLASSIC the only
reasonable choice?
Thanks again for working on this.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 13:08 ` Eli Zaretskii
@ 2024-10-10 15:14 ` Cecilio Pardo
2024-10-10 16:33 ` Eli Zaretskii
2024-10-15 22:18 ` Cecilio Pardo
1 sibling, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-10 15:14 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
On 10/10/2024 15:08, Eli Zaretskii wrote:
> IOW, how about just modifying the few methods of the HarfBuzz font
> driver when DirectWrite is available, and otherwise leaving the
> HarfBuzz font driver be the one which supports this? With Uniscribe
> we had legacy support issues (Windows 9X etc.), but there's no such
> problem with HarfBuzz vs DirectWrite, so adding yet another font
> driver which needs to coexist with the others is a complexity I'd like
> to avoid. In my mental model, we just use DirectWrite for low-level
> drawing of font glyphs, and otherwise we still keep the HarfBuzz font
> driver. Does that make sense?
Yes. I'll rewrite it like that.
>> + hr = dwrite_factory->lpVtbl->
>> + CreateCustomRenderingParams (dwrite_factory,
>> + def->lpVtbl->GetGamma(def),
>> + def->lpVtbl->GetEnhancedContrast(def),
>> + def->lpVtbl->GetClearTypeLevel(def),
>> + def->lpVtbl->GetPixelGeometry(def),
>> + DWRITE_RENDERING_MODE_GDI_CLASSIC,
>> + &rendering_params);
>
> Are there some options for the rendering here that we perhaps need to
> discuss? E.g., is DWRITE_RENDERING_MODE_GDI_CLASSIC the only
> reasonable choice?
This gives poor quality, we will definitely use something else. But in
my tests other methods gave fractional widths for glyphs, so I took this
one for the first version.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 15:14 ` Cecilio Pardo
@ 2024-10-10 16:33 ` Eli Zaretskii
2024-10-10 16:46 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-10 16:33 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Thu, 10 Oct 2024 17:14:34 +0200
> Cc: 73730@debbugs.gnu.org
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> On 10/10/2024 15:08, Eli Zaretskii wrote:
>
> > IOW, how about just modifying the few methods of the HarfBuzz font
> > driver when DirectWrite is available, and otherwise leaving the
> > HarfBuzz font driver be the one which supports this? With Uniscribe
> > we had legacy support issues (Windows 9X etc.), but there's no such
> > problem with HarfBuzz vs DirectWrite, so adding yet another font
> > driver which needs to coexist with the others is a complexity I'd like
> > to avoid. In my mental model, we just use DirectWrite for low-level
> > drawing of font glyphs, and otherwise we still keep the HarfBuzz font
> > driver. Does that make sense?
>
> Yes. I'll rewrite it like that.
Thanks, SGTM.
> >> + hr = dwrite_factory->lpVtbl->
> >> + CreateCustomRenderingParams (dwrite_factory,
> >> + def->lpVtbl->GetGamma(def),
> >> + def->lpVtbl->GetEnhancedContrast(def),
> >> + def->lpVtbl->GetClearTypeLevel(def),
> >> + def->lpVtbl->GetPixelGeometry(def),
> >> + DWRITE_RENDERING_MODE_GDI_CLASSIC,
> >> + &rendering_params);
> >
> > Are there some options for the rendering here that we perhaps need to
> > discuss? E.g., is DWRITE_RENDERING_MODE_GDI_CLASSIC the only
> > reasonable choice?
>
> This gives poor quality, we will definitely use something else. But in
> my tests other methods gave fractional widths for glyphs, so I took this
> one for the first version.
I'd guess DWRITE_RENDERING_MODE_DEFAULT should be the first choice?
And if it gets you corrupted glyphs on display, this is something that
needs debugging, I think. Maybe the code which processes the glyphs,
which you basically copied from w32font.c, needs some adjustment for
DirectWrite or something?
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 16:33 ` Eli Zaretskii
@ 2024-10-10 16:46 ` Cecilio Pardo
0 siblings, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-10 16:46 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
> I'd guess DWRITE_RENDERING_MODE_DEFAULT should be the first choice?
> And if it gets you corrupted glyphs on display, this is something that
> needs debugging, I think. Maybe the code which processes the glyphs,
> which you basically copied from w32font.c, needs some adjustment for
> DirectWrite or something?
It draws well, but when measuring character sizes it gives fractional
values, not integers. Anyway, I will investigate it throroughly.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 11:16 bug#73730: 31.0.50; Support for color fonts on MS-Windows Cecilio Pardo
2024-10-10 13:08 ` Eli Zaretskii
@ 2024-10-10 21:50 ` Cecilio Pardo
2024-10-11 3:36 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-10 21:50 UTC (permalink / raw)
To: 73730
It looks like DirectWrite can't handle .FON files. When I build emacs,
the fixed-pitch face, which uses the Monospace family, gets the
"Courier" font which we can't use.
I'd say that this wasn't failing before today's windows update, but who
knows...
Any pointers?
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 21:50 ` Cecilio Pardo
@ 2024-10-11 3:36 ` Eli Zaretskii
2024-10-11 6:28 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-11 3:36 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Thu, 10 Oct 2024 23:50:00 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> It looks like DirectWrite can't handle .FON files. When I build emacs,
> the fixed-pitch face, which uses the Monospace family, gets the
> "Courier" font which we can't use.
>
> I'd say that this wasn't failing before today's windows update, but who
> knows...
>
> Any pointers?
What happens if you revert commit 7eba90c12227 ?
If this fixes the problem, please look at the rationale described in
the commit log message and see if you get back the problem described
there, and in particular what happens with the faces requested by
info.el. It is possible that the problem described there only happens
on XP, where there's no DirectWrite, in which case we could condition
Qascii_0 so that it doesn't get in the way.
But it is somewhat bothersome that .fon fonts are not supported. Are
you sure this is true? What does Internet search bring? There are
example programs on MSDN which use DirectWrite, can you try them with
Courier and see if they also fail?
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-11 3:36 ` Eli Zaretskii
@ 2024-10-11 6:28 ` Eli Zaretskii
2024-10-11 7:19 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-11 6:28 UTC (permalink / raw)
To: cpardo; +Cc: 73730
> Cc: 73730@debbugs.gnu.org
> Date: Fri, 11 Oct 2024 06:36:55 +0300
> From: Eli Zaretskii <eliz@gnu.org>
>
> > Date: Thu, 10 Oct 2024 23:50:00 +0200
> > From: Cecilio Pardo <cpardo@imayhem.com>
> >
> > It looks like DirectWrite can't handle .FON files. When I build emacs,
> > the fixed-pitch face, which uses the Monospace family, gets the
> > "Courier" font which we can't use.
> >
> > I'd say that this wasn't failing before today's windows update, but who
> > knows...
> >
> > Any pointers?
>
> What happens if you revert commit 7eba90c12227 ?
>
> If this fixes the problem, please look at the rationale described in
> the commit log message and see if you get back the problem described
> there, and in particular what happens with the faces requested by
> info.el. It is possible that the problem described there only happens
> on XP, where there's no DirectWrite, in which case we could condition
> Qascii_0 so that it doesn't get in the way.
>
> But it is somewhat bothersome that .fon fonts are not supported. Are
> you sure this is true? What does Internet search bring? There are
> example programs on MSDN which use DirectWrite, can you try them with
> Courier and see if they also fail?
After some more thinking: I think something is amiss in your code,
because AFAICT the existing code draws .fon fonts using the 'gdi' font
driver. Here's how to see that:
emacs -Q
M-x font-lock-mode
M-x load-library RET facemenu RET
Now mark some text in *scratch* and type:
M-x facemenu-set-face RET fixed-pitch RET
Then go to some character displayed with fixed-pitch face and type
"C-u C-x =". When I do that in the current Emacs, I see:
position: 78 of 147 (52%), column: 6
character: c (displayed as c) (codepoint 99, #o143, #x63)
charset: ascii (ASCII (ISO646 IRV))
code point in charset: 0x63
script: latin
syntax: w which means: word
category: .:Base, L:Strong L2R, a:ASCII, l:Latin, r:Roman
to input: type "C-x 8 RET 63" or "C-x 8 RET LATIN SMALL LETTER C"
buffer code: #x63
file code: #x63 (encoded by coding system iso-latin-1-dos)
display: by this font (glyph code):
gdi:-raster-Courier-regular-normal-normal-mono-13-*-*-*-c-*-iso8859-1 (#x63)
Note the last line: we already detect that this font should use the
'gdi' font backend. So either your new code somehow bypassed this
detection, or the fallback on old glyph-drawing APIs which are used on
the current master branch by the 'gdi' font driver is missing
something. AFAIR (and my memory is not reliable in this matter, so
please check with the code), we decide that this font needs to be
drawn by the 'gdi' font driver when we look for fonts for the
"Monospace" family: I think the 'harfbuzz' font backend rejects it for
some reason (I don't remember which), whereas 'gdi' accepts it.
Bottom line: we should use the 'gdi' font backend for these fonts,
like we do with the current code. The 'harfbuzz' font driver, with or
without the new DirectWrite methods, should not be used for them,
presumably for reasons similar to those which determined how the
current code works.
If the above doesn't help, please tell more details about how
DirectWrite "cannot handle .FON fonts", and let's take it from there.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-11 6:28 ` Eli Zaretskii
@ 2024-10-11 7:19 ` Cecilio Pardo
0 siblings, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-11 7:19 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
On 11/10/2024 8:28, Eli Zaretskii wrote:
> Bottom line: we should use the 'gdi' font backend for these fonts,
> like we do with the current code. The 'harfbuzz' font driver, with or
> without the new DirectWrite methods, should not be used for them,
> presumably for reasons similar to those which determined how the
> current code works.
Ok. Thanks a lot.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-10 13:08 ` Eli Zaretskii
2024-10-10 15:14 ` Cecilio Pardo
@ 2024-10-15 22:18 ` Cecilio Pardo
2024-10-16 11:01 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-15 22:18 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
[-- Attachment #1: Type: text/plain, Size: 822 bytes --]
The attached patch works without a new font driver.
This requires the harfbuzz font backend to be available.
It works by modifying the harfbuzz backend to use DirectWrite at
some points, if it is available:
- When encoding characters: w32hb_encode_char
- When measuring text: w32font_text_extents
- When drawing text: w32font_draw
DirectWrite is setup by calling w32_initialize_direct_write. From
that point, the function w32_use_direct_write will return true if
DirectWrite is to be used.
The lisp variable w32-inhibit-direct-write can switches it on and off.
It builds with Mingw64 and Mingw. As mingw doesn't have the DirectWrite
includes, I had to declare a lot of things at the start of the file.
This line marks the end of that:
/* End of dwrite_3.h definitions. */
I have tested this only on Windows 11.
[-- Attachment #2: 0001-Implement-drawing-text-with-DirectWrite-on-MS-Window.patch --]
[-- Type: text/plain, Size: 36136 bytes --]
From 608f81421553b087ca7eb822c14b6d889787719b Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Implement drawing text with DirectWrite on MS-Windows.
This adds support for color fonts.
* configure.ac: Add src/w32drite to W32_OBJ
* src/w32dwrite.c: This is a new file.
(w32-initialize-direct-write): Initializes the DirectWrite library if
it is available, and required global variables.
(w32_use_direct_write): Allows to check if DirectWrite is available
and activated by the user.
(w32_dwrite_encode_char): Replacement for harfbuzz's encode_char.
(w32_dwrite_text_extents): Replacement for w32font text_extents.
(w32_dwrite_draw): Replacement for w32font draw.
(w32_dwrite_free_cached_face): Used on the font deletion process to
also delete DirectWrite data.
(verify_hr): Verify COM method results.
* src/w32font.c:
(w32font_text_extents): If DirectWrite is enabled, call
w32_dwrite_text_extents.
(w32font_draw): If DirectWrite is enabled, call w32_dwrite_draw.
* src/w32uniscribe.c:
(w32hb_encode_char): If DirectWrite is enabled, call
w32_dwrite_encode_char.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.
---
configure.ac | 2 +-
src/w32dwrite.c | 930 +++++++++++++++++++++++++++++++++++++++++++++
src/w32font.c | 53 ++-
src/w32uniscribe.c | 39 ++
4 files changed, 1010 insertions(+), 14 deletions(-)
create mode 100644 src/w32dwrite.c
diff --git a/configure.ac b/configure.ac
index 8a5ba7db3d1..ba9cd8f0cd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
AC_CHECK_TOOL([WINDRES], [windres],
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
- W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+ W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..cdb61dd7176
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,930 @@
+/* Support for using DirectWrite on MS-Windows to draw text. This allows
+ for color fonts.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/*
+ This requires the harfbuzz font backend to be available.
+
+ It works by modifying the harfbuzz backend to use DirectWrite at
+ some points, if it is available:
+
+ - When encoding characters: w32hb_encode_char
+ - When measuring text: w32font_text_extents
+ - When drawing text: w32font_draw
+
+ DirectWrite is setup by calling w32_initialize_direct_write. From
+ that point, the function w32_use_direct_write will return true if
+ DirectWrite is to be used. */
+
+
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#include "frame.h"
+#include "w32font.h"
+#include "w32common.h"
+#include "w32term.h"
+
+#include "initguid.h"
+#include <ole2.h>
+#include <unknwn.h>
+
+/* The following definitions would be included from dwrite_3.h, but it
+ is not available when building with MinGW. Methods that we don't use
+ are declared with the UNUSED macro, to avoid bringing in more types
+ that would need to be declared.
+*/
+
+#define UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
+
+#define DWRITE_E_NOCOLOR _HRESULT_TYPEDEF_(0x8898500CL)
+
+typedef enum DWRITE_PIXEL_GEOMETRY {
+ DWRITE_PIXEL_GEOMETRY_FLAT = 0,
+ DWRITE_PIXEL_GEOMETRY_RGB = 1,
+ DWRITE_PIXEL_GEOMETRY_BGR = 2
+} DWRITE_PIXEL_GEOMETRY;
+
+typedef enum DWRITE_RENDERING_MODE {
+ DWRITE_RENDERING_MODE_DEFAULT = 0,
+ DWRITE_RENDERING_MODE_ALIASED = 1,
+ DWRITE_RENDERING_MODE_GDI_CLASSIC = 2,
+ DWRITE_RENDERING_MODE_GDI_NATURAL = 3,
+ DWRITE_RENDERING_MODE_NATURAL = 4,
+ DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC = 5,
+ DWRITE_RENDERING_MODE_OUTLINE = 6
+} DWRITE_RENDERING_MODE;
+
+typedef enum DWRITE_MEASURING_MODE {
+ DWRITE_MEASURING_MODE_NATURAL = 0,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC = 1,
+ DWRITE_MEASURING_MODE_GDI_NATURAL = 2
+} DWRITE_MEASURING_MODE;
+
+typedef enum DWRITE_TEXT_ANTIALIAS_MODE {
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE = 0,
+ DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE = 1
+} DWRITE_TEXT_ANTIALIAS_MODE;
+
+typedef enum DWRITE_FACTORY_TYPE {
+ DWRITE_FACTORY_TYPE_SHARED = 0,
+ DWRITE_FACTORY_TYPE_ISOLATED = 1
+} DWRITE_FACTORY_TYPE;
+
+typedef struct DWRITE_FONT_METRICS {
+ UINT16 designUnitsPerEm;
+ UINT16 ascent;
+ UINT16 descent;
+ INT16 lineGap;
+ UINT16 capHeight;
+ UINT16 xHeight;
+ INT16 underlinePosition;
+ UINT16 underlineThickness;
+ INT16 strikethroughPosition;
+ UINT16 strikethroughThickness;
+} DWRITE_FONT_METRICS;
+
+typedef struct DWRITE_GLYPH_METRICS {
+ INT32 leftSideBearing;
+ UINT32 advanceWidth;
+ INT32 rightSideBearing;
+ INT32 topSideBearing;
+ UINT32 advanceHeight;
+ INT32 bottomSideBearing;
+ INT32 verticalOriginY;
+} DWRITE_GLYPH_METRICS;
+
+typedef interface IDWriteRenderingParams IDWriteRenderingParams;
+typedef interface IDWriteFont IDWriteFont;
+typedef interface IDWriteGdiInterop IDWriteGdiInterop;
+typedef interface IDWriteFactory IDWriteFactory;
+typedef interface IDWriteFactory2 IDWriteFactory2;
+typedef interface IDWriteFontFace IDWriteFontFace;
+typedef interface IDWriteBitmapRenderTarget IDWriteBitmapRenderTarget;
+typedef interface IDWriteBitmapRenderTarget1 IDWriteBitmapRenderTarget1;
+typedef interface IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator;
+
+DEFINE_GUID(IID_IDWriteBitmapRenderTarget1, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
+DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
+DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
+
+typedef struct DWRITE_GLYPH_OFFSET {
+ FLOAT advanceOffset;
+ FLOAT ascenderOffset;
+} DWRITE_GLYPH_OFFSET;
+
+typedef struct DWRITE_GLYPH_RUN {
+ IDWriteFontFace *fontFace;
+ FLOAT fontEmSize;
+ UINT32 glyphCount;
+ const UINT16 *glyphIndices;
+ const FLOAT *glyphAdvances;
+ const DWRITE_GLYPH_OFFSET *glyphOffsets;
+ WINBOOL isSideways;
+ UINT32 bidiLevel;
+} DWRITE_GLYPH_RUN;
+
+typedef struct _D3DCOLORVALUE {
+ float r;
+ float g;
+ float b;
+ float a;
+} D3DCOLORVALUE;
+
+typedef D3DCOLORVALUE DWRITE_COLOR_F;
+
+typedef struct DWRITE_COLOR_GLYPH_RUN {
+ DWRITE_GLYPH_RUN glyphRun;
+ void *glyphRunDescription;
+ FLOAT baselineOriginX;
+ FLOAT baselineOriginY;
+ DWRITE_COLOR_F runColor;
+ UINT16 paletteIndex;
+} DWRITE_COLOR_GLYPH_RUN;
+
+
+typedef struct IDWriteFontFaceVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFontFace *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFontFace *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFontFace *This);
+
+ UNUSED(GetType);
+ UNUSED(GetFiles);
+ UNUSED(GetIndex);
+ UNUSED(GetSimulations);
+ UNUSED(IsSymbolFont);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics);
+
+ UNUSED (GetGlyphCount);
+ UNUSED (GetDesignGlyphMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)
+ (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count,
+ UINT16 *glyph_indices);
+
+ UNUSED (TryGetFontTable);
+ UNUSED (ReleaseFontTable);
+ UNUSED (GetGlyphRunOutline);
+ UNUSED (GetRecommendedRenderingMode);
+ UNUSED (GetGdiCompatibleMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)
+ (IDWriteFontFace *This,
+ FLOAT emSize,
+ FLOAT pixels_per_dip,
+ void *transform,
+ WINBOOL use_gdi_natural,
+ const UINT16 *glyph_indices,
+ UINT32 glyph_count,
+ DWRITE_GLYPH_METRICS *metrics,
+ WINBOOL is_sideways);
+ END_INTERFACE
+} IDWriteFontFaceVtbl;
+
+interface IDWriteFontFace {
+ CONST_VTBL IDWriteFontFaceVtbl* lpVtbl;
+};
+
+typedef struct IDWriteRenderingParamsVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteRenderingParams *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteRenderingParams *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteRenderingParams *This);
+
+ FLOAT (STDMETHODCALLTYPE *GetGamma)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)
+ (IDWriteRenderingParams *This);
+ int (STDMETHODCALLTYPE *GetPixelGeometry)
+ (IDWriteRenderingParams *This);
+ END_INTERFACE
+} IDWriteRenderingParamsVtbl;
+
+interface IDWriteRenderingParams {
+ CONST_VTBL IDWriteRenderingParamsVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFontVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFont *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFont *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFont *This);
+
+ UNUSED (GetFontFamily);
+ UNUSED (GetWeight);
+ UNUSED (GetStretch);
+ UNUSED (GetStyle);
+ UNUSED (IsSymbolFont);
+ UNUSED (GetFaceNames);
+ UNUSED (GetInformationalStrings);
+ UNUSED (GetSimulations);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFont *This, DWRITE_FONT_METRICS *metrics);
+
+ UNUSED(HasCharacter);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFace)
+ (IDWriteFont *This, IDWriteFontFace **face);
+
+ END_INTERFACE
+} IDWriteFontVtbl;
+
+interface IDWriteFont {
+ CONST_VTBL IDWriteFontVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTargetVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteBitmapRenderTarget *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteBitmapRenderTarget *This);
+
+ HRESULT (STDMETHODCALLTYPE *DrawGlyphRun)
+ (IDWriteBitmapRenderTarget *This,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuring_mode,
+ const DWRITE_GLYPH_RUN *glyph_run,
+ IDWriteRenderingParams *params,
+ COLORREF textColor,
+ RECT *blackbox_rect);
+
+ HDC (STDMETHODCALLTYPE *GetMemoryDC)(IDWriteBitmapRenderTarget *This);
+
+ UNUSED (GetPixelsPerDip);
+
+ HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip)
+ (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip);
+
+ UNUSED (GetCurrentTransform);
+ UNUSED (SetCurrentTransform);
+ UNUSED (GetSize);
+ UNUSED (Resize);
+ END_INTERFACE
+} IDWriteBitmapRenderTargetVtbl;
+
+interface IDWriteBitmapRenderTarget {
+ CONST_VTBL IDWriteBitmapRenderTargetVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTarget1Vtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget1 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef) (IDWriteBitmapRenderTarget1 *This);
+ ULONG (STDMETHODCALLTYPE *Release) (IDWriteBitmapRenderTarget1 *This);
+
+ UNUSED (DrawGlyphRun);
+ UNUSED (GetMemoryDC);
+ UNUSED (GetPixelsPerDip);
+ UNUSED (SetPixelsPerDip);
+ UNUSED (GetCurrentTransform);
+ UNUSED (SetCurrentTransform);
+ UNUSED (GetSize);
+ UNUSED (Resize);
+ UNUSED (GetTextAntialiasMode);
+
+ HRESULT (STDMETHODCALLTYPE *SetTextAntialiasMode)
+ (IDWriteBitmapRenderTarget1 *This, DWRITE_TEXT_ANTIALIAS_MODE mode);
+
+ END_INTERFACE
+} IDWriteBitmapRenderTarget1Vtbl;
+
+interface IDWriteBitmapRenderTarget1 {
+ CONST_VTBL IDWriteBitmapRenderTarget1Vtbl* lpVtbl;
+};
+
+typedef struct IDWriteGdiInteropVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteGdiInterop *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteGdiInterop *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteGdiInterop *This);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)
+ (IDWriteGdiInterop *This, const LOGFONTW *logfont,
+ IDWriteFont **font);
+
+ UNUSED (ConvertFontToLOGFONT);
+ UNUSED (ConvertFontFaceToLOGFONT);
+ UNUSED (CreateFontFaceFromHdc);
+
+ HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)
+ (IDWriteGdiInterop *This, HDC hdc, UINT32 width, UINT32 height,
+ IDWriteBitmapRenderTarget **target);
+ END_INTERFACE
+} IDWriteGdiInteropVtbl;
+
+interface IDWriteGdiInterop {
+ CONST_VTBL IDWriteGdiInteropVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactoryVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory *This);
+
+ UNUSED (GetSystemFontCollection);
+ UNUSED (CreateCustomFontCollection);
+ UNUSED (RegisterFontCollectionLoader);
+ UNUSED (UnregisterFontCollectionLoader);
+ UNUSED (CreateFontFileReference);
+ UNUSED (CreateCustomFontFileReference);
+ UNUSED (CreateFontFace);
+ HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)
+ (IDWriteFactory *This, IDWriteRenderingParams **params);
+ UNUSED (CreateMonitorRenderingParams);
+ HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)
+ (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast,
+ FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry,
+ DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params);
+ UNUSED (RegisterFontFileLoader);
+ UNUSED (UnregisterFontFileLoader);
+ UNUSED (CreateTextFormat);
+ UNUSED (CreateTypography);
+ HRESULT (STDMETHODCALLTYPE *GetGdiInterop)
+ (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop);
+ UNUSED (CreateTextLayout);
+ UNUSED (CreateGdiCompatibleTextLayout);
+ UNUSED (CreateEllipsisTrimmingSign);
+ UNUSED (CreateTextAnalyzer);
+ UNUSED (CreateNumberSubstitution);
+ UNUSED (CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactoryVtbl;
+
+interface IDWriteFactory { CONST_VTBL IDWriteFactoryVtbl* lpVtbl; };
+
+typedef struct IDWriteColorGlyphRunEnumeratorVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteColorGlyphRunEnumerator *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteColorGlyphRunEnumerator *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteColorGlyphRunEnumerator *This);
+
+ HRESULT (STDMETHODCALLTYPE *MoveNext)(
+ IDWriteColorGlyphRunEnumerator *This,
+ WINBOOL *hasRun);
+
+ HRESULT (STDMETHODCALLTYPE *GetCurrentRun)(
+ IDWriteColorGlyphRunEnumerator *This,
+ const DWRITE_COLOR_GLYPH_RUN **run);
+
+ END_INTERFACE
+} IDWriteColorGlyphRunEnumeratorVtbl;
+
+interface IDWriteColorGlyphRunEnumerator {
+ CONST_VTBL IDWriteColorGlyphRunEnumeratorVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactory2Vtbl {
+ BEGIN_INTERFACE
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory2 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory2 *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory2 *This);
+ UNUSED (GetSystemFontCollection);
+ UNUSED (CreateCustomFontCollection);
+ UNUSED (RegisterFontCollectionLoader);
+ UNUSED (UnregisterFontCollectionLoader);
+ UNUSED (CreateFontFileReference);
+ UNUSED (CreateCustomFontFileReference);
+ UNUSED (CreateFontFace);
+ UNUSED (CreateRenderingParams);
+ UNUSED (CreateMonitorRenderingParams);
+ UNUSED (CreateCustomRenderingParams);
+ UNUSED (RegisterFontFileLoader);
+ UNUSED (UnregisterFontFileLoader);
+ UNUSED (CreateTextFormat);
+ UNUSED (CreateTypography);
+ UNUSED (GetGdiInterop);
+ UNUSED (CreateTextLayout);
+ UNUSED (CreateGdiCompatibleTextLayout);
+ UNUSED (CreateEllipsisTrimmingSign);
+ UNUSED (CreateTextAnalyzer);
+ UNUSED (CreateNumberSubstitution);
+ UNUSED (CreateGlyphRunAnalysis);
+
+ UNUSED (GetEudcFontCollection);
+ UNUSED (IDWriteFactory1_CreateCustomRenderingParams);
+
+ UNUSED (GetSystemFontFallback);
+ UNUSED (CreateFontFallbackBuilder);
+ HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)
+ (IDWriteFactory2 *This,
+ FLOAT originX,
+ FLOAT originY,
+ const DWRITE_GLYPH_RUN *run,
+ void *rundescr,
+ DWRITE_MEASURING_MODE mode,
+ void *transform,
+ UINT32 palette_index,
+ IDWriteColorGlyphRunEnumerator **colorlayers);
+
+ UNUSED (IDWriteFactory2_CreateCustomRenderingParams);
+ UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactory2Vtbl;
+
+interface IDWriteFactory2 {
+ CONST_VTBL IDWriteFactory2Vtbl* lpVtbl;
+};
+
+/* End of dwrite_3.h definitions. */
+
+/* Values to use for DirectWrite rendering. */
+#define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL
+#define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+#define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE
+
+#define RELEASE_COM(i) (i)->lpVtbl->Release (i)
+
+/* Global variables for DirectWrite. */
+static bool direct_write_available = false;
+static IDWriteFactory *dwrite_factory;
+static IDWriteFactory2 *dwrite_factory2;
+static IDWriteGdiInterop *gdi_interop;
+static IDWriteRenderingParams *rendering_params;
+
+/* Function prototypes. */
+void w32_initialize_direct_write (void);
+bool w32_use_direct_write (struct w32font_info *w32font);
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+void w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face(void *cache);
+
+/* This two functions are on w32uninscribe.c */
+void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
+void w32_font_set_dwrite_cache (struct font *font, void *cache,
+ float font_size);
+
+static void
+verify_hr (HRESULT hr, const char *msg)
+{
+ if (FAILED (hr))
+ emacs_abort ();
+}
+
+/* Gets a IDWriteFontFace from a struct font (its HFONT). Returns the
+ font size in points. It may fail to get a DirectWrite font, and face
+ will be NULL on return. This happens for some fonts like Courier.
+
+ Never call Release on the result, as it is cached for reuse on the
+ struct font. */
+static float
+get_font_face (struct font *infont, IDWriteFontFace **face)
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+ IDWriteFont *font;
+ float font_size;
+
+ /* Check the cache. */
+ *face = w32_font_get_dwrite_cache (infont, &font_size);
+ if (*face)
+ return font_size;
+
+ GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont);
+
+ hr = gdi_interop->lpVtbl->
+ CreateFontFromLOGFONT (gdi_interop, (const LOGFONTW *) &logfont, &font);
+
+ if (FAILED (hr))
+ {
+ *face = NULL;
+ return 0.0;
+ }
+
+ hr = font->lpVtbl->CreateFontFace (font, face);
+ RELEASE_COM (font);
+ verify_hr (hr, "Failed to create DWriteFontFace");
+ w32_font_set_dwrite_cache (infont, *face, abs(logfont.lfHeight));
+
+ return abs(logfont.lfHeight);
+}
+
+void
+w32_dwrite_free_cached_face(void *cache)
+{
+ if (cache)
+ RELEASE_COM ((IDWriteFontFace *) cache);
+}
+
+static float
+convert_metrics_sz( int sz, float font_size, int units_per_em )
+{
+ return (float)sz * font_size / units_per_em;
+}
+
+
+/* Does not fill in the ascent and descent fields of metrics. */
+static void
+text_extents_internal (IDWriteFontFace *dwrite_font_face,
+ float font_size, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ HRESULT hr;
+
+ DWRITE_FONT_METRICS dwrite_font_metrics;
+ dwrite_font_face->lpVtbl->
+ GetMetrics (dwrite_font_face, &dwrite_font_metrics);
+
+ UINT16 *indices = alloca (nglyphs * sizeof (UINT16));
+ for (int i = 0; i < nglyphs; i++)
+ indices[i] = code[i];
+
+ DWRITE_GLYPH_METRICS* gmetrics =
+ alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+ hr = dwrite_font_face->lpVtbl->
+ GetGdiCompatibleGlyphMetrics (dwrite_font_face,
+ font_size,
+ 1.0,
+ NULL,
+ TRUE,
+ indices,
+ nglyphs,
+ gmetrics,
+ false);
+ verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics");
+
+ float width = 0;
+ int du_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+ for (int i = 0; i < nglyphs; i++)
+ {
+ float advance =
+ convert_metrics_sz (gmetrics[i].advanceWidth, font_size, du_per_em);
+
+ width += advance;
+
+ float lbearing =
+ round (convert_metrics_sz (gmetrics[i].leftSideBearing, font_size,
+ du_per_em));
+ float rbearing =
+ round (advance -
+ convert_metrics_sz (gmetrics[i].rightSideBearing,
+ font_size, du_per_em));
+ if (i == 0)
+ {
+ metrics->lbearing = lbearing;
+ metrics->rbearing = rbearing;
+ }
+ if (metrics->lbearing > lbearing)
+ metrics->lbearing = lbearing;
+ if (metrics->rbearing < rbearing)
+ metrics->rbearing = rbearing;
+ }
+ metrics->width = round(width);
+}
+
+unsigned
+w32_dwrite_encode_char (struct font *font, int c)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+ UINT16 index;
+
+ get_font_face (font, &dwrite_font_face);
+ hr = dwrite_font_face->lpVtbl->
+ GetGlyphIndices (dwrite_font_face, &c, 1, &index);
+ verify_hr (hr, "Failed to GetGlyphIndices");
+
+ if (index == 0)
+ return FONT_INVALID_CODE;
+
+ return index;
+}
+
+void
+w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs,
+ struct font_metrics *metrics)
+{
+ IDWriteFontFace *dwrite_font_face;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+
+ if (dwrite_font_face == NULL)
+ {
+ metrics->width = 0;
+ metrics->lbearing = 0;
+ metrics->rbearing = 0;
+ return;
+ }
+
+ text_extents_internal (dwrite_font_face, font_size, code, nglyphs, metrics);
+
+ metrics->ascent = font->ascent;
+ metrics->descent = font->descent;
+}
+
+/* Never call Release on the value returned by this function, as it is
+ reused. */
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+ HRESULT hr;
+ static IDWriteBitmapRenderTarget *brt = NULL;
+ static SIZE size = {0, 0};
+
+ if (brt)
+ {
+ /* Check if we need to make a bigger one. */
+ if (width <= size.cx && height <= size.cy)
+ return brt;
+ RELEASE_COM (brt);
+ }
+
+ if (width > size.cx)
+ size.cx = width;
+ if (height > size.cy)
+ size.cy = height;
+
+ hr = gdi_interop->lpVtbl->
+ CreateBitmapRenderTarget (gdi_interop, hdc, size.cx, size.cy, &brt);
+ verify_hr (hr, "Failed to create DWriteBitmapRenderTarget");
+
+ /* We handle high dpi displays by incresing font size, so override
+ PixelsPerDip. */
+ brt->lpVtbl->SetPixelsPerDip (brt, 1.0);
+
+ /* The SetTextAntialiasMode method is only available in
+ IDWriteBitmapRenderTarget1. */
+ IDWriteBitmapRenderTarget1 *brt1;
+ hr = brt->lpVtbl->
+ QueryInterface (brt, &IID_IDWriteBitmapRenderTarget1, (void **) &brt1);
+ if (SUCCEEDED (hr))
+ {
+ brt1->lpVtbl->
+ SetTextAntialiasMode (brt1, ANTIALIAS_MODE);
+ RELEASE_COM (brt1);
+ }
+
+ return brt;
+}
+
+void
+w32_initialize_direct_write (void)
+{
+ direct_write_available = false;
+ HMODULE direct_write = LoadLibrary ("dwrite.dll");
+ if (!direct_write)
+ return;
+
+ /* This is only used here, no need to define it globally. */
+ typedef HRESULT (WINAPI *CreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u);
+
+ CreateFactory create_factory
+ = (CreateFactory) get_proc_addr (direct_write, "DWriteCreateFactory");
+
+ if (!create_factory)
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ HRESULT hr = create_factory (DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown **) &dwrite_factory);
+ if (FAILED (hr))
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->
+ QueryInterface (dwrite_factory, &IID_IDWriteFactory2,
+ (void **) &dwrite_factory2);
+
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->
+ GetGdiInterop (dwrite_factory, &gdi_interop);
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ IDWriteRenderingParams *def;
+
+ hr = dwrite_factory->lpVtbl->
+ CreateRenderingParams (dwrite_factory, &def);
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ /* range: [0.0, 1.0] */
+ float enhanced_contrast = def->lpVtbl->GetEnhancedContrast(def);
+ /* range: [0.0, 1.0] */
+ float clear_type_level = def->lpVtbl->GetClearTypeLevel(def);
+ /* range: (0.0, 256.0] */
+ float gamma = 1.4; /* def->lpVtbl->GetGamma(def); */
+
+ hr = dwrite_factory->lpVtbl->
+ CreateCustomRenderingParams (dwrite_factory,
+ gamma,
+ enhanced_contrast,
+ clear_type_level,
+ def->lpVtbl->GetPixelGeometry(def),
+ RENDERING_MODE,
+ &rendering_params);
+ RELEASE_COM (def);
+
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ direct_write_available = true;
+
+ DEFVAR_BOOL ("w32-inhibit-direct-write", w32_inhibit_direct_write,
+ doc: /* If t, don't use DirectWrite. */);
+ w32_inhibit_direct_write = false;
+}
+
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+
+ /* What we get as y is the baseline position. */
+ y -= font->ascent;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return;
+
+ struct font_metrics metrics;
+ text_extents_internal (dwrite_font_face, font_size, glyphs, len, &metrics);
+
+ int bitmap_width = metrics.width + metrics.rbearing;
+ int bitmap_height = font->ascent + font->descent;
+
+ /* We never Release this, get_bitmap_render_target reuses it. */
+ IDWriteBitmapRenderTarget *bitmap_render_target =
+ get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+ /* This DC can't be released. */
+ HDC text_dc = bitmap_render_target->lpVtbl->
+ GetMemoryDC (bitmap_render_target);
+
+ /* Copy the background pixel to the render target bitmap. */
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+
+ UINT16 *indices = alloca (len * sizeof (UINT16));
+
+ for (int i = 0; i < len; i++)
+ indices[i] = glyphs[i];
+
+ FLOAT *advances = alloca (len * sizeof (FLOAT));
+
+ for (int i = 0; i < len; i++)
+ {
+ text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1,
+ &metrics);
+ advances[i] = metrics.width;
+ }
+
+ DWRITE_GLYPH_RUN glyph_run;
+ glyph_run.fontFace = dwrite_font_face;
+ glyph_run.fontEmSize = font_size;
+ glyph_run.glyphIndices = indices;
+ glyph_run.glyphCount = len;
+ glyph_run.isSideways = false;
+ glyph_run.bidiLevel = 0;
+ glyph_run.glyphOffsets = NULL;
+ glyph_run.glyphAdvances = advances;
+
+ IDWriteColorGlyphRunEnumerator *layers;
+ /* This call will tell us if we hace to handle any color glyph. */
+ hr = dwrite_factory2->lpVtbl->
+ TranslateColorGlyphRun (dwrite_factory2,
+ 0, font->ascent,
+ &glyph_run,
+ NULL,
+ MEASURING_MODE,
+ NULL,
+ 0,
+ &layers);
+
+ /* No color. Just draw the GlyphRun. */
+ if (hr == DWRITE_E_NOCOLOR)
+ {
+ bitmap_render_target->lpVtbl->
+ DrawGlyphRun (bitmap_render_target,
+ 0, font->ascent,
+ MEASURING_MODE,
+ &glyph_run,
+ rendering_params,
+ color,
+ NULL);
+ }
+ else
+ {
+ /* If there were color glyphs, layers contains a containts a list
+ of GlyphRun with a color and a position for each. We draw them
+ individually. */
+ verify_hr (hr, "Failed at TranslateColorGlyphRun");
+ for (;;) {
+ HRESULT hr;
+ BOOL more_layers;
+ const DWRITE_COLOR_GLYPH_RUN *layer;
+
+ hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+ verify_hr (hr, "Failed at MoveNext");
+ if (!more_layers)
+ break;
+ hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+ verify_hr (hr, "Failed at GetCurrentRun");
+ bitmap_render_target->lpVtbl->
+ DrawGlyphRun (bitmap_render_target,
+ layer->baselineOriginX, layer->baselineOriginY,
+ MEASURING_MODE,
+ &layer->glyphRun,
+ rendering_params,
+ RGB (layer->runColor.r * 255,
+ layer->runColor.g * 255,
+ layer->runColor.b * 255),
+ NULL);
+ }
+ RELEASE_COM (layers);
+ }
+
+ /* Finally, copy the rendered text back to the original DC. */
+ BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+}
+
+/* Returns true if DirectWrite is to be used:
+ - Is is available.
+ - The font is handled by harfbuzz
+ - w32-inhibit-direct-write is false
+ */
+bool
+w32_use_direct_write (struct w32font_info *w32font)
+{
+#ifdef HAVE_HARFBUZZ
+ return direct_write_available
+ && w32font->font.driver == &harfbuzz_font_driver
+ && !w32_inhibit_direct_write;
+#else
+ return false;
+#endif
+}
diff --git a/src/w32font.c b/src/w32font.c
index efb42d80336..16aa9308584 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -55,6 +55,13 @@ #define VIETNAMESE_CHARSET 163
#define JOHAB_CHARSET 130
#endif
+/* Function prototypes from w32write.c */
+bool w32_use_direct_write (struct w32font_info *w32font);
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+void w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+
static void fill_in_logfont (struct frame *, LOGFONT *, Lisp_Object);
static BYTE w32_antialias_type (Lisp_Object);
@@ -452,6 +459,12 @@ w32font_text_extents (struct font *font, const unsigned *code,
memset (metrics, 0, sizeof (struct font_metrics));
+ if (w32_use_direct_write (w32_font))
+ {
+ w32_dwrite_text_extents (font, code, nglyphs, metrics);
+ return;
+ }
+
for (i = 0, first = true; i < nglyphs; i++)
{
struct w32_metric_cache *char_metric;
@@ -706,22 +719,36 @@ w32font_draw (struct glyph_string *s, int from, int to,
int i;
for (i = 0; i < len; i++)
- {
- WCHAR c = s->char2b[from + i] & 0xFFFF;
- ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
- }
+ if (w32_use_direct_write (w32font))
+ {
+ w32_dwrite_draw (s->hdc, x, y, s->char2b + from, 1,
+ GetTextColor(s->hdc), s->font);
+ }
+ else
+ {
+ WCHAR c = s->char2b[from + i] & 0xFFFF;
+ ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
+ }
}
else
{
- /* The number of glyphs in a glyph_string cannot be larger than
- the maximum value of the 'used' member of a glyph_row, so we
- are OK using alloca here. */
- eassert (len <= SHRT_MAX);
- WCHAR *chars = alloca (len * sizeof (WCHAR));
- int j;
- for (j = 0; j < len; j++)
- chars[j] = s->char2b[from + j] & 0xFFFF;
- ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ if (w32_use_direct_write (w32font))
+ {
+ w32_dwrite_draw ( s->hdc, x, y,
+ s->char2b + from, len, GetTextColor(s->hdc), s->font );
+ }
+ else
+ {
+ /* The number of glyphs in a glyph_string cannot be larger than
+ the maximum value of the 'used' member of a glyph_row, so we
+ are OK using alloca here. */
+ eassert (len <= SHRT_MAX);
+ WCHAR *chars = alloca (len * sizeof (WCHAR));
+ int j;
+ for (j = 0; j < len; j++)
+ chars[j] = s->char2b[from + j] & 0xFFFF;
+ ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ }
}
/* Restore clip region. */
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..af05d050936 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -44,6 +44,15 @@ #define _WIN32_WINNT 0x0600
#include "pdumper.h"
#include "w32common.h"
+/* Function prototypes from w32dwrite.c */
+bool w32_use_direct_write (struct w32font_info *w32font);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face (void *cache);
+
+/* Function prototypes from this file */
+void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
+void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size);
+
/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */
struct uniscribe_font_info
{
@@ -54,8 +63,25 @@ #define _WIN32_WINNT 0x0600
void *cache;
/* This is used by the HarfBuzz backend to store the font scale. */
double scale;
+ /* This is used by DirectWrite to store the FontFace object.
+ DirectWrite works on top of the HarfBuzz backend, modifying some
+ calls. */
+ void *dwrite_cache;
+ float dwrite_font_size;
};
+void *w32_font_get_dwrite_cache (struct font *font, float *font_size)
+{
+ *font_size = ((struct uniscribe_font_info *) font)->dwrite_font_size;
+ return ((struct uniscribe_font_info *) font)->dwrite_cache;
+}
+
+void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size)
+{
+ ((struct uniscribe_font_info *) font)->dwrite_font_size = font_size;
+ ((struct uniscribe_font_info *) font)->dwrite_cache = cache;
+}
+
int uniscribe_available = 0;
/* EnumFontFamiliesEx callback. */
@@ -200,6 +226,7 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
/* Initialize the cache for this font. */
uniscribe_font->cache = NULL;
+ uniscribe_font->dwrite_cache = NULL;
/* Uniscribe and HarfBuzz backends use glyph indices. */
uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
@@ -221,6 +248,7 @@ uniscribe_close (struct font *font)
= (struct uniscribe_font_info *) font;
#ifdef HAVE_HARFBUZZ
+ w32_dwrite_free_cached_face (uniscribe_font->dwrite_cache);
if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
&& uniscribe_font->cache)
hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
@@ -1372,6 +1400,12 @@ w32hb_encode_char (struct font *font, int c)
struct uniscribe_font_info *uniscribe_font
= (struct uniscribe_font_info *) font;
eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+ if (w32_use_direct_write(&uniscribe_font->w32_font))
+ {
+ return w32_dwrite_encode_char(font, c);
+ }
+
hb_font_t *hb_font = uniscribe_font->cache;
/* First time we use this font with HarfBuzz, create the hb_font_t
@@ -1531,6 +1565,8 @@ load_harfbuzz_funcs (HMODULE library)
}
#endif /* HAVE_HARFBUZZ */
+void w32_initialize_direct_write (void);
+
static void
syms_of_w32uniscribe_for_pdumper (void)
{
@@ -1624,5 +1660,8 @@ syms_of_w32uniscribe_for_pdumper (void)
harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
register_font_driver (&harfbuzz_font_driver, NULL);
+
+ w32_initialize_direct_write ();
+
#endif /* HAVE_HARFBUZZ */
}
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-15 22:18 ` Cecilio Pardo
@ 2024-10-16 11:01 ` Eli Zaretskii
2024-10-16 11:32 ` Eli Zaretskii
2024-10-16 21:35 ` Cecilio Pardo
0 siblings, 2 replies; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-16 11:01 UTC (permalink / raw)
To: Cecilio Pardo, Ken Brown; +Cc: 73730
> Date: Wed, 16 Oct 2024 00:18:20 +0200
> Cc: 73730@debbugs.gnu.org
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> The attached patch works without a new font driver.
Thanks a lot for working on this!
I'n CC'ing Ken, who maintains the Cygwin ports of Emacs, because I
think the cygw32 build would like to use this feature as well. Ken,
could you please give the patch some testing and report any problems?
> This requires the harfbuzz font backend to be available.
>
> It works by modifying the harfbuzz backend to use DirectWrite at
> some points, if it is available:
>
> - When encoding characters: w32hb_encode_char
> - When measuring text: w32font_text_extents
> - When drawing text: w32font_draw
>
> DirectWrite is setup by calling w32_initialize_direct_write. From
> that point, the function w32_use_direct_write will return true if
> DirectWrite is to be used.
>
> The lisp variable w32-inhibit-direct-write can switches it on and off.
This design SGTM, thanks. Eventually, we will need a NEWS entry for
this change, and w32-inhibit-direct-write (which I'd rename to
w32-inhibit-dwrite for better mnemonic value) should be mentioned
there.
> It builds with Mingw64 and Mingw. As mingw doesn't have the DirectWrite
> includes, I had to declare a lot of things at the start of the file.
> This line marks the end of that:
>
> /* End of dwrite_3.h definitions. */
I wonder if this is sufficiently future-proof. AFAIU, dwrite_3.h is a
generated file, so it could change in the future. So maybe we should
only define these on our own for mingw.org, and in MinGW64 builds use
the header file they provide? Maybe even on a separate header file?
This way, at least most of the users will be immune to future changes
in the header file. WDYT?
> * configure.ac: Add src/w32drite to W32_OBJ
^
Period missing there.
> * src/w32dwrite.c: This is a new file.
> (w32-initialize-direct-write): Initializes the DirectWrite library if
^^^^^^^^^^^
A nit: our style is to say "Initialize".
> (w32_use_direct_write): Allows to check if DirectWrite is available
^^^^^^^^^^^^^^^
And here, just "Check" will do.
> (w32_dwrite_encode_char): Replacement for harfbuzz's encode_char.
> (w32_dwrite_text_extents): Replacement for w32font text_extents.
> (w32_dwrite_draw): Replacement for w32font draw.
> (w32_dwrite_free_cached_face): Used on the font deletion process to
> also delete DirectWrite data.
> (verify_hr): Verify COM method results.
These are new functions, so please say so. Like this:
(w32_dwrite_encode_char): New function, implements encode_char for
DirectWrite.
> * src/w32font.c:
> (w32font_text_extents): If DirectWrite is enabled, call
This and similar entries should be formatted like so:
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled, ...
We only break the line between the file name and the function name if
the latter is too long to be placed on the same line.
> * src/w32uniscribe.c:
> (w32hb_encode_char): If DirectWrite is enabled, call
> w32_dwrite_encode_char.
Same here.
> --- /dev/null
> +++ b/src/w32dwrite.c
> @@ -0,0 +1,930 @@
> +/* Support for using DirectWrite on MS-Windows to draw text. This allows
> + for color fonts. ^^
Two spaces between sentences, per our conventions.
> +/*
> + This requires the harfbuzz font backend to be available.
^^^^^^^^
HarfBuzz, here and elsewhere (except in code).
> +#include <config.h>
> +#include <math.h>
> +#include <windows.h>
> +
> +#include "frame.h"
> +#include "w32font.h"
> +#include "w32common.h"
> +#include "w32term.h"
> +
> +#include "initguid.h"
> +#include <ole2.h>
> +#include <unknwn.h>
That should be <initguid.h>. And I think the last 3 includes should
be before our Emacs-specific header files.
> +/* The following definitions would be included from dwrite_3.h, but it
> + is not available when building with MinGW. Methods that we don't use
^^^^^
Please say "mingw.org's MinGW", to make this more clear.
> + are declared with the UNUSED macro, to avoid bringing in more types
> + that would need to be declared.
> +*/
> +
> +#define UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
Can we use something more specific than UNUSED here? Like W32_UNUSED
or maybe EMACS_DWRITE_UNUSED?
> +/* Function prototypes. */
> +void w32_initialize_direct_write (void);
> +bool w32_use_direct_write (struct w32font_info *w32font);
> +void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
> + COLORREF color, struct font *font );
> +void w32_dwrite_text_extents (struct font *font, const unsigned *code,
> + int nglyphs, struct font_metrics *metrics);
> +unsigned w32_dwrite_encode_char (struct font *font, int c);
> +void w32_dwrite_free_cached_face(void *cache);
> +
> +/* This two functions are on w32uninscribe.c */
> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
> +void w32_font_set_dwrite_cache (struct font *font, void *cache,
> + float font_size);
These prototypes should go to w32font.h. Then you won't need to
repeat them in other source files.
> +static void
> +verify_hr (HRESULT hr, const char *msg)
> +{
> + if (FAILED (hr))
> + emacs_abort ();
> +}
I think aborting is too harsh here. We could use eassert instead (in
which case it will abort, but only in a build with --enable-checking).
We could also augment that with DebPrint in important places, which
will show a message when a production build of Emacs is run under a
debugger. In a production build (i.e., when eassert compiles to
nothing), it is better to disable the use of DirectWrite when one of
these problems happen, if you detect that early enough, and fall back
to HarfBuzz instead. Is that reasonable here?
> + return abs(logfont.lfHeight);
^
SPC missing there.
> +void
> +w32_dwrite_free_cached_face(void *cache)
^
And here (and in a few places elsewhere in the patch).
> +static float
> +convert_metrics_sz( int sz, float font_size, int units_per_em )
> +{
> + return (float)sz * font_size / units_per_em;
^^
Here also.
> + dwrite_font_face->lpVtbl->
> + GetMetrics (dwrite_font_face, &dwrite_font_metrics);
It is better not to break at the dereference operator:
dwrite_font_face->lpVtbl->GetMetrics (dwrite_font_face,
&dwrite_font_metrics);
Same in other places in the patch.
> + CreateFactory create_factory
> + = (CreateFactory) get_proc_addr (direct_write, "DWriteCreateFactory");
It is better to call the procedure pointers by names that are closer
to their names in the library. In this case, I'd use
dw_create_factory instead of a more general create_factory.
> + /* We never Release this, get_bitmap_render_target reuses it. */
^^^^^^^
"release", in lower case.
> + DWRITE_GLYPH_RUN glyph_run;
> + glyph_run.fontFace = dwrite_font_face;
> + glyph_run.fontEmSize = font_size;
> + glyph_run.glyphIndices = indices;
> + glyph_run.glyphCount = len;
> + glyph_run.isSideways = false;
> + glyph_run.bidiLevel = 0;
^^^^^^^^^^^^^
Is this because otherwise DirectWrite will possibly reorder the
glyphs or something? Did you test this with R2L text, e.g. by
displaying TUTORIAL.he? I did try, and it looks OK, but are there any
color fonts that cover the Hebrew block, so I could be sure everything
works in that case?
More generally, what color fonts can be used with this build on
Windows for characters other than Emoji, in addition to Segoe UI
Emoji?
> + /* No color. Just draw the GlyphRun. */
^^
Two spaces.
> + /* If there were color glyphs, layers contains a containts a list
^^^^^^^^^^ ^^^^^^^^^^^
One of these is redundant.
> --- a/src/w32font.c
> +++ b/src/w32font.c
> @@ -55,6 +55,13 @@ #define VIETNAMESE_CHARSET 163
> #define JOHAB_CHARSET 130
> #endif
>
> +/* Function prototypes from w32write.c */
^^^^^^^^^^
w32dwrite.c
> +bool w32_use_direct_write (struct w32font_info *w32font);
> +void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
> + COLORREF color, struct font *font );
> +void w32_dwrite_text_extents (struct font *font, const unsigned *code,
> + int nglyphs, struct font_metrics *metrics);
> +
If you put them on w32font.h, this won't be needed.
> --- a/src/w32uniscribe.c
> +++ b/src/w32uniscribe.c
> @@ -44,6 +44,15 @@ #define _WIN32_WINNT 0x0600
> #include "pdumper.h"
> #include "w32common.h"
>
> +/* Function prototypes from w32dwrite.c */
> +bool w32_use_direct_write (struct w32font_info *w32font);
> +unsigned w32_dwrite_encode_char (struct font *font, int c);
> +void w32_dwrite_free_cached_face (void *cache);
Likewise: if these are in w32font.h, you won't need this.
> +/* Function prototypes from this file */
> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
> +void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size);
Why aren't these functions defined in w32dwrite.c?
> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size)
We break the line between the return type and the function name:
void *
w32_font_get_dwrite_cache (struct font *font, float *font_size)
> + if (w32_use_direct_write(&uniscribe_font->w32_font))
> + {
> + return w32_dwrite_encode_char(font, c);
> + }
No need for braces if you have just one statement.
Compiling this with mingw.org's MinGW GCC 9.2.0, I get the following
warnings:
In file included from w32dwrite.c:44:
w32dwrite.c:123:13: warning: 'IID_IDWriteBitmapRenderTarget1' initialized and declared 'extern'
123 | DEFINE_GUID( CC w32inevt.oIID_IDWriteBitmapRenderTarget1
, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
w32dwrite.c:124:13: warning: 'IID_IDWriteFactory2' initialized and declared 'extern'
124 | DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
| ^~~~~~~~~~~~~~~~~~~
w32dwrite.c:125:13: warning: 'IID_IDWriteFactory' initialized and declared 'extern'
125 | DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
| ^~~~~~~~~~~~~~~~~~
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-16 11:01 ` Eli Zaretskii
@ 2024-10-16 11:32 ` Eli Zaretskii
2024-10-16 21:35 ` Cecilio Pardo
1 sibling, 0 replies; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-16 11:32 UTC (permalink / raw)
To: cpardo; +Cc: 73730, kbrown
> Cc: 73730@debbugs.gnu.org
> Date: Wed, 16 Oct 2024 14:01:36 +0300
> From: Eli Zaretskii <eliz@gnu.org>
>
> > Date: Wed, 16 Oct 2024 00:18:20 +0200
> > Cc: 73730@debbugs.gnu.org
> > From: Cecilio Pardo <cpardo@imayhem.com>
> >
> > The attached patch works without a new font driver.
>
> Thanks a lot for working on this!
And here's the first bug report after applying this patch:
emacs -Q
M-: (w32-find-non-USB-fonts) RET
This pops up the Emacs Abort dialog. The backtrace and some
additional information are below. The error from
GetGdiCompatibleGlyphMetrics (0x80070057) seems to be E_INVALIDARG.
Perhaps if w32_dwrite_text_extents fails, it should not abort, but
instead fall back on the old code?
If you cannot reproduce this (maybe it depends on what fonts are
installed), feel free to ask me to provide more information or try
some solutions. In case it matters, this is a 32-bit build configured
as --with-wide-int --without-native-compilation.
Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
0x76611813 in KERNELBASE!DebugBreak () from C:\WINDOWS\SysWOW64\KernelBase.dll
(gdb) bt
#0 0x76611813 in KERNELBASE!DebugBreak ()
from C:\WINDOWS\SysWOW64\KernelBase.dll
#1 0x012f47e1 in emacs_abort () at w32fns.c:11570
#2 0x013221ba in verify_hr (hr=-2147024809,
msg=0x18cbe58 <IID_IDWriteFactory+48> "Failed to GetGdiCompatibleGlyphMetrics") at w32dwrite.c:504
#3 0x01322430 in text_extents_internal (dwrite_font_face=0x9600720,
font_size=0, code=0xbfc6c0, nglyphs=1, metrics=0xbfc6b6) at w32dwrite.c:588
#4 0x013226d4 in w32_dwrite_text_extents (font=0xbf3e9f0, code=0xbfc6c0,
nglyphs=1, metrics=0xbfc6b6) at w32dwrite.c:654
#5 0x012fb29a in w32font_text_extents (font=0xbf3e9f0, code=0xbfc6c0,
nglyphs=1, metrics=0xbfc6b6) at w32font.c:464
#6 0x01229249 in Ffont_get_glyphs (font_object=XIL(0xa00000000bf3e9f0),
from=make_fixnum(0), to=make_fixnum(3), object=XIL(0xa00000000bc5cd60))
at font.c:5454
#7 0x011f1176 in funcall_subr (subr=0x187cb40 <Sfont_get_glyphs>, numargs=4,
args=0xa2eb2a0) at eval.c:3144
#8 0x0125eeb2 in exec_byte_code (fun=XIL(0xa00000000bd9e450),
args_template=257, nargs=1, args=0xbfcf70) at bytecode.c:813
#9 0x011f198b in funcall_lambda (fun=XIL(0xa00000000bd9e450), nargs=1,
arg_vector=0xbfcf68) at eval.c:3229
#10 0x011f0a51 in funcall_general (fun=XIL(0xa00000000bd9e450), numargs=1,
args=0xbfcf68) at eval.c:3021
#11 0x011f0d7a in Ffuncall (nargs=2, args=0xbfcf60) at eval.c:3070
#12 0x01206ebc in mapcar1 (leni=76, vals=0x0, fn=XIL(0xa00000000bd9e450),
seq=XIL(0xc000000000f3ec30)) at fns.c:3370
#13 0x01207af8 in Fmapc (function=XIL(0xa00000000bd9e450),
sequence=XIL(0xc000000000f3ec30)) at fns.c:3507
#14 0x011f10c7 in funcall_subr (subr=0x187bb40 <Smapc>, numargs=2,
args=0xa2eb1f0) at eval.c:3140
#15 0x0125eeb2 in exec_byte_code (fun=XIL(0xa000000009d8ab78),
args_template=0, nargs=0, args=0xa2eb200) at bytecode.c:813
#16 0x011f198b in funcall_lambda (fun=XIL(0xa000000009d8aa58), nargs=0,
arg_vector=0xbfd6d0) at eval.c:3229
#17 0x011f17a6 in apply_lambda (fun=XIL(0xa000000009d8aa58), args=XIL(0),
count=480) at eval.c:3192
#18 0x011ef553 in eval_sub (form=XIL(0xc000000000efa680)) at eval.c:2622
#19 0x011ee62c in Feval (form=XIL(0xc000000000efa680), lexical=XIL(0x30))
at eval.c:2439
#20 0x011f10c7 in funcall_subr (subr=0x187a1c0 <Seval>, numargs=2,
args=0xa2eb1a0) at eval.c:3140
#21 0x0125eeb2 in exec_byte_code (fun=XIL(0xa0000000098cdce8),
args_template=513, nargs=2, args=0xa2eb2f8) at bytecode.c:813
#22 0x011f198b in funcall_lambda (fun=XIL(0xa00000000bd346a0), nargs=0,
arg_vector=0xbfe040) at eval.c:3229
#23 0x011f0a51 in funcall_general (fun=XIL(0xa00000000bd346a0), numargs=0,
args=0xbfe040) at eval.c:3021
#24 0x011f0d7a in Ffuncall (nargs=1, args=0xbfe038) at eval.c:3070
#25 0x011e67ba in call0 (fn=XIL(0xa00000000bd346a0)) at lisp.h:3528
#26 0x011eb1e1 in Fhandler_bind_1 (nargs=3, args=0xa2eb128) at eval.c:1463
#27 0x011f1523 in funcall_subr (subr=0x187a080 <Shandler_bind_1>, numargs=3,
args=0xa2eb128) at eval.c:3161
#28 0x0125eeb2 in exec_byte_code (fun=XIL(0xa00000000997d720),
args_template=1025, nargs=4, args=0xbfe9e0) at bytecode.c:813
#29 0x011f198b in funcall_lambda (fun=XIL(0xa00000000997d720), nargs=4,
arg_vector=0xbfe9c0) at eval.c:3229
#30 0x011f0a51 in funcall_general (fun=XIL(0xa00000000997d720), numargs=4,
args=0xbfe9c0) at eval.c:3021
#31 0x011f0d7a in Ffuncall (nargs=5, args=0xbfe9b8) at eval.c:3070
#32 0x011e1c1a in Ffuncall_interactively (nargs=5, args=0xbfe9b8)
at callint.c:250
#33 0x011f1523 in funcall_subr (subr=0x18798c0 <Sfuncall_interactively>,
numargs=5, args=0xbfe9b8) at eval.c:3161
#34 0x011f09ea in funcall_general (fun=XIL(0xa0000000018798c0), numargs=5,
args=0xbfe9b8) at eval.c:3017
#35 0x011f0d7a in Ffuncall (nargs=6, args=0xbfe9b0) at eval.c:3070
#36 0x011efedd in Fapply (nargs=3, args=0xbfebd8) at eval.c:2742
#37 0x011e226a in Fcall_interactively (function=XIL(0x7fcb530),
record_flag=XIL(0), keys=XIL(0xa000000009ef2300)) at callint.c:342
#38 0x011f110f in funcall_subr (subr=0x1879900 <Scall_interactively>,
numargs=3, args=0xa2eb078) at eval.c:3142
#39 0x0125eeb2 in exec_byte_code (fun=XIL(0xa000000009f0ad40),
args_template=1025, nargs=1, args=0xbff7a0) at bytecode.c:813
#40 0x011f198b in funcall_lambda (fun=XIL(0xa000000009f0ad40), nargs=1,
arg_vector=0xbff798) at eval.c:3229
#41 0x011f0a51 in funcall_general (fun=XIL(0xa000000009f0ad40), numargs=1,
args=0xbff798) at eval.c:3021
#42 0x011f0d7a in Ffuncall (nargs=2, args=0xbff790) at eval.c:3070
#43 0x010f681d in command_loop_1 () at keyboard.c:1545
#44 0x011eba71 in internal_condition_case (bfun=0x10f5c2d <command_loop_1>,
handlers=XIL(0x90), hfun=0x10f4c86 <cmd_error>) at eval.c:1598
#45 0x010f5692 in command_loop_2 (handlers=XIL(0x90)) at keyboard.c:1163
#46 0x011eabee in internal_catch (tag=XIL(0x12750),
func=0x10f565b <command_loop_2>, arg=XIL(0x90)) at eval.c:1277
#47 0x010f55fd in command_loop () at keyboard.c:1141
#48 0x010f46e6 in recursive_edit_1 () at keyboard.c:749
#49 0x010f4984 in Frecursive_edit () at keyboard.c:832
#50 0x010efaa5 in main (argc=2, argv=0x7c72570) at emacs.c:2624
Lisp Backtrace:
"font-get-glyphs" (0xa2eb2a0)
0xbd9e450 PVEC_CLOSURE
"mapc" (0xa2eb1f0)
"w32-find-non-USB-fonts" (0xbfd6d0)
"eval" (0xa2eb1a0)
0xbd34678 PVEC_CLOSURE
0xbd346a0 PVEC_CLOSURE
"handler-bind-1" (0xa2eb128)
"eval-expression" (0xbfe9c0)
"funcall-interactively" (0xbfe9b8)
"call-interactively" (0xa2eb078)
"command-execute" (0xbff798)
(gdb) fr 6
#6 0x01229249 in Ffont_get_glyphs (font_object=XIL(0xa00000000bf3e9f0),
from=make_fixnum(0), to=make_fixnum(3), object=XIL(0xa00000000bc5cd60))
at font.c:5454
5454 font->driver->text_extents (font, &code, 1, &metrics);
(gdb) pp font_object
#<font-object "-outline-Sans Serif Collection-regular-normal-normal-sans-*-*-*-*-p-*-iso10646-1">
(gdb) fr 3
#3 0x01322430 in text_extents_internal (dwrite_font_face=0x9600720,
font_size=0, code=0xbfc6c0, nglyphs=1, metrics=0xbfc6b6) at w32dwrite.c:588
588 verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics");
(gdb) p/x hr
$1 = 0x80070057
(gdb) fr 5
#5 0x012fb29a in w32font_text_extents (font=0xbf3e9f0, code=0xbfc6c0,
nglyphs=1, metrics=0xbfc6b6) at w32font.c:464
464 w32_dwrite_text_extents (font, code, nglyphs, metrics);
(gdb) p *w32_font
$2 = {
font = {
header = {
size = 1661034513
},
props = {XIL(0x9b40), XIL(0xe910), XIL(0xa5622a0), XIL(0x103b0),
XIL(0xb010), make_fixnum(20544), make_fixnum(25632), make_fixnum(25664),
make_fixnum(0), XIL(0), make_fixnum(0), XIL(0), XIL(0xc00000000be75600),
XIL(0), XIL(0x800000000bce6cf0), XIL(0x800000000bce6ce0), XIL(0)},
min_width = 11,
max_width = 76,
pixel_size = 0,
height = 39,
space_width = 11,
average_width = 11,
ascent = 26,
descent = 13,
underline_thickness = 1,
underline_position = 2,
vertical_centering = false,
baseline_offset = 0,
relative_compose = 0,
default_ascent = 26,
encoding_charset = -1,
repertory_charset = -1,
driver = 0x19c78a0 <harfbuzz_font_driver>
},
metrics = {
tmHeight = 39,
tmAscent = 26,
tmDescent = 13,
tmInternalLeading = 23,
tmExternalLeading = 0,
tmAveCharWidth = 11,
tmMaxCharWidth = 76,
tmWeight = 400,
tmOverhang = 0,
tmDigitizedAspectX = 96,
tmDigitizedAspectY = 96,
tmFirstChar = 0 L'\000',
tmLastChar = 65071 L'\xfe2f',
tmDefaultChar = 1 L'\001',
tmBreakChar = 2 L'\002',
tmItalic = 0 '\000',
tmUnderlined = 0 '\000',
tmStruckOut = 0 '\000',
tmPitchAndFamily = 39 '\'',
tmCharSet = 0 '\000'
},
glyph_idx = 16,
cached_metrics = 0x0,
n_cache_blocks = 0,
hfont = 0x2d0a1e66
}
(gdb)
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-16 11:01 ` Eli Zaretskii
2024-10-16 11:32 ` Eli Zaretskii
@ 2024-10-16 21:35 ` Cecilio Pardo
2024-10-17 6:21 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-16 21:35 UTC (permalink / raw)
To: Eli Zaretskii, Ken Brown; +Cc: 73730
[-- Attachment #1: Type: text/plain, Size: 1866 bytes --]
Still not done with this, but I am sending a new patch to fix the bug
with w32-find-non-USB-fonts. It calls text_extents with a font of size
0. Checking for that seems to solve the problem.
Also, most of the style changes are done.
- With MingGW64, use dwrite_3.h normally.
- renamed w32-inhibit-direct-write to w32-inhibit-dwrite, and some other
things.
BTW, why does that variable show as undocumented with
describe-variables? What am I doing wrong?
Pending:
- NEWS entry.
- Instead of aborting on failure, just disable DirectWrite and let emacs
continue. This will take some work.
- mingw warnings.
>
>> + glyph_run.bidiLevel = 0;
> ^^^^^^^^^^^^^
> Is this because otherwise DirectWrite will possibly reorder the
> glyphs or something? Did you test this with R2L text, e.g. by
> displaying TUTORIAL.he? I did try, and it looks OK, but are there any
> color fonts that cover the Hebrew block, so I could be sure everything
> works in that case?
>
> More generally, what color fonts can be used with this build on
> Windows for characters other than Emoji, in addition to Segoe UI
> Emoji?
The documentation describes this field as the "implicit resolved level
of the glyph run". Odd levels indicate RTL, and the function asks for
the right side position as origin, so I think they would be reordered.
As there is no reference to bidi in w32font.c, I assumed glyphs reach
this layer prepared to be rendered ltr.
I think the only color font that Windows has is the emoji one. I'll try
to find some other to test this.
>> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
>> +void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size);
>
> Why aren't these functions defined in w32dwrite.c?
They need to know about the uniscribe_font struct, which is defined there.
[-- Attachment #2: 0001-Implement-drawing-text-with-DirectWrite-on-MS-Window.patch --]
[-- Type: text/plain, Size: 37434 bytes --]
From f595bed3e984b6574e59467b2cd83efe351fbc5c Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Implement drawing text with DirectWrite on MS-Windows.
This adds support for color fonts.
* configure.ac: Add src/w32drite to W32_OBJ.
* src/w32dwrite.c: This is a new file.
(w32-initialize-direct-write): New function, initialize the DirectWrite
library if it is available, and required global variables.
(w32_use_direct_write): New function, check if DirectWrite is available
and activated by the user.
(w32_dwrite_encode_char): New function, replacement for harfbuzz's
encode_char.
(w32_dwrite_text_extents): New function, replacement for w32font
text_extents.
(w32_dwrite_draw): New function, replacement for w32font draw.
(w32_dwrite_free_cached_face): New function, used on the font deletion
process to also delete DirectWrite data.
(verify_hr): New function, verify COM method results.
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled, call
w32_dwrite_text_extents.
(w32font_draw): If DirectWrite is enabled, call w32_dwrite_draw.
* src/w32uniscribe.c: (w32hb_encode_char): If DirectWrite is enabled,
call w32_dwrite_encode_char.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.
---
configure.ac | 2 +-
src/w32dwrite.c | 928 +++++++++++++++++++++++++++++++++++++++++++++
src/w32font.c | 53 ++-
src/w32font.h | 13 +
src/w32uniscribe.c | 30 ++
5 files changed, 1012 insertions(+), 14 deletions(-)
create mode 100644 src/w32dwrite.c
diff --git a/configure.ac b/configure.ac
index 8a5ba7db3d1..ba9cd8f0cd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
AC_CHECK_TOOL([WINDRES], [windres],
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
- W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+ W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..1f8102de767
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,928 @@
+/* Support for using DirectWrite on MS-Windows to draw text. This
+ allows for color fonts.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/*
+ This requires the HarfBuzz font backend to be available.
+
+ It works by modifying the HarfBuzz backend to use DirectWrite at
+ some points, if it is available:
+
+ - When encoding characters: w32hb_encode_char
+ - When measuring text: w32font_text_extents
+ - When drawing text: w32font_draw
+
+ DirectWrite is setup by calling w32_initialize_direct_write. From
+ that point, the function w32_use_direct_write will return true if
+ DirectWrite is to be used.
+
+ DirectWrite is available since Windows 7, but we don't activate it on
+ versions before 8.1, because color fonts are only available since that. */
+
+
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#include <initguid.h>
+#include <ole2.h>
+#include <unknwn.h>
+
+#include "frame.h"
+#include "w32font.h"
+#include "w32common.h"
+#include "w32term.h"
+
+#ifndef MINGW_W64
+
+/* The following definitions would be included from dwrite_3.h, but it
+ is not available when building with mingw.org's MinGW. Methods that
+ we don't use are declared with the EMACS_DWRITE_UNUSED macro, to
+ avoid bringing in more types that would need to be declared.
+*/
+
+#define EMACS_DWRITE_UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
+
+#define DWRITE_E_NOCOLOR _HRESULT_TYPEDEF_(0x8898500CL)
+
+typedef enum DWRITE_PIXEL_GEOMETRY {
+ DWRITE_PIXEL_GEOMETRY_FLAT = 0,
+ DWRITE_PIXEL_GEOMETRY_RGB = 1,
+ DWRITE_PIXEL_GEOMETRY_BGR = 2
+} DWRITE_PIXEL_GEOMETRY;
+
+typedef enum DWRITE_RENDERING_MODE {
+ DWRITE_RENDERING_MODE_DEFAULT = 0,
+ DWRITE_RENDERING_MODE_ALIASED = 1,
+ DWRITE_RENDERING_MODE_GDI_CLASSIC = 2,
+ DWRITE_RENDERING_MODE_GDI_NATURAL = 3,
+ DWRITE_RENDERING_MODE_NATURAL = 4,
+ DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC = 5,
+ DWRITE_RENDERING_MODE_OUTLINE = 6
+} DWRITE_RENDERING_MODE;
+
+typedef enum DWRITE_MEASURING_MODE {
+ DWRITE_MEASURING_MODE_NATURAL = 0,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC = 1,
+ DWRITE_MEASURING_MODE_GDI_NATURAL = 2
+} DWRITE_MEASURING_MODE;
+
+typedef enum DWRITE_TEXT_ANTIALIAS_MODE {
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE = 0,
+ DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE = 1
+} DWRITE_TEXT_ANTIALIAS_MODE;
+
+typedef enum DWRITE_FACTORY_TYPE {
+ DWRITE_FACTORY_TYPE_SHARED = 0,
+ DWRITE_FACTORY_TYPE_ISOLATED = 1
+} DWRITE_FACTORY_TYPE;
+
+typedef struct DWRITE_FONT_METRICS {
+ UINT16 designUnitsPerEm;
+ UINT16 ascent;
+ UINT16 descent;
+ INT16 lineGap;
+ UINT16 capHeight;
+ UINT16 xHeight;
+ INT16 underlinePosition;
+ UINT16 underlineThickness;
+ INT16 strikethroughPosition;
+ UINT16 strikethroughThickness;
+} DWRITE_FONT_METRICS;
+
+typedef struct DWRITE_GLYPH_METRICS {
+ INT32 leftSideBearing;
+ UINT32 advanceWidth;
+ INT32 rightSideBearing;
+ INT32 topSideBearing;
+ UINT32 advanceHeight;
+ INT32 bottomSideBearing;
+ INT32 verticalOriginY;
+} DWRITE_GLYPH_METRICS;
+
+typedef interface IDWriteRenderingParams IDWriteRenderingParams;
+typedef interface IDWriteFont IDWriteFont;
+typedef interface IDWriteGdiInterop IDWriteGdiInterop;
+typedef interface IDWriteFactory IDWriteFactory;
+typedef interface IDWriteFactory2 IDWriteFactory2;
+typedef interface IDWriteFontFace IDWriteFontFace;
+typedef interface IDWriteBitmapRenderTarget IDWriteBitmapRenderTarget;
+typedef interface IDWriteBitmapRenderTarget1 IDWriteBitmapRenderTarget1;
+typedef interface IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator;
+
+DEFINE_GUID(IID_IDWriteBitmapRenderTarget1, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
+DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
+DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
+
+typedef struct DWRITE_GLYPH_OFFSET {
+ FLOAT advanceOffset;
+ FLOAT ascenderOffset;
+} DWRITE_GLYPH_OFFSET;
+
+typedef struct DWRITE_GLYPH_RUN {
+ IDWriteFontFace *fontFace;
+ FLOAT fontEmSize;
+ UINT32 glyphCount;
+ const UINT16 *glyphIndices;
+ const FLOAT *glyphAdvances;
+ const DWRITE_GLYPH_OFFSET *glyphOffsets;
+ WINBOOL isSideways;
+ UINT32 bidiLevel;
+} DWRITE_GLYPH_RUN;
+
+typedef struct _D3DCOLORVALUE {
+ float r;
+ float g;
+ float b;
+ float a;
+} D3DCOLORVALUE;
+
+typedef D3DCOLORVALUE DWRITE_COLOR_F;
+
+typedef struct DWRITE_COLOR_GLYPH_RUN {
+ DWRITE_GLYPH_RUN glyphRun;
+ void *glyphRunDescription;
+ FLOAT baselineOriginX;
+ FLOAT baselineOriginY;
+ DWRITE_COLOR_F runColor;
+ UINT16 paletteIndex;
+} DWRITE_COLOR_GLYPH_RUN;
+
+typedef struct IDWriteFontFaceVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFontFace *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFontFace *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFontFace *This);
+
+ EMACS_DWRITE_UNUSED(GetType);
+ EMACS_DWRITE_UNUSED(GetFiles);
+ EMACS_DWRITE_UNUSED(GetIndex);
+ EMACS_DWRITE_UNUSED(GetSimulations);
+ EMACS_DWRITE_UNUSED(IsSymbolFont);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED (GetGlyphCount);
+ EMACS_DWRITE_UNUSED (GetDesignGlyphMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)
+ (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count,
+ UINT16 *glyph_indices);
+
+ EMACS_DWRITE_UNUSED (TryGetFontTable);
+ EMACS_DWRITE_UNUSED (ReleaseFontTable);
+ EMACS_DWRITE_UNUSED (GetGlyphRunOutline);
+ EMACS_DWRITE_UNUSED (GetRecommendedRenderingMode);
+ EMACS_DWRITE_UNUSED (GetGdiCompatibleMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)
+ (IDWriteFontFace *This,
+ FLOAT emSize,
+ FLOAT pixels_per_dip,
+ void *transform,
+ WINBOOL use_gdi_natural,
+ const UINT16 *glyph_indices,
+ UINT32 glyph_count,
+ DWRITE_GLYPH_METRICS *metrics,
+ WINBOOL is_sideways);
+ END_INTERFACE
+} IDWriteFontFaceVtbl;
+
+interface IDWriteFontFace {
+ CONST_VTBL IDWriteFontFaceVtbl* lpVtbl;
+};
+
+typedef struct IDWriteRenderingParamsVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteRenderingParams *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteRenderingParams *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteRenderingParams *This);
+
+ FLOAT (STDMETHODCALLTYPE *GetGamma)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)
+ (IDWriteRenderingParams *This);
+ int (STDMETHODCALLTYPE *GetPixelGeometry)
+ (IDWriteRenderingParams *This);
+ END_INTERFACE
+} IDWriteRenderingParamsVtbl;
+
+interface IDWriteRenderingParams {
+ CONST_VTBL IDWriteRenderingParamsVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFontVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFont *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFont *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFont *This);
+
+ EMACS_DWRITE_UNUSED (GetFontFamily);
+ EMACS_DWRITE_UNUSED (GetWeight);
+ EMACS_DWRITE_UNUSED (GetStretch);
+ EMACS_DWRITE_UNUSED (GetStyle);
+ EMACS_DWRITE_UNUSED (IsSymbolFont);
+ EMACS_DWRITE_UNUSED (GetFaceNames);
+ EMACS_DWRITE_UNUSED (GetInformationalStrings);
+ EMACS_DWRITE_UNUSED (GetSimulations);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFont *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED(HasCharacter);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFace)
+ (IDWriteFont *This, IDWriteFontFace **face);
+
+ END_INTERFACE
+} IDWriteFontVtbl;
+
+interface IDWriteFont {
+ CONST_VTBL IDWriteFontVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTargetVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteBitmapRenderTarget *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteBitmapRenderTarget *This);
+
+ HRESULT (STDMETHODCALLTYPE *DrawGlyphRun)
+ (IDWriteBitmapRenderTarget *This,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuring_mode,
+ const DWRITE_GLYPH_RUN *glyph_run,
+ IDWriteRenderingParams *params,
+ COLORREF textColor,
+ RECT *blackbox_rect);
+
+ HDC (STDMETHODCALLTYPE *GetMemoryDC)(IDWriteBitmapRenderTarget *This);
+
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+
+ HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip)
+ (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip);
+
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ END_INTERFACE
+} IDWriteBitmapRenderTargetVtbl;
+
+interface IDWriteBitmapRenderTarget {
+ CONST_VTBL IDWriteBitmapRenderTargetVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTarget1Vtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget1 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef) (IDWriteBitmapRenderTarget1 *This);
+ ULONG (STDMETHODCALLTYPE *Release) (IDWriteBitmapRenderTarget1 *This);
+
+ EMACS_DWRITE_UNUSED (DrawGlyphRun);
+ EMACS_DWRITE_UNUSED (GetMemoryDC);
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (SetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ EMACS_DWRITE_UNUSED (GetTextAntialiasMode);
+
+ HRESULT (STDMETHODCALLTYPE *SetTextAntialiasMode)
+ (IDWriteBitmapRenderTarget1 *This, DWRITE_TEXT_ANTIALIAS_MODE mode);
+
+ END_INTERFACE
+} IDWriteBitmapRenderTarget1Vtbl;
+
+interface IDWriteBitmapRenderTarget1 {
+ CONST_VTBL IDWriteBitmapRenderTarget1Vtbl* lpVtbl;
+};
+
+typedef struct IDWriteGdiInteropVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteGdiInterop *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteGdiInterop *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteGdiInterop *This);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)
+ (IDWriteGdiInterop *This, const LOGFONTW *logfont,
+ IDWriteFont **font);
+
+ EMACS_DWRITE_UNUSED (ConvertFontToLOGFONT);
+ EMACS_DWRITE_UNUSED (ConvertFontFaceToLOGFONT);
+ EMACS_DWRITE_UNUSED (CreateFontFaceFromHdc);
+
+ HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)
+ (IDWriteGdiInterop *This, HDC hdc, UINT32 width, UINT32 height,
+ IDWriteBitmapRenderTarget **target);
+ END_INTERFACE
+} IDWriteGdiInteropVtbl;
+
+interface IDWriteGdiInterop {
+ CONST_VTBL IDWriteGdiInteropVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactoryVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory *This);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)
+ (IDWriteFactory *This, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)
+ (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast,
+ FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry,
+ DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ HRESULT (STDMETHODCALLTYPE *GetGdiInterop)
+ (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactoryVtbl;
+
+interface IDWriteFactory { CONST_VTBL IDWriteFactoryVtbl* lpVtbl; };
+
+typedef struct IDWriteColorGlyphRunEnumeratorVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteColorGlyphRunEnumerator *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteColorGlyphRunEnumerator *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteColorGlyphRunEnumerator *This);
+
+ HRESULT (STDMETHODCALLTYPE *MoveNext)(
+ IDWriteColorGlyphRunEnumerator *This,
+ WINBOOL *hasRun);
+
+ HRESULT (STDMETHODCALLTYPE *GetCurrentRun)(
+ IDWriteColorGlyphRunEnumerator *This,
+ const DWRITE_COLOR_GLYPH_RUN **run);
+
+ END_INTERFACE
+} IDWriteColorGlyphRunEnumeratorVtbl;
+
+interface IDWriteColorGlyphRunEnumerator {
+ CONST_VTBL IDWriteColorGlyphRunEnumeratorVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactory2Vtbl {
+ BEGIN_INTERFACE
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory2 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory2 *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory2 *This);
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ EMACS_DWRITE_UNUSED (CreateRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ EMACS_DWRITE_UNUSED (GetGdiInterop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+
+ EMACS_DWRITE_UNUSED (GetEudcFontCollection);
+ EMACS_DWRITE_UNUSED (IDWriteFactory1_CreateCustomRenderingParams);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontFallback);
+ EMACS_DWRITE_UNUSED (CreateFontFallbackBuilder);
+ HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)
+ (IDWriteFactory2 *This,
+ FLOAT originX,
+ FLOAT originY,
+ const DWRITE_GLYPH_RUN *run,
+ void *rundescr,
+ DWRITE_MEASURING_MODE mode,
+ void *transform,
+ UINT32 palette_index,
+ IDWriteColorGlyphRunEnumerator **colorlayers);
+
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactory2Vtbl;
+
+interface IDWriteFactory2 {
+ CONST_VTBL IDWriteFactory2Vtbl* lpVtbl;
+};
+#else /* ifndef MINGW_W64 */
+# include <dwrite_3.h>
+#endif
+
+/* Values to use for DirectWrite rendering. */
+#define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL
+#define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+#define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE
+
+#define RELEASE_COM(i) (i)->lpVtbl->Release (i)
+
+/* Global variables for DirectWrite. */
+static bool direct_write_available = false;
+static IDWriteFactory *dwrite_factory;
+static IDWriteFactory2 *dwrite_factory2;
+static IDWriteGdiInterop *gdi_interop;
+static IDWriteRenderingParams *rendering_params;
+
+static void
+verify_hr (HRESULT hr, const char *msg)
+{
+ if (FAILED (hr))
+ emacs_abort ();
+}
+
+/* Gets a IDWriteFontFace from a struct font (its HFONT). Returns the
+ font size in points. It may fail to get a DirectWrite font, and face
+ will be NULL on return. This happens for some fonts like Courier.
+
+ Never call Release on the result, as it is cached for reuse on the
+ struct font. */
+static float
+get_font_face (struct font *infont, IDWriteFontFace **face)
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+ IDWriteFont *font;
+ float font_size;
+
+ /* Check the cache. */
+ *face = w32_font_get_dwrite_cache (infont, &font_size);
+ if (*face)
+ return font_size;
+
+ GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont);
+
+ hr = gdi_interop->lpVtbl->CreateFontFromLOGFONT (gdi_interop,
+ (const LOGFONTW *) &logfont,
+ &font);
+
+ if (FAILED (hr))
+ {
+ *face = NULL;
+ return 0.0;
+ }
+
+ hr = font->lpVtbl->CreateFontFace (font, face);
+ RELEASE_COM (font);
+ verify_hr (hr, "Failed to create DWriteFontFace");
+ w32_font_set_dwrite_cache (infont, *face, abs (logfont.lfHeight));
+
+ return abs(logfont.lfHeight);
+}
+
+void
+w32_dwrite_free_cached_face (void *cache)
+{
+ if (cache)
+ RELEASE_COM ((IDWriteFontFace *) cache);
+}
+
+static float
+convert_metrics_sz (int sz, float font_size, int units_per_em)
+{
+ return (float) sz * font_size / units_per_em;
+}
+
+
+/* Does not fill in the ascent and descent fields of metrics. */
+static void
+text_extents_internal (IDWriteFontFace *dwrite_font_face,
+ float font_size, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ HRESULT hr;
+
+ if (font_size <= 0.0f)
+ {
+ metrics->width = 0;
+ metrics->lbearing = 0;
+ metrics->rbearing = 0;
+ return;
+ }
+
+ DWRITE_FONT_METRICS dwrite_font_metrics;
+ dwrite_font_face->lpVtbl->GetMetrics (dwrite_font_face,
+ &dwrite_font_metrics);
+
+ UINT16 *indices = alloca (nglyphs * sizeof (UINT16));
+ for (int i = 0; i < nglyphs; i++)
+ indices[i] = code[i];
+
+ DWRITE_GLYPH_METRICS* gmetrics =
+ alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+ hr = dwrite_font_face->lpVtbl->GetGdiCompatibleGlyphMetrics (dwrite_font_face,
+ font_size,
+ 1.0,
+ NULL,
+ TRUE,
+ indices,
+ nglyphs,
+ gmetrics,
+ false);
+ verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics");
+
+ float width = 0;
+ int du_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+ for (int i = 0; i < nglyphs; i++)
+ {
+ float advance =
+ convert_metrics_sz (gmetrics[i].advanceWidth, font_size, du_per_em);
+
+ width += advance;
+
+ float lbearing =
+ round (convert_metrics_sz (gmetrics[i].leftSideBearing, font_size,
+ du_per_em));
+ float rbearing =
+ round (advance -
+ convert_metrics_sz (gmetrics[i].rightSideBearing,
+ font_size, du_per_em));
+ if (i == 0)
+ {
+ metrics->lbearing = lbearing;
+ metrics->rbearing = rbearing;
+ }
+ if (metrics->lbearing > lbearing)
+ metrics->lbearing = lbearing;
+ if (metrics->rbearing < rbearing)
+ metrics->rbearing = rbearing;
+ }
+ metrics->width = round(width);
+}
+
+unsigned
+w32_dwrite_encode_char (struct font *font, int c)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+ UINT16 index;
+
+ get_font_face (font, &dwrite_font_face);
+ hr = dwrite_font_face->lpVtbl->GetGlyphIndices (dwrite_font_face,
+ &c, 1, &index);
+ verify_hr (hr, "Failed to GetGlyphIndices");
+
+ if (index == 0)
+ return FONT_INVALID_CODE;
+
+ return index;
+}
+
+void
+w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs,
+ struct font_metrics *metrics)
+{
+ IDWriteFontFace *dwrite_font_face;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+
+ if (dwrite_font_face == NULL)
+ {
+ metrics->width = 0;
+ metrics->lbearing = 0;
+ metrics->rbearing = 0;
+ return;
+ }
+
+ text_extents_internal (dwrite_font_face, font_size, code, nglyphs, metrics);
+
+ metrics->ascent = font->ascent;
+ metrics->descent = font->descent;
+}
+
+/* Never call Release on the value returned by this function, as it is
+ reused. */
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+ HRESULT hr;
+ static IDWriteBitmapRenderTarget *brt = NULL;
+ static SIZE size = {0, 0};
+
+ if (brt)
+ {
+ /* Check if we need to make a bigger one. */
+ if (width <= size.cx && height <= size.cy)
+ return brt;
+ RELEASE_COM (brt);
+ }
+
+ if (width > size.cx)
+ size.cx = width;
+ if (height > size.cy)
+ size.cy = height;
+
+ hr = gdi_interop->lpVtbl->CreateBitmapRenderTarget (gdi_interop,
+ hdc,
+ size.cx, size.cy,
+ &brt);
+ verify_hr (hr, "Failed to create DWriteBitmapRenderTarget");
+
+ /* We handle high dpi displays by incresing font size, so override
+ PixelsPerDip. */
+ brt->lpVtbl->SetPixelsPerDip (brt, 1.0);
+
+ /* The SetTextAntialiasMode method is only available in
+ IDWriteBitmapRenderTarget1. */
+ IDWriteBitmapRenderTarget1 *brt1;
+ hr = brt->lpVtbl->QueryInterface (brt,
+ &IID_IDWriteBitmapRenderTarget1,
+ (void **) &brt1);
+ if (SUCCEEDED (hr))
+ {
+ brt1->lpVtbl->SetTextAntialiasMode (brt1, ANTIALIAS_MODE);
+ RELEASE_COM (brt1);
+ }
+
+ return brt;
+}
+
+void
+w32_initialize_direct_write (void)
+{
+ direct_write_available = false;
+ HMODULE direct_write = LoadLibrary ("dwrite.dll");
+ if (!direct_write)
+ return;
+
+ /* This is only used here, no need to define it globally. */
+ typedef HRESULT (WINAPI *DWCreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u);
+
+ DWCreateFactory dw_create_factory
+ = (DWCreateFactory) get_proc_addr (direct_write,
+ "DWriteCreateFactory");
+
+ if (!dw_create_factory)
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ HRESULT hr = dw_create_factory (DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown **) &dwrite_factory);
+ if (FAILED (hr))
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->QueryInterface (dwrite_factory,
+ &IID_IDWriteFactory2,
+ (void **) &dwrite_factory2);
+
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->GetGdiInterop (dwrite_factory,
+ &gdi_interop);
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ IDWriteRenderingParams *def;
+
+ hr = dwrite_factory->lpVtbl->CreateRenderingParams (dwrite_factory,
+ &def);
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ /* range: [0.0, 1.0] */
+ float enhanced_contrast = def->lpVtbl->GetEnhancedContrast (def);
+ /* range: [0.0, 1.0] */
+ float clear_type_level = def->lpVtbl->GetClearTypeLevel (def);
+ /* range: (0.0, 256.0] */
+ float gamma = 1.4; /* def->lpVtbl->GetGamma(def); */
+
+ hr = dwrite_factory->lpVtbl->CreateCustomRenderingParams (dwrite_factory,
+ gamma,
+ enhanced_contrast,
+ clear_type_level,
+ def->lpVtbl->GetPixelGeometry(def),
+ RENDERING_MODE,
+ &rendering_params);
+
+ RELEASE_COM (def);
+
+ if (FAILED (hr))
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ direct_write_available = true;
+
+ DEFVAR_BOOL ("w32-inhibit-dwrite", w32_inhibit_dwrite,
+ doc: /* If t, don't use DirectWrite. */);
+ w32_inhibit_dwrite = false;
+}
+
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+
+ /* What we get as y is the baseline position. */
+ y -= font->ascent;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return;
+
+ struct font_metrics metrics;
+ text_extents_internal (dwrite_font_face, font_size, glyphs, len, &metrics);
+
+ int bitmap_width = metrics.width + metrics.rbearing;
+ int bitmap_height = font->ascent + font->descent;
+
+ /* We never release this, get_bitmap_render_target reuses it. */
+ IDWriteBitmapRenderTarget *bitmap_render_target =
+ get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+ /* This DC can't be released. */
+ HDC text_dc = bitmap_render_target->lpVtbl->GetMemoryDC
+ (bitmap_render_target);
+
+ /* Copy the background pixel to the render target bitmap. */
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+
+ UINT16 *indices = alloca (len * sizeof (UINT16));
+
+ for (int i = 0; i < len; i++)
+ indices[i] = glyphs[i];
+
+ FLOAT *advances = alloca (len * sizeof (FLOAT));
+
+ for (int i = 0; i < len; i++)
+ {
+ text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1,
+ &metrics);
+ advances[i] = metrics.width;
+ }
+
+ DWRITE_GLYPH_RUN glyph_run;
+ glyph_run.fontFace = dwrite_font_face;
+ glyph_run.fontEmSize = font_size;
+ glyph_run.glyphIndices = indices;
+ glyph_run.glyphCount = len;
+ glyph_run.isSideways = false;
+ glyph_run.bidiLevel = 0;
+ glyph_run.glyphOffsets = NULL;
+ glyph_run.glyphAdvances = advances;
+
+ IDWriteColorGlyphRunEnumerator *layers;
+ /* This call will tell us if we hace to handle any color glyph. */
+ hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
+ 0, font->ascent,
+ &glyph_run,
+ NULL,
+ MEASURING_MODE,
+ NULL,
+ 0,
+ &layers);
+
+ /* No color. Just draw the GlyphRun. */
+ if (hr == DWRITE_E_NOCOLOR)
+ bitmap_render_target->lpVtbl->DrawGlyphRun (bitmap_render_target,
+ 0, font->ascent,
+ MEASURING_MODE,
+ &glyph_run,
+ rendering_params,
+ color,
+ NULL);
+ else
+ {
+ /* If there were color glyphs, layers contains a list of GlyphRun
+ with a color and a position for each. We draw them
+ individually. */
+ verify_hr (hr, "Failed at TranslateColorGlyphRun");
+ for (;;) {
+ HRESULT hr;
+ BOOL more_layers;
+ const DWRITE_COLOR_GLYPH_RUN *layer;
+
+ hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+ verify_hr (hr, "Failed at MoveNext");
+ if (!more_layers)
+ break;
+ hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+ verify_hr (hr, "Failed at GetCurrentRun");
+ bitmap_render_target->lpVtbl->DrawGlyphRun
+ (bitmap_render_target,
+ layer->baselineOriginX,
+ layer->baselineOriginY,
+ MEASURING_MODE,
+ &layer->glyphRun,
+ rendering_params,
+ RGB (layer->runColor.r * 255,
+ layer->runColor.g * 255,
+ layer->runColor.b * 255),
+ NULL);
+ }
+ RELEASE_COM (layers);
+ }
+
+ /* Finally, copy the rendered text back to the original DC. */
+ BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+}
+
+/* Returns true if DirectWrite is to be used:
+ - It is available.
+ - The font is handled by HarfBuzz.
+ - w32-inhibit-dwrite is false.
+ */
+bool
+w32_use_direct_write (struct w32font_info *w32font)
+{
+#ifdef HAVE_HARFBUZZ
+ return direct_write_available
+ && w32font->font.driver == &harfbuzz_font_driver
+ && !w32_inhibit_dwrite;
+#else
+ return false;
+#endif
+}
diff --git a/src/w32font.c b/src/w32font.c
index efb42d80336..16aa9308584 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -55,6 +55,13 @@ #define VIETNAMESE_CHARSET 163
#define JOHAB_CHARSET 130
#endif
+/* Function prototypes from w32write.c */
+bool w32_use_direct_write (struct w32font_info *w32font);
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+void w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+
static void fill_in_logfont (struct frame *, LOGFONT *, Lisp_Object);
static BYTE w32_antialias_type (Lisp_Object);
@@ -452,6 +459,12 @@ w32font_text_extents (struct font *font, const unsigned *code,
memset (metrics, 0, sizeof (struct font_metrics));
+ if (w32_use_direct_write (w32_font))
+ {
+ w32_dwrite_text_extents (font, code, nglyphs, metrics);
+ return;
+ }
+
for (i = 0, first = true; i < nglyphs; i++)
{
struct w32_metric_cache *char_metric;
@@ -706,22 +719,36 @@ w32font_draw (struct glyph_string *s, int from, int to,
int i;
for (i = 0; i < len; i++)
- {
- WCHAR c = s->char2b[from + i] & 0xFFFF;
- ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
- }
+ if (w32_use_direct_write (w32font))
+ {
+ w32_dwrite_draw (s->hdc, x, y, s->char2b + from, 1,
+ GetTextColor(s->hdc), s->font);
+ }
+ else
+ {
+ WCHAR c = s->char2b[from + i] & 0xFFFF;
+ ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
+ }
}
else
{
- /* The number of glyphs in a glyph_string cannot be larger than
- the maximum value of the 'used' member of a glyph_row, so we
- are OK using alloca here. */
- eassert (len <= SHRT_MAX);
- WCHAR *chars = alloca (len * sizeof (WCHAR));
- int j;
- for (j = 0; j < len; j++)
- chars[j] = s->char2b[from + j] & 0xFFFF;
- ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ if (w32_use_direct_write (w32font))
+ {
+ w32_dwrite_draw ( s->hdc, x, y,
+ s->char2b + from, len, GetTextColor(s->hdc), s->font );
+ }
+ else
+ {
+ /* The number of glyphs in a glyph_string cannot be larger than
+ the maximum value of the 'used' member of a glyph_row, so we
+ are OK using alloca here. */
+ eassert (len <= SHRT_MAX);
+ WCHAR *chars = alloca (len * sizeof (WCHAR));
+ int j;
+ for (j = 0; j < len; j++)
+ chars[j] = s->char2b[from + j] & 0xFFFF;
+ ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ }
}
/* Restore clip region. */
diff --git a/src/w32font.h b/src/w32font.h
index 3f780c1d866..0774e92faa9 100644
--- a/src/w32font.h
+++ b/src/w32font.h
@@ -84,6 +84,19 @@ #define CACHE_BLOCKSIZE 128
Lisp_Object intern_font_name (char *);
+/* Function prototypes for DirectWrite. */
+void w32_initialize_direct_write (void);
+bool w32_use_direct_write (struct w32font_info *w32font);
+void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+void w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face(void *cache);
+void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
+void w32_font_set_dwrite_cache (struct font *font, void *cache,
+ float font_size);
+
extern void globals_of_w32font (void);
#endif
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..92df49610bb 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -54,8 +54,27 @@ #define _WIN32_WINNT 0x0600
void *cache;
/* This is used by the HarfBuzz backend to store the font scale. */
double scale;
+ /* This is used by DirectWrite to store the FontFace object.
+ DirectWrite works on top of the HarfBuzz backend, modifying some
+ calls. */
+ void *dwrite_cache;
+ float dwrite_font_size;
};
+void *
+w32_font_get_dwrite_cache (struct font *font, float *font_size)
+{
+ *font_size = ((struct uniscribe_font_info *) font)->dwrite_font_size;
+ return ((struct uniscribe_font_info *) font)->dwrite_cache;
+}
+
+void
+w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size)
+{
+ ((struct uniscribe_font_info *) font)->dwrite_font_size = font_size;
+ ((struct uniscribe_font_info *) font)->dwrite_cache = cache;
+}
+
int uniscribe_available = 0;
/* EnumFontFamiliesEx callback. */
@@ -200,6 +219,7 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
/* Initialize the cache for this font. */
uniscribe_font->cache = NULL;
+ uniscribe_font->dwrite_cache = NULL;
/* Uniscribe and HarfBuzz backends use glyph indices. */
uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
@@ -221,6 +241,7 @@ uniscribe_close (struct font *font)
= (struct uniscribe_font_info *) font;
#ifdef HAVE_HARFBUZZ
+ w32_dwrite_free_cached_face (uniscribe_font->dwrite_cache);
if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
&& uniscribe_font->cache)
hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
@@ -1372,6 +1393,10 @@ w32hb_encode_char (struct font *font, int c)
struct uniscribe_font_info *uniscribe_font
= (struct uniscribe_font_info *) font;
eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+ if (w32_use_direct_write(&uniscribe_font->w32_font))
+ return w32_dwrite_encode_char(font, c);
+
hb_font_t *hb_font = uniscribe_font->cache;
/* First time we use this font with HarfBuzz, create the hb_font_t
@@ -1531,6 +1556,8 @@ load_harfbuzz_funcs (HMODULE library)
}
#endif /* HAVE_HARFBUZZ */
+void w32_initialize_direct_write (void);
+
static void
syms_of_w32uniscribe_for_pdumper (void)
{
@@ -1624,5 +1651,8 @@ syms_of_w32uniscribe_for_pdumper (void)
harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
register_font_driver (&harfbuzz_font_driver, NULL);
+
+ w32_initialize_direct_write ();
+
#endif /* HAVE_HARFBUZZ */
}
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-16 21:35 ` Cecilio Pardo
@ 2024-10-17 6:21 ` Eli Zaretskii
2024-10-17 10:38 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-17 6:21 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730, kbrown
> Date: Wed, 16 Oct 2024 23:35:34 +0200
> Cc: 73730@debbugs.gnu.org
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> Still not done with this, but I am sending a new patch to fix the bug
> with w32-find-non-USB-fonts. It calls text_extents with a font of size
> 0. Checking for that seems to solve the problem.
Is that really TRT? What does it mean font_size = 0 in this case?
> BTW, why does that variable show as undocumented with
> describe-variables? What am I doing wrong?
You used DEFVAR_BOOL inside a function. It should be on the top level
(look at other places where we define such variables), then
make-docfile will collect its definition and it will be written to
etc/DOC.
> - Instead of aborting on failure, just disable DirectWrite and let emacs
> continue. This will take some work.
Instead of disabling DirectWrite, it might be better to fall back on
previous code -- that way, the glyphs will be shown, but without
colors. The trick is to detect this early enough, because some fonts
AFAIR cannot be displayed using non-dwrite code.
> >> + glyph_run.bidiLevel = 0;
> > ^^^^^^^^^^^^^
> > Is this because otherwise DirectWrite will possibly reorder the
> > glyphs or something? Did you test this with R2L text, e.g. by
> > displaying TUTORIAL.he? I did try, and it looks OK, but are there any
> > color fonts that cover the Hebrew block, so I could be sure everything
> > works in that case?
> >
> > More generally, what color fonts can be used with this build on
> > Windows for characters other than Emoji, in addition to Segoe UI
> > Emoji?
>
>
> The documentation describes this field as the "implicit resolved level
> of the glyph run". Odd levels indicate RTL, and the function asks for
> the right side position as origin, so I think they would be reordered.
>
> As there is no reference to bidi in w32font.c, I assumed glyphs reach
> this layer prepared to be rendered ltr.
Yes, that's true.
> I think the only color font that Windows has is the emoji one. I'll try
> to find some other to test this.
Thanks.
> >> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
> >> +void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size);
> >
> > Why aren't these functions defined in w32dwrite.c?
>
> They need to know about the uniscribe_font struct, which is defined there.
If that's the only reason, we could perhaps move the definition of
uniscribe_font struct to w32font.h.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-17 6:21 ` Eli Zaretskii
@ 2024-10-17 10:38 ` Cecilio Pardo
2024-10-20 13:35 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-17 10:38 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730, kbrown
On 17/10/2024 8:21, Eli Zaretskii wrote:
>> Still not done with this, but I am sending a new patch to fix the bug
>> with w32-find-non-USB-fonts. It calls text_extents with a font of size
>> 0. Checking for that seems to solve the problem.
>
> Is that really TRT? What does it mean font_size = 0 in this case?
No, I was wrong, sorry. DirectDwrite is failing to create a font from
certain GDI fonts. I need to then fall back to w32font. I'm on it.
>> BTW, why does that variable show as undocumented with
>> describe-variables? What am I doing wrong?
>
> You used DEFVAR_BOOL inside a function. It should be on the top level
> (look at other places where we define such variables), then
> make-docfile will collect its definition and it will be written to
> etc/DOC.
Thanks.
>> - Instead of aborting on failure, just disable DirectWrite and let emacs
>> continue. This will take some work.
>
> Instead of disabling DirectWrite, it might be better to fall back on
> previous code -- that way, the glyphs will be shown, but without
> colors. The trick is to detect this early enough, because some fonts
> AFAIR cannot be displayed using non-dwrite code.
Ok.
>>>> +void *w32_font_get_dwrite_cache (struct font *font, float *font_size);
>>>> +void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size);
>>>
>>> Why aren't these functions defined in w32dwrite.c?
>>
>> They need to know about the uniscribe_font struct, which is defined there.
>
> If that's the only reason, we could perhaps move the definition of
> uniscribe_font struct to w32font.h.
Will do that.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-17 10:38 ` Cecilio Pardo
@ 2024-10-20 13:35 ` Cecilio Pardo
2024-10-20 13:55 ` Eli Zaretskii
2024-10-20 16:17 ` Eli Zaretskii
0 siblings, 2 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-20 13:35 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730, kbrown
On 17/10/2024 12:38, Cecilio Pardo wrote:
>>> Still not done with this, but I am sending a new patch to fix the bug
>>> with w32-find-non-USB-fonts. It calls text_extents with a font of size
>>> 0. Checking for that seems to solve the problem.
>>
>> Is that really TRT? What does it mean font_size = 0 in this case?
>
> No, I was wrong, sorry. DirectDwrite is failing to create a font from
> certain GDI fonts. I need to then fall back to w32font. I'm on it.
>
The error is caused indeed by fonts with size == 0.
- w32-find-non-USB-fonts calls open-font with size == nil.
- open-font gets the size from font-entity, that is also empty.
- It ends up calling CreateFontIndirect with a LOGFONT
with lfHeight == 0
- Then, w32-find-non-USB-fonts calls font-get-glyphs, which call
text_extents.
- text_extents on DirectWrite fails with a font of size 0, but
w32font_text_extents does return values that are not 0.
If the case of size <= 0, we are now falling back to
w32font_text_extents.
Also, in the case of failures to get text size, draw, or encode char,
now we will disable directwrite for the particular font.
I'll send a patch as soon as I can.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 13:35 ` Cecilio Pardo
@ 2024-10-20 13:55 ` Eli Zaretskii
2024-10-22 22:14 ` Cecilio Pardo
2024-10-20 16:17 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-20 13:55 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730, kbrown
> Date: Sun, 20 Oct 2024 15:35:10 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
> Cc: 73730@debbugs.gnu.org, kbrown@cornell.edu
>
>
> On 17/10/2024 12:38, Cecilio Pardo wrote:
>
> >>> Still not done with this, but I am sending a new patch to fix the bug
> >>> with w32-find-non-USB-fonts. It calls text_extents with a font of size
> >>> 0. Checking for that seems to solve the problem.
> >>
> >> Is that really TRT? What does it mean font_size = 0 in this case?
> >
> > No, I was wrong, sorry. DirectDwrite is failing to create a font from
> > certain GDI fonts. I need to then fall back to w32font. I'm on it.
> >
>
> The error is caused indeed by fonts with size == 0.
>
> - w32-find-non-USB-fonts calls open-font with size == nil.
> - open-font gets the size from font-entity, that is also empty.
> - It ends up calling CreateFontIndirect with a LOGFONT
> with lfHeight == 0
> - Then, w32-find-non-USB-fonts calls font-get-glyphs, which call
> text_extents.
> - text_extents on DirectWrite fails with a font of size 0, but
> w32font_text_extents does return values that are not 0.
>
> If the case of size <= 0, we are now falling back to
> w32font_text_extents.
>
> Also, in the case of failures to get text size, draw, or encode char,
> now we will disable directwrite for the particular font.
>
> I'll send a patch as soon as I can.
Great, thanks.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 13:35 ` Cecilio Pardo
2024-10-20 13:55 ` Eli Zaretskii
@ 2024-10-20 16:17 ` Eli Zaretskii
2024-10-20 18:20 ` Cecilio Pardo
1 sibling, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-20 16:17 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730, kbrown
> Date: Sun, 20 Oct 2024 15:35:10 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
> Cc: 73730@debbugs.gnu.org, kbrown@cornell.edu
>
> The error is caused indeed by fonts with size == 0.
>
> - w32-find-non-USB-fonts calls open-font with size == nil.
> - open-font gets the size from font-entity, that is also empty.
> - It ends up calling CreateFontIndirect with a LOGFONT
> with lfHeight == 0
> - Then, w32-find-non-USB-fonts calls font-get-glyphs, which call
> text_extents.
> - text_extents on DirectWrite fails with a font of size 0, but
> w32font_text_extents does return values that are not 0.
>
> If the case of size <= 0, we are now falling back to
> w32font_text_extents.
Maybe it's better to use some default size in text_extents instead?
The pixel size of the default font is known (see FRAME_LINE_HEIGHT),
so maybe just use it if you get a font of size zero? That could be a
more future-proof fix, since we could get such fonts in other
situations as well.
Regardless, we should keep the strategy of falling back to the old
code if DirectWrite fails.
Thanks.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 16:17 ` Eli Zaretskii
@ 2024-10-20 18:20 ` Cecilio Pardo
2024-10-20 18:33 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-20 18:20 UTC (permalink / raw)
To: 73730
On 20/10/2024 18:17, Eli Zaretskii wrote:
>> If the case of size <= 0, we are now falling back to
>> w32font_text_extents.
>
> Maybe it's better to use some default size in text_extents instead?
> The pixel size of the default font is known (see FRAME_LINE_HEIGHT),
> so maybe just use it if you get a font of size zero? That could be a
> more future-proof fix, since we could get such fonts in other
> situations as well.
The reason w32fint_text_extents has a value to return is because when
the specified font size is 0:
"The font mapper uses a default height value when it searches for a match."
So it gives a font with a size that we cannot know, because when we ask
it says it is 0.
So we may return our own default.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 18:20 ` Cecilio Pardo
@ 2024-10-20 18:33 ` Eli Zaretskii
2024-10-20 18:37 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-20 18:33 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Sun, 20 Oct 2024 20:20:29 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> On 20/10/2024 18:17, Eli Zaretskii wrote:
>
> >> If the case of size <= 0, we are now falling back to
> >> w32font_text_extents.
> >
> > Maybe it's better to use some default size in text_extents instead?
> > The pixel size of the default font is known (see FRAME_LINE_HEIGHT),
> > so maybe just use it if you get a font of size zero? That could be a
> > more future-proof fix, since we could get such fonts in other
> > situations as well.
>
> The reason w32fint_text_extents has a value to return is because when
> the specified font size is 0:
>
> "The font mapper uses a default height value when it searches for a match."
>
> So it gives a font with a size that we cannot know, because when we ask
> it says it is 0.
>
> So we may return our own default.
I'm not sure I understand the last sentence above.
It should be easy to modify w32-find-non-USB-fonts to specify a
default value for SIZE if the caller didn't provide it. I just
thought that other callers could do something similar, and therefore
it would be better to use FRAME_LINE_HEIGHT of the selected frame as
the size internally in text_extents if the caller specified zero. If
what you say above disagrees with that, please elaborate why you
disagree.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 18:33 ` Eli Zaretskii
@ 2024-10-20 18:37 ` Cecilio Pardo
0 siblings, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-20 18:37 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
On 20/10/2024 20:33, Eli Zaretskii wrote:
>> Date: Sun, 20 Oct 2024 20:20:29 +0200
>> From: Cecilio Pardo <cpardo@imayhem.com>
>>
>> On 20/10/2024 18:17, Eli Zaretskii wrote:
>>
>>>> If the case of size <= 0, we are now falling back to
>>>> w32font_text_extents.
>>>
>>> Maybe it's better to use some default size in text_extents instead?
>>> The pixel size of the default font is known (see FRAME_LINE_HEIGHT),
>>> so maybe just use it if you get a font of size zero? That could be a
>>> more future-proof fix, since we could get such fonts in other
>>> situations as well.
>>
>> The reason w32fint_text_extents has a value to return is because when
>> the specified font size is 0:
>>
>> "The font mapper uses a default height value when it searches for a match."
>>
>> So it gives a font with a size that we cannot know, because when we ask
>> it says it is 0.
>>
>> So we may return our own default.
>
> I'm not sure I understand the last sentence above.
>
> It should be easy to modify w32-find-non-USB-fonts to specify a
> default value for SIZE if the caller didn't provide it. I just
> thought that other callers could do something similar, and therefore
> it would be better to use FRAME_LINE_HEIGHT of the selected frame as
> the size internally in text_extents if the caller specified zero. If
> what you say above disagrees with that, please elaborate why you
> disagree.
No, I meant to agree. Sorry I wasn't clear.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-20 13:55 ` Eli Zaretskii
@ 2024-10-22 22:14 ` Cecilio Pardo
2024-10-23 10:03 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-22 22:14 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730, kbrown
[-- Attachment #1: Type: text/plain, Size: 1189 bytes --]
Here is a new version with pending issues addressed.
- No abort any more. Now, any failure marks the font as not usable,
falling back to previous driver. If the error is general, then all
of DirectWrite is disabled. eassert and DebPrint are called in these
cases.
- Added a NEWS entry.
- The struct uniscribe_font_info is now in w32font.h
- Build without warnings in both versions on MinGW.
Some things I think we didn't discuss before:
- DirectWrite is available since Windows 7, but color glyphs are
available only since Windows 8.1. We only enable DirectWrite since
Windows 8.1. This is stated on the NEWS entry.
- The default value for the Gamma render parameter in DirectWrite
gives very 'light' characters. I changed it to 1.4, which matches
GDI and looks better. But that's just my opinion.
I couldn't find any hebrew font with color glyphs to test RTL
display. I found a latin+arabic one, Cairo Play:
https://github.com/Gue3bara/Cairo
https://fonts.google.com/specimen/Cairo+Play
It displays diacritics in different colors on both scripts.
It seems to work fine in emacs. I attach captures with and without
DirectWrite, and compared to Courier New.
[-- Attachment #2: emacs_KbWp4eBFOp.png --]
[-- Type: image/png, Size: 33979 bytes --]
[-- Attachment #3: emacs_SiXOdpA67i.png --]
[-- Type: image/png, Size: 29664 bytes --]
[-- Attachment #4: 0001-Implement-drawing-text-with-DirectWrite-on-MS-Window.patch --]
[-- Type: text/plain, Size: 40944 bytes --]
From aa8c7777ddc9ca9e8dc323c0cbd99636fc52f0ee Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Implement drawing text with DirectWrite on MS-Windows.
This adds support for color fonts.
* configure.ac: Add src/w32drite to W32_OBJ.
* src/w32dwrite.c: This is a new file.
(w32-initialize-direct-write): New function, initialize the DirectWrite
library if it is available, and required global variables.
(w32_use_direct_write): New function, check if DirectWrite is available
and activated by the user.
(w32_dwrite_encode_char): New function, replacement for HarfBuzz's
encode_char.
(w32_dwrite_text_extents): New function, replacement for w32font
text_extents.
(w32_dwrite_draw): New function, replacement for w32font draw.
(w32_dwrite_free_cached_face): New function, used on the font deletion
process to also delete DirectWrite data.
(verify_hr): New function, verify COM method results.
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled, call
w32_dwrite_text_extents.
(w32font_draw): If DirectWrite is enabled, call w32_dwrite_draw.
* src/w32uniscribe.c: (w32hb_encode_char): If DirectWrite is enabled,
call w32_dwrite_encode_char.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.
---
configure.ac | 2 +-
etc/NEWS | 5 +
src/w32dwrite.c | 997 +++++++++++++++++++++++++++++++++++++++++++++
src/w32font.c | 39 +-
src/w32font.h | 30 ++
src/w32uniscribe.c | 33 +-
6 files changed, 1080 insertions(+), 26 deletions(-)
create mode 100644 src/w32dwrite.c
diff --git a/configure.ac b/configure.ac
index 8a5ba7db3d1..ba9cd8f0cd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
AC_CHECK_TOOL([WINDRES], [windres],
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
- W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+ W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/etc/NEWS b/etc/NEWS
index 63c294fac41..f65333a36c0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -750,6 +750,11 @@ and later versions.
** Emacs on MS-Windows now supports drag-n-drop of text into a buffer.
This is in addition to drag-n-drop of files, that was already supported.
+** Emacs on MS-Windows now supports color fonts.
+On Windows 8.1 and later versions Emacs now uses DirectWrite to draw
+text, which supports color fonts. This can be disabled by setting the
+variable w32-inhibit-dwrite to t.
+
\f
----------------------------------------------------------------------
This file is part of GNU Emacs.
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..47b93a5340f
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,997 @@
+/* Support for using DirectWrite on MS-Windows to draw text. This
+ allows for color fonts.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/* This requires the HarfBuzz font backend to be available.
+
+ It works by modifying the HarfBuzz backend to use DirectWrite at
+ some points, if it is available:
+
+ - When encoding characters: w32hb_encode_char
+ - When measuring text: w32font_text_extents
+ - When drawing text: w32font_draw
+
+ DirectWrite is setup by calling w32_initialize_direct_write. From
+ that point, the function w32_use_direct_write will return true if
+ DirectWrite is to be used.
+
+ DirectWrite is available since Windows 7, but we don't activate it on
+ versions before 8.1, because color fonts are only available since that. */
+
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#ifndef MINGW_W64
+# define INITGUID
+#endif
+#include <initguid.h>
+#include <ole2.h>
+#include <unknwn.h>
+
+#include "frame.h"
+#include "w32font.h"
+#include "w32common.h"
+#include "w32term.h"
+
+#ifndef MINGW_W64
+
+/* The following definitions would be included from dwrite_3.h, but it
+ is not available when building with mingw.org's MinGW. Methods that
+ we don't use are declared with the EMACS_DWRITE_UNUSED macro, to
+ avoid bringing in more types that would need to be declared. */
+
+#define EMACS_DWRITE_UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
+
+#define DWRITE_E_NOCOLOR _HRESULT_TYPEDEF_(0x8898500CL)
+
+typedef enum DWRITE_PIXEL_GEOMETRY {
+ DWRITE_PIXEL_GEOMETRY_FLAT = 0,
+ DWRITE_PIXEL_GEOMETRY_RGB = 1,
+ DWRITE_PIXEL_GEOMETRY_BGR = 2
+} DWRITE_PIXEL_GEOMETRY;
+
+typedef enum DWRITE_RENDERING_MODE {
+ DWRITE_RENDERING_MODE_DEFAULT = 0,
+ DWRITE_RENDERING_MODE_ALIASED = 1,
+ DWRITE_RENDERING_MODE_GDI_CLASSIC = 2,
+ DWRITE_RENDERING_MODE_GDI_NATURAL = 3,
+ DWRITE_RENDERING_MODE_NATURAL = 4,
+ DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC = 5,
+ DWRITE_RENDERING_MODE_OUTLINE = 6
+} DWRITE_RENDERING_MODE;
+
+typedef enum DWRITE_MEASURING_MODE {
+ DWRITE_MEASURING_MODE_NATURAL = 0,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC = 1,
+ DWRITE_MEASURING_MODE_GDI_NATURAL = 2
+} DWRITE_MEASURING_MODE;
+
+typedef enum DWRITE_TEXT_ANTIALIAS_MODE {
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE = 0,
+ DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE = 1
+} DWRITE_TEXT_ANTIALIAS_MODE;
+
+typedef enum DWRITE_FACTORY_TYPE {
+ DWRITE_FACTORY_TYPE_SHARED = 0,
+ DWRITE_FACTORY_TYPE_ISOLATED = 1
+} DWRITE_FACTORY_TYPE;
+
+typedef struct DWRITE_FONT_METRICS {
+ UINT16 designUnitsPerEm;
+ UINT16 ascent;
+ UINT16 descent;
+ INT16 lineGap;
+ UINT16 capHeight;
+ UINT16 xHeight;
+ INT16 underlinePosition;
+ UINT16 underlineThickness;
+ INT16 strikethroughPosition;
+ UINT16 strikethroughThickness;
+} DWRITE_FONT_METRICS;
+
+typedef struct DWRITE_GLYPH_METRICS {
+ INT32 leftSideBearing;
+ UINT32 advanceWidth;
+ INT32 rightSideBearing;
+ INT32 topSideBearing;
+ UINT32 advanceHeight;
+ INT32 bottomSideBearing;
+ INT32 verticalOriginY;
+} DWRITE_GLYPH_METRICS;
+
+typedef interface IDWriteRenderingParams IDWriteRenderingParams;
+typedef interface IDWriteFont IDWriteFont;
+typedef interface IDWriteGdiInterop IDWriteGdiInterop;
+typedef interface IDWriteFactory IDWriteFactory;
+typedef interface IDWriteFactory2 IDWriteFactory2;
+typedef interface IDWriteFontFace IDWriteFontFace;
+typedef interface IDWriteBitmapRenderTarget IDWriteBitmapRenderTarget;
+typedef interface IDWriteBitmapRenderTarget1 IDWriteBitmapRenderTarget1;
+typedef interface IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator;
+
+DEFINE_GUID(IID_IDWriteBitmapRenderTarget1, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
+DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
+DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
+
+typedef struct DWRITE_GLYPH_OFFSET {
+ FLOAT advanceOffset;
+ FLOAT ascenderOffset;
+} DWRITE_GLYPH_OFFSET;
+
+typedef struct DWRITE_GLYPH_RUN {
+ IDWriteFontFace *fontFace;
+ FLOAT fontEmSize;
+ UINT32 glyphCount;
+ const UINT16 *glyphIndices;
+ const FLOAT *glyphAdvances;
+ const DWRITE_GLYPH_OFFSET *glyphOffsets;
+ WINBOOL isSideways;
+ UINT32 bidiLevel;
+} DWRITE_GLYPH_RUN;
+
+typedef struct _D3DCOLORVALUE {
+ float r;
+ float g;
+ float b;
+ float a;
+} D3DCOLORVALUE;
+
+typedef D3DCOLORVALUE DWRITE_COLOR_F;
+
+typedef struct DWRITE_COLOR_GLYPH_RUN {
+ DWRITE_GLYPH_RUN glyphRun;
+ void *glyphRunDescription;
+ FLOAT baselineOriginX;
+ FLOAT baselineOriginY;
+ DWRITE_COLOR_F runColor;
+ UINT16 paletteIndex;
+} DWRITE_COLOR_GLYPH_RUN;
+
+typedef struct IDWriteFontFaceVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFontFace *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFontFace *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFontFace *This);
+
+ EMACS_DWRITE_UNUSED(GetType);
+ EMACS_DWRITE_UNUSED(GetFiles);
+ EMACS_DWRITE_UNUSED(GetIndex);
+ EMACS_DWRITE_UNUSED(GetSimulations);
+ EMACS_DWRITE_UNUSED(IsSymbolFont);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED (GetGlyphCount);
+ EMACS_DWRITE_UNUSED (GetDesignGlyphMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)
+ (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count,
+ UINT16 *glyph_indices);
+
+ EMACS_DWRITE_UNUSED (TryGetFontTable);
+ EMACS_DWRITE_UNUSED (ReleaseFontTable);
+ EMACS_DWRITE_UNUSED (GetGlyphRunOutline);
+ EMACS_DWRITE_UNUSED (GetRecommendedRenderingMode);
+ EMACS_DWRITE_UNUSED (GetGdiCompatibleMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)
+ (IDWriteFontFace *This,
+ FLOAT emSize,
+ FLOAT pixels_per_dip,
+ void *transform,
+ WINBOOL use_gdi_natural,
+ const UINT16 *glyph_indices,
+ UINT32 glyph_count,
+ DWRITE_GLYPH_METRICS *metrics,
+ WINBOOL is_sideways);
+ END_INTERFACE
+} IDWriteFontFaceVtbl;
+
+interface IDWriteFontFace {
+ CONST_VTBL IDWriteFontFaceVtbl* lpVtbl;
+};
+
+typedef struct IDWriteRenderingParamsVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteRenderingParams *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteRenderingParams *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteRenderingParams *This);
+
+ FLOAT (STDMETHODCALLTYPE *GetGamma)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)
+ (IDWriteRenderingParams *This);
+ int (STDMETHODCALLTYPE *GetPixelGeometry)
+ (IDWriteRenderingParams *This);
+ END_INTERFACE
+} IDWriteRenderingParamsVtbl;
+
+interface IDWriteRenderingParams {
+ CONST_VTBL IDWriteRenderingParamsVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFontVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFont *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFont *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFont *This);
+
+ EMACS_DWRITE_UNUSED (GetFontFamily);
+ EMACS_DWRITE_UNUSED (GetWeight);
+ EMACS_DWRITE_UNUSED (GetStretch);
+ EMACS_DWRITE_UNUSED (GetStyle);
+ EMACS_DWRITE_UNUSED (IsSymbolFont);
+ EMACS_DWRITE_UNUSED (GetFaceNames);
+ EMACS_DWRITE_UNUSED (GetInformationalStrings);
+ EMACS_DWRITE_UNUSED (GetSimulations);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFont *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED(HasCharacter);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFace)
+ (IDWriteFont *This, IDWriteFontFace **face);
+
+ END_INTERFACE
+} IDWriteFontVtbl;
+
+interface IDWriteFont {
+ CONST_VTBL IDWriteFontVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTargetVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteBitmapRenderTarget *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteBitmapRenderTarget *This);
+
+ HRESULT (STDMETHODCALLTYPE *DrawGlyphRun)
+ (IDWriteBitmapRenderTarget *This,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuring_mode,
+ const DWRITE_GLYPH_RUN *glyph_run,
+ IDWriteRenderingParams *params,
+ COLORREF textColor,
+ RECT *blackbox_rect);
+
+ HDC (STDMETHODCALLTYPE *GetMemoryDC)(IDWriteBitmapRenderTarget *This);
+
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+
+ HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip)
+ (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip);
+
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ END_INTERFACE
+} IDWriteBitmapRenderTargetVtbl;
+
+interface IDWriteBitmapRenderTarget {
+ CONST_VTBL IDWriteBitmapRenderTargetVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTarget1Vtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget1 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef) (IDWriteBitmapRenderTarget1 *This);
+ ULONG (STDMETHODCALLTYPE *Release) (IDWriteBitmapRenderTarget1 *This);
+
+ EMACS_DWRITE_UNUSED (DrawGlyphRun);
+ EMACS_DWRITE_UNUSED (GetMemoryDC);
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (SetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ EMACS_DWRITE_UNUSED (GetTextAntialiasMode);
+
+ HRESULT (STDMETHODCALLTYPE *SetTextAntialiasMode)
+ (IDWriteBitmapRenderTarget1 *This, DWRITE_TEXT_ANTIALIAS_MODE mode);
+
+ END_INTERFACE
+} IDWriteBitmapRenderTarget1Vtbl;
+
+interface IDWriteBitmapRenderTarget1 {
+ CONST_VTBL IDWriteBitmapRenderTarget1Vtbl* lpVtbl;
+};
+
+typedef struct IDWriteGdiInteropVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteGdiInterop *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteGdiInterop *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteGdiInterop *This);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)
+ (IDWriteGdiInterop *This, const LOGFONTW *logfont,
+ IDWriteFont **font);
+
+ EMACS_DWRITE_UNUSED (ConvertFontToLOGFONT);
+ EMACS_DWRITE_UNUSED (ConvertFontFaceToLOGFONT);
+ EMACS_DWRITE_UNUSED (CreateFontFaceFromHdc);
+
+ HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)
+ (IDWriteGdiInterop *This, HDC hdc, UINT32 width, UINT32 height,
+ IDWriteBitmapRenderTarget **target);
+ END_INTERFACE
+} IDWriteGdiInteropVtbl;
+
+interface IDWriteGdiInterop {
+ CONST_VTBL IDWriteGdiInteropVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactoryVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory *This);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)
+ (IDWriteFactory *This, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)
+ (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast,
+ FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry,
+ DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ HRESULT (STDMETHODCALLTYPE *GetGdiInterop)
+ (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactoryVtbl;
+
+interface IDWriteFactory { CONST_VTBL IDWriteFactoryVtbl* lpVtbl; };
+
+typedef struct IDWriteColorGlyphRunEnumeratorVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteColorGlyphRunEnumerator *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteColorGlyphRunEnumerator *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteColorGlyphRunEnumerator *This);
+
+ HRESULT (STDMETHODCALLTYPE *MoveNext)(
+ IDWriteColorGlyphRunEnumerator *This,
+ WINBOOL *hasRun);
+
+ HRESULT (STDMETHODCALLTYPE *GetCurrentRun)(
+ IDWriteColorGlyphRunEnumerator *This,
+ const DWRITE_COLOR_GLYPH_RUN **run);
+
+ END_INTERFACE
+} IDWriteColorGlyphRunEnumeratorVtbl;
+
+interface IDWriteColorGlyphRunEnumerator {
+ CONST_VTBL IDWriteColorGlyphRunEnumeratorVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactory2Vtbl {
+ BEGIN_INTERFACE
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory2 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory2 *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory2 *This);
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ EMACS_DWRITE_UNUSED (CreateRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ EMACS_DWRITE_UNUSED (GetGdiInterop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+
+ EMACS_DWRITE_UNUSED (GetEudcFontCollection);
+ EMACS_DWRITE_UNUSED (IDWriteFactory1_CreateCustomRenderingParams);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontFallback);
+ EMACS_DWRITE_UNUSED (CreateFontFallbackBuilder);
+ HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)
+ (IDWriteFactory2 *This,
+ FLOAT originX,
+ FLOAT originY,
+ const DWRITE_GLYPH_RUN *run,
+ void *rundescr,
+ DWRITE_MEASURING_MODE mode,
+ void *transform,
+ UINT32 palette_index,
+ IDWriteColorGlyphRunEnumerator **colorlayers);
+
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactory2Vtbl;
+
+interface IDWriteFactory2 {
+ CONST_VTBL IDWriteFactory2Vtbl* lpVtbl;
+};
+#else /* ifndef MINGW_W64 */
+# include <dwrite_3.h>
+#endif
+
+/* Values to use for DirectWrite rendering. */
+#define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL
+#define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+#define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE
+
+#define RELEASE_COM(i) (i)->lpVtbl->Release (i)
+
+/* Global variables for DirectWrite. */
+static bool direct_write_available = false;
+static IDWriteFactory *dwrite_factory;
+static IDWriteFactory2 *dwrite_factory2;
+static IDWriteGdiInterop *gdi_interop;
+static IDWriteRenderingParams *rendering_params;
+
+static bool
+verify_hr (HRESULT hr, const char *msg)
+{
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) %s\n", hr, msg));
+ eassert (SUCCEEDED (hr));
+ return false;
+ }
+ return true;
+}
+
+/* Gets a IDWriteFontFace from a struct font (its HFONT). Returns the
+ font size in points. It may fail to get a DirectWrite font, and face
+ will be NULL on return. This happens for some fonts like Courier.
+
+ Never call Release on the result, as it is cached for reuse on the
+ struct font. */
+static float
+get_font_face (struct font *infont, IDWriteFontFace **face)
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+ IDWriteFont *font;
+
+ struct uniscribe_font_info *uniscribe_font =
+ (struct uniscribe_font_info *) infont;
+
+ /* Check the cache. */
+ *face = uniscribe_font->dwrite_cache;
+ if (*face)
+ return uniscribe_font->dwrite_font_size;
+
+ GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont);
+
+ hr = gdi_interop->lpVtbl->CreateFontFromLOGFONT (gdi_interop,
+ (const LOGFONTW *) &logfont,
+ &font);
+
+ if (!verify_hr (hr, "Failed to CreateFontFromLOGFONT"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ *face = NULL;
+ return 0.0;
+ }
+
+ hr = font->lpVtbl->CreateFontFace (font, face);
+ RELEASE_COM (font);
+ if (!verify_hr (hr, "Failed to create DWriteFontFace"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ *face = NULL;
+ return 0.0;
+ }
+
+ /* Cache this FontFace. */
+ uniscribe_font->dwrite_font_size = abs (logfont.lfHeight);
+ uniscribe_font->dwrite_cache = *face;
+
+ return abs (logfont.lfHeight);
+}
+
+void
+w32_dwrite_free_cached_face (void *cache)
+{
+ if (cache)
+ RELEASE_COM ((IDWriteFontFace *) cache);
+}
+
+static float
+convert_metrics_sz (int sz, float font_size, int units_per_em)
+{
+ return (float) sz * font_size / units_per_em;
+}
+
+/* Does not fill in the ascent and descent fields of metrics. */
+static bool
+text_extents_internal (IDWriteFontFace *dwrite_font_face,
+ float font_size, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ HRESULT hr;
+
+ DWRITE_FONT_METRICS dwrite_font_metrics;
+ dwrite_font_face->lpVtbl->GetMetrics (dwrite_font_face,
+ &dwrite_font_metrics);
+
+ UINT16 *indices = alloca (nglyphs * sizeof (UINT16));
+ for (int i = 0; i < nglyphs; i++)
+ indices[i] = code[i];
+
+ DWRITE_GLYPH_METRICS* gmetrics =
+ alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+ hr = dwrite_font_face->lpVtbl->GetGdiCompatibleGlyphMetrics (dwrite_font_face,
+ font_size,
+ 1.0,
+ NULL,
+ TRUE,
+ indices,
+ nglyphs,
+ gmetrics,
+ false);
+ if (!verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics"))
+ return false;
+
+ float width = 0;
+ int du_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+ for (int i = 0; i < nglyphs; i++)
+ {
+ float advance =
+ convert_metrics_sz (gmetrics[i].advanceWidth, font_size, du_per_em);
+
+ width += advance;
+
+ float lbearing =
+ round (convert_metrics_sz (gmetrics[i].leftSideBearing, font_size,
+ du_per_em));
+ float rbearing =
+ round (advance -
+ convert_metrics_sz (gmetrics[i].rightSideBearing,
+ font_size, du_per_em));
+ if (i == 0)
+ {
+ metrics->lbearing = lbearing;
+ metrics->rbearing = rbearing;
+ }
+ if (metrics->lbearing > lbearing)
+ metrics->lbearing = lbearing;
+ if (metrics->rbearing < rbearing)
+ metrics->rbearing = rbearing;
+ }
+ metrics->width = round(width);
+
+ return true;
+}
+
+unsigned
+w32_dwrite_encode_char (struct font *font, int c)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+ UINT16 index;
+
+ get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return FONT_INVALID_CODE;
+ hr = dwrite_font_face->lpVtbl->GetGlyphIndices (dwrite_font_face,
+ &c, 1, &index);
+ if (verify_hr (hr, "Failed to GetGlyphIndices"))
+ {
+ if (index == 0)
+ return FONT_INVALID_CODE;
+ return index;
+ }
+ ((struct uniscribe_font_info *) font)->dwrite_skip_font = true;
+ return FONT_INVALID_CODE;
+}
+
+bool
+w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs,
+ struct font_metrics *metrics)
+{
+ IDWriteFontFace *dwrite_font_face;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+
+ if (dwrite_font_face == NULL)
+ return false;
+
+ /* We can get fonts with a size of 0. GDI handles this by using a default
+ size. We do the same. */
+ if (font_size <= 0.0f)
+ font_size = FRAME_LINE_HEIGHT (XFRAME (selected_frame));
+
+ metrics->ascent = font->ascent;
+ metrics->descent = font->descent;
+
+ return text_extents_internal (dwrite_font_face, font_size, code, nglyphs,
+ metrics);
+}
+
+/* Never call Release on the value returned by this function, as it is
+ reused. */
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+ HRESULT hr;
+ static IDWriteBitmapRenderTarget *brt = NULL;
+ static SIZE size = {0, 0};
+
+ if (brt)
+ {
+ /* Check if we need to make a bigger one. */
+ if (width <= size.cx && height <= size.cy)
+ return brt;
+ RELEASE_COM (brt);
+ }
+
+ if (width > size.cx)
+ size.cx = width;
+ if (height > size.cy)
+ size.cy = height;
+
+ hr = gdi_interop->lpVtbl->CreateBitmapRenderTarget (gdi_interop,
+ hdc,
+ size.cx, size.cy,
+ &brt);
+ if (!verify_hr (hr, "Failed to CreateBitmapRenderTarget"))
+ return NULL;
+
+ /* We handle high dpi displays by incresing font size, so override
+ PixelsPerDip. */
+ brt->lpVtbl->SetPixelsPerDip (brt, 1.0);
+
+ /* The SetTextAntialiasMode method is only available in
+ IDWriteBitmapRenderTarget1. */
+ IDWriteBitmapRenderTarget1 *brt1;
+ hr = brt->lpVtbl->QueryInterface (brt,
+ &IID_IDWriteBitmapRenderTarget1,
+ (void **) &brt1);
+ /* This error should not happen, but is not catastrofic */
+ if (verify_hr (hr, "Failed to QueryInterface for IDWriteBitmapRenderTarget1"))
+ {
+ brt1->lpVtbl->SetTextAntialiasMode (brt1, ANTIALIAS_MODE);
+ RELEASE_COM (brt1);
+ }
+
+ return brt;
+}
+
+void
+w32_initialize_direct_write (void)
+{
+ direct_write_available = false;
+ HMODULE direct_write = LoadLibrary ("dwrite.dll");
+ if (!direct_write)
+ return;
+
+ /* This is only used here, no need to define it globally. */
+ typedef HRESULT (WINAPI *DWCreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u);
+
+ DWCreateFactory dw_create_factory
+ = (DWCreateFactory) get_proc_addr (direct_write,
+ "DWriteCreateFactory");
+
+ if (!dw_create_factory)
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ HRESULT hr = dw_create_factory (DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown **) &dwrite_factory);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateFactory\n", hr));
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->QueryInterface (dwrite_factory,
+ &IID_IDWriteFactory2,
+ (void **) &dwrite_factory2);
+
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) QueryInterface IDWriteFactory2\n", hr));
+ RELEASE_COM (dwrite_factory);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->GetGdiInterop (dwrite_factory,
+ &gdi_interop);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) GetGdiInterop\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ IDWriteRenderingParams *def;
+
+ hr = dwrite_factory->lpVtbl->CreateRenderingParams (dwrite_factory,
+ &def);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateRenderingParams\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ /* range: [0.0, 1.0] */
+ float enhanced_contrast = def->lpVtbl->GetEnhancedContrast (def);
+ /* range: [0.0, 1.0] */
+ float clear_type_level = def->lpVtbl->GetClearTypeLevel (def);
+ /* range: (0.0, 256.0] */
+ float gamma = 1.4; /* def->lpVtbl->GetGamma(def); */
+
+ hr = dwrite_factory->lpVtbl->CreateCustomRenderingParams (dwrite_factory,
+ gamma,
+ enhanced_contrast,
+ clear_type_level,
+ def->lpVtbl->GetPixelGeometry(def),
+ RENDERING_MODE,
+ &rendering_params);
+
+ RELEASE_COM (def);
+
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateCustomRenderingParams\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ direct_write_available = true;
+
+ w32_inhibit_dwrite = false;
+}
+
+bool
+w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+
+ struct uniscribe_font_info *uniscribe_font =
+ (struct uniscribe_font_info *) font;
+
+ /* What we get as y is the baseline position. */
+ y -= font->ascent;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return false;
+
+ struct font_metrics metrics;
+ if (!text_extents_internal (dwrite_font_face, font_size, glyphs, len,
+ &metrics))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ return false;
+ }
+
+ int bitmap_width = metrics.width + metrics.rbearing;
+ int bitmap_height = font->ascent + font->descent;
+
+ /* We never release this, get_bitmap_render_target reuses it. */
+ IDWriteBitmapRenderTarget *bitmap_render_target =
+ get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+ /* If this fails, completely disable DirectWrite. */
+ if (bitmap_render_target == NULL)
+ {
+ direct_write_available = false;
+ return false;
+ }
+
+ /* This DC can't be released. */
+ HDC text_dc = bitmap_render_target->lpVtbl->GetMemoryDC
+ (bitmap_render_target);
+
+ /* Copy the background pixel to the render target bitmap. */
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+ UINT16 *indices = alloca (len * sizeof (UINT16));
+
+ for (int i = 0; i < len; i++)
+ indices[i] = glyphs[i];
+
+ FLOAT *advances = alloca (len * sizeof (FLOAT));
+
+ for (int i = 0; i < len; i++)
+ {
+ if (!text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1,
+ &metrics))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ return false;
+ }
+ advances[i] = metrics.width;
+ }
+
+ DWRITE_GLYPH_RUN glyph_run;
+ glyph_run.fontFace = dwrite_font_face;
+ glyph_run.fontEmSize = font_size;
+ glyph_run.glyphIndices = indices;
+ glyph_run.glyphCount = len;
+ glyph_run.isSideways = false;
+ glyph_run.bidiLevel = 0;
+ glyph_run.glyphOffsets = NULL;
+ glyph_run.glyphAdvances = advances;
+
+ IDWriteColorGlyphRunEnumerator *layers;
+ /* This call will tell us if we hace to handle any color glyph. */
+ hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
+ 0, font->ascent,
+ &glyph_run,
+ NULL,
+ MEASURING_MODE,
+ NULL,
+ 0,
+ &layers);
+
+ /* No color. Just draw the GlyphRun. */
+ if (hr == DWRITE_E_NOCOLOR)
+ bitmap_render_target->lpVtbl->DrawGlyphRun (bitmap_render_target,
+ 0, font->ascent,
+ MEASURING_MODE,
+ &glyph_run,
+ rendering_params,
+ color,
+ NULL);
+ else
+ {
+ /* If there were color glyphs, layers contains a list of GlyphRun
+ with a color and a position for each. We draw them
+ individually. */
+ if (!verify_hr (hr, "Failed at TranslateColorGlyphRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ return false;
+ }
+ for (;;) {
+ HRESULT hr;
+ BOOL more_layers;
+ const DWRITE_COLOR_GLYPH_RUN *layer;
+
+ hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+ if (!verify_hr (hr, "Failed at MoveNext"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ return false;
+ }
+ if (!more_layers)
+ break;
+ hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+ if (!verify_hr (hr, "Failed at GetCurrentRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ return false;
+ }
+ hr = bitmap_render_target->lpVtbl->DrawGlyphRun
+ (bitmap_render_target,
+ layer->baselineOriginX,
+ layer->baselineOriginY,
+ MEASURING_MODE,
+ &layer->glyphRun,
+ rendering_params,
+ RGB (layer->runColor.r * 255,
+ layer->runColor.g * 255,
+ layer->runColor.b * 255),
+ NULL);
+ if (!verify_hr (hr, "Failed at GetCurrentRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ return false;
+ }
+ }
+ RELEASE_COM (layers);
+ }
+
+ /* Finally, copy the rendered text back to the original DC. */
+ BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+
+ return true;
+}
+
+/* Returns true if DirectWrite is to be used:
+ - It is available.
+ - The font is handled by HarfBuzz.
+ - w32-inhibit-dwrite is false.
+ - The font has not been marked after a failed DirectWrite operation.
+ */
+bool
+w32_use_direct_write (struct w32font_info *w32font)
+{
+#ifdef HAVE_HARFBUZZ
+ return direct_write_available
+ && w32font->font.driver == &harfbuzz_font_driver
+ && !w32_inhibit_dwrite
+ && !((struct uniscribe_font_info *) w32font)->dwrite_skip_font;
+#else
+ return false;
+#endif
+}
diff --git a/src/w32font.c b/src/w32font.c
index efb42d80336..05e5a067f20 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -452,6 +452,10 @@ w32font_text_extents (struct font *font, const unsigned *code,
memset (metrics, 0, sizeof (struct font_metrics));
+ if (w32_use_direct_write (w32_font))
+ if (w32_dwrite_text_extents (font, code, nglyphs, metrics))
+ return;
+
for (i = 0, first = true; i < nglyphs; i++)
{
struct w32_metric_cache *char_metric;
@@ -706,22 +710,31 @@ w32font_draw (struct glyph_string *s, int from, int to,
int i;
for (i = 0; i < len; i++)
- {
- WCHAR c = s->char2b[from + i] & 0xFFFF;
- ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
- }
+ if (!w32_use_direct_write (w32font) ||
+ !w32_dwrite_draw (s->hdc, x, y, s->char2b + from, 1,
+ GetTextColor(s->hdc), s->font))
+ {
+ WCHAR c = s->char2b[from + i] & 0xFFFF;
+ ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
+ }
}
else
{
- /* The number of glyphs in a glyph_string cannot be larger than
- the maximum value of the 'used' member of a glyph_row, so we
- are OK using alloca here. */
- eassert (len <= SHRT_MAX);
- WCHAR *chars = alloca (len * sizeof (WCHAR));
- int j;
- for (j = 0; j < len; j++)
- chars[j] = s->char2b[from + j] & 0xFFFF;
- ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ if (!w32_use_direct_write (w32font) ||
+ !w32_dwrite_draw (s->hdc, x, y,
+ s->char2b + from, len, GetTextColor(s->hdc),
+ s->font))
+ {
+ /* The number of glyphs in a glyph_string cannot be larger than
+ the maximum value of the 'used' member of a glyph_row, so we
+ are OK using alloca here. */
+ eassert (len <= SHRT_MAX);
+ WCHAR *chars = alloca (len * sizeof (WCHAR));
+ int j;
+ for (j = 0; j < len; j++)
+ chars[j] = s->char2b[from + j] & 0xFFFF;
+ ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ }
}
/* Restore clip region. */
diff --git a/src/w32font.h b/src/w32font.h
index 3f780c1d866..68d28156125 100644
--- a/src/w32font.h
+++ b/src/w32font.h
@@ -57,6 +57,26 @@ #define W32METRIC_FAIL 2
HFONT hfont;
};
+/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */
+struct uniscribe_font_info
+{
+ struct w32font_info w32_font;
+ /* This is used by the Uniscribe backend as a pointer to the script
+ cache, and by the HarfBuzz backend as a pointer to a hb_font_t
+ object. */
+ void *cache;
+ /* This is used by the HarfBuzz backend to store the font scale. */
+ double scale;
+ /* This is used by DirectWrite to store the FontFace object.
+ DirectWrite works on top of the HarfBuzz backend, modifying some
+ calls. If there are problems manipulating this font,
+ dwrite_skip_font is set to true. Future operations will not use
+ DirectWrite and fall back to the HarfBuzz backend. */
+ void *dwrite_cache;
+ float dwrite_font_size;
+ bool dwrite_skip_font;
+};
+
/* Macros for getting OS specific information from a font struct. */
#define FONT_HANDLE(f) (((struct w32font_info *)(f))->hfont)
#define FONT_TEXTMETRIC(f) (((struct w32font_info *)(f))->metrics)
@@ -84,6 +104,16 @@ #define CACHE_BLOCKSIZE 128
Lisp_Object intern_font_name (char *);
+/* Function prototypes for DirectWrite. */
+void w32_initialize_direct_write (void);
+bool w32_use_direct_write (struct w32font_info *w32font);
+bool w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+bool w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face(void *cache);
+
extern void globals_of_w32font (void);
#endif
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..98e072ce2be 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -44,18 +44,6 @@ #define _WIN32_WINNT 0x0600
#include "pdumper.h"
#include "w32common.h"
-/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */
-struct uniscribe_font_info
-{
- struct w32font_info w32_font;
- /* This is used by the Uniscribe backend as a pointer to the script
- cache, and by the HarfBuzz backend as a pointer to a hb_font_t
- object. */
- void *cache;
- /* This is used by the HarfBuzz backend to store the font scale. */
- double scale;
-};
-
int uniscribe_available = 0;
/* EnumFontFamiliesEx callback. */
@@ -200,6 +188,9 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
/* Initialize the cache for this font. */
uniscribe_font->cache = NULL;
+ uniscribe_font->dwrite_cache = NULL;
+
+ uniscribe_font->dwrite_skip_font = false;
/* Uniscribe and HarfBuzz backends use glyph indices. */
uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
@@ -221,6 +212,7 @@ uniscribe_close (struct font *font)
= (struct uniscribe_font_info *) font;
#ifdef HAVE_HARFBUZZ
+ w32_dwrite_free_cached_face (uniscribe_font->dwrite_cache);
if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
&& uniscribe_font->cache)
hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
@@ -1372,6 +1364,17 @@ w32hb_encode_char (struct font *font, int c)
struct uniscribe_font_info *uniscribe_font
= (struct uniscribe_font_info *) font;
eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+ if (w32_use_direct_write (&uniscribe_font->w32_font))
+ {
+ unsigned encoded = w32_dwrite_encode_char (font, c);
+
+ /* The call to w32_dwrite_encode_char may fail, disabling
+ DirectWrite for this font. So check again. */
+ if (w32_use_direct_write (&uniscribe_font->w32_font))
+ return encoded;
+ }
+
hb_font_t *hb_font = uniscribe_font->cache;
/* First time we use this font with HarfBuzz, create the hb_font_t
@@ -1510,6 +1513,9 @@ w32hb_get_variation_glyphs (struct font *font, int c, unsigned variations[256])
void
syms_of_w32uniscribe (void)
{
+ DEFVAR_BOOL ("w32-inhibit-dwrite", w32_inhibit_dwrite,
+ doc: /* If t, don't use DirectWrite. */);
+
pdumper_do_now_and_after_load (syms_of_w32uniscribe_for_pdumper);
}
@@ -1624,5 +1630,8 @@ syms_of_w32uniscribe_for_pdumper (void)
harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
register_font_driver (&harfbuzz_font_driver, NULL);
+
+ w32_initialize_direct_write ();
+
#endif /* HAVE_HARFBUZZ */
}
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-22 22:14 ` Cecilio Pardo
@ 2024-10-23 10:03 ` Eli Zaretskii
2024-10-23 14:17 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-23 10:03 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730, kbrown
> Date: Wed, 23 Oct 2024 00:14:12 +0200
> Cc: 73730@debbugs.gnu.org, kbrown@cornell.edu
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> Here is a new version with pending issues addressed.
Thanks.
> - DirectWrite is available since Windows 7, but color glyphs are
> available only since Windows 8.1. We only enable DirectWrite since
> Windows 8.1. This is stated on the NEWS entry.
Sounds okay, but I don't see the Windows version tested anywhere in
the code which initializes DWrite. What did I miss?
> - The default value for the Gamma render parameter in DirectWrite
> gives very 'light' characters. I changed it to 1.4, which matches
> GDI and looks better. But that's just my opinion.
Does the best value depend on whether the theme is light or dark? In
any case, this sounds like a good candidate for a variable that people
could modify if they want. Maybe we should also expose to Lisp the
values of enhanced contrast and clear-type level? Assuming that
setting these from .emacs (or maybe in early-init?) at all could take
effect, that is.
> I couldn't find any hebrew font with color glyphs to test RTL
> display. I found a latin+arabic one, Cairo Play:
>
> https://github.com/Gue3bara/Cairo
> https://fonts.google.com/specimen/Cairo+Play
>
> It displays diacritics in different colors on both scripts.
>
> It seems to work fine in emacs. I attach captures with and without
> DirectWrite, and compared to Courier New.
Looks okay to me, thanks.
> +** Emacs on MS-Windows now supports color fonts.
> +On Windows 8.1 and later versions Emacs now uses DirectWrite to draw
> +text, which supports color fonts. This can be disabled by setting the
> +variable w32-inhibit-dwrite to t.
Names of variables and functions in NEWS should be quoted 'like this'.
> + /* We can get fonts with a size of 0. GDI handles this by using a default
> + size. We do the same. */
> + if (font_size <= 0.0f)
> + font_size = FRAME_LINE_HEIGHT (XFRAME (selected_frame));
^^^^^^^^^^^^^^
Maybe use SLECTED_FRAME() here.
> + if (!verify_hr (hr, "Failed to CreateBitmapRenderTarget"))
> + return NULL;
^^^^^^^^^^^^^^^
The indentation here is incorrect: should be 2 columns, not 4.
> + direct_write_available = true;
> +
> + w32_inhibit_dwrite = false;
Does it work to set w32-inhibit-dwrite in the .emacs init file?
> + UINT16 *indices = alloca (len * sizeof (UINT16));
> +
> + for (int i = 0; i < len; i++)
> + indices[i] = glyphs[i];
> +
> + FLOAT *advances = alloca (len * sizeof (FLOAT));
How large can 'len' be? Maybe we should use USE_SAFE_ALLOCA and
friends here, to avoid the possibility of overflowing the C stack if
alloca is called with a too-large argument?
> + for (;;) {
That's not how we format for-loops: the brace should be on the next
line.
Thanks again for working on this.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-23 10:03 ` Eli Zaretskii
@ 2024-10-23 14:17 ` Cecilio Pardo
2024-10-23 17:53 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-23 14:17 UTC (permalink / raw)
To: 73730
On 23/10/2024 12:03, Eli Zaretskii wrote:
>> - DirectWrite is available since Windows 7, but color glyphs are
>> available only since Windows 8.1. We only enable DirectWrite since
>> Windows 8.1. This is stated on the NEWS entry.
>
> Sounds okay, but I don't see the Windows version tested anywhere in
> the code which initializes DWrite. What did I miss?
The IDWriteFactory2 interface is only available from 8.1, so it will
fail on previous versions when we request it in
w32_initialize_direct_write.
>> - The default value for the Gamma render parameter in DirectWrite
>> gives very 'light' characters. I changed it to 1.4, which matches
>> GDI and looks better. But that's just my opinion.
>
> Does the best value depend on whether the theme is light or dark?
No, it looks too dim for me on both.
> In any case, this sounds like a good candidate for a variable that people
> could modify if they want. Maybe we should also expose to Lisp the
> values of enhanced contrast and clear-type level? Assuming that
> setting these from .emacs (or maybe in early-init?) at all could take
> effect, that is.
[...]
> Does it work to set w32-inhibit-dwrite in the .emacs init file?
If you change it in .emacs or early-int it will work, but will not
prevent dwrite initialization and some text output. I am
initializing on syms_of_w32uniscribe_for_pdumper. Could I initialize
on some other place, so that the user can skip dwrite completely?
Also, it will allow to add configuracion variables for Gamma, etc,
without having to reinit if the user sets them.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-23 14:17 ` Cecilio Pardo
@ 2024-10-23 17:53 ` Eli Zaretskii
2024-10-24 20:19 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-23 17:53 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Wed, 23 Oct 2024 16:17:03 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
>
> On 23/10/2024 12:03, Eli Zaretskii wrote:
>
> >> - DirectWrite is available since Windows 7, but color glyphs are
> >> available only since Windows 8.1. We only enable DirectWrite since
> >> Windows 8.1. This is stated on the NEWS entry.
> >
> > Sounds okay, but I don't see the Windows version tested anywhere in
> > the code which initializes DWrite. What did I miss?
>
> The IDWriteFactory2 interface is only available from 8.1, so it will
> fail on previous versions when we request it in
> w32_initialize_direct_write.
OK, so please add a comment there to this effect.
> >> - The default value for the Gamma render parameter in DirectWrite
> >> gives very 'light' characters. I changed it to 1.4, which matches
> >> GDI and looks better. But that's just my opinion.
> >
> > Does the best value depend on whether the theme is light or dark?
>
> No, it looks too dim for me on both.
>
> > In any case, this sounds like a good candidate for a variable that people
> > could modify if they want. Maybe we should also expose to Lisp the
> > values of enhanced contrast and clear-type level? Assuming that
> > setting these from .emacs (or maybe in early-init?) at all could take
> > effect, that is.
>
> [...]
>
> > Does it work to set w32-inhibit-dwrite in the .emacs init file?
>
> If you change it in .emacs or early-int it will work, but will not
> prevent dwrite initialization and some text output. I am
> initializing on syms_of_w32uniscribe_for_pdumper. Could I initialize
> on some other place, so that the user can skip dwrite completely?
> Also, it will allow to add configuracion variables for Gamma, etc,
> without having to reinit if the user sets them.
Initializing dwrite even though the user said we shouldn't use it
doesn't sound too bad to me. We could provide a function to
reinitialize (if it's needed) and document that it should be called if
the user wants to disable dwrite in the init file. I wouldn't go
farther than this unless we get complaints and discover that
initializing dwrite could cause trouble in some configurations.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-23 17:53 ` Eli Zaretskii
@ 2024-10-24 20:19 ` Cecilio Pardo
2024-10-25 7:26 ` Eli Zaretskii
2024-10-25 11:51 ` Eli Zaretskii
0 siblings, 2 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-24 20:19 UTC (permalink / raw)
To: 73730
[-- Attachment #1: Type: text/plain, Size: 358 bytes --]
New version attached.
- Added comments
- Use SAFE_ALLOCA instead of alloca.
- Added two lisp functions:
w32-dwrite-available to find out is dwrite is working
w32-dwrite-reinit to reinitialize dwrite, optionally
changing render parameters (gamma, cleartype level, contrast).
Can go back to defaults passing nil.
- Updated NEWS and changelog.
[-- Attachment #2: 0001-Implement-drawing-text-with-DirectWrite-on-MS-Window.patch --]
[-- Type: text/plain, Size: 44259 bytes --]
From a92b401ca8729c3e82e399d47e4f2d71cdaca266 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Implement drawing text with DirectWrite on MS-Windows.
This adds support for color fonts.
* configure.ac: Add src/w32drite to W32_OBJ.
* src/w32dwrite.c: This is a new file.
(w32-initialize-direct-write): New function, initialize the DirectWrite
library if it is available, and required global variables.
(w32_use_direct_write): New function, check if DirectWrite is available
and activated by the user.
(w32_dwrite_encode_char): New function, replacement for HarfBuzz's
encode_char.
(w32_dwrite_text_extents): New function, replacement for w32font
text_extents.
(w32_dwrite_draw): New function, replacement for w32font draw.
(w32_dwrite_free_cached_face): New function, used on the font deletion
process to also delete DirectWrite data.
(verify_hr): New function, verify COM method results.
(release_com): New function, release a COM object.
(w32-dwrite-available): New function, returns true if DirectWrite is
available.
(w32-dwrite-reinit): New function, reinitialize DirectWrite, optionally
setting some rendering parameters.
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled, call
w32_dwrite_text_extents.
(w32font_draw): If DirectWrite is enabled, call w32_dwrite_draw.
* src/w32uniscribe.c: (w32hb_encode_char): If DirectWrite is enabled,
call w32_dwrite_encode_char.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.
---
configure.ac | 2 +-
etc/NEWS | 8 +
src/w32dwrite.c | 1099 ++++++++++++++++++++++++++++++++++++++++++++
src/w32font.c | 39 +-
src/w32font.h | 31 ++
src/w32uniscribe.c | 31 +-
6 files changed, 1184 insertions(+), 26 deletions(-)
create mode 100644 src/w32dwrite.c
diff --git a/configure.ac b/configure.ac
index 947c2827b8e..1c7545ef984 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
AC_CHECK_TOOL([WINDRES], [windres],
[AC_MSG_ERROR([No resource compiler found.])])
W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
- W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+ W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
EMACSRES="emacs.res"
case "$canonical" in
x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/etc/NEWS b/etc/NEWS
index a4a036cbb50..420c906baa9 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -783,6 +783,14 @@ and later versions.
** Emacs on MS-Windows now supports drag-n-drop of text into a buffer.
This is in addition to drag-n-drop of files, that was already supported.
+---
+** Emacs on MS-Windows now supports color fonts.
+On Windows 8.1 and later versions Emacs now uses DirectWrite to draw
+text, which supports color fonts. This can be disabled by setting the
+variable 'w32-inhibit-dwrite' to t. Also see 'w32-dwrite-available' and
+'w32-dwrite-reinit' to check availability and to configure render
+parameters.
+
\f
----------------------------------------------------------------------
This file is part of GNU Emacs.
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..9f7b8d96977
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,1099 @@
+/* Support for using DirectWrite on MS-Windows to draw text. This
+ allows for color fonts.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
+
+/* This requires the HarfBuzz font backend to be available.
+
+ It works by modifying the HarfBuzz backend to use DirectWrite at
+ some points, if it is available:
+
+ - When encoding characters: w32hb_encode_char
+ - When measuring text: w32font_text_extents
+ - When drawing text: w32font_draw
+
+ DirectWrite is setup by calling w32_initialize_direct_write. From
+ that point, the function w32_use_direct_write will return true if
+ DirectWrite is to be used.
+
+ DirectWrite is available since Windows 7, but we don't activate it on
+ versions before 8.1, because color fonts are only available since that. */
+
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#ifndef MINGW_W64
+# define INITGUID
+#endif
+#include <initguid.h>
+#include <ole2.h>
+#include <unknwn.h>
+
+#include "frame.h"
+#include "w32font.h"
+#include "w32common.h"
+#include "w32term.h"
+
+#ifndef MINGW_W64
+
+/* The following definitions would be included from dwrite_3.h, but it
+ is not available when building with mingw.org's MinGW. Methods that
+ we don't use are declared with the EMACS_DWRITE_UNUSED macro, to
+ avoid bringing in more types that would need to be declared. */
+
+#define EMACS_DWRITE_UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
+
+#define DWRITE_E_NOCOLOR _HRESULT_TYPEDEF_(0x8898500CL)
+
+typedef enum DWRITE_PIXEL_GEOMETRY {
+ DWRITE_PIXEL_GEOMETRY_FLAT = 0,
+ DWRITE_PIXEL_GEOMETRY_RGB = 1,
+ DWRITE_PIXEL_GEOMETRY_BGR = 2
+} DWRITE_PIXEL_GEOMETRY;
+
+typedef enum DWRITE_RENDERING_MODE {
+ DWRITE_RENDERING_MODE_DEFAULT = 0,
+ DWRITE_RENDERING_MODE_ALIASED = 1,
+ DWRITE_RENDERING_MODE_GDI_CLASSIC = 2,
+ DWRITE_RENDERING_MODE_GDI_NATURAL = 3,
+ DWRITE_RENDERING_MODE_NATURAL = 4,
+ DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC = 5,
+ DWRITE_RENDERING_MODE_OUTLINE = 6
+} DWRITE_RENDERING_MODE;
+
+typedef enum DWRITE_MEASURING_MODE {
+ DWRITE_MEASURING_MODE_NATURAL = 0,
+ DWRITE_MEASURING_MODE_GDI_CLASSIC = 1,
+ DWRITE_MEASURING_MODE_GDI_NATURAL = 2
+} DWRITE_MEASURING_MODE;
+
+typedef enum DWRITE_TEXT_ANTIALIAS_MODE {
+ DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE = 0,
+ DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE = 1
+} DWRITE_TEXT_ANTIALIAS_MODE;
+
+typedef enum DWRITE_FACTORY_TYPE {
+ DWRITE_FACTORY_TYPE_SHARED = 0,
+ DWRITE_FACTORY_TYPE_ISOLATED = 1
+} DWRITE_FACTORY_TYPE;
+
+typedef struct DWRITE_FONT_METRICS {
+ UINT16 designUnitsPerEm;
+ UINT16 ascent;
+ UINT16 descent;
+ INT16 lineGap;
+ UINT16 capHeight;
+ UINT16 xHeight;
+ INT16 underlinePosition;
+ UINT16 underlineThickness;
+ INT16 strikethroughPosition;
+ UINT16 strikethroughThickness;
+} DWRITE_FONT_METRICS;
+
+typedef struct DWRITE_GLYPH_METRICS {
+ INT32 leftSideBearing;
+ UINT32 advanceWidth;
+ INT32 rightSideBearing;
+ INT32 topSideBearing;
+ UINT32 advanceHeight;
+ INT32 bottomSideBearing;
+ INT32 verticalOriginY;
+} DWRITE_GLYPH_METRICS;
+
+typedef interface IDWriteRenderingParams IDWriteRenderingParams;
+typedef interface IDWriteFont IDWriteFont;
+typedef interface IDWriteGdiInterop IDWriteGdiInterop;
+typedef interface IDWriteFactory IDWriteFactory;
+typedef interface IDWriteFactory2 IDWriteFactory2;
+typedef interface IDWriteFontFace IDWriteFontFace;
+typedef interface IDWriteBitmapRenderTarget IDWriteBitmapRenderTarget;
+typedef interface IDWriteBitmapRenderTarget1 IDWriteBitmapRenderTarget1;
+typedef interface IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator;
+
+DEFINE_GUID(IID_IDWriteBitmapRenderTarget1, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
+DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
+DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
+
+typedef struct DWRITE_GLYPH_OFFSET {
+ FLOAT advanceOffset;
+ FLOAT ascenderOffset;
+} DWRITE_GLYPH_OFFSET;
+
+typedef struct DWRITE_GLYPH_RUN {
+ IDWriteFontFace *fontFace;
+ FLOAT fontEmSize;
+ UINT32 glyphCount;
+ const UINT16 *glyphIndices;
+ const FLOAT *glyphAdvances;
+ const DWRITE_GLYPH_OFFSET *glyphOffsets;
+ WINBOOL isSideways;
+ UINT32 bidiLevel;
+} DWRITE_GLYPH_RUN;
+
+typedef struct _D3DCOLORVALUE {
+ float r;
+ float g;
+ float b;
+ float a;
+} D3DCOLORVALUE;
+
+typedef D3DCOLORVALUE DWRITE_COLOR_F;
+
+typedef struct DWRITE_COLOR_GLYPH_RUN {
+ DWRITE_GLYPH_RUN glyphRun;
+ void *glyphRunDescription;
+ FLOAT baselineOriginX;
+ FLOAT baselineOriginY;
+ DWRITE_COLOR_F runColor;
+ UINT16 paletteIndex;
+} DWRITE_COLOR_GLYPH_RUN;
+
+typedef struct IDWriteFontFaceVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFontFace *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFontFace *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFontFace *This);
+
+ EMACS_DWRITE_UNUSED(GetType);
+ EMACS_DWRITE_UNUSED(GetFiles);
+ EMACS_DWRITE_UNUSED(GetIndex);
+ EMACS_DWRITE_UNUSED(GetSimulations);
+ EMACS_DWRITE_UNUSED(IsSymbolFont);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED (GetGlyphCount);
+ EMACS_DWRITE_UNUSED (GetDesignGlyphMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)
+ (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count,
+ UINT16 *glyph_indices);
+
+ EMACS_DWRITE_UNUSED (TryGetFontTable);
+ EMACS_DWRITE_UNUSED (ReleaseFontTable);
+ EMACS_DWRITE_UNUSED (GetGlyphRunOutline);
+ EMACS_DWRITE_UNUSED (GetRecommendedRenderingMode);
+ EMACS_DWRITE_UNUSED (GetGdiCompatibleMetrics);
+
+ HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)
+ (IDWriteFontFace *This,
+ FLOAT emSize,
+ FLOAT pixels_per_dip,
+ void *transform,
+ WINBOOL use_gdi_natural,
+ const UINT16 *glyph_indices,
+ UINT32 glyph_count,
+ DWRITE_GLYPH_METRICS *metrics,
+ WINBOOL is_sideways);
+ END_INTERFACE
+} IDWriteFontFaceVtbl;
+
+interface IDWriteFontFace {
+ CONST_VTBL IDWriteFontFaceVtbl* lpVtbl;
+};
+
+typedef struct IDWriteRenderingParamsVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteRenderingParams *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteRenderingParams *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteRenderingParams *This);
+
+ FLOAT (STDMETHODCALLTYPE *GetGamma)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)
+ (IDWriteRenderingParams *This);
+ FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)
+ (IDWriteRenderingParams *This);
+ int (STDMETHODCALLTYPE *GetPixelGeometry)
+ (IDWriteRenderingParams *This);
+ END_INTERFACE
+} IDWriteRenderingParamsVtbl;
+
+interface IDWriteRenderingParams {
+ CONST_VTBL IDWriteRenderingParamsVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFontVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFont *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFont *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFont *This);
+
+ EMACS_DWRITE_UNUSED (GetFontFamily);
+ EMACS_DWRITE_UNUSED (GetWeight);
+ EMACS_DWRITE_UNUSED (GetStretch);
+ EMACS_DWRITE_UNUSED (GetStyle);
+ EMACS_DWRITE_UNUSED (IsSymbolFont);
+ EMACS_DWRITE_UNUSED (GetFaceNames);
+ EMACS_DWRITE_UNUSED (GetInformationalStrings);
+ EMACS_DWRITE_UNUSED (GetSimulations);
+
+ void (STDMETHODCALLTYPE *GetMetrics)
+ (IDWriteFont *This, DWRITE_FONT_METRICS *metrics);
+
+ EMACS_DWRITE_UNUSED(HasCharacter);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFace)
+ (IDWriteFont *This, IDWriteFontFace **face);
+
+ END_INTERFACE
+} IDWriteFontVtbl;
+
+interface IDWriteFont {
+ CONST_VTBL IDWriteFontVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTargetVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteBitmapRenderTarget *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteBitmapRenderTarget *This);
+
+ HRESULT (STDMETHODCALLTYPE *DrawGlyphRun)
+ (IDWriteBitmapRenderTarget *This,
+ FLOAT baselineOriginX,
+ FLOAT baselineOriginY,
+ DWRITE_MEASURING_MODE measuring_mode,
+ const DWRITE_GLYPH_RUN *glyph_run,
+ IDWriteRenderingParams *params,
+ COLORREF textColor,
+ RECT *blackbox_rect);
+
+ HDC (STDMETHODCALLTYPE *GetMemoryDC)(IDWriteBitmapRenderTarget *This);
+
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+
+ HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip)
+ (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip);
+
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ END_INTERFACE
+} IDWriteBitmapRenderTargetVtbl;
+
+interface IDWriteBitmapRenderTarget {
+ CONST_VTBL IDWriteBitmapRenderTargetVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTarget1Vtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteBitmapRenderTarget1 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef) (IDWriteBitmapRenderTarget1 *This);
+ ULONG (STDMETHODCALLTYPE *Release) (IDWriteBitmapRenderTarget1 *This);
+
+ EMACS_DWRITE_UNUSED (DrawGlyphRun);
+ EMACS_DWRITE_UNUSED (GetMemoryDC);
+ EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (SetPixelsPerDip);
+ EMACS_DWRITE_UNUSED (GetCurrentTransform);
+ EMACS_DWRITE_UNUSED (SetCurrentTransform);
+ EMACS_DWRITE_UNUSED (GetSize);
+ EMACS_DWRITE_UNUSED (Resize);
+ EMACS_DWRITE_UNUSED (GetTextAntialiasMode);
+
+ HRESULT (STDMETHODCALLTYPE *SetTextAntialiasMode)
+ (IDWriteBitmapRenderTarget1 *This, DWRITE_TEXT_ANTIALIAS_MODE mode);
+
+ END_INTERFACE
+} IDWriteBitmapRenderTarget1Vtbl;
+
+interface IDWriteBitmapRenderTarget1 {
+ CONST_VTBL IDWriteBitmapRenderTarget1Vtbl* lpVtbl;
+};
+
+typedef struct IDWriteGdiInteropVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteGdiInterop *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteGdiInterop *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteGdiInterop *This);
+
+ HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)
+ (IDWriteGdiInterop *This, const LOGFONTW *logfont,
+ IDWriteFont **font);
+
+ EMACS_DWRITE_UNUSED (ConvertFontToLOGFONT);
+ EMACS_DWRITE_UNUSED (ConvertFontFaceToLOGFONT);
+ EMACS_DWRITE_UNUSED (CreateFontFaceFromHdc);
+
+ HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)
+ (IDWriteGdiInterop *This, HDC hdc, UINT32 width, UINT32 height,
+ IDWriteBitmapRenderTarget **target);
+ END_INTERFACE
+} IDWriteGdiInteropVtbl;
+
+interface IDWriteGdiInterop {
+ CONST_VTBL IDWriteGdiInteropVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactoryVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory *This);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)
+ (IDWriteFactory *This, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)
+ (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast,
+ FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry,
+ DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ HRESULT (STDMETHODCALLTYPE *GetGdiInterop)
+ (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactoryVtbl;
+
+interface IDWriteFactory { CONST_VTBL IDWriteFactoryVtbl* lpVtbl; };
+
+typedef struct IDWriteColorGlyphRunEnumeratorVtbl {
+ BEGIN_INTERFACE
+
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteColorGlyphRunEnumerator *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteColorGlyphRunEnumerator *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteColorGlyphRunEnumerator *This);
+
+ HRESULT (STDMETHODCALLTYPE *MoveNext)(
+ IDWriteColorGlyphRunEnumerator *This,
+ WINBOOL *hasRun);
+
+ HRESULT (STDMETHODCALLTYPE *GetCurrentRun)(
+ IDWriteColorGlyphRunEnumerator *This,
+ const DWRITE_COLOR_GLYPH_RUN **run);
+
+ END_INTERFACE
+} IDWriteColorGlyphRunEnumeratorVtbl;
+
+interface IDWriteColorGlyphRunEnumerator {
+ CONST_VTBL IDWriteColorGlyphRunEnumeratorVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactory2Vtbl {
+ BEGIN_INTERFACE
+ HRESULT (STDMETHODCALLTYPE *QueryInterface)
+ (IDWriteFactory2 *This, REFIID riid, void **ppvObject);
+ ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory2 *This);
+ ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory2 *This);
+ EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+ EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+ EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+ EMACS_DWRITE_UNUSED (CreateFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+ EMACS_DWRITE_UNUSED (CreateFontFace);
+ EMACS_DWRITE_UNUSED (CreateRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+ EMACS_DWRITE_UNUSED (CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+ EMACS_DWRITE_UNUSED (CreateTextFormat);
+ EMACS_DWRITE_UNUSED (CreateTypography);
+ EMACS_DWRITE_UNUSED (GetGdiInterop);
+ EMACS_DWRITE_UNUSED (CreateTextLayout);
+ EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+ EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+ EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+ EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+ EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+
+ EMACS_DWRITE_UNUSED (GetEudcFontCollection);
+ EMACS_DWRITE_UNUSED (IDWriteFactory1_CreateCustomRenderingParams);
+
+ EMACS_DWRITE_UNUSED (GetSystemFontFallback);
+ EMACS_DWRITE_UNUSED (CreateFontFallbackBuilder);
+ HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)
+ (IDWriteFactory2 *This,
+ FLOAT originX,
+ FLOAT originY,
+ const DWRITE_GLYPH_RUN *run,
+ void *rundescr,
+ DWRITE_MEASURING_MODE mode,
+ void *transform,
+ UINT32 palette_index,
+ IDWriteColorGlyphRunEnumerator **colorlayers);
+
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateCustomRenderingParams);
+ EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis);
+ END_INTERFACE
+} IDWriteFactory2Vtbl;
+
+interface IDWriteFactory2 {
+ CONST_VTBL IDWriteFactory2Vtbl* lpVtbl;
+};
+#else /* ifndef MINGW_W64 */
+# include <dwrite_3.h>
+#endif
+
+/* User configurable variables. If they are lower than 0 use
+ DirectWrite's defaults, or our defaults. To set them, the user calls
+ 'w32-dwrite-reinit' */
+static float config_enhanced_contrast = -1.0f;
+static float config_clear_type_level = -1.0f;
+static float config_gamma = -1.0f;
+
+/* Values to use for DirectWrite rendering. */
+#define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL
+#define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+#define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE
+
+static void
+release_com (IUnknown **i)
+{
+ if ( *i )
+ {
+ ((IUnknown *) (*i))->lpVtbl->Release (*i);
+ *i = NULL;
+ }
+}
+
+#define RELEASE_COM(i) release_com ( (IUnknown **) &i )
+
+/* Global variables for DirectWrite. */
+static bool direct_write_available = false;
+static IDWriteFactory *dwrite_factory = NULL;
+static IDWriteFactory2 *dwrite_factory2 = NULL;
+static IDWriteGdiInterop *gdi_interop = NULL;
+static IDWriteRenderingParams *rendering_params = NULL;
+
+static bool
+verify_hr (HRESULT hr, const char *msg)
+{
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) %s\n", hr, msg));
+ eassert (SUCCEEDED (hr));
+ return false;
+ }
+ return true;
+}
+
+/* Gets a IDWriteFontFace from a struct font (its HFONT). Returns the
+ font size in points. It may fail to get a DirectWrite font, and face
+ will be NULL on return. This happens for some fonts like Courier.
+
+ Never call Release on the result, as it is cached for reuse on the
+ struct font. */
+static float
+get_font_face (struct font *infont, IDWriteFontFace **face)
+{
+ HRESULT hr;
+ LOGFONTW logfont;
+ IDWriteFont *font;
+
+ struct uniscribe_font_info *uniscribe_font =
+ (struct uniscribe_font_info *) infont;
+
+ /* Check the cache. */
+ *face = uniscribe_font->dwrite_cache;
+ if (*face)
+ return uniscribe_font->dwrite_font_size;
+
+ GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont);
+
+ hr = gdi_interop->lpVtbl->CreateFontFromLOGFONT (gdi_interop,
+ (const LOGFONTW *) &logfont,
+ &font);
+
+ if (!verify_hr (hr, "Failed to CreateFontFromLOGFONT"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ *face = NULL;
+ return 0.0;
+ }
+
+ hr = font->lpVtbl->CreateFontFace (font, face);
+ RELEASE_COM (font);
+ if (!verify_hr (hr, "Failed to create DWriteFontFace"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ *face = NULL;
+ return 0.0;
+ }
+
+ /* Cache this FontFace. */
+ uniscribe_font->dwrite_font_size = abs (logfont.lfHeight);
+ uniscribe_font->dwrite_cache = *face;
+
+ return abs (logfont.lfHeight);
+}
+
+void
+w32_dwrite_free_cached_face (void *cache)
+{
+ if (cache)
+ RELEASE_COM (cache);
+}
+
+static float
+convert_metrics_sz (int sz, float font_size, int units_per_em)
+{
+ return (float) sz * font_size / units_per_em;
+}
+
+/* Does not fill in the ascent and descent fields of metrics. */
+static bool
+text_extents_internal (IDWriteFontFace *dwrite_font_face,
+ float font_size, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ HRESULT hr;
+
+ USE_SAFE_ALLOCA;
+
+ DWRITE_FONT_METRICS dwrite_font_metrics;
+ dwrite_font_face->lpVtbl->GetMetrics (dwrite_font_face,
+ &dwrite_font_metrics);
+
+ UINT16 *indices = SAFE_ALLOCA (nglyphs * sizeof (UINT16));
+ for (int i = 0; i < nglyphs; i++)
+ indices[i] = code[i];
+
+ DWRITE_GLYPH_METRICS* gmetrics =
+ SAFE_ALLOCA (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+ hr = dwrite_font_face->lpVtbl->GetGdiCompatibleGlyphMetrics (dwrite_font_face,
+ font_size,
+ 1.0,
+ NULL,
+ TRUE,
+ indices,
+ nglyphs,
+ gmetrics,
+ false);
+ if (!verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics"))
+ {
+ SAFE_FREE ();
+ return false;
+ }
+
+ float width = 0;
+ int du_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+ for (int i = 0; i < nglyphs; i++)
+ {
+ float advance =
+ convert_metrics_sz (gmetrics[i].advanceWidth, font_size, du_per_em);
+
+ width += advance;
+
+ float lbearing =
+ round (convert_metrics_sz (gmetrics[i].leftSideBearing, font_size,
+ du_per_em));
+ float rbearing =
+ round (advance -
+ convert_metrics_sz (gmetrics[i].rightSideBearing,
+ font_size, du_per_em));
+ if (i == 0)
+ {
+ metrics->lbearing = lbearing;
+ metrics->rbearing = rbearing;
+ }
+ if (metrics->lbearing > lbearing)
+ metrics->lbearing = lbearing;
+ if (metrics->rbearing < rbearing)
+ metrics->rbearing = rbearing;
+ }
+ metrics->width = round(width);
+ SAFE_FREE ();
+ return true;
+}
+
+unsigned
+w32_dwrite_encode_char (struct font *font, int c)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+ UINT16 index;
+
+ get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return FONT_INVALID_CODE;
+ hr = dwrite_font_face->lpVtbl->GetGlyphIndices (dwrite_font_face,
+ &c, 1, &index);
+ if (verify_hr (hr, "Failed to GetGlyphIndices"))
+ {
+ if (index == 0)
+ return FONT_INVALID_CODE;
+ return index;
+ }
+ ((struct uniscribe_font_info *) font)->dwrite_skip_font = true;
+ return FONT_INVALID_CODE;
+}
+
+bool
+w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs,
+ struct font_metrics *metrics)
+{
+ IDWriteFontFace *dwrite_font_face;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+
+ if (dwrite_font_face == NULL)
+ return false;
+
+ /* We can get fonts with a size of 0. GDI handles this by using a default
+ size. We do the same. */
+ if (font_size <= 0.0f)
+ font_size = FRAME_LINE_HEIGHT (SELECTED_FRAME ());
+
+ metrics->ascent = font->ascent;
+ metrics->descent = font->descent;
+
+ return text_extents_internal (dwrite_font_face, font_size, code, nglyphs,
+ metrics);
+}
+
+/* Never call Release on the value returned by this function, as it is
+ reused. */
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+ HRESULT hr;
+ static IDWriteBitmapRenderTarget *brt = NULL;
+ static SIZE size = {0, 0};
+
+ if (brt)
+ {
+ /* Check if we need to make a bigger one. */
+ if (width <= size.cx && height <= size.cy)
+ return brt;
+ RELEASE_COM (brt);
+ }
+
+ if (width > size.cx)
+ size.cx = width;
+ if (height > size.cy)
+ size.cy = height;
+
+ hr = gdi_interop->lpVtbl->CreateBitmapRenderTarget (gdi_interop,
+ hdc,
+ size.cx, size.cy,
+ &brt);
+ if (!verify_hr (hr, "Failed to CreateBitmapRenderTarget"))
+ return NULL;
+
+ /* We handle high dpi displays by incresing font size, so override
+ PixelsPerDip. */
+ brt->lpVtbl->SetPixelsPerDip (brt, 1.0);
+
+ /* The SetTextAntialiasMode method is only available in
+ IDWriteBitmapRenderTarget1. */
+ IDWriteBitmapRenderTarget1 *brt1;
+ hr = brt->lpVtbl->QueryInterface (brt,
+ &IID_IDWriteBitmapRenderTarget1,
+ (void **) &brt1);
+ /* This error should not happen, but is not catastrofic */
+ if (verify_hr (hr, "Failed to QueryInterface for IDWriteBitmapRenderTarget1"))
+ {
+ brt1->lpVtbl->SetTextAntialiasMode (brt1, ANTIALIAS_MODE);
+ RELEASE_COM (brt1);
+ }
+
+ return brt;
+}
+
+void
+w32_initialize_direct_write (void)
+{
+ direct_write_available = false;
+
+ if (dwrite_factory)
+ {
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ RELEASE_COM (rendering_params);
+ }
+
+ HMODULE direct_write = LoadLibrary ("dwrite.dll");
+ if (!direct_write)
+ return;
+
+ /* This is only used here, no need to define it globally. */
+ typedef HRESULT (WINAPI *DWCreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u);
+
+ DWCreateFactory dw_create_factory
+ = (DWCreateFactory) get_proc_addr (direct_write,
+ "DWriteCreateFactory");
+
+ if (!dw_create_factory)
+ {
+ FreeLibrary (direct_write);
+ return;
+ }
+
+ HRESULT hr = dw_create_factory (DWRITE_FACTORY_TYPE_SHARED,
+ &IID_IDWriteFactory,
+ (IUnknown **) &dwrite_factory);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateFactory\n", hr));
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ /* IDWriteFactory2 is only available on Windows 8.1 and later.
+ Without this, we can't use color fonts. So we disable DirectWrite
+ if it is not available. */
+ hr = dwrite_factory->lpVtbl->QueryInterface (dwrite_factory,
+ &IID_IDWriteFactory2,
+ (void **) &dwrite_factory2);
+
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) QueryInterface IDWriteFactory2\n", hr));
+ RELEASE_COM (dwrite_factory);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ hr = dwrite_factory->lpVtbl->GetGdiInterop (dwrite_factory,
+ &gdi_interop);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) GetGdiInterop\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ IDWriteRenderingParams *def;
+
+ hr = dwrite_factory->lpVtbl->CreateRenderingParams (dwrite_factory,
+ &def);
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateRenderingParams\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ /* range: [0.0, 1.0] */
+ if (config_enhanced_contrast < 0.0f || config_enhanced_contrast > 1.0f)
+ config_enhanced_contrast = def->lpVtbl->GetEnhancedContrast (def);
+
+ /* range: [0.0, 1.0] */
+ if (config_clear_type_level < 0.0f || config_clear_type_level > 1.0f)
+ config_clear_type_level = def->lpVtbl->GetClearTypeLevel (def);
+
+ /* range: (0.0, 256.0] */
+ /* We change the default value of 2.2 for gamma to 1.4, that looks
+ very similar to GDI. The default looks too dim for emacs,
+ subjectively. */
+ if (config_gamma <= 0.0f || config_gamma > 256.0f)
+ config_gamma = 1.4; /* def->lpVtbl->GetGamma (def); */
+
+ hr = dwrite_factory->lpVtbl->CreateCustomRenderingParams (dwrite_factory,
+ config_gamma,
+ config_enhanced_contrast,
+ config_clear_type_level,
+ def->lpVtbl->GetPixelGeometry(def),
+ RENDERING_MODE,
+ &rendering_params);
+
+ RELEASE_COM (def);
+
+ if (FAILED (hr))
+ {
+ DebPrint (("DirectWrite HRESULT failed: (%d) CreateCustomRenderingParams\n", hr));
+ RELEASE_COM (dwrite_factory);
+ RELEASE_COM (dwrite_factory2);
+ RELEASE_COM (gdi_interop);
+ FreeLibrary (direct_write);
+ eassert (SUCCEEDED (hr));
+ return;
+ }
+
+ direct_write_available = true;
+
+ w32_inhibit_dwrite = false;
+}
+
+bool
+w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font)
+{
+ HRESULT hr;
+ IDWriteFontFace *dwrite_font_face;
+
+ USE_SAFE_ALLOCA;
+
+ struct uniscribe_font_info *uniscribe_font =
+ (struct uniscribe_font_info *) font;
+
+ /* What we get as y is the baseline position. */
+ y -= font->ascent;
+
+ float font_size = get_font_face (font, &dwrite_font_face);
+ if (dwrite_font_face == NULL)
+ return false;
+
+ struct font_metrics metrics;
+ if (!text_extents_internal (dwrite_font_face, font_size, glyphs, len,
+ &metrics))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ return false;
+ }
+
+ int bitmap_width = metrics.width + metrics.rbearing;
+ int bitmap_height = font->ascent + font->descent;
+
+ /* We never release this, get_bitmap_render_target reuses it. */
+ IDWriteBitmapRenderTarget *bitmap_render_target =
+ get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+ /* If this fails, completely disable DirectWrite. */
+ if (bitmap_render_target == NULL)
+ {
+ direct_write_available = false;
+ return false;
+ }
+
+ /* This DC can't be released. */
+ HDC text_dc = bitmap_render_target->lpVtbl->GetMemoryDC
+ (bitmap_render_target);
+
+ /* Copy the background pixel to the render target bitmap. */
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+ UINT16 *indices = SAFE_ALLOCA (len * sizeof (UINT16));
+
+ for (int i = 0; i < len; i++)
+ indices[i] = glyphs[i];
+
+ FLOAT *advances = SAFE_ALLOCA (len * sizeof (FLOAT));
+
+ for (int i = 0; i < len; i++)
+ {
+ if (!text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1,
+ &metrics))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ SAFE_FREE ();
+ return false;
+ }
+ advances[i] = metrics.width;
+ }
+
+ DWRITE_GLYPH_RUN glyph_run;
+ glyph_run.fontFace = dwrite_font_face;
+ glyph_run.fontEmSize = font_size;
+ glyph_run.glyphIndices = indices;
+ glyph_run.glyphCount = len;
+ glyph_run.isSideways = false;
+ glyph_run.bidiLevel = 0;
+ glyph_run.glyphOffsets = NULL;
+ glyph_run.glyphAdvances = advances;
+
+ IDWriteColorGlyphRunEnumerator *layers;
+ /* This call will tell us if we hace to handle any color glyph. */
+ hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
+ 0, font->ascent,
+ &glyph_run,
+ NULL,
+ MEASURING_MODE,
+ NULL,
+ 0,
+ &layers);
+
+ /* No color. Just draw the GlyphRun. */
+ if (hr == DWRITE_E_NOCOLOR)
+ bitmap_render_target->lpVtbl->DrawGlyphRun (bitmap_render_target,
+ 0, font->ascent,
+ MEASURING_MODE,
+ &glyph_run,
+ rendering_params,
+ color,
+ NULL);
+ else
+ {
+ /* If there were color glyphs, layers contains a list of GlyphRun
+ with a color and a position for each. We draw them
+ individually. */
+ if (!verify_hr (hr, "Failed at TranslateColorGlyphRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ SAFE_FREE ();
+ return false;
+ }
+ for (;;)
+ {
+ HRESULT hr;
+ BOOL more_layers;
+ const DWRITE_COLOR_GLYPH_RUN *layer;
+
+ hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+ if (!verify_hr (hr, "Failed at MoveNext"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ SAFE_FREE ();
+ return false;
+ }
+ if (!more_layers)
+ break;
+ hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+ if (!verify_hr (hr, "Failed at GetCurrentRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ SAFE_FREE ();
+ return false;
+ }
+ hr = bitmap_render_target->lpVtbl->DrawGlyphRun
+ (bitmap_render_target,
+ layer->baselineOriginX,
+ layer->baselineOriginY,
+ MEASURING_MODE,
+ &layer->glyphRun,
+ rendering_params,
+ RGB (layer->runColor.r * 255,
+ layer->runColor.g * 255,
+ layer->runColor.b * 255),
+ NULL);
+ if (!verify_hr (hr, "Failed at GetCurrentRun"))
+ {
+ uniscribe_font->dwrite_skip_font = true;
+ RELEASE_COM (layers);
+ SAFE_FREE ();
+ return false;
+ }
+ }
+ RELEASE_COM (layers);
+ }
+
+ /* Finally, copy the rendered text back to the original DC. */
+ BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+ SAFE_FREE ();
+ return true;
+}
+
+/* Returns true if DirectWrite is to be used:
+ - It is available.
+ - The font is handled by HarfBuzz.
+ - w32-inhibit-dwrite is false.
+ - The font has not been marked after a failed DirectWrite operation.
+*/
+bool
+w32_use_direct_write (struct w32font_info *w32font)
+{
+#ifdef HAVE_HARFBUZZ
+ return direct_write_available
+ && w32font->font.driver == &harfbuzz_font_driver
+ && !w32_inhibit_dwrite
+ && !((struct uniscribe_font_info *) w32font)->dwrite_skip_font;
+#else
+ return false;
+#endif
+}
+
+DEFUN ("w32-dwrite-available", Fw32_dwrite_available, Sw32_dwrite_available, 0, 0, 0,
+ doc: /* Returns t if DirectWrite is available.
+DirectWrite will be used if it is available and 'w32-inhibit-dwrite' is nil. */)
+ (void)
+{
+ return direct_write_available ? Qt : Qnil;
+}
+
+DEFUN ("w32-dwrite-reinit", Fw32_dwrite_reinit, Sw32_dwrite_reinit, 0, 3, 0,
+ doc: /* Reinitialize DirectWrite with the given parameters.
+If a parameter is not specified, or is out of range, it will take a default
+value. Returns nil.
+
+ENHANCED_CONTRAST is in the range [0.0, 1.0]
+CLEAR_TYPE_LEVEL is in the range [0.0, 1.0]
+GAMMA is in the range (0.0, 256.0] */)
+ (Lisp_Object enhanced_contrast, Lisp_Object clear_type_level,
+ Lisp_Object gamma)
+{
+ config_enhanced_contrast = -1.0f;
+ if (FLOATP (enhanced_contrast))
+ config_enhanced_contrast = XFLOAT_DATA (enhanced_contrast);
+ if (FIXNUMP (enhanced_contrast))
+ config_enhanced_contrast = XFIXNUM (enhanced_contrast);
+
+ config_clear_type_level = -1.0f;
+ if (FLOATP (clear_type_level))
+ config_clear_type_level = XFLOAT_DATA (clear_type_level);
+ if (FIXNUMP (clear_type_level))
+ config_clear_type_level = XFIXNUM (clear_type_level);
+
+ config_gamma = -1.0f;
+ if (FLOATP (gamma))
+ config_gamma = XFLOAT_DATA (gamma);
+ if (FIXNUMP (gamma))
+ config_gamma = XFIXNUM (gamma);
+
+ w32_initialize_direct_write ();
+
+ return Qnil;
+}
+
+void
+syms_of_w32dwrite (void)
+{
+ DEFVAR_BOOL ("w32-inhibit-dwrite", w32_inhibit_dwrite,
+ doc: /* If t, don't use DirectWrite. */);
+
+ defsubr (&Sw32_dwrite_reinit);
+ defsubr (&Sw32_dwrite_available);
+}
diff --git a/src/w32font.c b/src/w32font.c
index efb42d80336..05e5a067f20 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -452,6 +452,10 @@ w32font_text_extents (struct font *font, const unsigned *code,
memset (metrics, 0, sizeof (struct font_metrics));
+ if (w32_use_direct_write (w32_font))
+ if (w32_dwrite_text_extents (font, code, nglyphs, metrics))
+ return;
+
for (i = 0, first = true; i < nglyphs; i++)
{
struct w32_metric_cache *char_metric;
@@ -706,22 +710,31 @@ w32font_draw (struct glyph_string *s, int from, int to,
int i;
for (i = 0; i < len; i++)
- {
- WCHAR c = s->char2b[from + i] & 0xFFFF;
- ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
- }
+ if (!w32_use_direct_write (w32font) ||
+ !w32_dwrite_draw (s->hdc, x, y, s->char2b + from, 1,
+ GetTextColor(s->hdc), s->font))
+ {
+ WCHAR c = s->char2b[from + i] & 0xFFFF;
+ ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
+ }
}
else
{
- /* The number of glyphs in a glyph_string cannot be larger than
- the maximum value of the 'used' member of a glyph_row, so we
- are OK using alloca here. */
- eassert (len <= SHRT_MAX);
- WCHAR *chars = alloca (len * sizeof (WCHAR));
- int j;
- for (j = 0; j < len; j++)
- chars[j] = s->char2b[from + j] & 0xFFFF;
- ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ if (!w32_use_direct_write (w32font) ||
+ !w32_dwrite_draw (s->hdc, x, y,
+ s->char2b + from, len, GetTextColor(s->hdc),
+ s->font))
+ {
+ /* The number of glyphs in a glyph_string cannot be larger than
+ the maximum value of the 'used' member of a glyph_row, so we
+ are OK using alloca here. */
+ eassert (len <= SHRT_MAX);
+ WCHAR *chars = alloca (len * sizeof (WCHAR));
+ int j;
+ for (j = 0; j < len; j++)
+ chars[j] = s->char2b[from + j] & 0xFFFF;
+ ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+ }
}
/* Restore clip region. */
diff --git a/src/w32font.h b/src/w32font.h
index 3f780c1d866..75e63e4a32e 100644
--- a/src/w32font.h
+++ b/src/w32font.h
@@ -57,6 +57,26 @@ #define W32METRIC_FAIL 2
HFONT hfont;
};
+/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */
+struct uniscribe_font_info
+{
+ struct w32font_info w32_font;
+ /* This is used by the Uniscribe backend as a pointer to the script
+ cache, and by the HarfBuzz backend as a pointer to a hb_font_t
+ object. */
+ void *cache;
+ /* This is used by the HarfBuzz backend to store the font scale. */
+ double scale;
+ /* This is used by DirectWrite to store the FontFace object.
+ DirectWrite works on top of the HarfBuzz backend, modifying some
+ calls. If there are problems manipulating this font,
+ dwrite_skip_font is set to true. Future operations will not use
+ DirectWrite and fall back to the HarfBuzz backend. */
+ void *dwrite_cache;
+ float dwrite_font_size;
+ bool dwrite_skip_font;
+};
+
/* Macros for getting OS specific information from a font struct. */
#define FONT_HANDLE(f) (((struct w32font_info *)(f))->hfont)
#define FONT_TEXTMETRIC(f) (((struct w32font_info *)(f))->metrics)
@@ -84,6 +104,17 @@ #define CACHE_BLOCKSIZE 128
Lisp_Object intern_font_name (char *);
+/* Function prototypes for DirectWrite. */
+void w32_initialize_direct_write (void);
+bool w32_use_direct_write (struct w32font_info *w32font);
+bool w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+ COLORREF color, struct font *font );
+bool w32_dwrite_text_extents (struct font *font, const unsigned *code,
+ int nglyphs, struct font_metrics *metrics);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face(void *cache);
+void syms_of_w32dwrite (void);
+
extern void globals_of_w32font (void);
#endif
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..015214b1e39 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -44,18 +44,6 @@ #define _WIN32_WINNT 0x0600
#include "pdumper.h"
#include "w32common.h"
-/* Extension of w32font_info used by Uniscribe and HarfBuzz backends. */
-struct uniscribe_font_info
-{
- struct w32font_info w32_font;
- /* This is used by the Uniscribe backend as a pointer to the script
- cache, and by the HarfBuzz backend as a pointer to a hb_font_t
- object. */
- void *cache;
- /* This is used by the HarfBuzz backend to store the font scale. */
- double scale;
-};
-
int uniscribe_available = 0;
/* EnumFontFamiliesEx callback. */
@@ -200,6 +188,9 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
/* Initialize the cache for this font. */
uniscribe_font->cache = NULL;
+ uniscribe_font->dwrite_cache = NULL;
+
+ uniscribe_font->dwrite_skip_font = false;
/* Uniscribe and HarfBuzz backends use glyph indices. */
uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
@@ -221,6 +212,7 @@ uniscribe_close (struct font *font)
= (struct uniscribe_font_info *) font;
#ifdef HAVE_HARFBUZZ
+ w32_dwrite_free_cached_face (uniscribe_font->dwrite_cache);
if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
&& uniscribe_font->cache)
hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
@@ -1372,6 +1364,17 @@ w32hb_encode_char (struct font *font, int c)
struct uniscribe_font_info *uniscribe_font
= (struct uniscribe_font_info *) font;
eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+ if (w32_use_direct_write (&uniscribe_font->w32_font))
+ {
+ unsigned encoded = w32_dwrite_encode_char (font, c);
+
+ /* The call to w32_dwrite_encode_char may fail, disabling
+ DirectWrite for this font. So check again. */
+ if (w32_use_direct_write (&uniscribe_font->w32_font))
+ return encoded;
+ }
+
hb_font_t *hb_font = uniscribe_font->cache;
/* First time we use this font with HarfBuzz, create the hb_font_t
@@ -1510,6 +1513,7 @@ w32hb_get_variation_glyphs (struct font *font, int c, unsigned variations[256])
void
syms_of_w32uniscribe (void)
{
+ syms_of_w32dwrite ();
pdumper_do_now_and_after_load (syms_of_w32uniscribe_for_pdumper);
}
@@ -1624,5 +1628,8 @@ syms_of_w32uniscribe_for_pdumper (void)
harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
register_font_driver (&harfbuzz_font_driver, NULL);
+
+ w32_initialize_direct_write ();
+
#endif /* HAVE_HARFBUZZ */
}
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-24 20:19 ` Cecilio Pardo
@ 2024-10-25 7:26 ` Eli Zaretskii
2024-10-25 10:17 ` Cecilio Pardo
2024-10-25 11:51 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-25 7:26 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Thu, 24 Oct 2024 22:19:06 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> New version attached.
>
> - Added comments
> - Use SAFE_ALLOCA instead of alloca.
> - Added two lisp functions:
> w32-dwrite-available to find out is dwrite is working
> w32-dwrite-reinit to reinitialize dwrite, optionally
> changing render parameters (gamma, cleartype level, contrast).
> Can go back to defaults passing nil.
> - Updated NEWS and changelog.
Thanks, I will review this shortly. Meanwhile, could you please look
at how Emacs with this patch displays some complex Emoji sequences?
Some of them look incorrect to me. For example, this sequence from
admin/unidata/emoji-zwj-sequences.txt:
1F469 200D 1F469 200D 1F467 200D 1F466 ; RGI_Emoji_ZWJ_Sequence ; family: woman, woman, girl, boy # E2.0 [1] (👩👩👧👦)
when displayed using Segoe UI Emoji, is shown with the "boy" part
overlapping the closing parenthesis, which looks incorrect to me.
"C-u C-x =" claims all of the codepoints of the sequence were composed
into a single grapheme cluster, but it doesn't look like that on
display, and the cursor doesn't include the "boy" part.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-25 7:26 ` Eli Zaretskii
@ 2024-10-25 10:17 ` Cecilio Pardo
2024-10-25 12:00 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-25 10:17 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
On 25/10/2024 9:26, Eli Zaretskii wrote:
> Thanks, I will review this shortly. Meanwhile, could you please look
> at how Emacs with this patch displays some complex Emoji sequences?
> Some of them look incorrect to me. For example, this sequence from
> admin/unidata/emoji-zwj-sequences.txt:
>
> 1F469 200D 1F469 200D 1F467 200D 1F466 ; RGI_Emoji_ZWJ_Sequence ; family: woman, woman, girl, boy # E2.0 [1] (👩👩👧👦)
>
> when displayed using Segoe UI Emoji, is shown with the "boy" part
> overlapping the closing parenthesis, which looks incorrect to me.
> "C-u C-x =" claims all of the codepoints of the sequence were composed
> into a single grapheme cluster, but it doesn't look like that on
> display, and the cursor doesn't include the "boy" part.
I dont's see that, but I do see that the individual glyphs are truncated
on the left side, even when removing color rendering. Do you see it? I
will investigate this first, in case the defects are related.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-24 20:19 ` Cecilio Pardo
2024-10-25 7:26 ` Eli Zaretskii
@ 2024-10-25 11:51 ` Eli Zaretskii
2024-10-25 13:43 ` Cecilio Pardo
1 sibling, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-25 11:51 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730-done
> Date: Thu, 24 Oct 2024 22:19:06 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> New version attached.
>
> - Added comments
> - Use SAFE_ALLOCA instead of alloca.
> - Added two lisp functions:
> w32-dwrite-available to find out is dwrite is working
> w32-dwrite-reinit to reinitialize dwrite, optionally
> changing render parameters (gamma, cleartype level, contrast).
> Can go back to defaults passing nil.
> - Updated NEWS and changelog.
Thanks, installed on the master branch, and closing the bug.
I also installed minor followup changes:
I removed the eassert from this fragment:
/* IDWriteFactory2 is only available on Windows 8.1 and later.
Without this, we can't use color fonts. So we disable DirectWrite
if it is not available. */
hr = dwrite_factory->lpVtbl->QueryInterface (dwrite_factory,
&IID_IDWriteFactory2,
(void **) &dwrite_factory2);
if (FAILED (hr))
{
DebPrint (("DirectWrite HRESULT failed: (%d) QueryInterface IDWriteFactory2\n", hr));
RELEASE_COM (dwrite_factory);
FreeLibrary (direct_write);
return;
}
That's because otherwise the debug build of Emacs will abort on
Windows 7 and 8.0, which is not what we want.
Also, I've added some information about the default values of the
parameters to the doc string of w32-dwrite-reinit; please see if I
made some mistakes there, as the default values don't seem to be
documented anywhere I could see.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-25 10:17 ` Cecilio Pardo
@ 2024-10-25 12:00 ` Eli Zaretskii
2024-10-26 20:13 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-25 12:00 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Fri, 25 Oct 2024 12:17:37 +0200
> Cc: 73730@debbugs.gnu.org
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> On 25/10/2024 9:26, Eli Zaretskii wrote:
>
> > Thanks, I will review this shortly. Meanwhile, could you please look
> > at how Emacs with this patch displays some complex Emoji sequences?
> > Some of them look incorrect to me. For example, this sequence from
> > admin/unidata/emoji-zwj-sequences.txt:
> >
> > 1F469 200D 1F469 200D 1F467 200D 1F466 ; RGI_Emoji_ZWJ_Sequence ; family: woman, woman, girl, boy # E2.0 [1] (👩👩👧👦)
> >
> > when displayed using Segoe UI Emoji, is shown with the "boy" part
> > overlapping the closing parenthesis, which looks incorrect to me.
> > "C-u C-x =" claims all of the codepoints of the sequence were composed
> > into a single grapheme cluster, but it doesn't look like that on
> > display, and the cursor doesn't include the "boy" part.
>
> I dont's see that, but I do see that the individual glyphs are truncated
> on the left side, even when removing color rendering. Do you see
> it?
Yes.
> I will investigate this first, in case the defects are related.
Thanks.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-25 11:51 ` Eli Zaretskii
@ 2024-10-25 13:43 ` Cecilio Pardo
0 siblings, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-25 13:43 UTC (permalink / raw)
To: 73730, eliz
> Also, I've added some information about the default values of the
> parameters to the doc string of w32-dwrite-reinit; please see if I
> made some mistakes there, as the default values don't seem to be
> documented anywhere I could see.
LGTM.
Thanks.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-25 12:00 ` Eli Zaretskii
@ 2024-10-26 20:13 ` Cecilio Pardo
2024-10-27 6:36 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-26 20:13 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
[-- Attachment #1: Type: text/plain, Size: 1378 bytes --]
>>> Thanks, I will review this shortly. Meanwhile, could you please look
>>> at how Emacs with this patch displays some complex Emoji sequences?
>>> Some of them look incorrect to me. For example, this sequence from
>>> admin/unidata/emoji-zwj-sequences.txt:
>>>
>>> 1F469 200D 1F469 200D 1F467 200D 1F466 ; RGI_Emoji_ZWJ_Sequence ; family: woman, woman, girl, boy # E2.0 [1] (👩👩👧👦)
>>>
>>> when displayed using Segoe UI Emoji, is shown with the "boy" part
>>> overlapping the closing parenthesis, which looks incorrect to me.
>>> "C-u C-x =" claims all of the codepoints of the sequence were composed
>>> into a single grapheme cluster, but it doesn't look like that on
>>> display, and the cursor doesn't include the "boy" part.
>>
>> I dont's see that, but I do see that the individual glyphs are truncated
>> on the left side, even when removing color rendering. Do you see
>> it?
>
> Yes.
>
>> I will investigate this first, in case the defects are related.
This patch fixes the left side glyph truncation, which happens when
glyphs have negative left bearing.
About the problem you reported, I can't reproduce when building for
64bit. It does show when building with mingw32 for 32bit. It also shows
when building version 30.0.92. Maybe is the Harfbuzz version? I have
2.4.0 for this build.
[-- Attachment #2: 0001-Fix-problem-with-DirectWrite-MS-Windows.patch --]
[-- Type: text/plain, Size: 2576 bytes --]
From 49e3534901b2b7b832ec15977d78962a62417789 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Sat, 26 Oct 2024 17:51:22 +0200
Subject: [PATCH] ; Fix problem with DirectWrite (MS-Windows)
Negative lbearings were not handled.
* src/w32dwrite.c (w32_dwrite_draw): Modified to handle negative
lbearing.
---
src/w32dwrite.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
index ecc33af5f3f..45be5b5beff 100644
--- a/src/w32dwrite.c
+++ b/src/w32dwrite.c
@@ -895,7 +895,9 @@ w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
return false;
}
- int bitmap_width = metrics.width + metrics.rbearing;
+ int left_margin = metrics.lbearing < 0 ? -metrics.lbearing : 0;
+
+ int bitmap_width = left_margin + metrics.width + metrics.rbearing;
int bitmap_height = font->ascent + font->descent;
/* We never release this, get_bitmap_render_target reuses it. */
@@ -914,7 +916,7 @@ w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
= bitmap_render_target->lpVtbl->GetMemoryDC (bitmap_render_target);
/* Copy the background pixel to the render target bitmap. */
- BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x - left_margin, y, SRCCOPY);
UINT16 *indices = SAFE_ALLOCA (len * sizeof (UINT16));
@@ -948,7 +950,7 @@ w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
IDWriteColorGlyphRunEnumerator *layers;
/* This call will tell us if we have to handle any color glyphs. */
hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
- 0, font->ascent,
+ left_margin, font->ascent,
&glyph_run,
NULL,
MEASURING_MODE,
@@ -959,7 +961,7 @@ w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
/* No color. Just draw the GlyphRun. */
if (hr == DWRITE_E_NOCOLOR)
bitmap_render_target->lpVtbl->DrawGlyphRun (bitmap_render_target,
- 0, font->ascent,
+ left_margin, font->ascent,
MEASURING_MODE,
&glyph_run,
rendering_params,
@@ -1024,7 +1026,7 @@ w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
}
/* Finally, copy the rendered text back to the original DC. */
- BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+ BitBlt (hdc, x - left_margin, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
SAFE_FREE ();
return true;
}
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-26 20:13 ` Cecilio Pardo
@ 2024-10-27 6:36 ` Eli Zaretskii
2024-10-27 13:32 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-27 6:36 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Sat, 26 Oct 2024 22:13:57 +0200
> From: Cecilio Pardo <cpardo@imayhem.com>
> Cc: 73730@debbugs.gnu.org
>
> This patch fixes the left side glyph truncation, which happens when
> glyphs have negative left bearing.
Thanks, installed.
> About the problem you reported, I can't reproduce when building for
> 64bit. It does show when building with mingw32 for 32bit. It also shows
> when building version 30.0.92. Maybe is the Harfbuzz version? I have
> 2.4.0 for this build.
Quite possible. I guess I'll have to build a newer HarfBuzz some day
soon (which I tried to avoid, since they dropped support for
Autoconf in favor of fancier build tools).
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 6:36 ` Eli Zaretskii
@ 2024-10-27 13:32 ` Eli Zaretskii
2024-10-27 13:41 ` Cecilio Pardo
2024-10-27 15:44 ` Cecilio Pardo
0 siblings, 2 replies; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-27 13:32 UTC (permalink / raw)
To: cpardo; +Cc: 73730
> Cc: 73730@debbugs.gnu.org
> Date: Sun, 27 Oct 2024 08:36:44 +0200
> From: Eli Zaretskii <eliz@gnu.org>
>
> > About the problem you reported, I can't reproduce when building for
> > 64bit. It does show when building with mingw32 for 32bit. It also shows
> > when building version 30.0.92. Maybe is the Harfbuzz version? I have
> > 2.4.0 for this build.
>
> Quite possible. I guess I'll have to build a newer HarfBuzz some day
> soon (which I tried to avoid, since they dropped support for
> Autoconf in favor of fancier build tools).
Could you perhaps try the 32-bit build with the latest win32 binaries
that the HarfBuzz project itself provides from their Releases page?
https://github.com/harfbuzz/harfbuzz/releases
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 13:32 ` Eli Zaretskii
@ 2024-10-27 13:41 ` Cecilio Pardo
2024-10-27 15:44 ` Cecilio Pardo
1 sibling, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-27 13:41 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
> Could you perhaps try the 32-bit build with the latest win32 binaries
> that the HarfBuzz project itself provides from their Releases page?
>
> https://github.com/harfbuzz/harfbuzz/releases
Ok.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 13:32 ` Eli Zaretskii
2024-10-27 13:41 ` Cecilio Pardo
@ 2024-10-27 15:44 ` Cecilio Pardo
2024-10-27 16:54 ` Eli Zaretskii
1 sibling, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-27 15:44 UTC (permalink / raw)
To: 73730
> Could you perhaps try the 32-bit build with the latest win32 binaries
> that the HarfBuzz project itself provides from their Releases page?
>
> https://github.com/harfbuzz/harfbuzz/releases
I replaced libharfbuzz-0.dll with the one from version 10.0.1 (the
latest one) and it just works, with and without DirectWrite, without
rebulding Emacs with the new headers.
Besides the normal build with meson/cmake, I've seen this:
There is also amalgamated source provided with HarfBuzz which reduces
whole process of building HarfBuzz to g++ src/harfbuzz.cc
-fno-exceptions but there is no guarantee provided with buildability
and reliability of features you get.
I will take a look at that.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 15:44 ` Cecilio Pardo
@ 2024-10-27 16:54 ` Eli Zaretskii
2024-10-27 17:50 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-27 16:54 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Sun, 27 Oct 2024 16:44:13 +0100
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> > Could you perhaps try the 32-bit build with the latest win32 binaries
> > that the HarfBuzz project itself provides from their Releases page?
> >
> > https://github.com/harfbuzz/harfbuzz/releases
>
> I replaced libharfbuzz-0.dll with the one from version 10.0.1 (the
> latest one) and it just works, with and without DirectWrite, without
> rebulding Emacs with the new headers.
And the problems with displaying emoji-zwj-sequences are gone?
It doesn't surprise me that no rebuilding is needed, because the
latest HarfBuzz is still ABI-compatible with the old ones (which is
why the name is still "-0.dll", not a larger number).
> Besides the normal build with meson/cmake, I've seen this:
>
> There is also amalgamated source provided with HarfBuzz which reduces
> whole process of building HarfBuzz to g++ src/harfbuzz.cc
> -fno-exceptions but there is no guarantee provided with buildability
> and reliability of features you get.
Yes, I know, I've seen the suggestion to TeX Live folks to use that.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 16:54 ` Eli Zaretskii
@ 2024-10-27 17:50 ` Cecilio Pardo
2024-10-27 19:15 ` Eli Zaretskii
0 siblings, 1 reply; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-27 17:50 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
On 27/10/2024 17:54, Eli Zaretskii wrote:
>> I replaced libharfbuzz-0.dll with the one from version 10.0.1 (the
>> latest one) and it just works, with and without DirectWrite, without
>> rebulding Emacs with the new headers.
>
> And the problems with displaying emoji-zwj-sequences are gone?
Yes, they show correctly.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 17:50 ` Cecilio Pardo
@ 2024-10-27 19:15 ` Eli Zaretskii
2024-10-29 8:57 ` Cecilio Pardo
0 siblings, 1 reply; 40+ messages in thread
From: Eli Zaretskii @ 2024-10-27 19:15 UTC (permalink / raw)
To: Cecilio Pardo; +Cc: 73730
> Date: Sun, 27 Oct 2024 18:50:50 +0100
> Cc: 73730@debbugs.gnu.org
> From: Cecilio Pardo <cpardo@imayhem.com>
>
> On 27/10/2024 17:54, Eli Zaretskii wrote:
>
> >> I replaced libharfbuzz-0.dll with the one from version 10.0.1 (the
> >> latest one) and it just works, with and without DirectWrite, without
> >> rebulding Emacs with the new headers.
> >
> > And the problems with displaying emoji-zwj-sequences are gone?
>
> Yes, they show correctly.
OK, good to know. Then this mystery can be put to rest.
^ permalink raw reply [flat|nested] 40+ messages in thread
* bug#73730: 31.0.50; Support for color fonts on MS-Windows
2024-10-27 19:15 ` Eli Zaretskii
@ 2024-10-29 8:57 ` Cecilio Pardo
0 siblings, 0 replies; 40+ messages in thread
From: Cecilio Pardo @ 2024-10-29 8:57 UTC (permalink / raw)
To: Eli Zaretskii; +Cc: 73730
[-- Attachment #1: Type: text/plain, Size: 158 bytes --]
I've built HarfBuzz with the attached patch with mingw, using
g++ src/harfbuzz.cc --shared -o libharfbuzz-0.dll --no-exceptions
and it seems to work fine.
[-- Attachment #2: 0001-harbuzz-mingw.patch --]
[-- Type: text/plain, Size: 950 bytes --]
From a5902ab42ae6dcfa8cded0b0e731691e4d8f7105 Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Tue, 29 Oct 2024 09:21:14 +0100
Subject: [PATCH] ..
---
src/harfbuzz.cc | 4 ++++
src/hb.hh | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/harfbuzz.cc b/src/harfbuzz.cc
index 9ff800daa..486e44bf3 100644
--- a/src/harfbuzz.cc
+++ b/src/harfbuzz.cc
@@ -1,3 +1,6 @@
+#define WINAPI_PARTITION_DESKTOP 1
+#define WINAPI_FAMILY_PARTITION(x) (x)
+
#include "OT/Var/VARC/VARC.cc"
#include "hb-aat-layout.cc"
#include "hb-aat-map.cc"
@@ -62,3 +65,4 @@
#include "hb-uniscribe.cc"
#include "hb-wasm-api.cc"
#include "hb-wasm-shape.cc"
+
diff --git a/src/hb.hh b/src/hb.hh
index fe466fe1f..9c54bc3bd 100644
--- a/src/hb.hh
+++ b/src/hb.hh
@@ -216,7 +216,7 @@
#ifdef _WIN32
#include <windows.h>
-#include <winapifamily.h>
+// #include <winapifamily.h>
#endif
#ifndef PRId32
--
2.35.1.windows.2
^ permalink raw reply related [flat|nested] 40+ messages in thread
end of thread, other threads:[~2024-10-29 8:57 UTC | newest]
Thread overview: 40+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-10-10 11:16 bug#73730: 31.0.50; Support for color fonts on MS-Windows Cecilio Pardo
2024-10-10 13:08 ` Eli Zaretskii
2024-10-10 15:14 ` Cecilio Pardo
2024-10-10 16:33 ` Eli Zaretskii
2024-10-10 16:46 ` Cecilio Pardo
2024-10-15 22:18 ` Cecilio Pardo
2024-10-16 11:01 ` Eli Zaretskii
2024-10-16 11:32 ` Eli Zaretskii
2024-10-16 21:35 ` Cecilio Pardo
2024-10-17 6:21 ` Eli Zaretskii
2024-10-17 10:38 ` Cecilio Pardo
2024-10-20 13:35 ` Cecilio Pardo
2024-10-20 13:55 ` Eli Zaretskii
2024-10-22 22:14 ` Cecilio Pardo
2024-10-23 10:03 ` Eli Zaretskii
2024-10-23 14:17 ` Cecilio Pardo
2024-10-23 17:53 ` Eli Zaretskii
2024-10-24 20:19 ` Cecilio Pardo
2024-10-25 7:26 ` Eli Zaretskii
2024-10-25 10:17 ` Cecilio Pardo
2024-10-25 12:00 ` Eli Zaretskii
2024-10-26 20:13 ` Cecilio Pardo
2024-10-27 6:36 ` Eli Zaretskii
2024-10-27 13:32 ` Eli Zaretskii
2024-10-27 13:41 ` Cecilio Pardo
2024-10-27 15:44 ` Cecilio Pardo
2024-10-27 16:54 ` Eli Zaretskii
2024-10-27 17:50 ` Cecilio Pardo
2024-10-27 19:15 ` Eli Zaretskii
2024-10-29 8:57 ` Cecilio Pardo
2024-10-25 11:51 ` Eli Zaretskii
2024-10-25 13:43 ` Cecilio Pardo
2024-10-20 16:17 ` Eli Zaretskii
2024-10-20 18:20 ` Cecilio Pardo
2024-10-20 18:33 ` Eli Zaretskii
2024-10-20 18:37 ` Cecilio Pardo
2024-10-10 21:50 ` Cecilio Pardo
2024-10-11 3:36 ` Eli Zaretskii
2024-10-11 6:28 ` Eli Zaretskii
2024-10-11 7:19 ` Cecilio Pardo
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.