diff --git a/darwin/attrstr.m b/darwin/attrstr.m index 180bf272..ca32216c 100644 --- a/darwin/attrstr.m +++ b/darwin/attrstr.m @@ -257,7 +257,7 @@ static void addFontAttributeToRange(struct foreachParams *p, size_t start, size_ else cfa = [cfa copy]; [cfa addAttribute:attr]; - // clamp effectiveRange within [start, end) + // clamp range within [start, end) if (range.location < start) { diff = start - range.location; range.location = start; diff --git a/windows/OLD_attrstr.cpp b/windows/attrstr.cpp similarity index 52% rename from windows/OLD_attrstr.cpp rename to windows/attrstr.cpp index c87804b5..92c39b9a 100644 --- a/windows/OLD_attrstr.cpp +++ b/windows/attrstr.cpp @@ -1,38 +1,147 @@ // 12 february 2017 #include "uipriv_windows.hpp" -#include "draw.hpp" +#include "attrstr.hpp" // TODO this whole file needs cleanup -// we need to combine color and underline style into one unit for IDWriteLayout::SetDrawingEffect() -// we also need to collect all the background blocks and add them all at once +// we need to collect all the background blocks and add them all at once // TODO contextual alternates override ligatures? // TODO rename this struct to something that isn't exclusively foreach-ing? struct foreachParams { const uint16_t *s; size_t len; IDWriteTextLayout *layout; - std::map *effects; std::vector *backgroundFuncs; }; -static void ensureEffectsInRange(struct foreachParams *p, size_t start, size_t end, std::function f) -{ - size_t i; - size_t *key; - textDrawingEffect *t; +// 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 +// we can at least try to goad it into doing so if we can deduplicate effects once they're all computed +// so what we do is use this class to store in-progress effects, much like uiprivCombinedFontAttr on the OS X code +// 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 - for (i = start; i < end; i++) { - t = (*(p->effects))[i]; - if (t != NULL) { - f(t); - continue; + void setAttribute(uiAttribute *a) + { + if (a == NULL) + return; + switch (uiAttributeGetType(a)) { + 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) @@ -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; DWRITE_TEXT_RANGE range; @@ -66,76 +175,69 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute end = attrstrUTF8ToUTF16((uiAttributedString *) s, end); range.startPosition = start; range.length = end - start; - switch (spec->Type) { - case uiAttributeFamily: - wfamily = toUTF16(spec->Family); + switch (uiAttributeGetType(attr)) { + case uiAttributeTypeFamily: + wfamily = toUTF16(uiAttributeFamily(attr)); hr = p->layout->SetFontFamilyName(wfamily, range); if (hr != S_OK) logHRESULT(L"error applying family name attribute", hr); uiFree(wfamily); break; - case uiAttributeSize: + case uiAttributeTypeSize: 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)) - pointSizeToDWriteSize(spec->Double), + pointSizeToDWriteSize(uiAttributeSize(attr)), range); if (hr != S_OK) logHRESULT(L"error applying size attribute", hr); break; - case uiAttributeWeight: - // TODO reverse the misalignment from drawtext.cpp if it is corrected + case uiAttributeTypeWeight: hr = p->layout->SetFontWeight( - (DWRITE_FONT_WEIGHT) (spec->Value), + uiprivWeightToDWriteWeight(uiAttributeWeight(attr)), range); if (hr != S_OK) logHRESULT(L"error applying weight attribute", hr); break; - case uiAttributeItalic: + case uiAttributeTypeItalic: hr = p->layout->SetFontStyle( - dwriteItalics[(uiDrawTextItalic) (spec->Value)], + uiprivItalicToDWriteStyle(uiAttributeItalic(attr)), range); if (hr != S_OK) logHRESULT(L"error applying italic attribute", hr); break; - case uiAttributeStretch: + case uiAttributeTypeStretch: hr = p->layout->SetFontStretch( - dwriteStretches[(uiDrawTextStretch) (spec->Value)], + uiprivStretchToDWriteStretch(uiAttributeStretch(attr)), range); if (hr != S_OK) logHRESULT(L"error applying stretch attribute", hr); break; - case uiAttributeColor: - 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; - }); + case uiAttributeTypeUnderline: + // mark that we have an underline; otherwise, DirectWrite will never call our custom renderer's DrawUnderline() method + hasUnderline = FALSE; + if (uiAttributeUnderline(attr) != uiUnderlineNone) + hasUnderline = TRUE; + hr = p->layout->SetUnderline(hasUnderline, range); + 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; case uiAttributeBackground: p->backgroundFuncs->push_back( mkBackgroundFunc(ostart, oend, spec->R, spec->G, spec->B, spec->A)); break; - case uiAttributeUnderline: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { - 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: +#if 0 +TODO switch (spec->Value) { case uiDrawUnderlineColorCustom: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = spec->R; 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 case uiDrawUnderlineColorSpelling: // TODO GtkTextView style property error-underline-color - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 1.0; t->ug = 0.0; @@ -155,7 +257,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorGrammar: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 1.0; @@ -164,7 +266,7 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute }); break; case uiDrawUnderlineColorAuxiliary: - ensureEffectsInRange(p, start, end, [=](textDrawingEffect *t) { + x(p, start, end, [=](textDrawingEffect *t) { t->hasUnderlineColor = true; t->ur = 0.0; t->ug = 0.0; @@ -174,24 +276,25 @@ static uiForEach processAttribute(const uiAttributedString *s, const uiAttribute break; } break; - case uiAttributeFeatures: - // only generate an attribute if spec->Features is not NULL - if (spec->Features == NULL) +#endif + case uiAttributeTypeFeatures: + // only generate an attribute if not NULL + // TODO do we still need to do this or not... + if (uiAttributeFeatures(attr) == NULL) break; - dt = otfToDirectWrite(spec->Features); + dt = uiprivOpenTypeFeaturesToIDWriteTypography(uiAttributeFeatures(attr)); hr = p->layout->SetTypography(dt, range); if (hr != S_OK) logHRESULT(L"error applying features attribute", hr); dt->Release(); break; - default: - // TODO complain - ; } return uiForEachContinue; } -static void applyAndFreeEffectsAttributes(struct foreachParams *p) +$$$$TODO CONTINUE HERE + +static void applyEffectsAttributes(struct foreachParams *p) { size_t i, n; textDrawingEffect *effect, *effectb;