More text attributes on Windows, including the beginning of drawing effects for colors and underlines.
This commit is contained in:
parent
fb04feaebb
commit
b42250e3a9
|
@ -2,19 +2,42 @@
|
|||
#include "uipriv_windows.hpp"
|
||||
#include "draw.hpp"
|
||||
|
||||
// we need to collect all the OpenType features and background blocks and add them all at once
|
||||
// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect()
|
||||
// we also need to collect all the OpenType features and background blocks and add them all at once
|
||||
// TODO(TODO does not seem to apply here) this is the wrong approach; it causes Pango to end runs early, meaning attributes like the ligature attributes never get applied properly
|
||||
// TODO contextual alternates override ligatures?
|
||||
// TODO rename this struct to something that isn't exclusively foreach-ing?
|
||||
struct foreachParams {
|
||||
const uint16_t *s;
|
||||
IDWriteTextLayout *layout;
|
||||
std::map<size_t, textDrawingEffect *> *effects;
|
||||
std::map<size_t, IDWriteTypography *> *features;
|
||||
//TODO GPtrArray *backgroundClosures;
|
||||
};
|
||||
|
||||
#define isCodepointStart(w) ((((uint16_t) (w)) < 0xDC00) || (((uint16_t) (w)) >= 0xE000))
|
||||
|
||||
static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function<void(textDrawingEffect *)> f)
|
||||
{
|
||||
size_t i;
|
||||
size_t *key;
|
||||
textDrawingEffect *t;
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
// don't create redundant entries; see below
|
||||
if (!isCodepointStart(p->s[i]))
|
||||
continue;
|
||||
t = (*(p->effects))[i];
|
||||
if (t != NULL) {
|
||||
f(t);
|
||||
continue;
|
||||
}
|
||||
t = new textDrawingEffect;
|
||||
f(t);
|
||||
(*(p->effects))[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
static void ensureFeaturesInRange(struct foreachParams *p, size_t start, size_t end)
|
||||
{
|
||||
size_t i;
|
||||
|
@ -124,9 +147,8 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t
|
|||
#if 0 /* TODO */
|
||||
GClosure *closure;
|
||||
PangoGravity gravity;
|
||||
PangoUnderline underline;
|
||||
PangoLanguage *lang;
|
||||
#endif
|
||||
WCHAR *localeName;
|
||||
struct otParam op;
|
||||
HRESULT hr;
|
||||
|
||||
|
@ -151,30 +173,38 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t
|
|||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying size attribute", hr);
|
||||
break;
|
||||
#if 0 /* TODO */
|
||||
case uiAttributeWeight:
|
||||
// TODO reverse the misalignment from drawtext.c if it is corrected
|
||||
addattr(p, start, end,
|
||||
pango_attr_weight_new((PangoWeight) (spec->Value)));
|
||||
// TODO reverse the misalignment from drawtext.cpp if it is corrected
|
||||
hr = p->layout->SetFontWeight(
|
||||
(DWRITE_FONT_WEIGHT) (spec->Value),
|
||||
range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying weight attribute", hr);
|
||||
break;
|
||||
case uiAttributeItalic:
|
||||
addattr(p, start, end,
|
||||
pango_attr_style_new(pangoItalics[(uiDrawTextItalic) (spec->Value)]));
|
||||
hr = p->layout->SetFontStyle(
|
||||
dwriteItalics[(uiDrawTextItalic) (spec->Value)],
|
||||
range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying italic attribute", hr);
|
||||
break;
|
||||
case uiAttributeStretch:
|
||||
addattr(p, start, end,
|
||||
pango_attr_stretch_new(pangoStretches[(uiDrawTextStretch) (spec->Value)]));
|
||||
hr = p->layout->SetFontStretch(
|
||||
dwriteStretches[(uiDrawTextStretch) (spec->Value)],
|
||||
range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying stretch attribute", hr);
|
||||
break;
|
||||
case uiAttributeColor:
|
||||
addattr(p, start, end,
|
||||
pango_attr_foreground_new(
|
||||
(guint16) (spec->R * 65535.0),
|
||||
(guint16) (spec->G * 65535.0),
|
||||
(guint16) (spec->B * 65535.0)));
|
||||
addattr(p, start, end,
|
||||
FUTURE_pango_attr_foreground_alpha_new(
|
||||
(guint16) (spec->A * 65535.0)));
|
||||
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) {
|
||||
t->hasColor = true;
|
||||
t->r = spec->R;
|
||||
t->g = spec->G;
|
||||
t->b = spec->B;
|
||||
t->a = spec->A;
|
||||
});
|
||||
break;
|
||||
#if 0
|
||||
case uiAttributeBackground:
|
||||
closure = mkBackgroundClosure(start, end,
|
||||
spec->R, spec->G, spec->B, spec->A);
|
||||
|
@ -231,14 +261,15 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t
|
|||
break;
|
||||
}
|
||||
break;
|
||||
// language strings are specified as BCP 47: https://developer.gnome.org/pango/1.30/pango-Scripts-and-Languages.html#pango-language-from-string https://www.ietf.org/rfc/rfc3066.txt
|
||||
case uiAttributeLanguage:
|
||||
lang = pango_language_from_string((const char *) (spec->Value));
|
||||
addattr(p, start, end,
|
||||
pango_attr_language_new(lang));
|
||||
// lang *cannot* be freed
|
||||
break;
|
||||
#endif
|
||||
// locale names are specified as BCP 47: https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx https://www.ietf.org/rfc/rfc4646.txt
|
||||
case uiAttributeLanguage:
|
||||
localeName = toUTF16((char *) (spec->Value));
|
||||
hr = p->layout->SetLocaleName(localeName, range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying locale name attribute", hr);
|
||||
uiFree(localeName);
|
||||
break;
|
||||
// TODO
|
||||
default:
|
||||
// handle typographic features
|
||||
|
@ -252,6 +283,25 @@ static int processAttribute(uiAttributedString *s, uiAttributeSpec *spec, size_t
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void applyAndFreeEffectsAttributes(struct foreachParams *p)
|
||||
{
|
||||
DWRITE_TEXT_RANGE range;
|
||||
HRESULT hr;
|
||||
|
||||
for (auto iter = p->effects->begin(); iter != p->effects->end(); iter++) {
|
||||
// make sure we cover an entire code point
|
||||
range.startPosition = iter->first;
|
||||
range.length = 1;
|
||||
if (!isCodepointStart(p->s[iter->first]))
|
||||
range.length = 2;
|
||||
hr = p->layout->SetDrawingEffect(iter->second, range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying drawing effects attributes", hr);
|
||||
iter->second->Release();
|
||||
}
|
||||
delete p->effects;
|
||||
}
|
||||
|
||||
static void applyAndFreeFeatureAttributes(struct foreachParams *p)
|
||||
{
|
||||
DWRITE_TEXT_RANGE range;
|
||||
|
@ -265,7 +315,7 @@ static void applyAndFreeFeatureAttributes(struct foreachParams *p)
|
|||
range.length = 2;
|
||||
hr = p->layout->SetTypography(iter->second, range);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error applying typographic features", hr);
|
||||
logHRESULT(L"error applying typographic features attributes", hr);
|
||||
iter->second->Release();
|
||||
}
|
||||
delete p->features;
|
||||
|
@ -284,9 +334,11 @@ void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayou
|
|||
|
||||
fep.s = attrstrUTF16(p->String);
|
||||
fep.layout = layout;
|
||||
fep.effects = new std::map<size_t, textDrawingEffect *>;
|
||||
fep.features = new std::map<size_t, IDWriteTypography *>;
|
||||
//TODO fep.backgroundClosures = g_ptr_array_new_with_free_func(unrefClosure);
|
||||
uiAttributedStringForEachAttribute(p->String, processAttribute, &fep);
|
||||
applyAndFreeEffectsAttributes(&fep);
|
||||
applyAndFreeFeatureAttributes(&fep);
|
||||
//TODO *backgroundClosures = fep.backgroundClosures;
|
||||
}
|
||||
|
|
|
@ -17,3 +17,65 @@ extern void m2d(uiDrawMatrix *m, D2D1_MATRIX_3X2_F *d);
|
|||
|
||||
// attrstr.cpp
|
||||
extern void attrstrToIDWriteTextLayoutAttrs(uiDrawTextLayoutParams *p, IDWriteTextLayout *layout/*TODO, GPtrArray **backgroundClosures*/);
|
||||
|
||||
// drawtext.cpp
|
||||
class textDrawingEffect : public IUnknown {
|
||||
ULONG refcount;
|
||||
public:
|
||||
bool hasColor;
|
||||
double r;
|
||||
double g;
|
||||
double b;
|
||||
double a;
|
||||
|
||||
bool hasUnderline;
|
||||
uiDrawUnderlineStyle u;
|
||||
|
||||
bool hasUnderlineColor;
|
||||
double ur;
|
||||
double ug;
|
||||
double ub;
|
||||
double ua;
|
||||
|
||||
textDrawingEffect()
|
||||
{
|
||||
this->refcount = 1;
|
||||
this->hasColor = false;
|
||||
this->hasUnderline = false;
|
||||
this->hasUnderlineColor = false;
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject)
|
||||
{
|
||||
if (ppvObject == NULL)
|
||||
return E_POINTER;
|
||||
if (riid == IID_IUnknown) {
|
||||
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;
|
||||
}
|
||||
|
||||
};
|
||||
// TODO these should not be exported
|
||||
extern std::map<uiDrawTextItalic, DWRITE_FONT_STYLE> dwriteItalics;
|
||||
extern std::map<uiDrawTextStretch, DWRITE_FONT_STRETCH> dwriteStretches;
|
||||
|
|
|
@ -28,14 +28,14 @@ struct uiDrawTextLayout {
|
|||
#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
|
||||
|
||||
// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before)
|
||||
static std::map<uiDrawTextItalic, DWRITE_FONT_STYLE> dwriteItalics = {
|
||||
std::map<uiDrawTextItalic, DWRITE_FONT_STYLE> dwriteItalics = {
|
||||
{ uiDrawTextItalicNormal, DWRITE_FONT_STYLE_NORMAL },
|
||||
{ uiDrawTextItalicOblique, DWRITE_FONT_STYLE_OBLIQUE },
|
||||
{ uiDrawTextItalicItalic, DWRITE_FONT_STYLE_ITALIC },
|
||||
};
|
||||
|
||||
// TODO should be const but then I can't operator[] on it; the real solution is to find a way to do designated array initializers in C++11 but I do not know enough C++ voodoo to make it work (it is possible but no one else has actually done it before)
|
||||
static std::map<uiDrawTextStretch, DWRITE_FONT_STRETCH> dwriteStretches = {
|
||||
std::map<uiDrawTextStretch, DWRITE_FONT_STRETCH> dwriteStretches = {
|
||||
{ uiDrawTextStretchUltraCondensed, DWRITE_FONT_STRETCH_ULTRA_CONDENSED },
|
||||
{ uiDrawTextStretchExtraCondensed, DWRITE_FONT_STRETCH_EXTRA_CONDENSED },
|
||||
{ uiDrawTextStretchCondensed, DWRITE_FONT_STRETCH_CONDENSED },
|
||||
|
@ -219,12 +219,10 @@ void uiDrawFreeTextLayout(uiDrawTextLayout *tl)
|
|||
uiFree(tl);
|
||||
}
|
||||
|
||||
static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a)
|
||||
static HRESULT mkSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a, ID2D1SolidColorBrush **brush)
|
||||
{
|
||||
D2D1_BRUSH_PROPERTIES props;
|
||||
D2D1_COLOR_F color;
|
||||
ID2D1SolidColorBrush *brush;
|
||||
HRESULT hr;
|
||||
|
||||
ZeroMemory(&props, sizeof (D2D1_BRUSH_PROPERTIES));
|
||||
props.opacity = 1.0;
|
||||
|
@ -235,10 +233,18 @@ static ID2D1SolidColorBrush *mkSolidBrush(ID2D1RenderTarget *rt, double r, doubl
|
|||
color.g = g;
|
||||
color.b = b;
|
||||
color.a = a;
|
||||
hr = rt->CreateSolidColorBrush(
|
||||
return rt->CreateSolidColorBrush(
|
||||
&color,
|
||||
&props,
|
||||
&brush);
|
||||
brush);
|
||||
}
|
||||
|
||||
static ID2D1SolidColorBrush *mustMakeSolidBrush(ID2D1RenderTarget *rt, double r, double g, double b, double a)
|
||||
{
|
||||
ID2D1SolidColorBrush *brush;
|
||||
HRESULT hr;
|
||||
|
||||
hr = mkSolidBrush(rt, r, g, b, a, &brush);
|
||||
if (hr != S_OK)
|
||||
logHRESULT(L"error creating solid brush", hr);
|
||||
return brush;
|
||||
|
@ -250,9 +256,9 @@ class textRenderer : public IDWriteTextRenderer {
|
|||
ULONG refcount;
|
||||
ID2D1RenderTarget *rt;
|
||||
BOOL snap;
|
||||
IUnknown *black;
|
||||
ID2D1SolidColorBrush *black;
|
||||
public:
|
||||
textRenderer(ID2D1RenderTarget *rt, BOOL snap, IUnknown *black)
|
||||
textRenderer(ID2D1RenderTarget *rt, BOOL snap, ID2D1SolidColorBrush *black)
|
||||
{
|
||||
this->refcount = 1;
|
||||
this->rt = rt;
|
||||
|
@ -332,16 +338,26 @@ public:
|
|||
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;
|
||||
textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect;
|
||||
ID2D1SolidColorBrush *brush;
|
||||
|
||||
baseline.x = baselineOriginX;
|
||||
baseline.y = baselineOriginY;
|
||||
if (clientDrawingEffect == NULL)
|
||||
clientDrawingEffect = black;
|
||||
brush = this->black;
|
||||
if (t != NULL && t->hasColor) {
|
||||
HRESULT hr;
|
||||
|
||||
hr = mkSolidBrush(this->rt, t->r, t->g, t->b, t->a, &brush);
|
||||
if (hr != S_OK)
|
||||
return hr;
|
||||
}
|
||||
this->rt->DrawGlyphRun(
|
||||
baseline,
|
||||
glyphRun,
|
||||
(ID2D1Brush *) clientDrawingEffect,
|
||||
brush,
|
||||
measuringMode);
|
||||
if (t != NULL && t->hasColor)
|
||||
brush->Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
@ -357,12 +373,16 @@ public:
|
|||
|
||||
virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_STRIKETHROUGH *strikethrough, IUnknown *clientDrawingEffect)
|
||||
{
|
||||
textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect;
|
||||
|
||||
// TODO
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
virtual HRESULT DrawUnderline(void *clientDrawingContext, FLOAT baselineOriginX, FLOAT baselineOriginY, const DWRITE_UNDERLINE *underline, IUnknown *clientDrawingEffect)
|
||||
{
|
||||
textDrawingEffect *t = (textDrawingEffect *) clientDrawingEffect;
|
||||
|
||||
// TODO
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -372,13 +392,13 @@ public:
|
|||
void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
||||
{
|
||||
D2D1_POINT_2F pt;
|
||||
ID2D1Brush *black;
|
||||
ID2D1SolidColorBrush *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 figure out if this needs to be cleaned out
|
||||
black = mkSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
|
||||
black = mustMakeSolidBrush(c->rt, 0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
#define renderD2D 0
|
||||
#define renderOur 1
|
||||
|
@ -395,7 +415,7 @@ void uiDrawText(uiDrawContext *c, uiDrawTextLayout *tl, double x, double y)
|
|||
// draw ours semitransparent so we can check
|
||||
// TODO get the actual color Charles Petzold uses and use that
|
||||
black->Release();
|
||||
black = mkSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75);
|
||||
black = mustMakeSolidBrush(c->rt, 1.0, 0.0, 0.0, 0.75);
|
||||
#endif
|
||||
#if renderOur
|
||||
renderer = new textRenderer(c->rt,
|
||||
|
|
|
@ -47,4 +47,5 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue