зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1547209 - Compute transform matrix directly in nsSVGUtils::GetTransformMatrixInUserSpace r=longsonr
Since now most CSS transform related bugs are fixed, it's clear that the purpose of nsSVGUtils::GetTransformMatrixInUserSpace() should solely be to replace SVGElement::PrependLocalTransformsTo(eUserSpaceToParent). After fixing Bug 972041, the only other usecase (Bug 1247218) no longer exists. OTOH, there is a problem when the parent has a viewbox transform, in that case, SVGElement::PrependLocalTransformsTo() doesn't include it, but nsLayoutUtils::GetTransformToAncestor() does. In fact, it's much easier to compute the transform matrix directly based on the implementation of nsDisplayTransform::GetResultingTransformMatrixInternal(), which is also the function internally called by nsLayoutUtils::GetTransformToAncestor(). In particular, we don't need to look at 3D stuff since our matrix is inherently 2D. Differential Revision: https://phabricator.services.mozilla.com/D28970 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
92d63fc969
Коммит
17083b6e11
|
@ -473,7 +473,7 @@ static gfx::Matrix GetCTMInternal(SVGElement* aElement, bool aScreenCTM,
|
|||
gfxMatrix ret;
|
||||
|
||||
if (auto* f = e->GetPrimaryFrame()) {
|
||||
ret = nsSVGUtils::GetTransformMatrixInUserSpace(f, f->GetParent());
|
||||
ret = nsSVGUtils::GetTransformMatrixInUserSpace(f);
|
||||
} else {
|
||||
// FIXME: Ideally we should also return the correct matrix
|
||||
// for display:none, but currently transform related code relies
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<svg style="width: 500px; height: 500px; border: 1px solid green;">
|
||||
<defs>
|
||||
<mask id="mask">
|
||||
<rect id="square" x="200px" y="250px" width="100px" height="150px" fill="#ffffff" />
|
||||
<rect x="200px" y="250px" width="100px" height="150px" fill="#ffffff" />
|
||||
<rect x="50px" y="105px" width="110px" height="195px" fill="blue" />
|
||||
</mask>
|
||||
</defs>
|
||||
<rect mask="url(#mask)" width="500px" height="500px" fill="red" />
|
||||
|
|
До Ширина: | Высота: | Размер: 287 B После Ширина: | Высота: | Размер: 350 B |
|
@ -1,12 +1,18 @@
|
|||
<style>
|
||||
#square{
|
||||
#square1 {
|
||||
transform: translate(100px, 100px) scale(2,3);
|
||||
}
|
||||
#square2 {
|
||||
transform: scale(2,3);
|
||||
}
|
||||
</style>
|
||||
<svg style="width: 500px; height: 500px; border: 1px solid green;">
|
||||
<defs>
|
||||
<mask id="mask">
|
||||
<rect id="square" x="50px" y="50px" width="50px" height="50px" fill="#ffffff" />
|
||||
<rect id="square1" x="50px" y="50px" width="50px" height="50px" fill="#ffffff" />
|
||||
<svg viewBox="0 0 100 100">
|
||||
<rect id="square2" x="5" y="7" width="11" height="13" fill="blue" />
|
||||
</svg>
|
||||
</mask>
|
||||
</defs>
|
||||
<rect mask="url(#mask)" width="500px" height="500px" fill="red" />
|
||||
|
|
|
@ -59,7 +59,7 @@ void nsSVGClipPathFrame::ApplyClipPath(gfxContext& aContext,
|
|||
static_cast<SVGGeometryElement*>(pathFrame->GetContent());
|
||||
|
||||
gfxMatrix toChildsUserSpace =
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(pathFrame, this) *
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(pathFrame) *
|
||||
(GetClipPathTransform(aClippedFrame) * aMatrix);
|
||||
|
||||
gfxMatrix newMatrix = aContext.CurrentMatrixDouble()
|
||||
|
@ -220,8 +220,7 @@ void nsSVGClipPathFrame::PaintFrameIntoMask(nsIFrame* aFrame,
|
|||
nsIContent* childContent = child->GetContent();
|
||||
if (childContent->IsSVGElement()) {
|
||||
toChildsUserSpace =
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(child, child->GetParent()) *
|
||||
mMatrixForChildren;
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(child) * mMatrixForChildren;
|
||||
}
|
||||
|
||||
// clipPath does not result in any image rendering, so we just use a dummy
|
||||
|
@ -302,7 +301,7 @@ bool nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
|
|||
if (SVGFrame) {
|
||||
gfxPoint pointForChild = point;
|
||||
|
||||
gfxMatrix m = nsSVGUtils::GetTransformMatrixInUserSpace(kid, this);
|
||||
gfxMatrix m = nsSVGUtils::GetTransformMatrixInUserSpace(kid);
|
||||
if (!m.IsIdentity()) {
|
||||
if (!m.Invert()) {
|
||||
return false;
|
||||
|
@ -428,9 +427,8 @@ gfxMatrix nsSVGClipPathFrame::GetCanvasTM() { return mMatrixForChildren; }
|
|||
gfxMatrix nsSVGClipPathFrame::GetClipPathTransform(nsIFrame* aClippedFrame) {
|
||||
SVGClipPathElement* content = static_cast<SVGClipPathElement*>(GetContent());
|
||||
|
||||
gfxMatrix tm =
|
||||
content->PrependLocalTransformsTo({}, eChildToUserSpace) *
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(this, this->GetParent());
|
||||
gfxMatrix tm = content->PrependLocalTransformsTo({}, eChildToUserSpace) *
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(this);
|
||||
|
||||
SVGAnimatedEnumeration* clipPathUnits =
|
||||
&content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS];
|
||||
|
@ -464,7 +462,7 @@ SVGBBox nsSVGClipPathFrame::GetBBoxForClipPathFrame(const SVGBBox& aBBox,
|
|||
nsSVGDisplayableFrame* svg = do_QueryFrame(frame);
|
||||
if (svg) {
|
||||
gfxMatrix matrix =
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(frame, this) * aMatrix;
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(frame) * aMatrix;
|
||||
tmpBBox = svg->GetBBoxContribution(mozilla::gfx::ToMatrix(matrix),
|
||||
nsSVGUtils::eBBoxIncludeFill);
|
||||
nsSVGClipPathFrame* clipPathFrame;
|
||||
|
|
|
@ -261,7 +261,7 @@ void nsSVGDisplayContainerFrame::PaintSVG(gfxContext& aContext,
|
|||
continue; // nothing to paint for kid
|
||||
}
|
||||
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) * m;
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(kid) * m;
|
||||
if (m.IsSingular()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -392,8 +392,7 @@ SVGBBox nsSVGDisplayContainerFrame::GetBBoxContribution(
|
|||
if (content->IsSVGElement()) {
|
||||
transform = static_cast<SVGElement*>(content)->PrependLocalTransformsTo(
|
||||
{}, eChildToUserSpace) *
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) *
|
||||
transform;
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(kid) * transform;
|
||||
}
|
||||
// We need to include zero width/height vertical/horizontal lines, so we
|
||||
// have to use UnionEdges.
|
||||
|
|
|
@ -118,7 +118,7 @@ already_AddRefed<SourceSurface> nsSVGMaskFrame::GetMaskForMaskedFrame(
|
|||
nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
|
||||
if (SVGFrame) {
|
||||
SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) * m;
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(kid) * m;
|
||||
}
|
||||
|
||||
nsSVGUtils::PaintFrameWithEffects(kid, *tmpCtx, m, aParams.imgParams);
|
||||
|
|
|
@ -361,9 +361,7 @@ already_AddRefed<SourceSurface> nsSVGPatternFrame::PaintPattern(
|
|||
nsSVGDisplayableFrame *SVGFrame = do_QueryFrame(kid);
|
||||
if (SVGFrame) {
|
||||
SVGFrame->NotifySVGChanged(nsSVGDisplayableFrame::TRANSFORM_CHANGED);
|
||||
tm = nsSVGUtils::GetTransformMatrixInUserSpace(kid,
|
||||
patternWithChildren) *
|
||||
tm;
|
||||
tm = nsSVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
|
||||
}
|
||||
|
||||
nsSVGUtils::PaintFrameWithEffects(kid, *ctx, tm, aImgParams);
|
||||
|
|
|
@ -103,7 +103,7 @@ void nsSVGSwitchFrame::PaintSVG(gfxContext& aContext,
|
|||
if (kid) {
|
||||
gfxMatrix tm = aTransform;
|
||||
if (kid->GetContent()->IsSVGElement()) {
|
||||
tm = nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) * tm;
|
||||
tm = nsSVGUtils::GetTransformMatrixInUserSpace(kid) * tm;
|
||||
}
|
||||
nsSVGUtils::PaintFrameWithEffects(kid, aContext, tm, aImgParams,
|
||||
aDirtyRect);
|
||||
|
@ -255,8 +255,7 @@ SVGBBox nsSVGSwitchFrame::GetBBoxContribution(const Matrix& aToBBoxUserspace,
|
|||
if (content->IsSVGElement()) {
|
||||
transform = static_cast<SVGElement*>(content)->PrependLocalTransformsTo(
|
||||
{}, eChildToUserSpace) *
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(kid, this) *
|
||||
transform;
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(kid) * transform;
|
||||
}
|
||||
return svgKid->GetBBoxContribution(ToMatrix(transform), aFlags);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "nsStyleCoord.h"
|
||||
#include "nsStyleStruct.h"
|
||||
#include "nsStyleTransformMatrix.h"
|
||||
#include "SVGAnimatedLength.h"
|
||||
#include "nsSVGClipPathFrame.h"
|
||||
#include "nsSVGContainerFrame.h"
|
||||
|
@ -1094,9 +1095,8 @@ gfxRect nsSVGUtils::GetBBox(nsIFrame* aFrame, uint32_t aFlags,
|
|||
matrix = gfxMatrix();
|
||||
}
|
||||
|
||||
matrix = nsSVGUtils::GetTransformMatrixInUserSpace(
|
||||
clipPathFrame, clipPathFrame->GetParent()) *
|
||||
matrix;
|
||||
matrix =
|
||||
nsSVGUtils::GetTransformMatrixInUserSpace(clipPathFrame) * matrix;
|
||||
|
||||
bbox = clipPathFrame->GetBBoxForClipPathFrame(bbox, matrix, aFlags)
|
||||
.ToThebesRect();
|
||||
|
@ -1251,8 +1251,8 @@ bool nsSVGUtils::GetNonScalingStrokeTransform(nsIFrame* aFrame,
|
|||
|
||||
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "should be an SVG element");
|
||||
|
||||
nsSVGOuterSVGFrame* outer = nsSVGUtils::GetOuterSVGFrame(aFrame);
|
||||
*aUserToOuterSVG = nsSVGUtils::GetTransformMatrixInUserSpace(aFrame, outer);
|
||||
*aUserToOuterSVG = ThebesMatrix(SVGContentUtils::GetCTM(
|
||||
static_cast<SVGElement*>(aFrame->GetContent()), true));
|
||||
|
||||
return aUserToOuterSVG->HasNonTranslation();
|
||||
}
|
||||
|
@ -1642,7 +1642,7 @@ void nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext) {
|
|||
if (frame->GetContent()->IsSVGElement()) {
|
||||
// PaintSVG() expects the passed transform to be the transform to its own
|
||||
// SVG user space, so we need to account for any 'transform' attribute:
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(frame, frame->GetParent());
|
||||
m = nsSVGUtils::GetTransformMatrixInUserSpace(frame);
|
||||
}
|
||||
|
||||
// SVG-in-OpenType is not allowed to paint external resources, so we can
|
||||
|
@ -1696,29 +1696,49 @@ gfxMatrix nsSVGUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame) {
|
|||
return gfxMatrix(devPxPerCSSPx, 0.0, 0.0, devPxPerCSSPx, 0.0, 0.0);
|
||||
}
|
||||
|
||||
gfxMatrix nsSVGUtils::GetTransformMatrixInUserSpace(const nsIFrame* aFrame,
|
||||
const nsIFrame* aAncestor) {
|
||||
gfxMatrix nsSVGUtils::GetTransformMatrixInUserSpace(const nsIFrame* aFrame) {
|
||||
// We check element instead of aFrame directly because SVG element
|
||||
// may have non-SVG frame, <tspan> for example.
|
||||
MOZ_ASSERT(aFrame->GetContent() && aFrame->GetContent()->IsSVGElement(),
|
||||
"Only use this wrapper for SVG elements");
|
||||
|
||||
if (!aFrame->IsTransformed()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
nsDisplayTransform::FrameTransformProperties properties{
|
||||
aFrame, AppUnitsPerCSSPixel(), nullptr};
|
||||
nsStyleTransformMatrix::TransformReferenceBox refBox;
|
||||
refBox.Init(aFrame);
|
||||
|
||||
// SVG elements can have x/y offset, their default transform origin
|
||||
// is the origin of user space, not the top left point of the frame.
|
||||
Point3D svgTransformOrigin{
|
||||
properties.mToTransformOrigin.x - CSSPixel::FromAppUnits(refBox.X()),
|
||||
properties.mToTransformOrigin.y - CSSPixel::FromAppUnits(refBox.Y()),
|
||||
properties.mToTransformOrigin.z};
|
||||
|
||||
Matrix svgTransform;
|
||||
Matrix4x4 trans;
|
||||
(void)aFrame->IsSVGTransformed(&svgTransform);
|
||||
|
||||
if (properties.HasTransform()) {
|
||||
trans = nsStyleTransformMatrix::ReadTransforms(
|
||||
properties.mIndividualTransformList
|
||||
? properties.mIndividualTransformList->mHead
|
||||
: nullptr,
|
||||
properties.mMotion,
|
||||
properties.mTransformList ? properties.mTransformList->mHead : nullptr,
|
||||
refBox, AppUnitsPerCSSPixel());
|
||||
} else {
|
||||
trans = Matrix4x4::From2D(svgTransform);
|
||||
}
|
||||
|
||||
trans.ChangeBasis(svgTransformOrigin);
|
||||
|
||||
Matrix mm;
|
||||
auto trans = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor,
|
||||
nsIFrame::IN_CSS_UNITS);
|
||||
|
||||
trans.ProjectTo2D();
|
||||
trans.CanDraw2D(&mm);
|
||||
gfxMatrix ret = ThebesMatrix(mm);
|
||||
(void)trans.CanDraw2D(&mm);
|
||||
|
||||
float initPositionX = NSAppUnitsToFloatPixels(aFrame->GetPosition().x,
|
||||
AppUnitsPerCSSPixel()),
|
||||
initPositionY = NSAppUnitsToFloatPixels(aFrame->GetPosition().y,
|
||||
AppUnitsPerCSSPixel());
|
||||
|
||||
// Remove the initial displacement to mimic the behavior
|
||||
// of SVGElement::PrependLocalTransformsTo().
|
||||
ret = ret.PreTranslate(-initPositionX, -initPositionY);
|
||||
|
||||
return ret;
|
||||
return ThebesMatrix(mm);
|
||||
}
|
||||
|
|
|
@ -596,11 +596,13 @@ class nsSVGUtils {
|
|||
}
|
||||
|
||||
/**
|
||||
* A simple wrapper of nsLayoutUtils::GetTransformToAncestor to avoid
|
||||
* boilerplate code for changing unit and matrix format.
|
||||
* It is a replacement of
|
||||
* SVGElement::PrependLocalTransformsTo(eUserSpaceToParent).
|
||||
* If no CSS transform is involved, they should behave exactly the same;
|
||||
* if there are CSS transforms, this one will take them into account
|
||||
* while SVGElement::PrependLocalTransformsTo won't.
|
||||
*/
|
||||
static gfxMatrix GetTransformMatrixInUserSpace(const nsIFrame* aFrame,
|
||||
const nsIFrame* aAncestor);
|
||||
static gfxMatrix GetTransformMatrixInUserSpace(const nsIFrame* aFrame);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче