/* 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 . */ /* 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. */ #include #include #include #include "frame.h" #include "w32font.h" #include "w32common.h" #include "w32term.h" #include "initguid.h" #include #include /* The following definitions would be included from dwrite_3.h, but it is not available when building with MinGW. Methods that we don't use are declared with the UNUSED macro, to avoid bringing in more types that would need to be declared. */ #define 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); UNUSED(GetType); UNUSED(GetFiles); UNUSED(GetIndex); UNUSED(GetSimulations); UNUSED(IsSymbolFont); void (STDMETHODCALLTYPE *GetMetrics) (IDWriteFontFace *This, DWRITE_FONT_METRICS *metrics); UNUSED (GetGlyphCount); UNUSED (GetDesignGlyphMetrics); HRESULT (STDMETHODCALLTYPE *GetGlyphIndices) (IDWriteFontFace *This, const UINT32 *codepoints, UINT32 count, UINT16 *glyph_indices); UNUSED (TryGetFontTable); UNUSED (ReleaseFontTable); UNUSED (GetGlyphRunOutline); UNUSED (GetRecommendedRenderingMode); 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); UNUSED (GetFontFamily); UNUSED (GetWeight); UNUSED (GetStretch); UNUSED (GetStyle); UNUSED (IsSymbolFont); UNUSED (GetFaceNames); UNUSED (GetInformationalStrings); UNUSED (GetSimulations); void (STDMETHODCALLTYPE *GetMetrics) (IDWriteFont *This, DWRITE_FONT_METRICS *metrics); 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); UNUSED (GetPixelsPerDip); HRESULT (STDMETHODCALLTYPE *SetPixelsPerDip) (IDWriteBitmapRenderTarget *This, FLOAT pixels_per_dip); UNUSED (GetCurrentTransform); UNUSED (SetCurrentTransform); UNUSED (GetSize); 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); UNUSED (DrawGlyphRun); UNUSED (GetMemoryDC); UNUSED (GetPixelsPerDip); UNUSED (SetPixelsPerDip); UNUSED (GetCurrentTransform); UNUSED (SetCurrentTransform); UNUSED (GetSize); UNUSED (Resize); 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); UNUSED (ConvertFontToLOGFONT); UNUSED (ConvertFontFaceToLOGFONT); 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); UNUSED (GetSystemFontCollection); UNUSED (CreateCustomFontCollection); UNUSED (RegisterFontCollectionLoader); UNUSED (UnregisterFontCollectionLoader); UNUSED (CreateFontFileReference); UNUSED (CreateCustomFontFileReference); UNUSED (CreateFontFace); HRESULT (STDMETHODCALLTYPE *CreateRenderingParams) (IDWriteFactory *This, IDWriteRenderingParams **params); UNUSED (CreateMonitorRenderingParams); HRESULT (STDMETHODCALLTYPE *CreateCustomRenderingParams) (IDWriteFactory *This, FLOAT gamma, FLOAT enhancedContrast, FLOAT cleartype_level, DWRITE_PIXEL_GEOMETRY geometry, DWRITE_RENDERING_MODE mode, IDWriteRenderingParams **params); UNUSED (RegisterFontFileLoader); UNUSED (UnregisterFontFileLoader); UNUSED (CreateTextFormat); UNUSED (CreateTypography); HRESULT (STDMETHODCALLTYPE *GetGdiInterop) (IDWriteFactory *This, IDWriteGdiInterop **gdi_interop); UNUSED (CreateTextLayout); UNUSED (CreateGdiCompatibleTextLayout); UNUSED (CreateEllipsisTrimmingSign); UNUSED (CreateTextAnalyzer); UNUSED (CreateNumberSubstitution); 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); UNUSED (GetSystemFontCollection); UNUSED (CreateCustomFontCollection); UNUSED (RegisterFontCollectionLoader); UNUSED (UnregisterFontCollectionLoader); UNUSED (CreateFontFileReference); UNUSED (CreateCustomFontFileReference); UNUSED (CreateFontFace); UNUSED (CreateRenderingParams); UNUSED (CreateMonitorRenderingParams); UNUSED (CreateCustomRenderingParams); UNUSED (RegisterFontFileLoader); UNUSED (UnregisterFontFileLoader); UNUSED (CreateTextFormat); UNUSED (CreateTypography); UNUSED (GetGdiInterop); UNUSED (CreateTextLayout); UNUSED (CreateGdiCompatibleTextLayout); UNUSED (CreateEllipsisTrimmingSign); UNUSED (CreateTextAnalyzer); UNUSED (CreateNumberSubstitution); UNUSED (CreateGlyphRunAnalysis); UNUSED (GetEudcFontCollection); UNUSED (IDWriteFactory1_CreateCustomRenderingParams); UNUSED (GetSystemFontFallback); 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); UNUSED (IDWriteFactory2_CreateCustomRenderingParams); UNUSED (IDWriteFactory2_CreateGlyphRunAnalysis); END_INTERFACE } IDWriteFactory2Vtbl; interface IDWriteFactory2 { CONST_VTBL IDWriteFactory2Vtbl* lpVtbl; }; /* End of dwrite_3.h definitions. */ /* Values to use for DirectWrite rendering. */ #define MEASURING_MODE DWRITE_MEASURING_MODE_NATURAL #define RENDERING_MODE DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC #define ANTIALIAS_MODE DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE #define RELEASE_COM(i) (i)->lpVtbl->Release (i) /* Global variables for DirectWrite. */ static bool direct_write_available = false; static IDWriteFactory *dwrite_factory; static IDWriteFactory2 *dwrite_factory2; static IDWriteGdiInterop *gdi_interop; static IDWriteRenderingParams *rendering_params; /* Function prototypes. */ void w32_initialize_direct_write (void); bool w32_use_direct_write (struct w32font_info *w32font); void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len, COLORREF color, struct font *font ); void w32_dwrite_text_extents (struct font *font, const unsigned *code, int nglyphs, struct font_metrics *metrics); unsigned w32_dwrite_encode_char (struct font *font, int c); void w32_dwrite_free_cached_face(void *cache); /* This two functions are on w32uninscribe.c */ void *w32_font_get_dwrite_cache (struct font *font, float *font_size); void w32_font_set_dwrite_cache (struct font *font, void *cache, float font_size); static void verify_hr (HRESULT hr, const char *msg) { if (FAILED (hr)) emacs_abort (); } /* 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; float font_size; /* Check the cache. */ *face = w32_font_get_dwrite_cache (infont, &font_size); if (*face) return font_size; GetObjectW (FONT_HANDLE(infont), sizeof (LOGFONTW), &logfont); hr = gdi_interop->lpVtbl-> CreateFontFromLOGFONT (gdi_interop, (const LOGFONTW *) &logfont, &font); if (FAILED (hr)) { *face = NULL; return 0.0; } hr = font->lpVtbl->CreateFontFace (font, face); RELEASE_COM (font); verify_hr (hr, "Failed to create DWriteFontFace"); w32_font_set_dwrite_cache (infont, *face, abs(logfont.lfHeight)); return abs(logfont.lfHeight); } void w32_dwrite_free_cached_face(void *cache) { if (cache) RELEASE_COM ((IDWriteFontFace *) cache); } static float convert_metrics_sz( int sz, float font_size, int units_per_em ) { return (float)sz * font_size / units_per_em; } /* Does not fill in the ascent and descent fields of metrics. */ static void text_extents_internal (IDWriteFontFace *dwrite_font_face, float font_size, const unsigned *code, int nglyphs, struct font_metrics *metrics) { HRESULT hr; DWRITE_FONT_METRICS dwrite_font_metrics; dwrite_font_face->lpVtbl-> GetMetrics (dwrite_font_face, &dwrite_font_metrics); UINT16 *indices = alloca (nglyphs * sizeof (UINT16)); for (int i = 0; i < nglyphs; i++) indices[i] = code[i]; DWRITE_GLYPH_METRICS* gmetrics = alloca (nglyphs * sizeof (DWRITE_GLYPH_METRICS)); hr = dwrite_font_face->lpVtbl-> GetGdiCompatibleGlyphMetrics (dwrite_font_face, font_size, 1.0, NULL, TRUE, indices, nglyphs, gmetrics, false); verify_hr (hr, "Failed to GetGdiCompatibleGlyphMetrics"); 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); } 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); hr = dwrite_font_face->lpVtbl-> GetGlyphIndices (dwrite_font_face, &c, 1, &index); verify_hr (hr, "Failed to GetGlyphIndices"); if (index == 0) return FONT_INVALID_CODE; return index; } void 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) { metrics->width = 0; metrics->lbearing = 0; metrics->rbearing = 0; return; } text_extents_internal (dwrite_font_face, font_size, code, nglyphs, metrics); metrics->ascent = font->ascent; metrics->descent = font->descent; } /* 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); verify_hr (hr, "Failed to create DWriteBitmapRenderTarget"); /* 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); if (SUCCEEDED (hr)) { brt1->lpVtbl-> SetTextAntialiasMode (brt1, ANTIALIAS_MODE); RELEASE_COM (brt1); } return brt; } void w32_initialize_direct_write (void) { direct_write_available = false; HMODULE direct_write = LoadLibrary ("dwrite.dll"); if (!direct_write) return; /* This is only used here, no need to define it globally. */ typedef HRESULT (WINAPI *CreateFactory) (DWRITE_FACTORY_TYPE f, REFIID r, IUnknown** u); CreateFactory create_factory = (CreateFactory) get_proc_addr (direct_write, "DWriteCreateFactory"); if (!create_factory) { FreeLibrary (direct_write); return; } HRESULT hr = create_factory (DWRITE_FACTORY_TYPE_SHARED, &IID_IDWriteFactory, (IUnknown **) &dwrite_factory); if (FAILED (hr)) { FreeLibrary (direct_write); return; } hr = dwrite_factory->lpVtbl-> QueryInterface (dwrite_factory, &IID_IDWriteFactory2, (void **) &dwrite_factory2); if (FAILED (hr)) { RELEASE_COM (dwrite_factory); FreeLibrary (direct_write); return; } hr = dwrite_factory->lpVtbl-> GetGdiInterop (dwrite_factory, &gdi_interop); if (FAILED (hr)) { RELEASE_COM (dwrite_factory); RELEASE_COM (dwrite_factory2); FreeLibrary (direct_write); return; } IDWriteRenderingParams *def; hr = dwrite_factory->lpVtbl-> CreateRenderingParams (dwrite_factory, &def); if (FAILED (hr)) { RELEASE_COM (dwrite_factory); RELEASE_COM (dwrite_factory2); RELEASE_COM (gdi_interop); FreeLibrary (direct_write); return; } /* range: [0.0, 1.0] */ float enhanced_contrast = def->lpVtbl->GetEnhancedContrast(def); /* range: [0.0, 1.0] */ float clear_type_level = def->lpVtbl->GetClearTypeLevel(def); /* range: (0.0, 256.0] */ float gamma = 1.4; /* def->lpVtbl->GetGamma(def); */ hr = dwrite_factory->lpVtbl-> CreateCustomRenderingParams (dwrite_factory, gamma, enhanced_contrast, clear_type_level, def->lpVtbl->GetPixelGeometry(def), RENDERING_MODE, &rendering_params); RELEASE_COM (def); if (FAILED (hr)) { RELEASE_COM (dwrite_factory); RELEASE_COM (dwrite_factory2); RELEASE_COM (gdi_interop); FreeLibrary (direct_write); return; } direct_write_available = true; DEFVAR_BOOL ("w32-inhibit-direct-write", w32_inhibit_direct_write, doc: /* If t, don't use DirectWrite. */); w32_inhibit_direct_write = false; } void w32_dwrite_draw (HDC hdc, int x, int y, unsigned *glyphs, int len, COLORREF color, struct font *font) { HRESULT hr; IDWriteFontFace *dwrite_font_face; /* 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; struct font_metrics metrics; text_extents_internal (dwrite_font_face, font_size, glyphs, len, &metrics); 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); /* This DC can't be released. */ HDC text_dc = bitmap_render_target->lpVtbl-> GetMemoryDC (bitmap_render_target); /* Copy the background pixel to the render target bitmap. */ BitBlt (text_dc, 0, 0, bitmap_width, bitmap_height, hdc, x, y, SRCCOPY); UINT16 *indices = alloca (len * sizeof (UINT16)); for (int i = 0; i < len; i++) indices[i] = glyphs[i]; FLOAT *advances = alloca (len * sizeof (FLOAT)); for (int i = 0; i < len; i++) { text_extents_internal (dwrite_font_face, font_size, glyphs + i, 1, &metrics); 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 containts a list of GlyphRun with a color and a position for each. We draw them individually. */ verify_hr (hr, "Failed at TranslateColorGlyphRun"); for (;;) { HRESULT hr; BOOL more_layers; const DWRITE_COLOR_GLYPH_RUN *layer; hr = layers->lpVtbl->MoveNext (layers, &more_layers); verify_hr (hr, "Failed at MoveNext"); if (!more_layers) break; hr = layers->lpVtbl->GetCurrentRun (layers, &layer); verify_hr (hr, "Failed at GetCurrentRun"); 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); } 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); } /* Returns true if DirectWrite is to be used: - Is is available. - The font is handled by harfbuzz - w32-inhibit-direct-write is false */ bool w32_use_direct_write (struct w32font_info *w32font) { #ifdef HAVE_HARFBUZZ return direct_write_available && w32font->font.driver == &harfbuzz_font_driver && !w32_inhibit_direct_write; #else return false; #endif }