/* Copyright (C) 2003-2015 LiveCode Ltd.
This file is part of LiveCode.
LiveCode is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License v3 as published by the Free
Software Foundation.
LiveCode 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 LiveCode. If not see . */
#include "graphics.h"
#include "graphics-internal.h"
#include
#include
#include
#include
#include
#include
#pragma comment(lib, "DWrite")
////////////////////////////////////////////////////////////////////////////////
// Class implementing the DirectWrite renderer interface
template
class MCGDWRenderer :
public IDWriteTextRenderer
{
public:
MCGDWRenderer(Callback p_callback) :
m_callback(p_callback)
{
}
// IUnknown
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
STDMETHOD(QueryInterface)(REFIID, void**);
// IDWritePixelSnapping
STDMETHOD(GetCurrentTransform)(void*, DWRITE_MATRIX*);
STDMETHOD(GetPixelsPerDip)(void*, FLOAT*);
STDMETHOD(IsPixelSnappingDisabled)(void*, BOOL*);
// IDWriteTextRenderer
STDMETHOD(DrawGlyphRun)(void*, FLOAT, FLOAT, DWRITE_MEASURING_MODE, const DWRITE_GLYPH_RUN*, const DWRITE_GLYPH_RUN_DESCRIPTION*, IUnknown*);
STDMETHOD(DrawInlineObject)(void*, FLOAT, FLOAT, IDWriteInlineObject*, BOOL, BOOL, IUnknown*);
STDMETHOD(DrawStrikethrough)(void*, FLOAT, FLOAT, const DWRITE_STRIKETHROUGH*, IUnknown*);
STDMETHOD(DrawUnderline)(void*, FLOAT, FLOAT, const DWRITE_UNDERLINE*, IUnknown*);
private:
std::atomic m_refcount = 1;
Callback m_callback;
};
////////////////////////////////////////////////////////////////////////////////
template
STDMETHODIMP_(ULONG) MCGDWRenderer::AddRef()
{
return ++m_refcount;
}
template
STDMETHODIMP_(ULONG) MCGDWRenderer::Release()
{
ULONG t_remaining = --m_refcount;
if (t_remaining == 0)
delete this;
return t_remaining;
}
template
STDMETHODIMP MCGDWRenderer::QueryInterface(REFIID riid, void** ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IDWritePixelSnapping) || riid == __uuidof(IDWriteTextRenderer))
{
*ppvObject = this;
return S_OK;
}
// Not a recognised interface
return E_NOINTERFACE;
}
template
STDMETHODIMP MCGDWRenderer::GetCurrentTransform(void* context, DWRITE_MATRIX* transform)
{
// Get the graphics context
MCGContextRef t_context = static_cast(context);
// Get the transform used for drawing
MCGAffineTransform t_transform = MCGContextGetDeviceTransform(t_context);
// Translate to the DirectWrite matrix format
transform->m11 = t_transform.a;
transform->m12 = t_transform.b;
transform->m21 = t_transform.c;
transform->m22 = t_transform.d;
transform->dx = t_transform.tx;
transform->dy = t_transform.ty;
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::GetPixelsPerDip(void* context, FLOAT* pixelsPerDip)
{
// Get the graphics context
MCGContextRef t_context = static_cast(context);
// Get the transform being used for drawing
MCGAffineTransform t_transform = MCGContextGetDeviceTransform(t_context);
// The scale factor can be derived from the transform
*pixelsPerDip = sqrt((t_transform.a*t_transform.a
+ t_transform.b*t_transform.b
+ t_transform.c*t_transform.c
+ t_transform.d*t_transform.d) / 2);
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::IsPixelSnappingDisabled(void* context, BOOL* isDisabled)
{
// DirectWrite recommends this be enabled unless there is a good reason to disable it
*isDisabled = TRUE;
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::DrawGlyphRun(void* context, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN* glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION* glyphRunDescription, IUnknown* clientDrawingEffect)
{
// Forward to the callback
m_callback(baselineOriginX, baselineOriginY, measuringMode, glyphRun, glyphRunDescription);
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::DrawInlineObject(void* context, FLOAT originX, FLOAT originY, IDWriteInlineObject* inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown* clientDrawingEffect)
{
// Ignored
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::DrawStrikethrough(void* context, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH* strikethrough, IUnknown* clientDrawingEffect)
{
// Ignored
return S_OK;
}
template
STDMETHODIMP MCGDWRenderer::DrawUnderline(void* context, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE* underline, IUnknown* clientDrawingRect)
{
// Ignored
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
class MCGDWFontCollectionLoader :
public IDWriteFontCollectionLoader
{
public:
// IUnknown
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
STDMETHOD(QueryInterface)(REFIID, void**);
// IDWriteFontCollectionLoader
STDMETHOD(CreateEnumeratorFromKey)(IDWriteFactory* factory, void const* collectionKey, UINT32 collectionKeySize, IDWriteFontFileEnumerator** fontFileEnumerator);
private:
std::atomic m_refcount = 1;
};
class MCGDWFontFileEnumerator :
public IDWriteFontFileEnumerator
{
public:
MCGDWFontFileEnumerator(IDWriteFactory *factory, MCProperListRef files);
~MCGDWFontFileEnumerator();
// IUnknown
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
STDMETHOD(QueryInterface)(REFIID, void**);
// IDWriteFontFileEnumerator
STDMETHOD(MoveNext)(BOOL* hasCurrentFile);
STDMETHOD(GetCurrentFontFile)(IDWriteFontFile** fontFile);
private:
MCProperListRef m_files;
uindex_t m_file_index;
IDWriteFontFile *m_current_file;
IDWriteFactory *m_factory;
std::atomic m_refcount = 1;
};
////////////////////////////////////////////////////////////////////////////////
// IUnknown
STDMETHODIMP_(ULONG) MCGDWFontCollectionLoader::AddRef()
{
return ++m_refcount;
}
STDMETHODIMP_(ULONG) MCGDWFontCollectionLoader::Release()
{
ULONG t_remaining = --m_refcount;
if (t_remaining == 0)
delete this;
return t_remaining;
}
STDMETHODIMP MCGDWFontCollectionLoader::QueryInterface(REFIID riid, void **ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IDWriteFontCollectionLoader))
{
*ppvObject = this;
return S_OK;
}
return E_NOINTERFACE;
}
// IDWriteFontCollectionLoader
STDMETHODIMP MCGDWFontCollectionLoader::CreateEnumeratorFromKey(IDWriteFactory* factory, void const* collectionKey, UINT32 collectionKeySize, IDWriteFontFileEnumerator** fontFileEnumerator)
{
MCProperListRef t_font_files = *static_cast(collectionKey);
MCAssert(collectionKeySize == sizeof(MCProperListRef));
MCAssert(MCValueGetTypeCode(t_font_files) == kMCValueTypeCodeProperList);
MCGDWFontFileEnumerator *t_enum = new(std::nothrow) MCGDWFontFileEnumerator(factory, t_font_files);
if (t_enum == nil)
return E_OUTOFMEMORY;
*fontFileEnumerator = t_enum;
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
MCGDWFontFileEnumerator::MCGDWFontFileEnumerator(IDWriteFactory *p_factory, MCProperListRef p_files)
{
m_current_file = nil;
m_factory = p_factory;
p_factory->AddRef();
m_files = MCValueRetain(p_files);
m_file_index = 0;
}
MCGDWFontFileEnumerator::~MCGDWFontFileEnumerator()
{
m_factory->Release();
MCValueRelease(m_files);
if (m_current_file != nil)
m_current_file->Release();
}
// IUnknown
STDMETHODIMP_(ULONG) MCGDWFontFileEnumerator::AddRef()
{
return ++m_refcount;
}
STDMETHODIMP_(ULONG) MCGDWFontFileEnumerator::Release()
{
ULONG t_remaining = --m_refcount;
if (t_remaining == 0)
delete this;
return t_remaining;
}
STDMETHODIMP MCGDWFontFileEnumerator::QueryInterface(REFIID riid, void **ppvObject)
{
if (riid == __uuidof(IUnknown) || riid == __uuidof(IDWriteFontFileEnumerator))
{
*ppvObject = this;
return S_OK;
}
return E_NOINTERFACE;
}
// IDWriteFontFileEnumerator
STDMETHODIMP MCGDWFontFileEnumerator::MoveNext(BOOL *r_has_current_file)
{
if (m_file_index >= MCProperListGetLength(m_files))
{
*r_has_current_file = FALSE;
return S_OK;
}
MCStringRef t_fname;
t_fname = static_cast(MCProperListFetchElementAtIndex(m_files, m_file_index));
MCAssert(MCValueGetTypeCode(t_fname) == kMCValueTypeCodeString);
MCAutoStringRefAsUTF16String t_utf16_fname;
if (!t_utf16_fname.Lock(t_fname))
return E_OUTOFMEMORY;
IDWriteFontFile *t_font_file = nil;
HRESULT t_result;
t_result = m_factory->CreateFontFileReference(t_utf16_fname.Ptr(), NULL, &t_font_file);
if (!SUCCEEDED(t_result))
return t_result;
m_file_index++;
if (m_current_file != nil)
m_current_file->Release();
m_current_file = t_font_file;
*r_has_current_file = TRUE;
return t_result;
}
STDMETHODIMP MCGDWFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile **r_current_file)
{
if (m_current_file == nil)
return E_FAIL;
*r_current_file = m_current_file;
m_current_file->AddRef();
return S_OK;
}
////////////////////////////////////////////////////////////////////////////////
// Factory object for creating DirectWrite objects
static IDWriteFactory* s_DWFactory = nil;
// Custom loader for non-system fonts
static IDWriteFontCollectionLoader* s_DWFontCollectionLoader = nil;
// Custom font collection
static IDWriteFontCollection* s_DWFontCollection = nil;
// Set of custom fonts used by loader
static MCProperListRef s_FontFiles = nil;
// GDI interoperation object
static IDWriteGdiInterop* s_GdiInterop = nil;
// DirectWrite text analyser object
static IDWriteTextAnalyzer* s_DWTextAnalyzer = nil;
void MCGPlatformInitialize(void)
{
bool t_success = true;
// Create the DirectWrite factory object
if (t_success)
t_success = SUCCEEDED(DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), reinterpret_cast(&s_DWFactory)));
// Create the GDI interoperation object
if (t_success)
t_success = SUCCEEDED(s_DWFactory->GetGdiInterop(&s_GdiInterop));
// Create the DirectWrite text analyser
if (t_success)
t_success = SUCCEEDED(s_DWFactory->CreateTextAnalyzer(&s_DWTextAnalyzer));
if (t_success)
t_success = nil != (s_DWFontCollectionLoader = new(std::nothrow) MCGDWFontCollectionLoader());
if (t_success)
t_success = SUCCEEDED(s_DWFactory->RegisterFontCollectionLoader(s_DWFontCollectionLoader));
if (t_success)
s_FontFiles = MCValueRetain(kMCEmptyProperList);
}
void MCGPlatformFinalize(void)
{
// Release the custom font list
if (s_FontFiles)
MCValueRelease(s_FontFiles);
// Release the custom font collection
if (s_DWFontCollection)
s_DWFontCollection->Release();
// Release the custom font loader
if (s_DWFontCollectionLoader)
{
s_DWFactory->UnregisterFontCollectionLoader(s_DWFontCollectionLoader);
s_DWFontCollectionLoader->Release();
}
// Release the text analyser
if (s_DWTextAnalyzer)
s_DWTextAnalyzer->Release();
// Release the GDI interop object
if (s_GdiInterop)
s_GdiInterop->Release();
// Release the DirectWrite factory object
if (s_DWFactory)
s_DWFactory->Release();
}
////////////////////////////////////////////////////////////////////////////////
bool MCGDWSetFontFiles(MCProperListRef p_files)
{
IDWriteFontCollection *t_new_collection = nil;
if (!SUCCEEDED(s_DWFactory->CreateCustomFontCollection(s_DWFontCollectionLoader, &p_files, sizeof(MCProperListRef), &t_new_collection)))
return false;
MCValueAssign(s_FontFiles, p_files);
if (s_DWFontCollection)
s_DWFontCollection->Release();
s_DWFontCollection = t_new_collection;
return true;
}
bool MCGFontAddPlatformFileResource(MCStringRef p_file)
{
uindex_t t_offset;
if (MCProperListFirstIndexOfElement(s_FontFiles, p_file, 0, t_offset))
/* Font file already in collection */
return true;
MCAutoProperListRef t_files;
if (!MCProperListMutableCopy(s_FontFiles, &t_files))
return false;
if (!MCProperListPushElementOntoBack(*t_files, p_file))
return false;
return MCGDWSetFontFiles(*t_files);
}
bool MCGFontRemovePlatformFileResource(MCStringRef p_file)
{
uindex_t t_offset;
if (!MCProperListFirstIndexOfElement(s_FontFiles, p_file, 0, t_offset))
/* Font file not in collection */
return true;
MCAutoProperListRef t_files;
if (!MCProperListMutableCopy(s_FontFiles, &t_files))
return false;
if (!MCProperListRemoveElement(*t_files, t_offset))
return false;
return MCGDWSetFontFiles(*t_files);
}
////////////////////////////////////////////////////////////////////////////////
static bool MCGDWGetDefaultLocalizedString(IDWriteLocalizedStrings *p_strings, MCStringRef &r_string)
{
UINT32 t_index = 0;
BOOL t_found = FALSE;
if (!SUCCEEDED(p_strings->FindLocaleName(L"en-us", &t_index, &t_found)))
return false;
// Fallback to first name in list
if (!t_found)
t_index = 0;
UINT32 t_name_length;
if (!SUCCEEDED(p_strings->GetStringLength(t_index, &t_name_length)))
return false;
MCAutoArray t_buffer;
if (!t_buffer.New(t_name_length + 1))
return false;
if (!SUCCEEDED(p_strings->GetString(t_index, t_buffer.Ptr(), t_name_length + 1)))
return false;
return MCStringCreateWithWString(t_buffer.Ptr(), r_string);
}
static bool MCGDWAddCollectionFontsToList(IDWriteFontCollection *p_collection, MCProperListRef p_list)
{
if (p_collection == nil)
return true;
bool t_success = true;
UINT32 t_count;
t_count = p_collection->GetFontFamilyCount();
for (uint32_t i = 0; t_success && i < t_count; i++)
{
IDWriteFontFamily *t_family = nil;
t_success = SUCCEEDED(p_collection->GetFontFamily(i, &t_family));
IDWriteLocalizedStrings *t_names = nil;
if (t_success)
t_success = SUCCEEDED(t_family->GetFamilyNames(&t_names));
MCAutoStringRef t_name;
if (t_success)
t_success = MCGDWGetDefaultLocalizedString(t_names, &t_name);
if (t_success)
t_success = MCProperListPushElementOntoBack(p_list, *t_name);
if (t_names != nil)
t_names->Release();
if (t_family != nil)
t_family->Release();
}
}
bool MCGFontGetPlatformFontList(MCProperListRef &r_fonts)
{
bool t_success = true;
MCAutoProperListRef t_fonts;
t_success = MCProperListCreateMutable(&t_fonts);
IDWriteFontCollection *t_system_collection = nil;
if (t_success)
t_success = SUCCEEDED(s_DWFactory->GetSystemFontCollection(&t_system_collection));
if (t_success)
t_success = MCGDWAddCollectionFontsToList(t_system_collection, *t_fonts);
if (t_success && s_DWFontCollection != nil)
t_success = MCGDWAddCollectionFontsToList(s_DWFontCollection, *t_fonts);
if (t_success)
r_fonts = t_fonts.Take();
return t_success;
}
////////////////////////////////////////////////////////////////////////////////
// Creates a DirectWrite text format object from an HFONT
static IDWriteTextFormat* MCGDWTextFormatFromHFONT(HFONT p_hfont, MCGFloat p_size)
{
// Get the LOGFONT information for the given HFONT
// Note that we don't trust the height from this as it is the cell height, not "normal" height
LOGFONTW t_logfont;
GetObjectW(p_hfont, sizeof(LOGFONTW), &t_logfont);
// Extract the important parameters
// Note: same weight values in GDI and DirectWrite
DWRITE_FONT_WEIGHT t_font_weight = static_cast(t_logfont.lfWeight);
DWRITE_FONT_STRETCH t_font_stretch = DWRITE_FONT_STRETCH_NORMAL;
FLOAT t_font_size = p_size;
DWRITE_FONT_STYLE t_font_style = DWRITE_FONT_STYLE_NORMAL;
if (t_logfont.lfItalic)
t_font_style = DWRITE_FONT_STYLE_ITALIC;
else if (t_logfont.lfOrientation > 0)
t_font_style = DWRITE_FONT_STYLE_OBLIQUE;
// Create the text format object
IDWriteTextFormat* t_format = nil;
// First try custom font collection, then fall back to system collection
if (s_DWFontCollection != nil)
{
UINT32 t_index = 0;
BOOL t_found = FALSE;
s_DWFontCollection->FindFamilyName(t_logfont.lfFaceName, &t_index, &t_found);
if (t_found && SUCCEEDED(s_DWFactory->CreateTextFormat(t_logfont.lfFaceName, s_DWFontCollection, t_font_weight, t_font_style, t_font_stretch, t_font_size, L"en-us", &t_format)))
return t_format;
}
if (!SUCCEEDED(s_DWFactory->CreateTextFormat(t_logfont.lfFaceName, nil, t_font_weight, t_font_style, t_font_stretch, t_font_size, L"en-us", &t_format)))
return nil;
return t_format;
}
// Creates an SkPaint from a DirectWrite font face
static inline void MCGSkPaintFromDWFontFace(IDWriteFontFace* p_face, FLOAT p_emsize, const MCGAffineTransform& p_transform, SkPaint& x_paint)
{
// Map from font face to font (requires the font collection containing the font)
// Search the custom collection for the font
IDWriteFont* t_font = nil;
if (s_DWFontCollection != nil)
s_DWFontCollection->GetFontFromFontFace(p_face, &t_font);
if (t_font == nil)
{
// Fallback to checking system font collection
IDWriteFontCollection *t_sys_collection = nil;
s_DWFactory->GetSystemFontCollection(&t_sys_collection);
t_sys_collection->GetFontFromFontFace(p_face, &t_font);
t_sys_collection->Release();
}
// Get the family from the font
IDWriteFontFamily* t_family;
t_font->GetFontFamily(&t_family);
// Create a Skia typeface from the DirectWrite font face
sk_sp t_typeface(DWriteFontTypeface::Create(s_DWFactory, p_face, t_font, t_family));
// Clean up the DirectWrite resources
t_family->Release();
t_font->Release();
// Configurethe paint for text rendering.
// Both LCD font smoothing and embedded bitmap rendering are enable
x_paint.setTypeface(t_typeface);
x_paint.setLCDRenderText(true);
x_paint.setEmbeddedBitmapText(true);
// Set the size, converting device independent pixels (DIPs) to points
x_paint.setTextSize(p_emsize);
// Set the transform we're using
SkMatrix t_matrix;
MCGAffineTransformToSkMatrix(p_transform, t_matrix);
x_paint.setTextMatrix(&t_matrix);
}
////////////////////////////////////////////////////////////////////////////////
MCGFloat __MCGContextMeasurePlatformText(MCGContextRef self, const unichar_t *p_text, uindex_t p_length, const MCGFont &p_font, const MCGAffineTransform &p_transform)
{
// Calculated width
FLOAT t_width = 0.0f;
// Create a text format object describing the font
IDWriteTextFormat* t_format = MCGDWTextFormatFromHFONT(static_cast(p_font.fid), p_font.size);
if (t_format != nullptr)
{
// Create a text layout for the string and this font
IDWriteTextLayout* t_layout = nil;
if (SUCCEEDED(s_DWFactory->CreateTextLayout(p_text, p_length / sizeof(unichar_t), t_format, INFINITY, INFINITY, &t_layout)))
{
// Get the text metrics for the layout
DWRITE_TEXT_METRICS t_metrics;
if (SUCCEEDED(t_layout->GetMetrics(&t_metrics)))
{
t_width = t_metrics.widthIncludingTrailingWhitespace;
}
t_layout->Release();
}
t_format->Release();
}
return t_width;
}
bool MCGContextMeasurePlatformTextImageBounds(MCGContextRef self, const unichar_t *p_text, uindex_t p_length, const MCGFont &p_font, const MCGAffineTransform &p_transform, MCGRectangle &r_bounds)
{
// Create a text format object describing the font
IDWriteTextFormat* t_format = MCGDWTextFormatFromHFONT(static_cast(p_font.fid), p_font.size);
if (t_format == nil)
return false;
// Create a text layout for the string and this font
bool t_success = false;
IDWriteTextLayout* t_layout = nil;
if (SUCCEEDED(s_DWFactory->CreateTextLayout(p_text, p_length / sizeof(unichar_t), t_format, INFINITY, INFINITY, &t_layout)))
{
// Get the text metrics for the layout
DWRITE_LINE_METRICS t_line_metrics;
UINT32 t_line_metrics_count = 1;
DWRITE_TEXT_METRICS t_metrics;
if (SUCCEEDED(t_layout->GetMetrics(&t_metrics)) && SUCCEEDED(t_layout->GetLineMetrics(&t_line_metrics, 1, &t_line_metrics_count)))
{
r_bounds = MCGRectangleMake(t_metrics.left, -t_line_metrics.baseline, t_metrics.width, t_metrics.height);
t_success = true;
}
}
// Release the created objects and return
if (t_layout != NULL)
t_layout->Release();
if (t_format != NULL)
t_format->Release();
return t_success;
}
void MCGContextDrawPlatformText(MCGContextRef self, const unichar_t *p_text, uindex_t p_length, MCGPoint p_location, const MCGFont &p_font, bool p_rtl)
{
if (!MCGContextIsValid(self))
return;
// The transform that drawing will be using
MCGAffineTransform t_transform = MCGContextGetDeviceTransform(self);
// Create a text format object describing the font
IDWriteTextFormat* t_format = MCGDWTextFormatFromHFONT(static_cast(p_font.fid), p_font.size);
if (t_format == nil)
return;
// Create a text layout for the string and this font
bool t_success = false;
IDWriteTextLayout* t_layout = nil;
if (SUCCEEDED(s_DWFactory->CreateTextLayout(p_text, p_length / sizeof(unichar_t), t_format, INFINITY, INFINITY, &t_layout)))
{
MCGFloat t_advance = 0.0f;
MCGFloat t_advance_direction = 1.0f;
// If the text is RTL, the advance starts from the end and decreases
if (p_rtl)
{
DWRITE_TEXT_METRICS t_metrics;
t_layout->GetMetrics(&t_metrics);
t_advance = t_metrics.width;
t_advance_direction = -1.0f;
}
// Setup the paint
SkPaint t_paint;
if (MCGContextSetupFill(self, t_paint))
{
// Drawing callback
auto t_callback = [=, &t_advance, &t_paint](FLOAT p_x, FLOAT p_y, DWRITE_MEASURING_MODE p_mode, const DWRITE_GLYPH_RUN* p_run, const DWRITE_GLYPH_RUN_DESCRIPTION* p_description)
{
// Apply the font face to the Skia paint object
MCGSkPaintFromDWFontFace(p_run->fontFace, p_run->fontEmSize, t_transform, t_paint);
// disable LCD rendering when backing layer may be transparent
if (!MCGContextIsLayerOpaque(self))
t_paint.setLCDRenderText(false);
// Force anti-aliasing on so that we get nicely-rendered text
t_paint.setAntiAlias(true);
// Text will be specified by glyph index, not codepoint/codeunit
t_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
// Loop over the glyphs in the run
for (UINT32 i = 0; i < p_run->glyphCount; i++)
{
if (p_rtl)
{
// Advance the position for the next glyph
t_advance += t_advance_direction * p_run->glyphAdvances[i];
}
// Position for this glyph
MCGFloat t_x = p_location.x + t_advance;
MCGFloat t_y = p_location.y;
if (p_run->glyphOffsets)
{
t_x += t_advance_direction * p_run->glyphOffsets[i].advanceOffset;
t_y += p_run->glyphOffsets[i].ascenderOffset;
}
if (!p_rtl)
{
// Advance the position for the next glyph
t_advance += t_advance_direction * p_run->glyphAdvances[i];
}
// Render this glyph to the canvas
// Relies on DirectWrite and Skia both using 16-bit integers for glyph indices
self->layer->canvas->drawText(&p_run->glyphIndices[i], sizeof(SkGlyphID), t_x, t_y, t_paint);
}
};
// Create an object to receive the "Draw" callbacks
auto* t_renderer = new MCGDWRenderer(t_callback);
// Perform the drawing
t_layout->Draw(self, t_renderer, 0.0f, 0.0f);
// Free the renderer object
t_renderer->Release();
}
}
// Free the DirectWrite objects
if (t_layout != nil)
t_layout->Release();
if (t_format != nil)
t_format->Release();
}