all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Cecilio Pardo <cpardo@imayhem.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 73730@debbugs.gnu.org, kbrown@cornell.edu
Subject: bug#73730: 31.0.50; Support for color fonts on MS-Windows
Date: Wed, 23 Oct 2024 00:14:12 +0200	[thread overview]
Message-ID: <8e5ffc88-dda8-4b4d-a133-f80029825347@imayhem.com> (raw)
In-Reply-To: <86o73ej2u7.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 1189 bytes --]

Here is a new version with pending issues addressed.

- No abort any more.  Now, any failure marks the font as not usable,
   falling back to previous driver.  If the error is general, then all
   of DirectWrite is disabled.  eassert and DebPrint are called in these
   cases.
- Added a NEWS entry.
- The struct uniscribe_font_info is now in w32font.h
- Build without warnings in both versions on MinGW.

Some things I think we didn't discuss before:

- DirectWrite is available since Windows 7, but color glyphs are
   available only since Windows 8.1.  We only enable DirectWrite since
   Windows 8.1. This is stated on the NEWS entry.
- The default value for the Gamma render parameter in DirectWrite
   gives very 'light' characters.  I changed it to 1.4, which matches
   GDI and looks better. But that's just my opinion.

I couldn't find any hebrew font with color glyphs to test RTL
display. I found a latin+arabic one, Cairo Play:

https://github.com/Gue3bara/Cairo
https://fonts.google.com/specimen/Cairo+Play

It displays diacritics in different colors on both scripts.

It seems to work fine in emacs. I attach captures with and without
DirectWrite, and compared to Courier New.

