[svg] Refactor object bounding box access
Introduce a helper (SkSVGRenderContext::obbTransform) and refactor all
obb clients to funnel their calls through it.
This will facilitate some follow up obb changes.
Bug: skia:11936
Change-Id: If0d81b0fc9148389c2cb4bff597057a2afa2fb1b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/402956
Reviewed-by: Tyler Denniston <tdenniston@google.com>
Commit-Queue: Florin Malita <fmalita@google.com>
diff --git a/modules/svg/src/SkSVGClipPath.cpp b/modules/svg/src/SkSVGClipPath.cpp
index 065a024..06ef8ae 100644
--- a/modules/svg/src/SkSVGClipPath.cpp
+++ b/modules/svg/src/SkSVGClipPath.cpp
@@ -20,12 +20,10 @@
SkPath SkSVGClipPath::resolveClip(const SkSVGRenderContext& ctx) const {
auto clip = this->asPath(ctx);
- if (fClipPathUnits.type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- const auto obb = ctx.node()->objectBoundingBox(ctx);
- const auto obb_transform = SkMatrix::Translate(obb.x(), obb.y()) *
- SkMatrix::Scale(obb.width(), obb.height());
- clip.transform(obb_transform);
- }
+ const auto obbt = ctx.transformForCurrentOBB(fClipPathUnits);
+ const auto m = SkMatrix::Translate(obbt.offset.x, obbt.offset.y)
+ * SkMatrix::Scale(obbt.scale.x, obbt.scale.y);
+ clip.transform(m);
return clip;
}
diff --git a/modules/svg/src/SkSVGFe.cpp b/modules/svg/src/SkSVGFe.cpp
index 4aebb57..37834bc 100644
--- a/modules/svg/src/SkSVGFe.cpp
+++ b/modules/svg/src/SkSVGFe.cpp
@@ -23,26 +23,7 @@
const auto w = fWidth.isValid() ? *fWidth : SkSVGLength(100, SkSVGLength::Unit::kPercentage);
const auto h = fHeight.isValid() ? *fHeight : SkSVGLength(100, SkSVGLength::Unit::kPercentage);
- // Resolve the x/y/w/h boundary rect depending on primitiveUnits setting
- SkRect boundaries;
- switch (fctx.primitiveUnits().type()) {
- case SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse:
- boundaries = ctx.lengthContext().resolveRect(x, y, w, h);
- break;
- case SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox: {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- boundaries = SkSVGLengthContext({1, 1}).resolveRect(x, y, w, h);
- boundaries = SkRect::MakeXYWH(objBounds.fLeft + boundaries.fLeft * objBounds.width(),
- objBounds.fTop + boundaries.fTop * objBounds.height(),
- boundaries.width() * objBounds.width(),
- boundaries.height() * objBounds.height());
-
- break;
- }
- }
-
- return boundaries;
+ return ctx.resolveOBBRect(x, y, w, h, fctx.primitiveUnits());
}
static bool AnyIsStandardInput(const SkSVGFilterContext& fctx,
diff --git a/modules/svg/src/SkSVGFeDisplacementMap.cpp b/modules/svg/src/SkSVGFeDisplacementMap.cpp
index 966faa5..9698a55 100644
--- a/modules/svg/src/SkSVGFeDisplacementMap.cpp
+++ b/modules/svg/src/SkSVGFeDisplacementMap.cpp
@@ -36,10 +36,9 @@
SkScalar scale = fScale;
if (fctx.primitiveUnits().type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- const SkSVGLengthContext lctx({objBounds.width(), objBounds.height()});
- scale = lctx.resolve(SkSVGLength(scale, SkSVGLength::Unit::kPercentage),
+ const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits());
+ scale = SkSVGLengthContext({obbt.scale.x, obbt.scale.y})
+ .resolve(SkSVGLength(scale, SkSVGLength::Unit::kPercentage),
SkSVGLengthContext::LengthType::kOther);
}
diff --git a/modules/svg/src/SkSVGFeGaussianBlur.cpp b/modules/svg/src/SkSVGFeGaussianBlur.cpp
index 54ea011..1443845 100644
--- a/modules/svg/src/SkSVGFeGaussianBlur.cpp
+++ b/modules/svg/src/SkSVGFeGaussianBlur.cpp
@@ -20,17 +20,11 @@
sk_sp<SkImageFilter> SkSVGFeGaussianBlur::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const {
- SkScalar sigmaX = fStdDeviation.fX;
- SkScalar sigmaY = fStdDeviation.fY;
- if (fctx.primitiveUnits().type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- sigmaX *= objBounds.width();
- sigmaY *= objBounds.height();
- }
+ const auto sigma = SkV2{fStdDeviation.fX, fStdDeviation.fY}
+ * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale;
return SkImageFilters::Blur(
- sigmaX, sigmaY,
+ sigma.x, sigma.y,
fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx)),
this->resolveFilterSubregion(ctx, fctx));
}
diff --git a/modules/svg/src/SkSVGFeLighting.cpp b/modules/svg/src/SkSVGFeLighting.cpp
index 0882b00..31d50e2 100644
--- a/modules/svg/src/SkSVGFeLighting.cpp
+++ b/modules/svg/src/SkSVGFeLighting.cpp
@@ -71,16 +71,12 @@
SkSVGNumberType x,
SkSVGNumberType y,
SkSVGNumberType z) const {
- if (fctx.primitiveUnits().type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- const SkSVGLengthContext lctx({objBounds.width(), objBounds.height()});
- x = objBounds.left() + x * objBounds.width();
- y = objBounds.top() + y * objBounds.height();
- z = lctx.resolve(SkSVGLength(z * 100.f, SkSVGLength::Unit::kPercentage),
- SkSVGLengthContext::LengthType::kOther);
- }
- return SkPoint3::Make(x, y, z);
+ const auto obbt = ctx.transformForCurrentOBB(fctx.primitiveUnits());
+ const auto xy = SkV2{x,y} * obbt.scale + obbt.offset;
+ z = SkSVGLengthContext({obbt.scale.x, obbt.scale.y})
+ .resolve(SkSVGLength(z * 100.f, SkSVGLength::Unit::kPercentage),
+ SkSVGLengthContext::LengthType::kOther);
+ return SkPoint3::Make(xy.x, xy.y, z);
}
bool SkSVGFeSpecularLighting::parseAndSetAttribute(const char* n, const char* v) {
diff --git a/modules/svg/src/SkSVGFeMorphology.cpp b/modules/svg/src/SkSVGFeMorphology.cpp
index 67310c8..77b834b 100644
--- a/modules/svg/src/SkSVGFeMorphology.cpp
+++ b/modules/svg/src/SkSVGFeMorphology.cpp
@@ -26,20 +26,13 @@
const SkSVGColorspace colorspace = this->resolveColorspace(ctx, fctx);
sk_sp<SkImageFilter> input = fctx.resolveInput(ctx, this->getIn(), colorspace);
- SkScalar rx = fRadius.fX;
- SkScalar ry = fRadius.fY;
- if (fctx.primitiveUnits().type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- rx *= objBounds.width();
- ry *= objBounds.height();
- }
-
+ const auto r = SkV2{fRadius.fX, fRadius.fY}
+ * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale;
switch (fOperator) {
case Operator::kErode:
- return SkImageFilters::Erode(rx, ry, input, cropRect);
+ return SkImageFilters::Erode(r.x, r.y, input, cropRect);
case Operator::kDilate:
- return SkImageFilters::Dilate(rx, ry, input, cropRect);
+ return SkImageFilters::Dilate(r.x, r.y, input, cropRect);
}
SkUNREACHABLE;
diff --git a/modules/svg/src/SkSVGFeOffset.cpp b/modules/svg/src/SkSVGFeOffset.cpp
index df29c83..057a404 100644
--- a/modules/svg/src/SkSVGFeOffset.cpp
+++ b/modules/svg/src/SkSVGFeOffset.cpp
@@ -20,15 +20,10 @@
sk_sp<SkImageFilter> SkSVGFeOffset::onMakeImageFilter(const SkSVGRenderContext& ctx,
const SkSVGFilterContext& fctx) const {
- SkScalar dx = this->getDx(), dy = this->getDy();
- if (fctx.primitiveUnits().type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- dx *= objBounds.width();
- dy *= objBounds.height();
- }
+ const auto d = SkV2{this->getDx(), this->getDy()}
+ * ctx.transformForCurrentOBB(fctx.primitiveUnits()).scale;
sk_sp<SkImageFilter> in =
fctx.resolveInput(ctx, this->getIn(), this->resolveColorspace(ctx, fctx));
- return SkImageFilters::Offset(dx, dy, std::move(in), this->resolveFilterSubregion(ctx, fctx));
+ return SkImageFilters::Offset(d.x, d.y, std::move(in), this->resolveFilterSubregion(ctx, fctx));
}
diff --git a/modules/svg/src/SkSVGGradient.cpp b/modules/svg/src/SkSVGGradient.cpp
index 8a5ea8c..cf09ee5 100644
--- a/modules/svg/src/SkSVGGradient.cpp
+++ b/modules/svg/src/SkSVGGradient.cpp
@@ -86,14 +86,10 @@
SkTileMode::kMirror, "SkSVGSpreadMethod::Type is out of sync");
const auto tileMode = static_cast<SkTileMode>(fSpreadMethod.type());
- SkMatrix localMatrix = SkMatrix::I();
- if (fGradientUnits.type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- SkASSERT(ctx.node());
- const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
- localMatrix.preTranslate(objBounds.fLeft, objBounds.fTop);
- localMatrix.preScale(objBounds.width(), objBounds.height());
- }
- localMatrix.preConcat(fGradientTransform);
+ const auto obbt = ctx.transformForCurrentOBB(fGradientUnits);
+ const auto localMatrix = SkMatrix::Translate(obbt.offset.x, obbt.offset.y)
+ * SkMatrix::Scale(obbt.scale.x, obbt.scale.y)
+ * fGradientTransform;
paint->setShader(this->onMakeShader(ctx, colors.begin(), pos.begin(), colors.count(), tileMode,
localMatrix));
diff --git a/modules/svg/src/SkSVGMask.cpp b/modules/svg/src/SkSVGMask.cpp
index 9e32daf..a19246b 100644
--- a/modules/svg/src/SkSVGMask.cpp
+++ b/modules/svg/src/SkSVGMask.cpp
@@ -55,13 +55,9 @@
// Something to consider if masking performance ever becomes an issue.
lctx.canvas()->saveLayer(nullptr, &mask_filter);
- if (fMaskContentUnits.type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- // Fot maskContentUnits == OBB the mask content is rendered in a normalized coordinate
- // system, which maps to the node OBB.
- const auto obb = lctx.node()->objectBoundingBox(ctx);
- lctx.canvas()->translate(obb.x(), obb.y());
- lctx.canvas()->scale(obb.width(), obb.height());
- }
+ const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits);
+ lctx.canvas()->translate(obbt.offset.x, obbt.offset.y);
+ lctx.canvas()->scale(obbt.scale.x, obbt.scale.y);
for (const auto& child : fChildren) {
child->render(lctx);
diff --git a/modules/svg/src/SkSVGRenderContext.cpp b/modules/svg/src/SkSVGRenderContext.cpp
index 8d2094c..2358f15 100644
--- a/modules/svg/src/SkSVGRenderContext.cpp
+++ b/modules/svg/src/SkSVGRenderContext.cpp
@@ -467,6 +467,16 @@
SkUNREACHABLE;
}
+SkSVGRenderContext::OBBTransform
+SkSVGRenderContext::transformForCurrentOBB(SkSVGObjectBoundingBoxUnits u) const {
+ if (!fNode || u.type() == SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse) {
+ return {{0,0},{1,1}};
+ }
+
+ const auto obb = fNode->objectBoundingBox(*this);
+ return {{obb.x(), obb.y()}, {obb.width(), obb.height()}};
+}
+
SkRect SkSVGRenderContext::resolveOBBRect(const SkSVGLength& x, const SkSVGLength& y,
const SkSVGLength& w, const SkSVGLength& h,
SkSVGObjectBoundingBoxUnits obbu) const {
@@ -477,13 +487,10 @@
}
auto r = lctx->resolveRect(x, y, w, h);
- if (obbu.type() == SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox) {
- const auto obb = fNode->objectBoundingBox(*this);
- r = SkRect::MakeXYWH(obb.x() + r.x() * obb.width(),
- obb.y() + r.y() * obb.height(),
- r.width() * obb.width(),
- r.height() * obb.height());
- }
+ const auto obbt = this->transformForCurrentOBB(obbu);
- return r;
+ return SkRect::MakeXYWH(obbt.scale.x * r.x() + obbt.offset.x,
+ obbt.scale.y * r.y() + obbt.offset.y,
+ obbt.scale.x * r.width(),
+ obbt.scale.y * r.height());
}