mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-07 06:50:23 -08:00
1365 lines
41 KiB
C
1365 lines
41 KiB
C
/* 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 <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>
|
|
|
|
#if !defined MINGW_W64 && !defined CYGWIN
|
|
# 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 struct D2D1_POINT_2F {
|
|
float x;
|
|
float y;
|
|
} D2D1_POINT_2F;
|
|
|
|
typedef struct D2D1_BEZIER_SEGMENT {
|
|
D2D1_POINT_2F point1;
|
|
D2D1_POINT_2F point2;
|
|
D2D1_POINT_2F point3;
|
|
} D2D1_BEZIER_SEGMENT;
|
|
|
|
typedef enum D2D1_FILL_MODE {
|
|
D2D1_FILL_MODE_ALTERNATE = 0,
|
|
D2D1_FILL_MODE_WINDING = 1,
|
|
D2D1_FILL_MODE_FORCE_DWORD = 0xffffffff
|
|
} D2D1_FILL_MODE;
|
|
|
|
typedef enum D2D1_PATH_SEGMENT {
|
|
D2D1_PATH_SEGMENT_NONE = 0x00000000,
|
|
D2D1_PATH_SEGMENT_FORCE_UNSTROKED = 0x00000001,
|
|
D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN = 0x00000002,
|
|
D2D1_PATH_SEGMENT_FORCE_DWORD = 0xffffffff
|
|
} D2D1_PATH_SEGMENT;
|
|
|
|
typedef enum D2D1_FIGURE_BEGIN {
|
|
D2D1_FIGURE_BEGIN_FILLED = 0,
|
|
D2D1_FIGURE_BEGIN_HOLLOW = 1,
|
|
D2D1_FIGURE_BEGIN_FORCE_DWORD = 0xffffffff
|
|
} D2D1_FIGURE_BEGIN;
|
|
|
|
typedef enum D2D1_FIGURE_END {
|
|
D2D1_FIGURE_END_OPEN = 0,
|
|
D2D1_FIGURE_END_CLOSED = 1,
|
|
D2D1_FIGURE_END_FORCE_DWORD = 0xffffffff
|
|
} D2D1_FIGURE_END;
|
|
|
|
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;
|
|
typedef interface ID2D1SimplifiedGeometrySink ID2D1SimplifiedGeometrySink;
|
|
|
|
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);
|
|
HRESULT (STDMETHODCALLTYPE *GetGlyphRunOutline)
|
|
(IDWriteFontFace *This,
|
|
FLOAT emSize,
|
|
const UINT16 *glyph_indices,
|
|
const DWRITE_GLYPH_OFFSET *glyph_offsets,
|
|
const FLOAT *glyph_advances,
|
|
UINT32 glyph_count,
|
|
WINBOOL is_sideways,
|
|
WINBOOL is_right_to_left,
|
|
ID2D1SimplifiedGeometrySink *geometry_sink);
|
|
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;
|
|
};
|
|
|
|
typedef struct ID2D1SimplifiedGeometrySinkVtbl {
|
|
BEGIN_INTERFACE
|
|
|
|
HRESULT (STDMETHODCALLTYPE *QueryInterface)
|
|
(ID2D1SimplifiedGeometrySink *This, REFIID riid, void **ppvObject);
|
|
ULONG (STDMETHODCALLTYPE *AddRef) (ID2D1SimplifiedGeometrySink *This);
|
|
ULONG (STDMETHODCALLTYPE *Release) (ID2D1SimplifiedGeometrySink *This);
|
|
|
|
VOID (STDMETHODCALLTYPE *SetFillMode) (ID2D1SimplifiedGeometrySink *This, D2D1_FILL_MODE fillMode);
|
|
VOID (STDMETHODCALLTYPE *SetSegmentFlags) (ID2D1SimplifiedGeometrySink *This, D2D1_PATH_SEGMENT vertexFlags);
|
|
VOID (STDMETHODCALLTYPE *BeginFigure) (ID2D1SimplifiedGeometrySink *This, D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin);
|
|
VOID (STDMETHODCALLTYPE *AddLines) (ID2D1SimplifiedGeometrySink *This, const D2D1_POINT_2F *points, UINT pointsCount);
|
|
VOID (STDMETHODCALLTYPE *AddBeziers) (ID2D1SimplifiedGeometrySink *This, const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount);
|
|
VOID (STDMETHODCALLTYPE *EndFigure) (ID2D1SimplifiedGeometrySink *This, D2D1_FIGURE_END figureEnd);
|
|
HRESULT (STDMETHODCALLTYPE *Close) (ID2D1SimplifiedGeometrySink *This);
|
|
END_INTERFACE
|
|
} ID2D1SimplifiedGeometrySinkVtbl;
|
|
|
|
interface ID2D1SimplifiedGeometrySink {
|
|
const ID2D1SimplifiedGeometrySinkVtbl *lpVtbl;
|
|
};
|
|
|
|
typedef ID2D1SimplifiedGeometrySink IDWriteGeometrySink;
|
|
|
|
#else /* MINGW_W64 */
|
|
# include <dwrite_3.h>
|
|
# include <d2d1.h>
|
|
#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)
|
|
|
|
/* Implementation of IDWriteGeometrySink, used to the get bounding
|
|
vertical coordinates of glyphs (ascent/descent). The methods that
|
|
affect the bounding box are BeginFigure (which gives a start point),
|
|
AddBeziers and AddLines.
|
|
|
|
Normal procedures to get text extents fail to give correct
|
|
ascent/descent metrics for individual glyphs, using a default value
|
|
for an entire font. That is not acceptable, specially for fonts like
|
|
"Sans Serif Collection", which include glyphs for many different
|
|
scripts and have a huge default value.
|
|
|
|
Because of that, we need to use the GetGlyphRunOutline and examine
|
|
the glyph's geometry.
|
|
*/
|
|
|
|
struct geometry_sink
|
|
{
|
|
IDWriteGeometrySink sink;
|
|
int empty;
|
|
float min_y, max_y;
|
|
};
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
geometry_sink_QueryInterface (IUnknown *This, REFIID ri, void **r)
|
|
{
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
/* There is nothing to allocate of free heres, so we can safely skip ref counting. */
|
|
static ULONG STDMETHODCALLTYPE
|
|
geometry_sink_AddRef (IUnknown *This)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE
|
|
geometry_sink_Release (IUnknown *This)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_AddBeziers (IDWriteGeometrySink *This,
|
|
const D2D1_BEZIER_SEGMENT *beziers, UINT32 count)
|
|
{
|
|
struct geometry_sink *sink = (struct geometry_sink *) This;
|
|
for (UINT32 i = 0; i < count; i++)
|
|
{
|
|
if (sink->min_y > beziers[i].point1.y)
|
|
sink->min_y = beziers[i].point1.y;
|
|
if (sink->max_y < beziers[i].point1.y)
|
|
sink->max_y = beziers[i].point1.y;
|
|
if (sink->min_y > beziers[i].point2.y)
|
|
sink->min_y = beziers[i].point2.y;
|
|
if (sink->max_y < beziers[i].point2.y)
|
|
sink->max_y = beziers[i].point2.y;
|
|
if (sink->min_y > beziers[i].point3.y)
|
|
sink->min_y = beziers[i].point3.y;
|
|
if (sink->max_y < beziers[i].point3.y)
|
|
sink->max_y = beziers[i].point3.y;
|
|
}
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_AddLines (IDWriteGeometrySink *This,
|
|
const D2D1_POINT_2F *points, UINT32 count)
|
|
{
|
|
struct geometry_sink *sink = (struct geometry_sink *) This;
|
|
for (UINT32 i = 0; i < count; i++)
|
|
{
|
|
if (sink->min_y > points[i].y)
|
|
sink->min_y = points[i].y;
|
|
if (sink->max_y < points[i].y)
|
|
sink->max_y = points[i].y;
|
|
}
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_BeginFigure (IDWriteGeometrySink *This,
|
|
D2D1_POINT_2F startPoint,
|
|
D2D1_FIGURE_BEGIN figureBegin)
|
|
{
|
|
struct geometry_sink *sink = (struct geometry_sink *) This;
|
|
if (sink->min_y > startPoint.y)
|
|
sink->min_y = startPoint.y;
|
|
if (sink->max_y < startPoint.y)
|
|
sink->max_y = startPoint.y;
|
|
sink->empty = 0;
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_EndFigure (IDWriteGeometrySink *This, D2D1_FIGURE_END figureEnd)
|
|
{
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE
|
|
geometry_sink_Close (IDWriteGeometrySink *This)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_SetFillMode (IDWriteGeometrySink *This,
|
|
D2D1_FILL_MODE fillMode)
|
|
{
|
|
}
|
|
|
|
static void STDMETHODCALLTYPE
|
|
geometry_sink_SetSegmentFlags (IDWriteGeometrySink *This,
|
|
D2D1_PATH_SEGMENT vertexFlags)
|
|
{
|
|
}
|
|
|
|
/* 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 struct geometry_sink dwrite_geometry_sink;
|
|
static ID2D1SimplifiedGeometrySinkVtbl dwrite_geometry_sink_vtbl;
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/* If the caller does not need ascent/descent information, it should pass
|
|
NEED_ASCENT_DESCENT = false. This is used to avoid the overhead of
|
|
calling GetGlyphRunOutline. */
|
|
|
|
static bool
|
|
text_extents_internal (IDWriteFontFace *dwrite_font_face,
|
|
bool need_ascent_descent,
|
|
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);
|
|
|
|
/* E_INVALIDARG means some of the glyphs index is out of bounds for the font. */
|
|
if (hr == E_INVALIDARG)
|
|
{
|
|
SAFE_FREE ();
|
|
return 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);
|
|
|
|
if (need_ascent_descent)
|
|
{
|
|
dwrite_geometry_sink.min_y = FLT_MAX;
|
|
dwrite_geometry_sink.max_y = -FLT_MAX;
|
|
dwrite_geometry_sink.empty = 1;
|
|
|
|
hr = dwrite_font_face->lpVtbl->GetGlyphRunOutline (dwrite_font_face,
|
|
font_size,
|
|
indices,
|
|
NULL,
|
|
NULL,
|
|
nglyphs,
|
|
FALSE,
|
|
FALSE,
|
|
&dwrite_geometry_sink.sink);
|
|
|
|
if (!verify_hr (hr, "Failed to GetGlyhRunOutline"))
|
|
{
|
|
SAFE_FREE ();
|
|
return false;
|
|
}
|
|
|
|
if (dwrite_geometry_sink.empty)
|
|
{
|
|
dwrite_geometry_sink.min_y = 0;
|
|
dwrite_geometry_sink.max_y = 0;
|
|
}
|
|
|
|
metrics->ascent = (int) round (-dwrite_geometry_sink.min_y);
|
|
metrics->descent = (int) round (dwrite_geometry_sink.max_y);
|
|
}
|
|
|
|
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, true,
|
|
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;
|
|
}
|
|
|
|
dwrite_geometry_sink.sink.lpVtbl = &dwrite_geometry_sink_vtbl;
|
|
|
|
#ifdef MINGW_W64
|
|
dwrite_geometry_sink_vtbl.Base.AddRef = geometry_sink_AddRef;
|
|
dwrite_geometry_sink_vtbl.Base.Release = geometry_sink_Release;
|
|
dwrite_geometry_sink_vtbl.Base.QueryInterface =
|
|
geometry_sink_QueryInterface;
|
|
#else
|
|
dwrite_geometry_sink_vtbl.AddRef = (void *) geometry_sink_AddRef;
|
|
dwrite_geometry_sink_vtbl.Release = (void *) geometry_sink_Release;
|
|
dwrite_geometry_sink_vtbl.QueryInterface
|
|
= (void *) geometry_sink_QueryInterface;
|
|
#endif
|
|
|
|
dwrite_geometry_sink_vtbl.AddBeziers = geometry_sink_AddBeziers;
|
|
dwrite_geometry_sink_vtbl.AddLines = geometry_sink_AddLines;
|
|
dwrite_geometry_sink_vtbl.BeginFigure = geometry_sink_BeginFigure;
|
|
dwrite_geometry_sink_vtbl.EndFigure = geometry_sink_EndFigure;
|
|
dwrite_geometry_sink_vtbl.Close = geometry_sink_Close;
|
|
dwrite_geometry_sink_vtbl.SetFillMode = geometry_sink_SetFillMode;
|
|
dwrite_geometry_sink_vtbl.SetSegmentFlags = geometry_sink_SetSegmentFlags;
|
|
|
|
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, false, 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, false, 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);
|
|
}
|