all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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
  0 siblings, 0 replies; 15+ 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] 15+ messages in thread

end of thread, other threads:[~2024-10-17 10:38 UTC | newest]

Thread overview: 15+ 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-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.