[-- Attachment #2: emacs_KbWp4eBFOp.png --]
[-- Type: image/png, Size: 33979 bytes --]

[-- Attachment #3: emacs_SiXOdpA67i.png --]
[-- Type: image/png, Size: 29664 bytes --]

[-- Attachment #4: 0001-Implement-drawing-text-with-DirectWrite-on-MS-Window.patch --]
[-- Type: text/plain, Size: 40944 bytes --]

From aa8c7777ddc9ca9e8dc323c0cbd99636fc52f0ee Mon Sep 17 00:00:00 2001
From: Cecilio Pardo <cpardo@imayhem.com>
Date: Wed, 9 Oct 2024 11:40:28 +0200
Subject: [PATCH] Implement drawing text with DirectWrite on MS-Windows.

This adds support for color fonts.

* configure.ac: Add src/w32drite to W32_OBJ.
* src/w32dwrite.c: This is a new file.
(w32-initialize-direct-write): New function, initialize the DirectWrite
library if it is available, and required global variables.
(w32_use_direct_write): New function, check if DirectWrite is available
and activated by the user.
(w32_dwrite_encode_char): New function, replacement for HarfBuzz's
encode_char.
(w32_dwrite_text_extents): New function, replacement for w32font
text_extents.
(w32_dwrite_draw): New function, replacement for w32font draw.
(w32_dwrite_free_cached_face): New function, used on the font deletion
process to also delete DirectWrite data.
(verify_hr): New function, verify COM method results.
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled, call
w32_dwrite_text_extents.
(w32font_draw): If DirectWrite is enabled, call w32_dwrite_draw.
* src/w32uniscribe.c: (w32hb_encode_char): If DirectWrite is enabled,
call w32_dwrite_encode_char.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.
---
 configure.ac       |   2 +-
 etc/NEWS           |   5 +
 src/w32dwrite.c    | 997 +++++++++++++++++++++++++++++++++++++++++++++
 src/w32font.c      |  39 +-
 src/w32font.h      |  30 ++
 src/w32uniscribe.c |  33 +-
 6 files changed, 1080 insertions(+), 26 deletions(-)
 create mode 100644 src/w32dwrite.c

diff --git a/configure.ac b/configure.ac
index 8a5ba7db3d1..ba9cd8f0cd5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3172,7 +3172,7 @@ AC_DEFUN
   AC_CHECK_TOOL([WINDRES], [windres],
                 [AC_MSG_ERROR([No resource compiler found.])])
   W32_OBJ="w32fns.o w32menu.o w32reg.o w32font.o w32term.o"
-  W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32cygwinx.o"
+  W32_OBJ="$W32_OBJ w32xfns.o w32select.o w32uniscribe.o w32dwrite.o w32cygwinx.o"
   EMACSRES="emacs.res"
   case "$canonical" in
     x86_64-*-*) EMACS_MANIFEST="emacs-x64.manifest" ;;
diff --git a/etc/NEWS b/etc/NEWS
index 63c294fac41..f65333a36c0 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -750,6 +750,11 @@ and later versions.
 ** Emacs on MS-Windows now supports drag-n-drop of text into a buffer.
 This is in addition to drag-n-drop of files, that was already supported.
 
+** Emacs on MS-Windows now supports color fonts.
+On Windows 8.1 and later versions Emacs now uses DirectWrite to draw
+text, which supports color fonts.  This can be disabled by setting the
+variable w32-inhibit-dwrite to t.
+
 \f
 ----------------------------------------------------------------------
 This file is part of GNU Emacs.
diff --git a/src/w32dwrite.c b/src/w32dwrite.c
new file mode 100644
index 00000000000..47b93a5340f
--- /dev/null
+++ b/src/w32dwrite.c
@@ -0,0 +1,997 @@
+/* Support for using DirectWrite on MS-Windows to draw text.  This
+   allows for color fonts.
+   Copyright (C) 2024 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* This requires the HarfBuzz font backend to be available.
+
+   It works by modifying the HarfBuzz backend to use DirectWrite at
+   some points, if it is available:
+
+   - When encoding characters: w32hb_encode_char
+   - When measuring text: w32font_text_extents
+   - When drawing text: w32font_draw
+
+   DirectWrite is setup by calling w32_initialize_direct_write.  From
+   that point, the function w32_use_direct_write will return true if
+   DirectWrite is to be used.
+
+   DirectWrite is available since Windows 7, but we don't activate it on
+   versions before 8.1, because color fonts are only available since that.  */
+
+#include <config.h>
+#include <math.h>
+#include <windows.h>
+
+#ifndef MINGW_W64
+# define INITGUID
+#endif
+#include <initguid.h>
+#include <ole2.h>
+#include <unknwn.h>
+
+#include "frame.h"
+#include "w32font.h"
+#include "w32common.h"
+#include "w32term.h"
+
+#ifndef MINGW_W64
+
+/* The following definitions would be included from dwrite_3.h, but it
+   is not available when building with mingw.org's MinGW.  Methods that
+   we don't use are declared with the EMACS_DWRITE_UNUSED macro, to
+   avoid bringing in more types that would need to be declared.  */
+
+#define EMACS_DWRITE_UNUSED(name) void (STDMETHODCALLTYPE *name)(void)
+
+#define DWRITE_E_NOCOLOR _HRESULT_TYPEDEF_(0x8898500CL)
+
+typedef enum DWRITE_PIXEL_GEOMETRY {
+    DWRITE_PIXEL_GEOMETRY_FLAT = 0,
+    DWRITE_PIXEL_GEOMETRY_RGB = 1,
+    DWRITE_PIXEL_GEOMETRY_BGR = 2
+} DWRITE_PIXEL_GEOMETRY;
+
+typedef enum DWRITE_RENDERING_MODE {
+    DWRITE_RENDERING_MODE_DEFAULT = 0,
+    DWRITE_RENDERING_MODE_ALIASED = 1,
+    DWRITE_RENDERING_MODE_GDI_CLASSIC = 2,
+    DWRITE_RENDERING_MODE_GDI_NATURAL = 3,
+    DWRITE_RENDERING_MODE_NATURAL = 4,
+    DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC = 5,
+    DWRITE_RENDERING_MODE_OUTLINE = 6
+} DWRITE_RENDERING_MODE;
+
+typedef enum DWRITE_MEASURING_MODE {
+    DWRITE_MEASURING_MODE_NATURAL = 0,
+    DWRITE_MEASURING_MODE_GDI_CLASSIC = 1,
+    DWRITE_MEASURING_MODE_GDI_NATURAL = 2
+} DWRITE_MEASURING_MODE;
+
+typedef enum DWRITE_TEXT_ANTIALIAS_MODE {
+    DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE = 0,
+    DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE = 1
+} DWRITE_TEXT_ANTIALIAS_MODE;
+
+typedef enum DWRITE_FACTORY_TYPE {
+    DWRITE_FACTORY_TYPE_SHARED = 0,
+    DWRITE_FACTORY_TYPE_ISOLATED = 1
+} DWRITE_FACTORY_TYPE;
+
+typedef struct DWRITE_FONT_METRICS {
+    UINT16 designUnitsPerEm;
+    UINT16 ascent;
+    UINT16 descent;
+    INT16 lineGap;
+    UINT16 capHeight;
+    UINT16 xHeight;
+    INT16 underlinePosition;
+    UINT16 underlineThickness;
+    INT16 strikethroughPosition;
+    UINT16 strikethroughThickness;
+} DWRITE_FONT_METRICS;
+
+typedef struct DWRITE_GLYPH_METRICS {
+    INT32 leftSideBearing;
+    UINT32 advanceWidth;
+    INT32 rightSideBearing;
+    INT32 topSideBearing;
+    UINT32 advanceHeight;
+    INT32 bottomSideBearing;
+    INT32 verticalOriginY;
+} DWRITE_GLYPH_METRICS;
+
+typedef interface IDWriteRenderingParams IDWriteRenderingParams;
+typedef interface IDWriteFont IDWriteFont;
+typedef interface IDWriteGdiInterop IDWriteGdiInterop;
+typedef interface IDWriteFactory IDWriteFactory;
+typedef interface IDWriteFactory2 IDWriteFactory2;
+typedef interface IDWriteFontFace IDWriteFontFace;
+typedef interface IDWriteBitmapRenderTarget IDWriteBitmapRenderTarget;
+typedef interface IDWriteBitmapRenderTarget1 IDWriteBitmapRenderTarget1;
+typedef interface IDWriteColorGlyphRunEnumerator IDWriteColorGlyphRunEnumerator;
+
+DEFINE_GUID(IID_IDWriteBitmapRenderTarget1, 0x791e8298, 0x3ef3, 0x4230, 0x98,0x80, 0xc9,0xbd,0xec,0xc4,0x20,0x64);
+DEFINE_GUID(IID_IDWriteFactory2, 0x0439fc60, 0xca44, 0x4994, 0x8d,0xee, 0x3a,0x9a,0xf7,0xb7,0x32,0xec);
+DEFINE_GUID(IID_IDWriteFactory, 0xb859ee5a, 0xd838, 0x4b5b, 0xa2,0xe8, 0x1a,0xdc,0x7d,0x93,0xdb,0x48);
+
+typedef struct DWRITE_GLYPH_OFFSET {
+    FLOAT advanceOffset;
+    FLOAT ascenderOffset;
+} DWRITE_GLYPH_OFFSET;
+
+typedef struct DWRITE_GLYPH_RUN {
+    IDWriteFontFace *fontFace;
+    FLOAT fontEmSize;
+    UINT32 glyphCount;
+    const UINT16 *glyphIndices;
+    const FLOAT *glyphAdvances;
+    const DWRITE_GLYPH_OFFSET *glyphOffsets;
+    WINBOOL isSideways;
+    UINT32 bidiLevel;
+}  DWRITE_GLYPH_RUN;
+
+typedef struct _D3DCOLORVALUE {
+  float r;
+  float g;
+  float b;
+  float a;
+} D3DCOLORVALUE;
+
+typedef D3DCOLORVALUE DWRITE_COLOR_F;
+
+typedef struct DWRITE_COLOR_GLYPH_RUN {
+    DWRITE_GLYPH_RUN glyphRun;
+    void *glyphRunDescription;
+    FLOAT baselineOriginX;
+    FLOAT baselineOriginY;
+    DWRITE_COLOR_F runColor;
+    UINT16 paletteIndex;
+} DWRITE_COLOR_GLYPH_RUN;
+
+typedef struct IDWriteFontFaceVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteFontFace *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFontFace *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteFontFace *This);
+
+  EMACS_DWRITE_UNUSED(GetType);
+  EMACS_DWRITE_UNUSED(GetFiles);
+  EMACS_DWRITE_UNUSED(GetIndex);
+  EMACS_DWRITE_UNUSED(GetSimulations);
+  EMACS_DWRITE_UNUSED(IsSymbolFont);
+
+  void (STDMETHODCALLTYPE *GetMetrics)
+    (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics);
+
+  EMACS_DWRITE_UNUSED (GetGlyphCount);
+  EMACS_DWRITE_UNUSED (GetDesignGlyphMetrics);
+
+  HRESULT (STDMETHODCALLTYPE *GetGlyphIndices)
+    (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count,
+     UINT16 *glyph_indices);
+
+  EMACS_DWRITE_UNUSED (TryGetFontTable);
+  EMACS_DWRITE_UNUSED (ReleaseFontTable);
+  EMACS_DWRITE_UNUSED (GetGlyphRunOutline);
+  EMACS_DWRITE_UNUSED (GetRecommendedRenderingMode);
+  EMACS_DWRITE_UNUSED (GetGdiCompatibleMetrics);
+
+  HRESULT (STDMETHODCALLTYPE *GetGdiCompatibleGlyphMetrics)
+    (IDWriteFontFace *This,
+     FLOAT emSize,
+     FLOAT pixels_per_dip,
+     void *transform,
+     WINBOOL use_gdi_natural,
+     const UINT16 *glyph_indices,
+     UINT32 glyph_count,
+     DWRITE_GLYPH_METRICS *metrics,
+     WINBOOL is_sideways);
+  END_INTERFACE
+} IDWriteFontFaceVtbl;
+
+interface IDWriteFontFace {
+    CONST_VTBL IDWriteFontFaceVtbl* lpVtbl;
+};
+
+typedef struct IDWriteRenderingParamsVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteRenderingParams *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteRenderingParams *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteRenderingParams *This);
+
+  FLOAT (STDMETHODCALLTYPE *GetGamma)
+    (IDWriteRenderingParams *This);
+  FLOAT (STDMETHODCALLTYPE *GetEnhancedContrast)
+    (IDWriteRenderingParams *This);
+  FLOAT (STDMETHODCALLTYPE *GetClearTypeLevel)
+    (IDWriteRenderingParams *This);
+  int (STDMETHODCALLTYPE *GetPixelGeometry)
+    (IDWriteRenderingParams *This);
+  END_INTERFACE
+} IDWriteRenderingParamsVtbl;
+
+interface IDWriteRenderingParams {
+    CONST_VTBL IDWriteRenderingParamsVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFontVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteFont *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFont *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteFont *This);
+
+  EMACS_DWRITE_UNUSED (GetFontFamily);
+  EMACS_DWRITE_UNUSED (GetWeight);
+  EMACS_DWRITE_UNUSED (GetStretch);
+  EMACS_DWRITE_UNUSED (GetStyle);
+  EMACS_DWRITE_UNUSED (IsSymbolFont);
+  EMACS_DWRITE_UNUSED (GetFaceNames);
+  EMACS_DWRITE_UNUSED (GetInformationalStrings);
+  EMACS_DWRITE_UNUSED (GetSimulations);
+
+  void (STDMETHODCALLTYPE *GetMetrics)
+    (IDWriteFont *This, DWRITE_FONT_METRICS *metrics);
+
+  EMACS_DWRITE_UNUSED(HasCharacter);
+
+  HRESULT (STDMETHODCALLTYPE *CreateFontFace)
+    (IDWriteFont *This, IDWriteFontFace **face);
+
+  END_INTERFACE
+} IDWriteFontVtbl;
+
+interface IDWriteFont {
+    CONST_VTBL IDWriteFontVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTargetVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteBitmapRenderTarget *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteBitmapRenderTarget *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteBitmapRenderTarget *This);
+
+  HRESULT (STDMETHODCALLTYPE *DrawGlyphRun)
+    (IDWriteBitmapRenderTarget *This,
+     FLOAT baselineOriginX,
+     FLOAT baselineOriginY,
+     DWRITE_MEASURING_MODE measuring_mode,
+     const DWRITE_GLYPH_RUN *glyph_run,
+     IDWriteRenderingParams *params,
+     COLORREF textColor,
+     RECT *blackbox_rect);
+
+  HDC (STDMETHODCALLTYPE *GetMemoryDC)(IDWriteBitmapRenderTarget *This);
+
+  EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+
+  HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip)
+    (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip);
+
+  EMACS_DWRITE_UNUSED (GetCurrentTransform);
+  EMACS_DWRITE_UNUSED (SetCurrentTransform);
+  EMACS_DWRITE_UNUSED (GetSize);
+  EMACS_DWRITE_UNUSED (Resize);
+  END_INTERFACE
+} IDWriteBitmapRenderTargetVtbl;
+
+interface IDWriteBitmapRenderTarget {
+    CONST_VTBL IDWriteBitmapRenderTargetVtbl* lpVtbl;
+};
+
+typedef struct IDWriteBitmapRenderTarget1Vtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteBitmapRenderTarget1 *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef) (IDWriteBitmapRenderTarget1 *This);
+  ULONG (STDMETHODCALLTYPE *Release) (IDWriteBitmapRenderTarget1 *This);
+
+  EMACS_DWRITE_UNUSED (DrawGlyphRun);
+  EMACS_DWRITE_UNUSED (GetMemoryDC);
+  EMACS_DWRITE_UNUSED (GetPixelsPerDip);
+  EMACS_DWRITE_UNUSED (SetPixelsPerDip);
+  EMACS_DWRITE_UNUSED (GetCurrentTransform);
+  EMACS_DWRITE_UNUSED (SetCurrentTransform);
+  EMACS_DWRITE_UNUSED (GetSize);
+  EMACS_DWRITE_UNUSED (Resize);
+  EMACS_DWRITE_UNUSED (GetTextAntialiasMode);
+
+  HRESULT (STDMETHODCALLTYPE *SetTextAntialiasMode)
+    (IDWriteBitmapRenderTarget1 *This, DWRITE_TEXT_ANTIALIAS_MODE mode);
+
+  END_INTERFACE
+} IDWriteBitmapRenderTarget1Vtbl;
+
+interface IDWriteBitmapRenderTarget1 {
+    CONST_VTBL IDWriteBitmapRenderTarget1Vtbl* lpVtbl;
+};
+
+typedef struct IDWriteGdiInteropVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteGdiInterop *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteGdiInterop *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteGdiInterop *This);
+
+  HRESULT (STDMETHODCALLTYPE *CreateFontFromLOGFONT)
+    (IDWriteGdiInterop *This, const LOGFONTW *logfont,
+     IDWriteFont **font);
+
+  EMACS_DWRITE_UNUSED (ConvertFontToLOGFONT);
+  EMACS_DWRITE_UNUSED (ConvertFontFaceToLOGFONT);
+  EMACS_DWRITE_UNUSED (CreateFontFaceFromHdc);
+
+  HRESULT (STDMETHODCALLTYPE *CreateBitmapRenderTarget)
+    (IDWriteGdiInterop *This, HDC hdc, UINT32 width, UINT32 height,
+     IDWriteBitmapRenderTarget **target);
+  END_INTERFACE
+} IDWriteGdiInteropVtbl;
+
+interface IDWriteGdiInterop {
+    CONST_VTBL IDWriteGdiInteropVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactoryVtbl {
+  BEGIN_INTERFACE
+
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+    (IDWriteFactory *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory *This);
+
+  EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+  EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+  EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+  EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+  EMACS_DWRITE_UNUSED (CreateFontFileReference);
+  EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+  EMACS_DWRITE_UNUSED (CreateFontFace);
+  HRESULT (STDMETHODCALLTYPE *CreateRenderingParams)
+    (IDWriteFactory *This, IDWriteRenderingParams **params);
+  EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+  HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams)
+    (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast,
+     FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry,
+     DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params);
+  EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+  EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+  EMACS_DWRITE_UNUSED (CreateTextFormat);
+  EMACS_DWRITE_UNUSED (CreateTypography);
+  HRESULT (STDMETHODCALLTYPE *GetGdiInterop)
+    (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop);
+  EMACS_DWRITE_UNUSED (CreateTextLayout);
+  EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+  EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+  EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+  EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+  EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+  END_INTERFACE
+} IDWriteFactoryVtbl;
+
+interface IDWriteFactory { CONST_VTBL IDWriteFactoryVtbl* lpVtbl; };
+
+typedef struct IDWriteColorGlyphRunEnumeratorVtbl {
+    BEGIN_INTERFACE
+
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)
+      (IDWriteColorGlyphRunEnumerator *This, REFIID riid, void **ppvObject);
+    ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteColorGlyphRunEnumerator *This);
+    ULONG (STDMETHODCALLTYPE *Release)(IDWriteColorGlyphRunEnumerator *This);
+
+    HRESULT (STDMETHODCALLTYPE *MoveNext)(
+        IDWriteColorGlyphRunEnumerator *This,
+        WINBOOL *hasRun);
+
+    HRESULT (STDMETHODCALLTYPE *GetCurrentRun)(
+        IDWriteColorGlyphRunEnumerator *This,
+        const DWRITE_COLOR_GLYPH_RUN **run);
+
+    END_INTERFACE
+} IDWriteColorGlyphRunEnumeratorVtbl;
+
+interface IDWriteColorGlyphRunEnumerator {
+    CONST_VTBL IDWriteColorGlyphRunEnumeratorVtbl* lpVtbl;
+};
+
+typedef struct IDWriteFactory2Vtbl {
+  BEGIN_INTERFACE
+  HRESULT (STDMETHODCALLTYPE *QueryInterface)
+      (IDWriteFactory2 *This, REFIID riid, void **ppvObject);
+  ULONG (STDMETHODCALLTYPE *AddRef)(IDWriteFactory2 *This);
+  ULONG (STDMETHODCALLTYPE *Release)(IDWriteFactory2 *This);
+  EMACS_DWRITE_UNUSED (GetSystemFontCollection);
+  EMACS_DWRITE_UNUSED (CreateCustomFontCollection);
+  EMACS_DWRITE_UNUSED (RegisterFontCollectionLoader);
+  EMACS_DWRITE_UNUSED (UnregisterFontCollectionLoader);
+  EMACS_DWRITE_UNUSED (CreateFontFileReference);
+  EMACS_DWRITE_UNUSED (CreateCustomFontFileReference);
+  EMACS_DWRITE_UNUSED (CreateFontFace);
+  EMACS_DWRITE_UNUSED (CreateRenderingParams);
+  EMACS_DWRITE_UNUSED (CreateMonitorRenderingParams);
+  EMACS_DWRITE_UNUSED (CreateCustomRenderingParams);
+  EMACS_DWRITE_UNUSED (RegisterFontFileLoader);
+  EMACS_DWRITE_UNUSED (UnregisterFontFileLoader);
+  EMACS_DWRITE_UNUSED (CreateTextFormat);
+  EMACS_DWRITE_UNUSED (CreateTypography);
+  EMACS_DWRITE_UNUSED (GetGdiInterop);
+  EMACS_DWRITE_UNUSED (CreateTextLayout);
+  EMACS_DWRITE_UNUSED (CreateGdiCompatibleTextLayout);
+  EMACS_DWRITE_UNUSED (CreateEllipsisTrimmingSign);
+  EMACS_DWRITE_UNUSED (CreateTextAnalyzer);
+  EMACS_DWRITE_UNUSED (CreateNumberSubstitution);
+  EMACS_DWRITE_UNUSED (CreateGlyphRunAnalysis);
+
+  EMACS_DWRITE_UNUSED (GetEudcFontCollection);
+  EMACS_DWRITE_UNUSED (IDWriteFactory1_CreateCustomRenderingParams);
+
+  EMACS_DWRITE_UNUSED (GetSystemFontFallback);
+  EMACS_DWRITE_UNUSED (CreateFontFallbackBuilder);
+  HRESULT (STDMETHODCALLTYPE *TranslateColorGlyphRun)
+    (IDWriteFactory2 *This,
+     FLOAT originX,
+     FLOAT originY,
+     const DWRITE_GLYPH_RUN *run,
+     void *rundescr,
+     DWRITE_MEASURING_MODE mode,
+     void *transform,
+     UINT32 palette_index,
+     IDWriteColorGlyphRunEnumerator **colorlayers);
+
+  EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateCustomRenderingParams);
+  EMACS_DWRITE_UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis);
+  END_INTERFACE
+} IDWriteFactory2Vtbl;
+
+interface IDWriteFactory2 {
+    CONST_VTBL IDWriteFactory2Vtbl* lpVtbl;
+};
+#else /* ifndef MINGW_W64 */
+# include <dwrite_3.h>
+#endif
+
+/* Values to use for DirectWrite rendering.  */
+#define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL
+#define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC
+#define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE
+
+#define RELEASE_COM(i) (i)->lpVtbl->Release (i)
+
+/* Global variables for DirectWrite.  */
+static bool direct_write_available = false;
+static IDWriteFactory *dwrite_factory;
+static IDWriteFactory2 *dwrite_factory2;
+static IDWriteGdiInterop *gdi_interop;
+static IDWriteRenderingParams *rendering_params;
+
+static bool
+verify_hr (HRESULT hr, const char *msg)
+{
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) %s\n", hr, msg));
+      eassert (SUCCEEDED (hr));
+      return false;
+    }
+  return true;
+}
+
+/* Gets a IDWriteFontFace from a struct font (its HFONT). Returns the
+   font size in points.  It may fail to get a DirectWrite font, and face
+   will be NULL on return.  This happens for some fonts like Courier.
+
+   Never call Release on the result, as it is cached for reuse on the
+   struct font.  */
+static float
+get_font_face (struct font *infont, IDWriteFontFace **face)
+{
+  HRESULT hr;
+  LOGFONTW logfont;
+  IDWriteFont *font;
+
+  struct uniscribe_font_info *uniscribe_font =
+    (struct uniscribe_font_info *) infont;
+
+  /* Check the cache.  */
+  *face = uniscribe_font->dwrite_cache;
+  if (*face)
+    return uniscribe_font->dwrite_font_size;
+
+  GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont);
+
+  hr = gdi_interop->lpVtbl->CreateFontFromLOGFONT (gdi_interop,
+						   (const LOGFONTW *) &logfont,
+						   &font);
+
+  if (!verify_hr (hr, "Failed to CreateFontFromLOGFONT"))
+    {
+      uniscribe_font->dwrite_skip_font = true;
+      *face = NULL;
+      return 0.0;
+    }
+
+  hr = font->lpVtbl->CreateFontFace (font, face);
+  RELEASE_COM (font);
+  if (!verify_hr (hr, "Failed to create DWriteFontFace"))
+    {
+      uniscribe_font->dwrite_skip_font = true;
+      *face = NULL;
+      return 0.0;
+    }
+
+  /* Cache this FontFace.  */
+  uniscribe_font->dwrite_font_size = abs (logfont.lfHeight);
+  uniscribe_font->dwrite_cache = *face;
+
+  return abs (logfont.lfHeight);
+}
+
+void
+w32_dwrite_free_cached_face (void *cache)
+{
+  if (cache)
+    RELEASE_COM ((IDWriteFontFace *) cache);
+}
+
+static float
+convert_metrics_sz (int sz, float font_size, int units_per_em)
+{
+  return (float) sz * font_size / units_per_em;
+}
+
+/* Does not fill in the ascent and descent fields of metrics.  */
+static bool
+text_extents_internal (IDWriteFontFace *dwrite_font_face,
+		       float font_size, const unsigned *code,
+		       int nglyphs, struct font_metrics *metrics)
+{
+  HRESULT hr;
+
+  DWRITE_FONT_METRICS dwrite_font_metrics;
+  dwrite_font_face->lpVtbl->GetMetrics (dwrite_font_face,
+					&dwrite_font_metrics);
+
+  UINT16 *indices = alloca (nglyphs * sizeof (UINT16));
+  for (int i = 0; i < nglyphs; i++)
+    indices[i] = code[i];
+
+  DWRITE_GLYPH_METRICS* gmetrics =
+    alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS));
+
+  hr = dwrite_font_face->lpVtbl->GetGdiCompatibleGlyphMetrics (dwrite_font_face,
+							       font_size,
+							       1.0,
+							       NULL,
+							       TRUE,
+							       indices,
+							       nglyphs,
+							       gmetrics,
+							       false);
+  if (!verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics"))
+    return false;
+
+  float width = 0;
+  int du_per_em = dwrite_font_metrics.designUnitsPerEm;
+
+  for (int i = 0; i < nglyphs; i++)
+    {
+      float advance =
+	convert_metrics_sz (gmetrics[i].advanceWidth, font_size, du_per_em);
+
+      width += advance;
+
+      float lbearing =
+	round (convert_metrics_sz (gmetrics[i].leftSideBearing, font_size,
+				   du_per_em));
+      float rbearing =
+	round (advance -
+	       convert_metrics_sz (gmetrics[i].rightSideBearing,
+				   font_size, du_per_em));
+      if (i == 0)
+	{
+	  metrics->lbearing = lbearing;
+	  metrics->rbearing = rbearing;
+	}
+      if (metrics->lbearing > lbearing)
+	metrics->lbearing = lbearing;
+      if (metrics->rbearing < rbearing)
+	metrics->rbearing = rbearing;
+    }
+  metrics->width = round(width);
+
+  return true;
+}
+
+unsigned
+w32_dwrite_encode_char (struct font *font, int c)
+{
+  HRESULT hr;
+  IDWriteFontFace *dwrite_font_face;
+  UINT16 index;
+
+  get_font_face (font, &dwrite_font_face);
+  if (dwrite_font_face == NULL)
+    return FONT_INVALID_CODE;
+  hr = dwrite_font_face->lpVtbl->GetGlyphIndices (dwrite_font_face,
+						  &c, 1, &index);
+  if (verify_hr (hr, "Failed to GetGlyphIndices"))
+    {
+      if (index == 0)
+	return FONT_INVALID_CODE;
+      return index;
+    }
+  ((struct uniscribe_font_info *) font)->dwrite_skip_font = true;
+  return FONT_INVALID_CODE;
+}
+
+bool
+w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs,
+			 struct font_metrics *metrics)
+{
+  IDWriteFontFace *dwrite_font_face;
+
+  float font_size = get_font_face (font, &dwrite_font_face);
+
+  if (dwrite_font_face == NULL)
+      return false;
+
+  /* We can get fonts with a size of 0.  GDI handles this by using a default
+     size.  We do the same.  */
+  if (font_size <= 0.0f)
+      font_size = FRAME_LINE_HEIGHT (XFRAME (selected_frame));
+
+  metrics->ascent = font->ascent;
+  metrics->descent = font->descent;
+
+  return text_extents_internal (dwrite_font_face, font_size, code, nglyphs,
+				metrics);
+}
+
+/* Never call Release on the value returned by this function, as it is
+   reused.  */
+static IDWriteBitmapRenderTarget *
+get_bitmap_render_target (HDC hdc, int width, int height)
+{
+  HRESULT hr;
+  static IDWriteBitmapRenderTarget *brt = NULL;
+  static SIZE size = {0, 0};
+
+  if (brt)
+    {
+      /* Check if we need to make a bigger one.  */
+      if (width <= size.cx && height <= size.cy)
+	return brt;
+      RELEASE_COM (brt);
+    }
+
+  if (width > size.cx)
+    size.cx = width;
+  if (height > size.cy)
+    size.cy = height;
+
+  hr = gdi_interop->lpVtbl->CreateBitmapRenderTarget (gdi_interop,
+						      hdc,
+						      size.cx, size.cy,
+						      &brt);
+  if (!verify_hr (hr, "Failed to CreateBitmapRenderTarget"))
+      return NULL;
+
+  /* We handle high dpi displays by incresing font size, so override
+     PixelsPerDip.  */
+  brt->lpVtbl->SetPixelsPerDip (brt, 1.0);
+
+  /* The SetTextAntialiasMode method is only available in
+     IDWriteBitmapRenderTarget1.  */
+  IDWriteBitmapRenderTarget1 *brt1;
+  hr = brt->lpVtbl->QueryInterface (brt,
+				    &IID_IDWriteBitmapRenderTarget1,
+				    (void **) &brt1);
+  /* This error should not happen, but is not catastrofic  */
+  if (verify_hr (hr, "Failed to QueryInterface for IDWriteBitmapRenderTarget1"))
+    {
+      brt1->lpVtbl->SetTextAntialiasMode (brt1, ANTIALIAS_MODE);
+      RELEASE_COM (brt1);
+    }
+
+  return brt;
+}
+
+void
+w32_initialize_direct_write (void)
+{
+  direct_write_available = false;
+  HMODULE direct_write = LoadLibrary ("dwrite.dll");
+  if (!direct_write)
+    return;
+
+  /* This is only used here, no need to define it globally.  */
+  typedef HRESULT (WINAPI *DWCreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u);
+
+  DWCreateFactory dw_create_factory
+    = (DWCreateFactory) get_proc_addr (direct_write,
+				       "DWriteCreateFactory");
+
+  if (!dw_create_factory)
+    {
+      FreeLibrary (direct_write);
+      return;
+    }
+
+  HRESULT hr = dw_create_factory (DWRITE_FACTORY_TYPE_SHARED,
+				  &IID_IDWriteFactory,
+				  (IUnknown **) &dwrite_factory);
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) CreateFactory\n", hr));
+      FreeLibrary (direct_write);
+      eassert (SUCCEEDED (hr));
+      return;
+    }
+
+  hr = dwrite_factory->lpVtbl->QueryInterface (dwrite_factory,
+					       &IID_IDWriteFactory2,
+					       (void **) &dwrite_factory2);
+
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) QueryInterface IDWriteFactory2\n", hr));
+      RELEASE_COM (dwrite_factory);
+      FreeLibrary (direct_write);
+      eassert (SUCCEEDED (hr));
+      return;
+    }
+
+  hr = dwrite_factory->lpVtbl->GetGdiInterop (dwrite_factory,
+					      &gdi_interop);
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) GetGdiInterop\n", hr));
+      RELEASE_COM (dwrite_factory);
+      RELEASE_COM (dwrite_factory2);
+      FreeLibrary (direct_write);
+      eassert (SUCCEEDED (hr));
+      return;
+    }
+
+  IDWriteRenderingParams *def;
+
+  hr = dwrite_factory->lpVtbl->CreateRenderingParams (dwrite_factory,
+						      &def);
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) CreateRenderingParams\n", hr));
+      RELEASE_COM (dwrite_factory);
+      RELEASE_COM (dwrite_factory2);
+      RELEASE_COM (gdi_interop);
+      FreeLibrary (direct_write);
+      eassert (SUCCEEDED (hr));
+      return;
+    }
+
+  /* range: [0.0, 1.0]  */
+  float enhanced_contrast = def->lpVtbl->GetEnhancedContrast (def);
+  /* range: [0.0, 1.0]  */
+  float clear_type_level = def->lpVtbl->GetClearTypeLevel (def);
+  /* range: (0.0, 256.0] */
+  float gamma = 1.4; /* def->lpVtbl->GetGamma(def); */
+
+  hr = dwrite_factory->lpVtbl->CreateCustomRenderingParams (dwrite_factory,
+							    gamma,
+							    enhanced_contrast,
+							    clear_type_level,
+							    def->lpVtbl->GetPixelGeometry(def),
+							    RENDERING_MODE,
+							    &rendering_params);
+
+  RELEASE_COM (def);
+
+  if (FAILED (hr))
+    {
+      DebPrint (("DirectWrite HRESULT failed: (%d) CreateCustomRenderingParams\n", hr));
+      RELEASE_COM (dwrite_factory);
+      RELEASE_COM (dwrite_factory2);
+      RELEASE_COM (gdi_interop);
+      FreeLibrary (direct_write);
+      eassert (SUCCEEDED (hr));
+      return;
+    }
+
+  direct_write_available = true;
+
+  w32_inhibit_dwrite = false;
+}
+
+bool
+w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+		 COLORREF color, struct font *font)
+{
+  HRESULT hr;
+  IDWriteFontFace *dwrite_font_face;
+
+  struct uniscribe_font_info *uniscribe_font =
+    (struct uniscribe_font_info *) font;
+
+  /* What we get as y is the baseline position.  */
+  y -= font->ascent;
+
+  float font_size = get_font_face (font, &dwrite_font_face);
+  if (dwrite_font_face == NULL)
+    return false;
+
+  struct font_metrics metrics;
+  if (!text_extents_internal (dwrite_font_face, font_size, glyphs, len,
+			      &metrics))
+    {
+      uniscribe_font->dwrite_skip_font = true;
+      return false;
+    }
+
+  int bitmap_width = metrics.width + metrics.rbearing;
+  int bitmap_height = font->ascent + font->descent;
+
+  /* We never release this, get_bitmap_render_target reuses it.  */
+  IDWriteBitmapRenderTarget *bitmap_render_target =
+    get_bitmap_render_target (hdc, bitmap_width, bitmap_height);
+
+  /* If this fails, completely disable DirectWrite.  */
+  if (bitmap_render_target == NULL)
+    {
+      direct_write_available = false;
+      return false;
+    }
+
+  /* This DC can't be released.  */
+  HDC text_dc = bitmap_render_target->lpVtbl->GetMemoryDC
+    (bitmap_render_target);
+
+  /* Copy the background pixel to the render target bitmap.  */
+  BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY);
+
+  UINT16 *indices = alloca (len * sizeof (UINT16));
+
+  for (int i = 0; i < len; i++)
+    indices[i] = glyphs[i];
+
+  FLOAT *advances = alloca (len * sizeof (FLOAT));
+
+  for (int i = 0; i < len; i++)
+    {
+      if (!text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1,
+				 &metrics))
+	{
+	  uniscribe_font->dwrite_skip_font = true;
+	  return false;
+	}
+      advances[i] = metrics.width;
+    }
+
+  DWRITE_GLYPH_RUN glyph_run;
+  glyph_run.fontFace = dwrite_font_face;
+  glyph_run.fontEmSize = font_size;
+  glyph_run.glyphIndices = indices;
+  glyph_run.glyphCount = len;
+  glyph_run.isSideways = false;
+  glyph_run.bidiLevel = 0;
+  glyph_run.glyphOffsets = NULL;
+  glyph_run.glyphAdvances = advances;
+
+  IDWriteColorGlyphRunEnumerator *layers;
+  /* This call will tell us if we hace to handle any color glyph.  */
+  hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
+							0, font->ascent,
+							&glyph_run,
+							NULL,
+							MEASURING_MODE,
+							NULL,
+							0,
+							&layers);
+
+  /* No color.  Just draw the GlyphRun.  */
+  if (hr == DWRITE_E_NOCOLOR)
+      bitmap_render_target->lpVtbl->DrawGlyphRun (bitmap_render_target,
+						  0, font->ascent,
+						  MEASURING_MODE,
+						  &glyph_run,
+						  rendering_params,
+						  color,
+						  NULL);
+  else
+    {
+      /* If there were color glyphs, layers contains a list of GlyphRun
+	 with a color and a position for each.  We draw them
+	 individually.  */
+      if (!verify_hr (hr, "Failed at TranslateColorGlyphRun"))
+	{
+	  uniscribe_font->dwrite_skip_font = true;
+	  RELEASE_COM (layers);
+	  return false;
+	}
+      for (;;) {
+	HRESULT hr;
+	BOOL more_layers;
+	const DWRITE_COLOR_GLYPH_RUN *layer;
+
+	hr = layers->lpVtbl->MoveNext (layers, &more_layers);
+	if (!verify_hr (hr, "Failed at MoveNext"))
+	  {
+	    uniscribe_font->dwrite_skip_font = true;
+	    RELEASE_COM (layers);
+	    return false;
+	  }
+	if (!more_layers)
+	  break;
+	hr = layers->lpVtbl->GetCurrentRun (layers, &layer);
+	if (!verify_hr (hr, "Failed at GetCurrentRun"))
+	  {
+	    uniscribe_font->dwrite_skip_font = true;
+	    RELEASE_COM (layers);
+	    return false;
+	  }
+	hr = bitmap_render_target->lpVtbl->DrawGlyphRun
+	  (bitmap_render_target,
+	   layer->baselineOriginX,
+	   layer->baselineOriginY,
+	   MEASURING_MODE,
+	   &layer->glyphRun,
+	   rendering_params,
+	   RGB (layer->runColor.r * 255,
+		layer->runColor.g * 255,
+		layer->runColor.b * 255),
+	   NULL);
+	if (!verify_hr (hr, "Failed at GetCurrentRun"))
+	  {
+	    uniscribe_font->dwrite_skip_font = true;
+	    RELEASE_COM (layers);
+	    return false;
+	  }
+      }
+      RELEASE_COM (layers);
+    }
+
+  /* Finally, copy the rendered text back to the original DC.  */
+  BitBlt (hdc, x, y, bitmap_width, bitmap_height, text_dc, 0, 0, SRCCOPY);
+
+  return true;
+}
+
+/* Returns true if DirectWrite is to be used:
+   - It is available.
+   - The font is handled by HarfBuzz.
+   - w32-inhibit-dwrite is false.
+   - The font has not been marked after a failed DirectWrite operation.
+ */
+bool
+w32_use_direct_write (struct w32font_info *w32font)
+{
+#ifdef HAVE_HARFBUZZ
+  return direct_write_available
+    && w32font->font.driver == &harfbuzz_font_driver
+    && !w32_inhibit_dwrite
+    && !((struct uniscribe_font_info *) w32font)->dwrite_skip_font;
+#else
+  return false;
+#endif
+}
diff --git a/src/w32font.c b/src/w32font.c
index efb42d80336..05e5a067f20 100644
--- a/src/w32font.c
+++ b/src/w32font.c
@@ -452,6 +452,10 @@ w32font_text_extents (struct font *font, const unsigned *code,
 
   memset (metrics, 0, sizeof (struct font_metrics));
 
+  if (w32_use_direct_write (w32_font))
+      if (w32_dwrite_text_extents (font, code, nglyphs, metrics))
+	return;
+
   for (i = 0, first = true; i < nglyphs; i++)
     {
       struct w32_metric_cache *char_metric;
@@ -706,22 +710,31 @@ w32font_draw (struct glyph_string *s, int from, int to,
       int i;
 
       for (i = 0; i < len; i++)
-	{
-	  WCHAR c = s->char2b[from + i] & 0xFFFF;
-	  ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
-	}
+	if (!w32_use_direct_write (w32font) ||
+	    !w32_dwrite_draw (s->hdc, x, y, s->char2b + from, 1,
+			      GetTextColor(s->hdc), s->font))
+	  {
+	    WCHAR c = s->char2b[from + i] & 0xFFFF;
+	    ExtTextOutW (s->hdc, x + i, y, options, NULL, &c, 1, NULL);
+	  }
     }
   else
     {
-      /* The number of glyphs in a glyph_string cannot be larger than
-	 the maximum value of the 'used' member of a glyph_row, so we
-	 are OK using alloca here.  */
-      eassert (len <= SHRT_MAX);
-      WCHAR *chars = alloca (len * sizeof (WCHAR));
-      int j;
-      for (j = 0; j < len; j++)
-	chars[j] = s->char2b[from + j] & 0xFFFF;
-      ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+      if (!w32_use_direct_write (w32font) ||
+	  !w32_dwrite_draw (s->hdc, x, y,
+			    s->char2b + from, len, GetTextColor(s->hdc),
+			    s->font))
+	{
+	  /* The number of glyphs in a glyph_string cannot be larger than
+	     the maximum value of the 'used' member of a glyph_row, so we
+	     are OK using alloca here.  */
+	  eassert (len <= SHRT_MAX);
+	  WCHAR *chars = alloca (len * sizeof (WCHAR));
+	  int j;
+	  for (j = 0; j < len; j++)
+	    chars[j] = s->char2b[from + j] & 0xFFFF;
+	  ExtTextOutW (s->hdc, x, y, options, NULL, chars, len, NULL);
+	}
     }
 
   /* Restore clip region.  */
diff --git a/src/w32font.h b/src/w32font.h
index 3f780c1d866..68d28156125 100644
--- a/src/w32font.h
+++ b/src/w32font.h
@@ -57,6 +57,26 @@ #define W32METRIC_FAIL 2
   HFONT hfont;
 };
 
+/* Extension of w32font_info used by Uniscribe and HarfBuzz backends.  */
+struct uniscribe_font_info
+{
+  struct w32font_info w32_font;
+  /* This is used by the Uniscribe backend as a pointer to the script
+     cache, and by the HarfBuzz backend as a pointer to a hb_font_t
+     object.  */
+  void *cache;
+  /* This is used by the HarfBuzz backend to store the font scale.  */
+  double scale;
+  /* This is used by DirectWrite to store the FontFace object.
+     DirectWrite works on top of the HarfBuzz backend, modifying some
+     calls.  If there are problems manipulating this font,
+     dwrite_skip_font is set to true.  Future operations will not use
+     DirectWrite and fall back to the HarfBuzz backend.  */
+  void *dwrite_cache;
+  float dwrite_font_size;
+  bool dwrite_skip_font;
+};
+
 /* Macros for getting OS specific information from a font struct.  */
 #define FONT_HANDLE(f) (((struct w32font_info *)(f))->hfont)
 #define FONT_TEXTMETRIC(f) (((struct w32font_info *)(f))->metrics)
@@ -84,6 +104,16 @@ #define CACHE_BLOCKSIZE 128
 
 Lisp_Object intern_font_name (char *);
 
+/* Function prototypes for DirectWrite.  */
+void w32_initialize_direct_write (void);
+bool w32_use_direct_write (struct w32font_info *w32font);
+bool w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len,
+		      COLORREF color, struct font *font );
+bool w32_dwrite_text_extents (struct font *font, const unsigned *code,
+			      int nglyphs, struct font_metrics *metrics);
+unsigned w32_dwrite_encode_char (struct font *font, int c);
+void w32_dwrite_free_cached_face(void *cache);
+
 extern void globals_of_w32font (void);
 
 #endif
