Switched to a custom IDWriteTextRenderer, which will be necessary for some of our text attributes. More TODO.

This commit is contained in:
Pietro Gagliardi 2017-02-22 03:43:43 -05:00
parent efb7d5b21d
commit 9cc8b03516
3 changed files with 146 additions and 4 deletions

View File

@ -202,8 +202,9 @@ if(BUILD_SHARED_LIBS)
endif() endif()
macro(_add_exec _name) macro(_add_exec _name)
# TODOTODO re-add WIN32 when merging back
add_executable(${_name} add_executable(${_name}
WIN32 EXCLUDE_FROM_ALL EXCLUDE_FROM_ALL
${ARGN}) ${ARGN})
target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES}) target_link_libraries(${_name} libui ${_LIBUI_STATIC_RES})
_target_link_options_private(${_name} _target_link_options_private(${_name}

View File

@ -1,9 +1,10 @@
// 20 january 2017 // 20 january 2017
#include "drawtext.h" #include "drawtext.h"
// TODO double-check ligatures // TODO double-check ligatures on all platforms to make sure we can place the cursor at the right place
// TODO the hiding and showing does not work properly on GTK+ // TODO the hiding and showing does not work properly on GTK+
// TODO using the arrow keys allows us to walk back to the end of the line; IIRC arrow keys shouldn't do that // TODO using the arrow keys allows us to walk back to the end of the line on some platforms (TODO which?); IIRC arrow keys shouldn't do that
// TODO make sure to check the cursor positions of RTL on all platforms
static const char *text = static const char *text =
"Each of the glyphs an end user interacts with are called graphemes. " "Each of the glyphs an end user interacts with are called graphemes. "

View File

@ -7,6 +7,8 @@
// - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell... // - if that's not a problem, do we have overlapping rects in the hittest sample? I can't tell...
// - what happens if any nLines == 0? // - what happens if any nLines == 0?
// TODO verify our renderer is correct, especially with regards to snapping
struct uiDrawTextLayout { struct uiDrawTextLayout {
IDWriteTextFormat *format; IDWriteTextFormat *format;
IDWriteTextLayout *layout; IDWriteTextLayout *layout;
@ -240,23 +242,161 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl
return brush; return brush;
} }
// some of the stuff we want to do isn't possible with what DirectWrite provides itself; we need to do it ourselves
// this is based on http://www.charlespetzold.com/blog/2014/01/Character-Formatting-Extensions-with-DirectWrite.html
class textRenderer : public IDWriteTextRenderer {
ULONG refcount;
ID2D1RenderTarget *rt;
BOOL snap;
IUnknown *black;
public:
textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black)
{
this->refcount = 1;
this->rt = rt;
this->snap = snap;
this->black = black;
}
// IUnknown
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
{
if (ppvObject == NULL)
return E_POINTER;
if (riid == IID_IUnknown ||
riid == __uuidof (IDWritePixelSnapping) ||
riid == __uuidof (IDWriteTextRenderer)) {
this->AddRef();
*ppvObject = this;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
virtual ULONG STDMETHODCALLTYPE AddRef(void)
{
this->refcount++;
return this->refcount;
}
virtual ULONG STDMETHODCALLTYPE Release(void)
{
this->refcount--;
if (this->refcount == 0) {
delete this;
return 0;
}
return this->refcount;
}
// IDWritePixelSnapping
virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(void *clientDrawingContext, DWRITE_MATRIX *transform)
{
D2D1_MATRIX_3X2_F d2dtf;
if (transform == NULL)
return E_POINTER;
this->rt->GetTransform(&d2dtf);
transform->m11 = d2dtf._11;
transform->m12 = d2dtf._12;
transform->m21 = d2dtf._21;
transform->m22 = d2dtf._22;
transform->dx = d2dtf._31;
transform->dy = d2dtf._32;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(void *clientDrawingContext, FLOAT *pixelsPerDip)
{
FLOAT dpix, dpiy;
if (pixelsPerDip == NULL)
return E_POINTER;
this->rt->GetDpi(&dpix, &dpiy);
*pixelsPerDip = dpix / 96;
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(void *clientDrawingContext, BOOL *isDisabled)
{
if (isDisabled == NULL)
return E_POINTER;
*isDisabled = !this->snap;
return S_OK;
}
// IDWriteTextRenderer
virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, DWRITE_MEASURING_MODE measuringMode, const DWRITE_GLYPH_RUN *glyphRun, const DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, IUnknown *clientDrawingEffect)
{
D2D1_POINT_2F baseline;
baseline.x = baselineOriginX;
baseline.y = baselineOriginY;
if (clientDrawingEffect == NULL)
clientDrawingEffect = black;
this->rt->DrawGlyphRun(
baseline,
glyphRun,
(ID2D1Brush *) clientDrawingEffect,
measuringMode);
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(void *clientDrawingContext, FLOAT originX, FLOAT originY, IDWriteInlineObject *inlineObject, BOOL isSideways, BOOL isRightToLeft, IUnknown *clientDrawingEffect)
{
if (inlineObject == NULL)
return E_POINTER;
return inlineObject->Draw(clientDrawingContext, this,
originX, originY,
isSideways, isRightToLeft,
clientDrawingEffect);
}
virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect)
{
// TODO
return S_OK;
}
virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect)
{
// TODO
return S_OK;
}
};
// TODO this ignores clipping? // TODO this ignores clipping?
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y) void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
{ {
D2D1_POINT_2F pt; D2D1_POINT_2F pt;
ID2D1Brush *black; ID2D1Brush *black;
textRenderer *renderer;
HRESULT hr;
// TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms // TODO document that fully opaque black is the default text color; figure out whether this is upheld in various scenarios on other platforms
// TODO figure out if this needs to be cleaned out // TODO figure out if this needs to be cleaned out
black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0); black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
#if 0
pt.x = x; pt.x = x;
pt.y = y; pt.y = y;
// TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP? // TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
// TODO D2D1_DRAW_TEXT_OPTIONS_CLIP? // TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
// TODO when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT? // TODO LONGTERM when setting 8.1 as minimum (TODO verify), D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
// TODO what is our pixel snapping setting related to the OPTIONS enum values? // TODO what is our pixel snapping setting related to the OPTIONS enum values?
c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE); c->rt->DrawTextLayout(pt, tl->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
#else
renderer = new textRenderer(c->rt,
TRUE, // TODO FALSE for no-snap?
black);
hr = tl->layout->Draw(NULL,
renderer,
x, y);
if (hr != S_OK)
logHRESULT(L"error drawing IDWriteTextLayout", hr);
renderer->Release();
#endif
black->Release(); black->Release();
} }