]> git.eshelyaron.com Git - emacs.git/commitdiff
Implement drawing text with DirectWrite on MS-Windows.
authorCecilio Pardo <cpardo@imayhem.com>
Wed, 9 Oct 2024 09:40:28 +0000 (11:40 +0200)
committerEshel Yaron <me@eshelyaron.com>
Sat, 26 Oct 2024 16:45:16 +0000 (18:45 +0200)
This adds support for color fonts.
* configure.ac: Add src/w32drite to W32_OBJ.
* src/w32dwrite.c: 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 in the font
deletion process to also delete DirectWrite data.
(verify_hr): New function, verify COM method results.
(release_com): New function, release a COM object.
(w32-dwrite-available): New function, returns true if DirectWrite
is available.
(w32-dwrite-reinit): New function, reinitialize DirectWrite,
optionally setting some rendering parameters.
* src/w32font.c (w32font_text_extents): If DirectWrite is enabled,
call 'w32_dwrite_text_extents'.
(w32font_draw): If DirectWrite is enabled, call 'w32_dwrite_draw'.
* src/w32uniscribe.c: (w32hb_encode_char): If DirectWrite is enabled,
call 'w32_dwrite_encode_char'.
(syms_of_w32uniscribe_for_pdumper): Initialize DirectWrite.
(uniscribe_close): Free DirectWrite data for the font.

Bug#73730

(cherry picked from commit edf37e811cafa4092b13969613fa83f6e6d69ab3)

configure.ac
etc/NEWS
src/w32dwrite.c [new file with mode: 0644]
src/w32font.c
src/w32font.h
src/w32uniscribe.c

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