[svg] Refactor text rendering context plumbing
Instead of relying on RenderContext to pass text rendering options
downstack, introduce a dedicated virtual (onRenderText) and pass options
explicitly.
Root text nodes bridge from onRender() -> onRenderText().
This removes some complexity from RenderContext and incidentally fixes
xml:space = preserve (the value was being dropped during local ctx
copying).
Bug: skia:10840
Change-Id: Ic5fd9e0f9382f52f65108521574fcb2a422b97aa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/344559
Reviewed-by: Tyler Denniston <tdenniston@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
diff --git a/modules/svg/include/SkSVGRenderContext.h b/modules/svg/include/SkSVGRenderContext.h
index d459df8..ef54b8e 100644
--- a/modules/svg/include/SkSVGRenderContext.h
+++ b/modules/svg/include/SkSVGRenderContext.h
@@ -20,7 +20,6 @@
class SkCanvas;
class SkSVGLength;
-class SkSVGTextContext;
class SkSVGLengthContext {
public:
@@ -62,18 +61,16 @@
public:
SkSVGRenderContext(SkCanvas*, const sk_sp<SkFontMgr>&, const SkSVGIDMapper&,
const SkSVGLengthContext&, const SkSVGPresentationContext&,
- SkSVGTextContext*, const SkSVGNode*);
+ const SkSVGNode*);
SkSVGRenderContext(const SkSVGRenderContext&);
SkSVGRenderContext(const SkSVGRenderContext&, SkCanvas*);
SkSVGRenderContext(const SkSVGRenderContext&, const SkSVGNode*);
- SkSVGRenderContext(const SkSVGRenderContext&, SkSVGTextContext&);
~SkSVGRenderContext();
const SkSVGLengthContext& lengthContext() const { return *fLengthContext; }
SkSVGLengthContext* writableLengthContext() { return fLengthContext.writable(); }
const SkSVGPresentationContext& presentationContext() const { return *fPresentationContext; }
- SkSVGTextContext* textContext() const { return fTextContext; }
SkCanvas* canvas() const { return fCanvas; }
void saveOnce();
@@ -134,9 +131,6 @@
return fFontMgr ? fFontMgr : SkFontMgr::RefDefault();
}
- SkSVGXmlSpace getXmlSpace() const { return fXmlSpace; }
- void setXmlSpace(SkSVGXmlSpace xs) { fXmlSpace = xs; }
-
private:
// Stack-only
void* operator new(size_t) = delete;
@@ -152,7 +146,6 @@
const SkSVGIDMapper& fIDMapper;
SkTCopyOnFirstWrite<SkSVGLengthContext> fLengthContext;
SkTCopyOnFirstWrite<SkSVGPresentationContext> fPresentationContext;
- SkSVGTextContext* fTextContext;
SkCanvas* fCanvas;
// The save count on 'fCanvas' at construction time.
// A restoreToCount() will be issued on destruction.
@@ -161,8 +154,6 @@
// clipPath, if present for the current context (not inherited).
SkTLazy<SkPath> fClipPath;
- SkSVGXmlSpace fXmlSpace = SkSVGXmlSpace::kDefault;
-
const SkSVGNode* fNode;
};
diff --git a/modules/svg/include/SkSVGText.h b/modules/svg/include/SkSVGText.h
index 8fb4721..0710ab8 100644
--- a/modules/svg/include/SkSVGText.h
+++ b/modules/svg/include/SkSVGText.h
@@ -8,11 +8,32 @@
#ifndef SkSVGText_DEFINED
#define SkSVGText_DEFINED
-#include "modules/svg/include/SkSVGContainer.h"
+#include <vector>
+
+#include "modules/svg/include/SkSVGTransformableNode.h"
#include "modules/svg/include/SkSVGTypes.h"
+class SkSVGTextContext;
+
+// Base class for text-rendering nodes.
+class SkSVGTextFragment : public SkSVGTransformableNode {
+public:
+ void renderText(const SkSVGRenderContext&, SkSVGTextContext*, SkSVGXmlSpace) const;
+
+protected:
+ explicit SkSVGTextFragment(SkSVGTag t) : INHERITED(t) {}
+
+ virtual void onRenderText(const SkSVGRenderContext&, SkSVGTextContext*,
+ SkSVGXmlSpace) const = 0;
+
+private:
+ SkPath onAsPath(const SkSVGRenderContext&) const final;
+
+ using INHERITED = SkSVGTransformableNode;
+};
+
// Base class for nestable text containers (<text>, <tspan>, etc).
-class SkSVGTextContainer : public SkSVGContainer {
+class SkSVGTextContainer : public SkSVGTextFragment {
public:
// TODO: these should be arrays
SVG_ATTR(X, SkSVGLength, SkSVGLength(0))
@@ -25,11 +46,14 @@
private:
void appendChild(sk_sp<SkSVGNode>) final;
- bool onPrepareToRender(SkSVGRenderContext*) const final;
+ void onRender(const SkSVGRenderContext&) const final;
+ void onRenderText(const SkSVGRenderContext&, SkSVGTextContext*, SkSVGXmlSpace) const final;
bool parseAndSetAttribute(const char*, const char*) override;
- using INHERITED = SkSVGContainer;
+ std::vector<sk_sp<SkSVGTextFragment>> fChildren;
+
+ using INHERITED = SkSVGTextFragment;
};
class SkSVGText final : public SkSVGTextContainer {
@@ -39,8 +63,6 @@
private:
SkSVGText() : INHERITED(SkSVGTag::kText) {}
- void onRender(const SkSVGRenderContext&) const override;
-
using INHERITED = SkSVGTextContainer;
};
@@ -54,7 +76,7 @@
using INHERITED = SkSVGTextContainer;
};
-class SkSVGTextLiteral final : public SkSVGNode {
+class SkSVGTextLiteral final : public SkSVGTextFragment {
public:
static sk_sp<SkSVGTextLiteral> Make() {
return sk_sp<SkSVGTextLiteral>(new SkSVGTextLiteral());
@@ -65,12 +87,12 @@
private:
SkSVGTextLiteral() : INHERITED(SkSVGTag::kTextLiteral) {}
- void onRender(const SkSVGRenderContext&) const override;
- SkPath onAsPath(const SkSVGRenderContext&) const override;
+ void onRender(const SkSVGRenderContext&) const override {}
+ void onRenderText(const SkSVGRenderContext&, SkSVGTextContext*, SkSVGXmlSpace) const override;
void appendChild(sk_sp<SkSVGNode>) override {}
- using INHERITED = SkSVGNode;
+ using INHERITED = SkSVGTextFragment;
};
#endif // SkSVGText_DEFINED
diff --git a/modules/svg/src/SkSVGDOM.cpp b/modules/svg/src/SkSVGDOM.cpp
index a9c0346..54ee152 100644
--- a/modules/svg/src/SkSVGDOM.cpp
+++ b/modules/svg/src/SkSVGDOM.cpp
@@ -408,8 +408,7 @@
if (fRoot) {
SkSVGLengthContext lctx(fContainerSize);
SkSVGPresentationContext pctx;
- fRoot->render(SkSVGRenderContext(canvas, fFontMgr, fIDMapper,
- lctx, pctx, nullptr, nullptr));
+ fRoot->render(SkSVGRenderContext(canvas, fFontMgr, fIDMapper, lctx, pctx, nullptr));
}
}
diff --git a/modules/svg/src/SkSVGRenderContext.cpp b/modules/svg/src/SkSVGRenderContext.cpp
index d1b33a7..63c2c04 100644
--- a/modules/svg/src/SkSVGRenderContext.cpp
+++ b/modules/svg/src/SkSVGRenderContext.cpp
@@ -312,7 +312,7 @@
SkCanvas fakeCanvas(0, 0);
SkSVGRenderContext fake(&fakeCanvas, nullptr, SkSVGIDMapper(),
SkSVGLengthContext(SkSize::Make(0, 0)),
- *this, nullptr, nullptr);
+ *this, nullptr);
commitToPaint<SkSVGAttribute::kFill>(fInherited, fake, this);
commitToPaint<SkSVGAttribute::kFillOpacity>(fInherited, fake, this);
@@ -329,13 +329,11 @@
const SkSVGIDMapper& mapper,
const SkSVGLengthContext& lctx,
const SkSVGPresentationContext& pctx,
- SkSVGTextContext* tctx,
const SkSVGNode* node)
: fFontMgr(fmgr)
, fIDMapper(mapper)
, fLengthContext(lctx)
, fPresentationContext(pctx)
- , fTextContext(tctx)
, fCanvas(canvas)
, fCanvasSaveCount(canvas->getSaveCount())
, fNode(node) {}
@@ -346,7 +344,6 @@
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,
- other.fTextContext,
other.fNode) {}
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkCanvas* canvas)
@@ -355,7 +352,6 @@
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,
- other.fTextContext,
other.fNode) {}
SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, const SkSVGNode* node)
@@ -364,18 +360,8 @@
other.fIDMapper,
*other.fLengthContext,
*other.fPresentationContext,
- other.fTextContext,
node) {}
-SkSVGRenderContext::SkSVGRenderContext(const SkSVGRenderContext& other, SkSVGTextContext& tctx)
- : SkSVGRenderContext(other.fCanvas,
- other.fFontMgr,
- other.fIDMapper,
- *other.fLengthContext,
- *other.fPresentationContext,
- &tctx,
- other.fNode) {}
-
SkSVGRenderContext::~SkSVGRenderContext() {
fCanvas->restoreToCount(fCanvasSaveCount);
}
diff --git a/modules/svg/src/SkSVGText.cpp b/modules/svg/src/SkSVGText.cpp
index a6ba964..4e2d91b 100644
--- a/modules/svg/src/SkSVGText.cpp
+++ b/modules/svg/src/SkSVGText.cpp
@@ -118,7 +118,7 @@
{}
// Queues codepoints for rendering.
- void appendFragment(const SkString& txt, const SkSVGRenderContext& ctx) {
+ void appendFragment(const SkString& txt, const SkSVGRenderContext& ctx, SkSVGXmlSpace xs) {
// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
// https://www.w3.org/TR/2008/REC-xml-20081126/#NT-S
auto filterWSDefault = [this](SkUnichar ch) -> SkUnichar {
@@ -151,8 +151,6 @@
return ch;
};
- const auto xmlSpace = ctx.getXmlSpace();
-
SkSTArray<128, char, true> filtered;
filtered.reserve_back(SkToInt(txt.size()));
@@ -161,7 +159,7 @@
while (ch_ptr < ch_end) {
auto ch = SkUTF::NextUTF8(&ch_ptr, ch_end);
- ch = (xmlSpace == SkSVGXmlSpace::kDefault)
+ ch = (xs == SkSVGXmlSpace::kDefault)
? filterWSDefault(ch)
: filterWSPreserve(ch);
@@ -275,22 +273,40 @@
bool fPrevCharSpace = true; // WS filter state
};
+void SkSVGTextFragment::renderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace xs) const {
+ SkSVGRenderContext localContext(ctx, this);
+
+ if (this->onPrepareToRender(&localContext)) {
+ this->onRenderText(localContext, tctx, xs);
+ }
+}
+
+SkPath SkSVGTextFragment::onAsPath(const SkSVGRenderContext&) const {
+ // TODO
+ return SkPath();
+}
+
void SkSVGTextContainer::appendChild(sk_sp<SkSVGNode> child) {
// Only allow text nodes.
switch (child->tag()) {
case SkSVGTag::kText:
case SkSVGTag::kTextLiteral:
case SkSVGTag::kTSpan:
- this->INHERITED::appendChild(child);
+ fChildren.push_back(
+ sk_sp<SkSVGTextFragment>(static_cast<SkSVGTextFragment*>(child.release())));
break;
default:
break;
}
}
-bool SkSVGTextContainer::onPrepareToRender(SkSVGRenderContext* ctx) const {
- ctx->setXmlSpace(this->getXmlSpace());
- return this->INHERITED::onPrepareToRender(ctx);
+void SkSVGTextContainer::onRenderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace) const {
+ for (const auto& frag : fChildren) {
+ // Containers always override xml:space with the local value.
+ frag->renderText(ctx, tctx, this->getXmlSpace());
+ }
}
// https://www.w3.org/TR/SVG11/text.html#WhiteSpace
@@ -311,26 +327,18 @@
this->setXmlSpace(SkSVGAttributeParser::parse<SkSVGXmlSpace>("xml:space", name, value));
}
-void SkSVGText::onRender(const SkSVGRenderContext& ctx) const {
- // <text> establishes a new text layout context.
+void SkSVGTextContainer::onRender(const SkSVGRenderContext& ctx) const {
+ // Root text nodes establish a new text layout context.
SkSVGTextContext tctx(*this, ctx);
- SkSVGRenderContext local_ctx(ctx, tctx);
- this->INHERITED::onRender(local_ctx);
+ this->onRenderText(ctx, &tctx, this->getXmlSpace());
tctx.flushChunk(ctx);
}
-void SkSVGTextLiteral::onRender(const SkSVGRenderContext& ctx) const {
- auto* tctx = ctx.textContext();
- if (!tctx) {
- return;
- }
+void SkSVGTextLiteral::onRenderText(const SkSVGRenderContext& ctx, SkSVGTextContext* tctx,
+ SkSVGXmlSpace xs) const {
+ SkASSERT(tctx);
- tctx->appendFragment(this->getText(), ctx);
-}
-
-SkPath SkSVGTextLiteral::onAsPath(const SkSVGRenderContext&) const {
- // TODO
- return SkPath();
+ tctx->appendFragment(this->getText(), ctx, xs);
}