diff --git a/src/w32uniscribe.c b/src/w32uniscribe.c
index b77bf56b8cf..98e072ce2be 100644
--- a/src/w32uniscribe.c
+++ b/src/w32uniscribe.c
@@ -44,18 +44,6 @@ #define _WIN32_WINNT 0x0600
 #include "pdumper.h"
 #include "w32common.h"
 
-/* Extension of w32font_info used by Uniscribe and HarfBuzz backends.  */
-struct uniscribe_font_info
-{
-  struct w32font_info w32_font;
-  /* This is used by the Uniscribe backend as a pointer to the script
-     cache, and by the HarfBuzz backend as a pointer to a hb_font_t
-     object.  */
-  void *cache;
-  /* This is used by the HarfBuzz backend to store the font scale.  */
-  double scale;
-};
-
 int uniscribe_available = 0;
 
 /* EnumFontFamiliesEx callback.  */
@@ -200,6 +188,9 @@ uniscribe_open (struct frame *f, Lisp_Object font_entity, int pixel_size)
 
   /* Initialize the cache for this font.  */
   uniscribe_font->cache = NULL;
+  uniscribe_font->dwrite_cache = NULL;
+
+  uniscribe_font->dwrite_skip_font = false;
 
   /* Uniscribe and HarfBuzz backends use glyph indices.  */
   uniscribe_font->w32_font.glyph_idx = ETO_GLYPH_INDEX;
