Started migrating attrstr.cpp, using the same techniques as attrstr.m.

This commit is contained in:
Pietro Gagliardi 2018-03-13 18:43:32 -04:00
parent abc6fd2825
commit 12e97a1b29
2 changed files with 170 additions and 67 deletions

View File

@ -257,7 +257,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_
else else
cfa = [cfa copy]; cfa = [cfa copy];
[cfa addAttribute:attr]; [cfa addAttribute:attr];
// clamp effectiveRange within [start, end) // clamp range within [start, end)
if (range.location < start) { if (range.location < start) {
diff = start - range.location; diff = start - range.location;
range.location = start; range.location = start;

View File

@ -1,38 +1,147 @@
// 12 february 2017 // 12 february 2017
#include "uipriv_windows.hpp" #include "uipriv_windows.hpp"
#include "draw.hpp" #include "attrstr.hpp"
// TODO this whole file needs cleanup // TODO this whole file needs cleanup
// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() // we need to collect all the background blocks and add them all at once
// we also need to collect all the background blocks and add them all at once
// TODO contextual alternates override ligatures? // TODO contextual alternates override ligatures?
// TODO rename this struct to something that isn't exclusively foreach-ing? // TODO rename this struct to something that isn't exclusively foreach-ing?
struct foreachParams { struct foreachParams {
const uint16_t *s; const uint16_t *s;
size_t len; size_t len;
IDWriteTextLayout *layout; IDWriteTextLayout *layout;
std::map<size_t, textDrawingEffect *> *effects;
std::vector<backgroundFunc> *backgroundFuncs; std::vector<backgroundFunc> *backgroundFuncs;
}; };
static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function<void(textDrawingEffect *)> f) // we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect()
{ // we also want to combine identical effects, which DirectWrite doesn't seem to provide a way to do
size_t i; // we can at least try to goad it into doing so if we can deduplicate effects once they're all computed
size_t *key; // so what we do is use this class to store in-progress effects, much like uiprivCombinedFontAttr on the OS X code
textDrawingEffect *t; // we then deduplicate them later while converting them into a form suitable for drawing with; see applyEffectsAttributes() below
class combinedEffectsAttr : public IUnknown {
ULONG refcount;
uiAttribute *colorAttr;
uiAttribute *underlineAttr;
uiAttribute *underlineColorAttr;
// TODO explain why we make one for every character void setAttribute(uiAttribute *a)
for (i = start; i < end; i++) { {
t = (*(p->effects))[i]; if (a == NULL)
if (t != NULL) { return;
f(t); switch (uiAttributeGetType(a)) {
continue; case uiAttributeTypeColor:
if (this->colorAttr != NULL)
uiprivAttributeRelease(this->colorAttr);
this->colorAttr = uiprivAttributeRetain(a);
break;
case uiAttributeTypeUnderline:
if (this->underlineAttr != NULL)
uiprivAttributeRelease(this->underlineAttr);
this->underlineAttr = uiprivAttributeRetain(a);
break;
case uiAttributeTypeUnderlineColor:
if (this->underlineAttr != NULL)
uiprivAttributeRelease(this->underlineAttr);
this->underlineColorAttr = uiprivAttributeRetain(a);
break;
} }
t = new textDrawingEffect;
f(t);
(*(p->effects))[i] = t;
} }
public:
combinedEffectsAttr(uiAttribute *a)
{
this->refcount = 1;
this->colorAttr = NULL;
this->underlineAttr = NULL;
this->underlineColorAttr = NULL;
this->setAttribute(a);
}
~combinedEffectsAttr()
{
if (this->colorAttr != NULL)
uiprivAttributeRelease(this->colorAttr);
if (this->underlineAttr != NULL)
uiprivAttributeRelease(this->underlineAttr);
if (this->underlineColorAttr != NULL)
uiprivAttributeRelease(this->underlineColorAttr);
}
// 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;
}
combinedEffectsAttr *cloneWith(uiAttribute *a)
{
combinedEffectsAttr *b;
b = new combinedEffectsAttr(this->colorAttr);
b->setAttribute(this->underlineAttr);
b->setAttribute(this->underlineColorAttr);
b->setAttribute(a);
return b;
}
};
static HRESULT addEffectAttributeToRange(struct foreachParams *p, size_t start, size_t end, uiAttribute *attr)
{
IUnknown *u;
combinedEffectsAttr *cea;
DWRITE_TEXT_RANGE range;
size_t diff;
HRESULT hr;
while (start < end) {
hr = p->layout->GetDrawingEffect(start, &u, &range);
if (hr != S_OK)
{logHRESULT(L"HELP", hr);
return hr;
} cea = (combinedEffectsAttr *) u;
if (cea == NULL)
cea = new combinedEffectsAttr(attr);
else
cea = cea->cloneWith(attr);
// clamp range within [start, end)
if (range.startPosition < start) {
diff = start - range.startPosition;
range.startPosition = start;
range.length -= diff;
}
if ((range.startPosition + range.length) > end)
range.length = end - range.startPosition;
hr = p->layout->SetDrawingEffect(cea, range);
if (hr != S_OK)
return hr;
// TODO figure out what and when needs to be released
start += range.length;
}
return S_OK;
} }
static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a) static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, double g, double b, double a)
@ -49,7 +158,7 @@ static backgroundFunc mkBackgroundFunc(size_t start, size_t end, double r, doubl
}; };
} }
static uiForEach processAttribute(const uiAttributedString *s, const uiAttributeSpec *spec, size_t start, size_t end, void *data) static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute *attr, size_t start, size_t end, void *data)
{ {
struct foreachParams *p = (struct foreachParams *) data; struct foreachParams *p = (struct foreachParams *) data;
DWRITE_TEXT_RANGE range; DWRITE_TEXT_RANGE range;
@ -66,76 +175,69 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute
end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); end = attrstrUTF8ToUTF16((uiAttributedString *) s, end);
range.startPosition = start; range.startPosition = start;
range.length = end - start; range.length = end - start;
switch (spec->Type) { switch (uiAttributeGetType(attr)) {
case uiAttributeFamily: case uiAttributeTypeFamily:
wfamily = toUTF16(spec->Family); wfamily = toUTF16(uiAttributeFamily(attr));
hr = p->layout->SetFontFamilyName(wfamily, range); hr = p->layout->SetFontFamilyName(wfamily, range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying family name attribute", hr); logHRESULT(L"error applying family name attribute", hr);
uiFree(wfamily); uiFree(wfamily);
break; break;
case uiAttributeSize: case uiAttributeTypeSize:
hr = p->layout->SetFontSize( hr = p->layout->SetFontSize(
// TODO unify with drawtext.cpp // TODO unify with fontmatch.cpp and/or attrstr.hpp
#define pointSizeToDWriteSize(size) (size * (96.0 / 72.0)) #define pointSizeToDWriteSize(size) (size * (96.0 / 72.0))
pointSizeToDWriteSize(spec->Double), pointSizeToDWriteSize(uiAttributeSize(attr)),
range); range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying size attribute", hr); logHRESULT(L"error applying size attribute", hr);
break; break;
case uiAttributeWeight: case uiAttributeTypeWeight:
// TODO reverse the misalignment from drawtext.cpp if it is corrected
hr = p->layout->SetFontWeight( hr = p->layout->SetFontWeight(
(DWRITE_FONT_WEIGHT) (spec->Value), uiprivWeightToDWriteWeight(uiAttributeWeight(attr)),
range); range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying weight attribute", hr); logHRESULT(L"error applying weight attribute", hr);
break; break;
case uiAttributeItalic: case uiAttributeTypeItalic:
hr = p->layout->SetFontStyle( hr = p->layout->SetFontStyle(
dwriteItalics[(uiDrawTextItalic) (spec->Value)], uiprivItalicToDWriteStyle(uiAttributeItalic(attr)),
range); range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying italic attribute", hr); logHRESULT(L"error applying italic attribute", hr);
break; break;
case uiAttributeStretch: case uiAttributeTypeStretch:
hr = p->layout->SetFontStretch( hr = p->layout->SetFontStretch(
dwriteStretches[(uiDrawTextStretch) (spec->Value)], uiprivStretchToDWriteStretch(uiAttributeStretch(attr)),
range); range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying stretch attribute", hr); logHRESULT(L"error applying stretch attribute", hr);
break; break;
case uiAttributeColor: case uiAttributeTypeUnderline:
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method
t->hasColor = true; hasUnderline = FALSE;
t->r = spec->R; if (uiAttributeUnderline(attr) != uiUnderlineNone)
t->g = spec->G; hasUnderline = TRUE;
t->b = spec->B; hr = p->layout->SetUnderline(hasUnderline, range);
t->a = spec->A; if (hr != S_OK)
}); logHRESULT(L"error applying underline attribute", hr);
// and fall through to set the underline style through the drawing effect
case uiAttributeTypeColor:
case uiAttributeTypeUnderlineColor:
hr = addEffectAttributeToRange(p, start, end, attr);
if (hr != S_OK)
logHRESULT(L"error applying effect (color, underline, or underline color) attribute", hr);
break; break;
case uiAttributeBackground: case uiAttributeBackground:
p->backgroundFuncs->push_back( p->backgroundFuncs->push_back(
mkBackgroundFunc(ostart, oend, mkBackgroundFunc(ostart, oend,
spec->R, spec->G, spec->B, spec->A)); spec->R, spec->G, spec->B, spec->A));
break; break;
case uiAttributeUnderline: #if 0
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { TODO
t->hasUnderline = true;
t->u = (uiDrawUnderlineStyle) (spec->Value);
});
// mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method
hasUnderline = FALSE;
if ((uiDrawUnderlineStyle) (spec->Value) != uiDrawUnderlineStyleNone)
hasUnderline = TRUE;
hr = p->layout->SetUnderline(hasUnderline, range);
if (hr != S_OK)
logHRESULT(L"error applying underline attribute", hr);
break;
case uiAttributeUnderlineColor:
switch (spec->Value) { switch (spec->Value) {
case uiDrawUnderlineColorCustom: case uiDrawUnderlineColorCustom:
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { x(p, start, end, [=](textDrawingEffect *t) {
t->hasUnderlineColor = true; t->hasUnderlineColor = true;
t->ur = spec->R; t->ur = spec->R;
t->ug = spec->G; t->ug = spec->G;
@ -146,7 +248,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute
// TODO see if Microsoft has any standard colors for this // TODO see if Microsoft has any standard colors for this
case uiDrawUnderlineColorSpelling: case uiDrawUnderlineColorSpelling:
// TODO GtkTextView style property error-underline-color // TODO GtkTextView style property error-underline-color
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { x(p, start, end, [=](textDrawingEffect *t) {
t->hasUnderlineColor = true; t->hasUnderlineColor = true;
t->ur = 1.0; t->ur = 1.0;
t->ug = 0.0; t->ug = 0.0;
@ -155,7 +257,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute
}); });
break; break;
case uiDrawUnderlineColorGrammar: case uiDrawUnderlineColorGrammar:
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { x(p, start, end, [=](textDrawingEffect *t) {
t->hasUnderlineColor = true; t->hasUnderlineColor = true;
t->ur = 0.0; t->ur = 0.0;
t->ug = 1.0; t->ug = 1.0;
@ -164,7 +266,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute
}); });
break; break;
case uiDrawUnderlineColorAuxiliary: case uiDrawUnderlineColorAuxiliary:
ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { x(p, start, end, [=](textDrawingEffect *t) {
t->hasUnderlineColor = true; t->hasUnderlineColor = true;
t->ur = 0.0; t->ur = 0.0;
t->ug = 0.0; t->ug = 0.0;
@ -174,24 +276,25 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute
break; break;
} }
break; break;
case uiAttributeFeatures: #endif
// only generate an attribute if spec->Features is not NULL case uiAttributeTypeFeatures:
if (spec->Features == NULL) // only generate an attribute if not NULL
// TODO do we still need to do this or not...
if (uiAttributeFeatures(attr) == NULL)
break; break;
dt = otfToDirectWrite(spec->Features); dt = uiprivOpenTypeFeaturesToIDWriteTypography(uiAttributeFeatures(attr));
hr = p->layout->SetTypography(dt, range); hr = p->layout->SetTypography(dt, range);
if (hr != S_OK) if (hr != S_OK)
logHRESULT(L"error applying features attribute", hr); logHRESULT(L"error applying features attribute", hr);
dt->Release(); dt->Release();
break; break;
default:
// TODO complain
;
} }
return uiForEachContinue; return uiForEachContinue;
} }
static void applyAndFreeEffectsAttributes(struct foreachParams *p) $$$$TODO CONTINUE HERE
static void applyEffectsAttributes(struct foreachParams *p)
{ {
size_t i, n; size_t i, n;
textDrawingEffect *effect, *effectb; textDrawingEffect *effect, *effectb;