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:
violet 2019-04-27 07:22:53 +00:00
Родитель 92d63fc969
Коммит 17083b6e11
10 изменённых файлов: 72 добавлений и 49 удалений

Просмотреть файл

@ -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