@@ -221,6 +212,7 @@ uniscribe_close (struct font *font)
     = (struct uniscribe_font_info *) font;
 
 #ifdef HAVE_HARFBUZZ
+  w32_dwrite_free_cached_face (uniscribe_font->dwrite_cache);
   if (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver
       && uniscribe_font->cache)
     hb_font_destroy ((hb_font_t *) uniscribe_font->cache);
@@ -1372,6 +1364,17 @@ w32hb_encode_char (struct font *font, int c)
   struct uniscribe_font_info *uniscribe_font
     = (struct uniscribe_font_info *) font;
   eassert (uniscribe_font->w32_font.font.driver == &harfbuzz_font_driver);
+
+  if (w32_use_direct_write (&uniscribe_font->w32_font))
+    {
+      unsigned encoded = w32_dwrite_encode_char (font, c);
+
+      /* The call to w32_dwrite_encode_char may fail, disabling
+	 DirectWrite for this font.  So check again.  */
+      if (w32_use_direct_write (&uniscribe_font->w32_font))
+	return encoded;
+    }
+
   hb_font_t *hb_font = uniscribe_font->cache;
 
   /* First time we use this font with HarfBuzz, create the hb_font_t
@@ -1510,6 +1513,9 @@ w32hb_get_variation_glyphs (struct font *font, int c, unsigned variations[256])
 void
 syms_of_w32uniscribe (void)
 {
+  DEFVAR_BOOL ("w32-inhibit-dwrite", w32_inhibit_dwrite,
+	       doc: /* If t, don't use DirectWrite.  */);
+
   pdumper_do_now_and_after_load (syms_of_w32uniscribe_for_pdumper);
 }
 
