Implemented colored text on OS X and Windows.

This commit is contained in:
Pietro Gagliardi 2016-04-19 18:45:16 -04:00
parent e786d664cb
commit 721269b3eb
4 changed files with 108 additions and 22 deletions

View File

@ -601,7 +601,6 @@ void doDrawText(CGContextRef c, CGFloat cheight, double x, double y, uiDrawTextL
CGContextSetTextPosition(c, x, y);
#endif
// TODO length - 1?
#define rangeToCFRange() CFRangeMake(startChar, endChar - startChar)
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a)

View File

@ -890,3 +890,17 @@ void uiDrawText(uiDrawContext *c, double x, double y, uiDrawTextLayout *layout)
doDrawText(c->rt, black, x, y, layout);
ID2D1Brush_Release(black);
}
// TODO this is a mess
ID2D1Brush *createSolidColorBrushInternal(ID2D1RenderTarget *rt, double r, double g, double b, double a)
{
uiDrawBrush brush;
ZeroMemory(&brush, sizeof (uiDrawBrush));
brush.Type = uiDrawBrushTypeSolid;
brush.R = r;
brush.G = g;
brush.B = b;
brush.A = a;
return makeBrush(&brush, rt);
}

View File

@ -1,5 +1,7 @@
// 22 december 2015
#include "uipriv_windows.h"
// TODO
#include <vector>
// notes:
// only available in windows 8 and newer:
@ -315,15 +317,30 @@ void uiDrawTextFontGetMetrics(uiDrawTextFont *font, uiDrawTextFontMetrics *metri
metrics->UnderlineThickness = scaleUnits(dm.underlineThickness, dm.designUnitsPerEm, font->size);
}
// some attributes, such as foreground color, can't be applied until after we establish a Direct2D context :/ so we have to prepare all attributes in advance
// also since there's no way to clear the attributes from a layout en masse (apart from overwriting them all), we'll play it safe by creating a new layout each time
enum layoutAttrType {
layoutAttrColor,
};
struct layoutAttr {
enum layoutAttrType type;
intmax_t start;
intmax_t end;
double components[4];
};
struct uiDrawTextLayout {
WCHAR *text;
size_t textlen;
double width;
IDWriteTextFormat *format;
IDWriteTextLayout *layout;
std::vector<struct layoutAttr> *attrs;
};
uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultFont, double width)
{
uiDrawTextLayout *layout;
WCHAR *wtext;
HRESULT hr;
layout = uiNew(uiDrawTextLayout);
@ -343,73 +360,128 @@ uiDrawTextLayout *uiDrawNewTextLayout(const char *text, uiDrawTextFont *defaultF
if (hr != S_OK)
logHRESULT("error creating IDWriteTextFormat in uiDrawNewTextLayout()", hr);
wtext = toUTF16(text);
hr = dwfactory->CreateTextLayout(wtext, wcslen(wtext),
layout->format,
// FLOAT is float, not double, so this should work... TODO
FLT_MAX, FLT_MAX,
&(layout->layout));
if (hr != S_OK)
logHRESULT("error creating IDWriteTextLayout in uiDrawNewTextLayout()", hr);
uiFree(wtext);
layout->text = toUTF16(text);
layout->textlen = wcslen(layout->text);
uiDrawTextLayoutSetWidth(layout, width);
layout->attrs = new std::vector<struct layoutAttr>;
return layout;
}
void uiDrawFreeTextLayout(uiDrawTextLayout *layout)
{
layout->layout->Release();
delete layout->attrs;
layout->format->Release();
uiFree(layout->text);
uiFree(layout);
}
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
IDWriteTextLayout *prepareLayout(uiDrawTextLayout *layout, ID2D1RenderTarget *rt)
{
IDWriteTextLayout *dl;
DWRITE_TEXT_RANGE range;
IUnknown *unkBrush;
DWRITE_WORD_WRAPPING wrap;
FLOAT maxWidth;
HRESULT hr;
hr = dwfactory->CreateTextLayout(layout->text, layout->textlen,
layout->format,
// FLOAT is float, not double, so this should work... TODO
FLT_MAX, FLT_MAX,
&dl);
if (hr != S_OK)
logHRESULT("error creating IDWriteTextLayout in prepareLayout()", hr);
for (const struct layoutAttr &attr : *(layout->attrs)) {
range.startPosition = attr.start;
range.length = attr.end - attr.start;
switch (attr.type) {
case layoutAttrColor:
if (rt == NULL) // determining extents, not drawing
break;
unkBrush = createSolidColorBrushInternal(rt, attr.components[0], attr.components[1], attr.components[2], attr.components[3]);
hr = dl->SetDrawingEffect(unkBrush, range);
unkBrush->Release(); // associated with dl
break;
default:
hr = E_FAIL;
logHRESULT("invalid text attribute type in prepareLayout()", hr);
}
if (hr != S_OK)
logHRESULT("error adding attribute to text layout in prepareLayout()", hr);
}
// and set the width
// this is the only wrapping mode (apart from "no wrap") available prior to Windows 8.1
wrap = DWRITE_WORD_WRAPPING_WRAP;
maxWidth = width;
if (width < 0) {
maxWidth = layout->width;
if (layout->width < 0) {
wrap = DWRITE_WORD_WRAPPING_NO_WRAP;
// setting the max width in this case technically isn't needed since the wrap mode will simply ignore the max width, but let's do it just to be safe
maxWidth = FLT_MAX; // see TODO above
}
hr = layout->layout->SetWordWrapping(wrap);
hr = dl->SetWordWrapping(wrap);
if (hr != S_OK)
logHRESULT("error setting word wrapping mode in uiDrawTextLayoutSetWidth()", hr);
hr = layout->layout->SetMaxWidth(maxWidth);
logHRESULT("error setting word wrapping mode in prepareLayout()", hr);
hr = dl->SetMaxWidth(maxWidth);
if (hr != S_OK)
logHRESULT("error setting max layout width in uiDrawTextLayoutSetWidth()", hr);
logHRESULT("error setting max layout width in prepareLayout()", hr);
return dl;
}
void uiDrawTextLayoutSetWidth(uiDrawTextLayout *layout, double width)
{
layout->width = width;
}
// TODO for a single line the height includes the leading; it should not
void uiDrawTextLayoutExtents(uiDrawTextLayout *layout, double *width, double *height)
{
IDWriteTextLayout *dl;
DWRITE_TEXT_METRICS metrics;
HRESULT hr;
hr = layout->layout->GetMetrics(&metrics);
dl = prepareLayout(layout, NULL);
hr = dl->GetMetrics(&metrics);
if (hr != S_OK)
logHRESULT("error getting layout metrics in uiDrawTextLayoutExtents()", hr);
*width = metrics.width;
// TODO make sure the behavior of this on empty strings is the same on all platforms
*height = metrics.height;
dl->Release();
}
void doDrawText(ID2D1RenderTarget *rt, ID2D1Brush *black, double x, double y, uiDrawTextLayout *layout)
{
IDWriteTextLayout *dl;
D2D1_POINT_2F pt;
HRESULT hr;
dl = prepareLayout(layout, rt);
pt.x = x;
pt.y = y;
// TODO D2D1_DRAW_TEXT_OPTIONS_NO_SNAP?
// TODO D2D1_DRAW_TEXT_OPTIONS_CLIP?
// TODO when setting 8.1 as minimum, D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT?
rt->DrawTextLayout(pt, layout->layout, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
rt->DrawTextLayout(pt, dl, black, D2D1_DRAW_TEXT_OPTIONS_NONE);
dl->Release();
}
void uiDrawTextLayoutSetColor(uiDrawTextLayout *layout, intmax_t startChar, intmax_t endChar, double r, double g, double b, double a)
{
struct layoutAttr attr;
attr.type = layoutAttrColor;
attr.start = startChar;
attr.end = endChar;
attr.components[0] = r;
attr.components[1] = g;
attr.components[2] = b;
attr.components[3] = a;
layout->attrs->push_back(attr);
}

View File

@ -140,6 +140,7 @@ extern void uninitDraw(void);
extern ID2D1HwndRenderTarget *makeHWNDRenderTarget(HWND hwnd);
extern uiDrawContext *newContext(ID2D1RenderTarget *);
extern void freeContext(uiDrawContext *);
extern ID2D1Brush *createSolidColorBrushInternal(ID2D1RenderTarget *rt, double r, double g, double b, double a);
// dwrite.cpp
#ifdef __cplusplus