--- /dev/null
+/* 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);
+}