@@ -1624,5 +1630,8 @@ syms_of_w32uniscribe_for_pdumper (void)
   harfbuzz_font_driver.combining_capability = hbfont_combining_capability;
   harfbuzz_font_driver.begin_hb_font = w32hb_begin_font;
   register_font_driver (&harfbuzz_font_driver, NULL);
+
+  w32_initialize_direct_write ();
+
 #endif	/* HAVE_HARFBUZZ */
 }
-- 
2.35.1.windows.2


  reply	other threads:[~2024-10-22 22:14 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-10-10 11:16 bug#73730: 31.0.50; Support for color fonts on MS-Windows Cecilio Pardo
2024-10-10 13:08 ` Eli Zaretskii
2024-10-10 15:14   ` Cecilio Pardo
2024-10-10 16:33     ` Eli Zaretskii
2024-10-10 16:46       ` Cecilio Pardo
2024-10-15 22:18   ` Cecilio Pardo
2024-10-16 11:01     ` Eli Zaretskii
2024-10-16 11:32       ` Eli Zaretskii
2024-10-16 21:35       ` Cecilio Pardo
2024-10-17  6:21         ` Eli Zaretskii
2024-10-17 10:38           ` Cecilio Pardo
2024-10-20 13:35             ` Cecilio Pardo
2024-10-20 13:55               ` Eli Zaretskii
2024-10-22 22:14                 ` Cecilio Pardo [this message]
2024-10-23 10:03                   ` Eli Zaretskii
2024-10-23 14:17                     ` Cecilio Pardo
2024-10-23 17:53                       ` Eli Zaretskii
2024-10-24 20:19                         ` Cecilio Pardo
2024-10-25  7:26                           ` Eli Zaretskii
2024-10-25 10:17                             ` Cecilio Pardo
2024-10-25 12:00                               ` Eli Zaretskii
2024-10-26 20:13                                 ` Cecilio Pardo
2024-10-27  6:36                                   ` Eli Zaretskii
2024-10-27 13:32                                     ` Eli Zaretskii
2024-10-27 13:41                                       ` Cecilio Pardo
2024-10-27 15:44                                       ` Cecilio Pardo
2024-10-27 16:54                                         ` Eli Zaretskii
2024-10-27 17:50                                           ` Cecilio Pardo
2024-10-27 19:15                                             ` Eli Zaretskii
2024-10-29  8:57                                               ` Cecilio Pardo
2024-10-25 11:51                           ` Eli Zaretskii
2024-10-25 13:43                             ` Cecilio Pardo
2024-10-20 16:17               ` Eli Zaretskii
2024-10-20 18:20                 ` Cecilio Pardo
2024-10-20 18:33                   ` Eli Zaretskii
2024-10-20 18:37                     ` Cecilio Pardo
2024-10-10 21:50 ` Cecilio Pardo
2024-10-11  3:36   ` Eli Zaretskii
2024-10-11  6:28     ` Eli Zaretskii
2024-10-11  7:19       ` Cecilio Pardo

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=8e5ffc88-dda8-4b4d-a133-f80029825347@imayhem.com \
    --to=cpardo@imayhem.com \
    --cc=73730@debbugs.gnu.org \
    --cc=eliz@gnu.org \
    --cc=kbrown@cornell.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.