/* Support for using DirectWrite on MS-Windows to draw text. This
allows for color fonts.
Copyright (C) 2024-2025 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 . */
/* 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
#include
#include
#if !defined MINGW_W64 && !defined CYGWIN
# define INITGUID
#endif
#include
#include
#include
#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 /* MINGW_W64 */
# include
#endif
/* User configurable variables. If they are smaller 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 = eabs (logfont.lfHeight);
uniscribe_font->dwrite_cache = *face;
return eabs (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,
(UINT32 *) &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 increasing 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,
REFIID, IUnknown **);
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);
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 left_margin = metrics.lbearing < 0 ? -metrics.lbearing : 0;
int bitmap_width = left_margin + metrics.width + metrics.rbearing;
int bitmap_height = font->ascent + font->descent;
/* We never release this, get_bitmap_render_target reuses it. */
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 - left_margin, 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; /* we reorder bidi text ourselves */
glyph_run.glyphOffsets = NULL;
glyph_run.glyphAdvances = advances;
IDWriteColorGlyphRunEnumerator *layers;
/* This call will tell us if we have to handle any color glyphs. */
hr = dwrite_factory2->lpVtbl->TranslateColorGlyphRun (dwrite_factory2,
left_margin, 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,
left_margin, 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 - left_margin, 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.
Return value is nil.
ENHANCED_CONTRAST is in the range [0.0, 1.0], and defaults to 0.5.
CLEAR_TYPE_LEVEL is in the range [0.0, 1.0], and defaults to 1.0.
GAMMA is in the range (0.0, 256.0], and defaults to a system-dependent value
around 2.0 (sometimes 1.8, sometimes 2.2). */)
(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. */);
/* The actual value is determined at startup in
w32_initialize_direct_write, which is called from
syms_of_w32uniscribe_for_pdumper. */
w32_inhibit_dwrite = false;
defsubr (&Sw32_dwrite_reinit);
defsubr (&Sw32_dwrite_available);
}