[svg] Implement gradientUnits="objectBoundingBox"
Currently only works properly when filling rects, since that is the only node that implements bounds computation with this CL. Summary of changes: - Scale gradient coords by object bounds when units are kObjectBoundingBox. - Make fGradientUnits protected instead of private. - Change default value of fGradientUnits to kObjectBoundingBox, which is the default according to the spec. - Change SkSVGNode::onObjectBoundingBox to take a full render context instead of length context. - Implement bounds computation for SkSVGRect, SkSVGContainer and SkSVGUse. Bug: skia:10842 Change-Id: I2e999985e67644e50da7f681fde190bcf4823eec Reviewed-on: https://skia-review.googlesource.com/c/skia/+/329223 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
parent
532c74936b
commit
f548a028ce
@ -24,6 +24,8 @@ protected:
|
||||
|
||||
SkPath onAsPath(const SkSVGRenderContext&) const override;
|
||||
|
||||
SkRect onObjectBoundingBox(const SkSVGRenderContext&) const override;
|
||||
|
||||
bool hasChildren() const final;
|
||||
|
||||
// TODO: add some sort of child iterator, and hide the container.
|
||||
|
@ -36,6 +36,8 @@ protected:
|
||||
const SkColor*, const SkScalar*, int count,
|
||||
SkTileMode, const SkMatrix& localMatrix) const = 0;
|
||||
|
||||
SkSVGGradientUnits fGradientUnits = SkSVGGradientUnits(SkSVGGradientUnits::Type::kObjectBoundingBox);
|
||||
|
||||
private:
|
||||
using StopPositionArray = SkSTArray<2, SkScalar, true>;
|
||||
using StopColorArray = SkSTArray<2, SkColor, true>;
|
||||
@ -45,7 +47,6 @@ private:
|
||||
SkSVGStringType fHref;
|
||||
SkSVGTransformType fGradientTransform = SkSVGTransformType(SkMatrix::I());
|
||||
SkSVGSpreadMethod fSpreadMethod = SkSVGSpreadMethod(SkSVGSpreadMethod::Type::kPad);
|
||||
SkSVGGradientUnits fGradientUnits = SkSVGGradientUnits(SkSVGGradientUnits::Type::kUserSpaceOnUse);
|
||||
|
||||
using INHERITED = SkSVGHiddenContainer;
|
||||
};
|
||||
|
@ -73,7 +73,7 @@ public:
|
||||
void render(const SkSVGRenderContext&) const;
|
||||
bool asPaint(const SkSVGRenderContext&, SkPaint*) const;
|
||||
SkPath asPath(const SkSVGRenderContext&) const;
|
||||
SkRect objectBoundingBox(const SkSVGLengthContext&) const;
|
||||
SkRect objectBoundingBox(const SkSVGRenderContext&) const;
|
||||
|
||||
void setAttribute(SkSVGAttribute, const SkSVGValue&);
|
||||
bool setAttribute(const char* attributeName, const char* attributeValue);
|
||||
@ -121,7 +121,7 @@ protected:
|
||||
|
||||
virtual bool hasChildren() const { return false; }
|
||||
|
||||
virtual SkRect onObjectBoundingBox(const SkSVGLengthContext&) const {
|
||||
virtual SkRect onObjectBoundingBox(const SkSVGRenderContext&) const {
|
||||
return SkRect::MakeEmpty();
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@ protected:
|
||||
|
||||
SkPath onAsPath(const SkSVGRenderContext&) const override;
|
||||
|
||||
SkRect onObjectBoundingBox(const SkSVGRenderContext&) const override;
|
||||
|
||||
private:
|
||||
SkSVGRect();
|
||||
|
||||
|
@ -26,6 +26,8 @@ protected:
|
||||
|
||||
void mapToParent(SkPath*) const;
|
||||
|
||||
void mapToParent(SkRect*) const;
|
||||
|
||||
private:
|
||||
// FIXME: should be sparse
|
||||
SkSVGTransformType fTransform;
|
||||
|
@ -33,6 +33,7 @@ protected:
|
||||
bool onPrepareToRender(SkSVGRenderContext*) const override;
|
||||
void onRender(const SkSVGRenderContext&) const override;
|
||||
SkPath onAsPath(const SkSVGRenderContext&) const override;
|
||||
SkRect onObjectBoundingBox(const SkSVGRenderContext&) const override;
|
||||
|
||||
private:
|
||||
SkSVGUse();
|
||||
|
@ -39,3 +39,15 @@ SkPath SkSVGContainer::onAsPath(const SkSVGRenderContext& ctx) const {
|
||||
this->mapToParent(&path);
|
||||
return path;
|
||||
}
|
||||
|
||||
SkRect SkSVGContainer::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
|
||||
SkRect bounds = SkRect::MakeEmpty();
|
||||
|
||||
for (int i = 0; i < fChildren.count(); ++i) {
|
||||
const SkRect childBounds = fChildren[i]->objectBoundingBox(ctx);
|
||||
bounds.join(childBounds);
|
||||
}
|
||||
|
||||
this->mapToParent(&bounds);
|
||||
return bounds;
|
||||
}
|
||||
|
@ -126,7 +126,16 @@ bool SkSVGGradient::onAsPaint(const SkSVGRenderContext& ctx, SkPaint* paint) con
|
||||
SkTileMode::kMirror, "SkSVGSpreadMethod::Type is out of sync");
|
||||
const auto tileMode = static_cast<SkTileMode>(fSpreadMethod.type());
|
||||
|
||||
SkMatrix localMatrix = SkMatrix::I();
|
||||
if (fGradientUnits.type() == SkSVGGradientUnits::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);
|
||||
|
||||
paint->setShader(this->onMakeShader(ctx, colors.begin(), pos.begin(), colors.count(), tileMode,
|
||||
fGradientTransform));
|
||||
localMatrix));
|
||||
return true;
|
||||
}
|
||||
|
@ -59,7 +59,11 @@ sk_sp<SkShader> SkSVGLinearGradient::onMakeShader(const SkSVGRenderContext& ctx,
|
||||
const SkColor* colors, const SkScalar* pos,
|
||||
int count, SkTileMode tm,
|
||||
const SkMatrix& localMatrix) const {
|
||||
const auto& lctx = ctx.lengthContext();
|
||||
const SkSVGLengthContext lctx =
|
||||
fGradientUnits.type() == SkSVGGradientUnits::Type::kObjectBoundingBox
|
||||
? SkSVGLengthContext({1, 1})
|
||||
: ctx.lengthContext();
|
||||
|
||||
const auto x1 = lctx.resolve(fX1, SkSVGLengthContext::LengthType::kHorizontal);
|
||||
const auto y1 = lctx.resolve(fY1, SkSVGLengthContext::LengthType::kVertical);
|
||||
const auto x2 = lctx.resolve(fX2, SkSVGLengthContext::LengthType::kHorizontal);
|
||||
|
@ -48,8 +48,8 @@ SkPath SkSVGNode::asPath(const SkSVGRenderContext& ctx) const {
|
||||
return path;
|
||||
}
|
||||
|
||||
SkRect SkSVGNode::objectBoundingBox(const SkSVGLengthContext& lctx) const {
|
||||
return this->onObjectBoundingBox(lctx);
|
||||
SkRect SkSVGNode::objectBoundingBox(const SkSVGRenderContext& ctx) const {
|
||||
return this->onObjectBoundingBox(ctx);
|
||||
}
|
||||
|
||||
bool SkSVGNode::onPrepareToRender(SkSVGRenderContext* ctx) const {
|
||||
|
@ -68,7 +68,11 @@ sk_sp<SkShader> SkSVGRadialGradient::onMakeShader(const SkSVGRenderContext& ctx,
|
||||
const SkColor* colors, const SkScalar* pos,
|
||||
int count, SkTileMode tm,
|
||||
const SkMatrix& m) const {
|
||||
const auto& lctx = ctx.lengthContext();
|
||||
const SkSVGLengthContext lctx =
|
||||
fGradientUnits.type() == SkSVGGradientUnits::Type::kObjectBoundingBox
|
||||
? SkSVGLengthContext({1, 1})
|
||||
: ctx.lengthContext();
|
||||
|
||||
const auto r = lctx.resolve(fR , SkSVGLengthContext::LengthType::kOther);
|
||||
const auto center = SkPoint::Make(
|
||||
lctx.resolve(fCx, SkSVGLengthContext::LengthType::kHorizontal),
|
||||
@ -79,6 +83,9 @@ sk_sp<SkShader> SkSVGRadialGradient::onMakeShader(const SkSVGRenderContext& ctx,
|
||||
fFy.isValid() ? lctx.resolve(*fFy, SkSVGLengthContext::LengthType::kVertical)
|
||||
: center.y());
|
||||
|
||||
// TODO: Handle r == 0 which has a specific meaning according to the spec
|
||||
SkASSERT(r != 0);
|
||||
|
||||
return center == focal
|
||||
? SkGradientShader::MakeRadial(center, r, colors, pos, count, tm, 0, &m)
|
||||
: SkGradientShader::MakeTwoPointConical(focal, 0, center, r, colors, pos, count, tm, 0, &m);
|
||||
|
@ -94,3 +94,7 @@ SkPath SkSVGRect::onAsPath(const SkSVGRenderContext& ctx) const {
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
SkRect SkSVGRect::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
|
||||
return ctx.lengthContext().resolveRect(fX, fY, fWidth, fHeight);
|
||||
}
|
||||
|
@ -41,3 +41,7 @@ void SkSVGTransformableNode::mapToParent(SkPath* path) const {
|
||||
// transforms the path to parent node coordinates.
|
||||
path->transform(fTransform);
|
||||
}
|
||||
|
||||
void SkSVGTransformableNode::mapToParent(SkRect* rect) const {
|
||||
*rect = fTransform.mapRect(*rect);
|
||||
}
|
||||
|
@ -84,3 +84,15 @@ SkPath SkSVGUse::onAsPath(const SkSVGRenderContext& ctx) const {
|
||||
|
||||
return ref->asPath(ctx);
|
||||
}
|
||||
|
||||
SkRect SkSVGUse::onObjectBoundingBox(const SkSVGRenderContext& ctx) const {
|
||||
const auto ref = ctx.findNodeById(fHref);
|
||||
if (!ref) {
|
||||
return SkRect::MakeEmpty();
|
||||
}
|
||||
|
||||
const SkRect bounds = ref->objectBoundingBox(ctx);
|
||||
const SkScalar x = ctx.lengthContext().resolve(fX, SkSVGLengthContext::LengthType::kHorizontal);
|
||||
const SkScalar y = ctx.lengthContext().resolve(fY, SkSVGLengthContext::LengthType::kVertical);
|
||||
return SkRect::MakeXYWH(x, y, bounds.width(), bounds.height());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user