Issue #438: Make the nsSVGOuterSVGAnonChildFrame a reference frame.

This causes transforms of the <svg> contents to be unaffected by
scrolling / offset changes of the <svg> element.
pull/24/head
wolfbeast 3 years ago committed by Roy Tam
parent c2b844077e
commit c01fb23b1b
  1. 56
      layout/svg/nsSVGOuterSVGFrame.cpp

@ -979,46 +979,56 @@ nsSVGOuterSVGAnonChildFrame::GetType() const
return nsGkAtoms::svgOuterSVGAnonChildFrame;
}
bool
nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(Matrix* aOwnTransform,
Matrix* aFromParentTransform) const
static Matrix
ComputeOuterSVGAnonChildFrameTransform(const nsSVGOuterSVGAnonChildFrame* aFrame)
{
// Our elements 'transform' attribute is applied to our nsSVGOuterSVGFrame
// parent, and the element's children-only transforms are applied to us, the
// anonymous child frame. Since we are the child frame, we apply the
// children-only transforms as if they are our own transform.
SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent);
SVGSVGElement* content = static_cast<SVGSVGElement*>(aFrame->GetContent());
if (!content->HasChildrenOnlyTransform()) {
return false;
return Matrix();
}
// Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
gfxMatrix ownMatrix =
content->PrependLocalTransformsTo(gfxMatrix(), eChildToUserSpace);
if (ownMatrix.IsIdentity()) {
return false;
if (ownMatrix.HasNonTranslation()) {
// viewBox, currentScale and currentTranslate should only produce a
// rectilinear transform.
MOZ_ASSERT(ownMatrix.IsRectilinear(),
"Non-rectilinear transform will break the following logic");
// The nsDisplayTransform code will apply this transform to our frame,
// including to our frame position. We don't want our frame position to
// be scaled though, so we need to correct for that in the transform.
CSSPoint pos = CSSPixel::FromAppUnits(aFrame->GetPosition());
CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y);
CSSPoint deltaPos = scaledPos - pos;
ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y);
}
if (aOwnTransform) {
if (ownMatrix.HasNonTranslation()) {
// Note: viewBox, currentScale and currentTranslate should only
// produce a rectilinear transform.
MOZ_ASSERT(ownMatrix.IsRectilinear(),
"Non-rectilinear transform will break the following logic");
// The nsDisplayTransform code will apply this transform to our frame,
// including to our frame position. We don't want our frame position to
// be scaled though, so we need to correct for that in the transform.
CSSPoint pos = CSSPixel::FromAppUnits(GetPosition());
CSSPoint scaledPos = CSSPoint(ownMatrix._11 * pos.x, ownMatrix._22 * pos.y);
CSSPoint deltaPos = scaledPos - pos;
ownMatrix *= gfxMatrix::Translation(-deltaPos.x, -deltaPos.y);
}
return gfx::ToMatrix(ownMatrix);
}
*aOwnTransform = gfx::ToMatrix(ownMatrix);
// We want this frame to be a reference frame. An easy way to achieve that is
// to always return true from this method, even for identity transforms.
// This frame being a reference frame ensures that the offset between this
// <svg> element and the parent reference frame is completely absorbed by the
// nsDisplayTransform that's created for this frame, and that this offset does
// not affect our descendants' transforms. Consequently, if the <svg> element
// moves, e.g. during scrolling, the transform matrices of our contents are
// unaffected. This simplifies invalidation.
bool
nsSVGOuterSVGAnonChildFrame::IsSVGTransformed(Matrix* aOwnTransform,
Matrix* aFromParentTransform) const
{
if (aOwnTransform) {
*aOwnTransform = ComputeOuterSVGAnonChildFrameTransform(this);
}
return true;

Loading…
Cancel
Save