mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 923193, part 4 - Implement support for the 'transform-origin' property in SVG. r=heycam
This commit is contained in:
parent
ef7f0a9bfe
commit
e218dd339f
@ -4652,17 +4652,23 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
|
||||
* a distance, it's already computed for us!
|
||||
*/
|
||||
const nsStyleDisplay* display = aFrame->StyleDisplay();
|
||||
// We don't use aBoundsOverride for SVG since we need to account for
|
||||
// refBox.X/Y(). This happens to work because ReflowSVG sets the frame's
|
||||
// mRect before calling FinishAndStoreOverflow so we don't need the override.
|
||||
TransformReferenceBox refBox;
|
||||
if (aBoundsOverride) {
|
||||
if (aBoundsOverride &&
|
||||
!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
|
||||
refBox.Init(aBoundsOverride->Size());
|
||||
} else {
|
||||
refBox.Init(aFrame);
|
||||
}
|
||||
|
||||
/* Allows us to access dimension getters by index. */
|
||||
float coords[3];
|
||||
float coords[2];
|
||||
TransformReferenceBox::DimensionGetter dimensionGetter[] =
|
||||
{ &TransformReferenceBox::Width, &TransformReferenceBox::Height };
|
||||
TransformReferenceBox::DimensionGetter offsetGetter[] =
|
||||
{ &TransformReferenceBox::X, &TransformReferenceBox::Y };
|
||||
|
||||
for (uint8_t index = 0; index < 2; ++index) {
|
||||
/* If the -moz-transform-origin specifies a percentage, take the percentage
|
||||
@ -4684,20 +4690,19 @@ nsDisplayTransform::GetDeltaToTransformOrigin(const nsIFrame* aFrame,
|
||||
coords[index] =
|
||||
NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
|
||||
}
|
||||
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
|
||||
coord.GetUnit() != eStyleUnit_Percent) {
|
||||
// <length> values represent offsets from the origin of the SVG element's
|
||||
// user space, not the top left of its bounds, so we must adjust for that:
|
||||
nscoord offset =
|
||||
(index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
|
||||
coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
|
||||
|
||||
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
|
||||
// SVG frames (unlike other frames) have a reference box that can be (and
|
||||
// typically is) offset from the TopLeft() of the frame. We need to
|
||||
// account for that here.
|
||||
coords[index] +=
|
||||
NSAppUnitsToFloatPixels((refBox.*offsetGetter[index])(), aAppUnitsPerPixel);
|
||||
}
|
||||
}
|
||||
|
||||
coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
|
||||
aAppUnitsPerPixel);
|
||||
|
||||
return Point3D(coords[0], coords[1], coords[2]);
|
||||
return Point3D(coords[0], coords[1],
|
||||
NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
|
||||
aAppUnitsPerPixel));
|
||||
}
|
||||
|
||||
/* Returns the delta specified by the -moz-perspective-origin property.
|
||||
@ -4847,8 +4852,12 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
|
||||
|
||||
// Get the underlying transform matrix:
|
||||
|
||||
// We don't use aBoundsOverride for SVG since we need to account for
|
||||
// refBox.X/Y(). This happens to work because ReflowSVG sets the frame's
|
||||
// mRect before calling FinishAndStoreOverflow so we don't need the override.
|
||||
TransformReferenceBox refBox;
|
||||
if (aBoundsOverride) {
|
||||
if (aBoundsOverride &&
|
||||
(!frame || !(frame->GetStateBits() & NS_FRAME_SVG_LAYOUT))) {
|
||||
refBox.Init(aBoundsOverride->Size());
|
||||
} else {
|
||||
refBox.Init(frame);
|
||||
@ -4862,6 +4871,8 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
|
||||
Matrix svgTransform, transformFromSVGParent;
|
||||
bool hasSVGTransforms =
|
||||
frame && frame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
|
||||
bool hasTransformFromSVGParent =
|
||||
hasSVGTransforms && !transformFromSVGParent.IsIdentity();
|
||||
/* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
|
||||
if (aProperties.mTransformList) {
|
||||
result = nsStyleTransformMatrix::ReadTransforms(aProperties.mTransformList->mHead,
|
||||
@ -4877,15 +4888,6 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
|
||||
result = gfx3DMatrix::From2D(ThebesMatrix(svgTransform));
|
||||
}
|
||||
|
||||
if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
|
||||
// Correct the translation components for zoom:
|
||||
float pixelsPerCSSPx = frame->PresContext()->AppUnitsPerCSSPixel() /
|
||||
aAppUnitsPerPixel;
|
||||
transformFromSVGParent._31 *= pixelsPerCSSPx;
|
||||
transformFromSVGParent._32 *= pixelsPerCSSPx;
|
||||
result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent));
|
||||
}
|
||||
|
||||
if (aProperties.mChildPerspective > 0.0) {
|
||||
gfx3DMatrix perspective;
|
||||
perspective._34 =
|
||||
@ -4907,17 +4909,52 @@ nsDisplayTransform::GetResultingTransformMatrixInternal(const FrameTransformProp
|
||||
Point3D roundedOrigin(hasSVGTransforms ? newOrigin.x : NS_round(newOrigin.x),
|
||||
hasSVGTransforms ? newOrigin.y : NS_round(newOrigin.y),
|
||||
0);
|
||||
Point3D offsetBetweenOrigins = roundedOrigin + aProperties.mToTransformOrigin;
|
||||
|
||||
if (aOffsetByOrigin) {
|
||||
// We can fold the final translation by roundedOrigin into the first matrix
|
||||
// basis change translation. This is more stable against variation due to
|
||||
// insufficient floating point precision than reversing the translation
|
||||
// afterwards.
|
||||
result.Translate(-aProperties.mToTransformOrigin);
|
||||
result.TranslatePost(offsetBetweenOrigins);
|
||||
if (!hasSVGTransforms || !hasTransformFromSVGParent) {
|
||||
// This is a simplification of the following |else| block, the
|
||||
// simplification being possible because we don't need to apply
|
||||
// mToTransformOrigin between two transforms.
|
||||
Point3D offsets = roundedOrigin + aProperties.mToTransformOrigin;
|
||||
if (aOffsetByOrigin) {
|
||||
// We can fold the final translation by roundedOrigin into the first matrix
|
||||
// basis change translation. This is more stable against variation due to
|
||||
// insufficient floating point precision than reversing the translation
|
||||
// afterwards.
|
||||
result.Translate(-aProperties.mToTransformOrigin);
|
||||
result.TranslatePost(offsets);
|
||||
} else {
|
||||
result.ChangeBasis(offsets);
|
||||
}
|
||||
} else {
|
||||
result.ChangeBasis(offsetBetweenOrigins);
|
||||
Point3D refBoxOffset(NSAppUnitsToFloatPixels(refBox.X(), aAppUnitsPerPixel),
|
||||
NSAppUnitsToFloatPixels(refBox.Y(), aAppUnitsPerPixel),
|
||||
0);
|
||||
// We have both a transform and children-only transform. The
|
||||
// 'transform-origin' must apply between the two, so we need to apply it
|
||||
// now before we apply transformFromSVGParent. Since mToTransformOrigin is
|
||||
// relative to the frame's TopLeft(), we need to convert it to SVG user
|
||||
// space by subtracting refBoxOffset. (Then after applying
|
||||
// transformFromSVGParent we have to reapply refBoxOffset below.)
|
||||
result.ChangeBasis(aProperties.mToTransformOrigin - refBoxOffset);
|
||||
|
||||
// Now apply the children-only transforms, converting the translation
|
||||
// components to device pixels:
|
||||
float pixelsPerCSSPx =
|
||||
frame->PresContext()->AppUnitsPerCSSPixel() / aAppUnitsPerPixel;
|
||||
transformFromSVGParent._31 *= pixelsPerCSSPx;
|
||||
transformFromSVGParent._32 *= pixelsPerCSSPx;
|
||||
result = result * gfx3DMatrix::From2D(ThebesMatrix(transformFromSVGParent));
|
||||
|
||||
// Similar to the code in the |if| block above, but since we've accounted
|
||||
// for mToTransformOrigin so we don't include that. We also need to reapply
|
||||
// refBoxOffset.
|
||||
Point3D offsets = roundedOrigin + refBoxOffset;
|
||||
if (aOffsetByOrigin) {
|
||||
result.Translate(-refBoxOffset);
|
||||
result.TranslatePost(offsets);
|
||||
} else {
|
||||
result.ChangeBasis(offsets);
|
||||
}
|
||||
}
|
||||
|
||||
if (frame && frame->Preserves3D()) {
|
||||
|
@ -140,6 +140,7 @@ typedef nsStyleTransformMatrix::TransformReferenceBox TransformReferenceBox;
|
||||
/* static */ bool nsLayoutUtils::sInvalidationDebuggingIsEnabled;
|
||||
/* static */ bool nsLayoutUtils::sCSSVariablesEnabled;
|
||||
/* static */ bool nsLayoutUtils::sInterruptibleReflowEnabled;
|
||||
/* static */ bool nsLayoutUtils::sSVGTransformOriginEnabled;
|
||||
|
||||
static ViewID sScrollIdCounter = FrameMetrics::START_SCROLL_ID;
|
||||
|
||||
@ -7051,6 +7052,8 @@ nsLayoutUtils::Initialize()
|
||||
"layout.css.variables.enabled");
|
||||
Preferences::AddBoolVarCache(&sInterruptibleReflowEnabled,
|
||||
"layout.interruptible-reflow.enabled");
|
||||
Preferences::AddBoolVarCache(&sSVGTransformOriginEnabled,
|
||||
"svg.transform-origin.enabled");
|
||||
|
||||
Preferences::RegisterCallback(GridEnabledPrefChangeCallback,
|
||||
GRID_ENABLED_PREF_NAME);
|
||||
|
@ -2305,6 +2305,10 @@ public:
|
||||
return sFontSizeInflationDisabledInMasterProcess;
|
||||
}
|
||||
|
||||
static bool SVGTransformOriginEnabled() {
|
||||
return sSVGTransformOriginEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* See comment above "font.size.inflation.mappingIntercept" in
|
||||
* modules/libpref/src/init/all.js .
|
||||
@ -2657,6 +2661,7 @@ private:
|
||||
static bool sInvalidationDebuggingIsEnabled;
|
||||
static bool sCSSVariablesEnabled;
|
||||
static bool sInterruptibleReflowEnabled;
|
||||
static bool sSVGTransformOriginEnabled;
|
||||
|
||||
/**
|
||||
* Helper function for LogTestDataForPaint().
|
||||
|
@ -9,8 +9,10 @@
|
||||
|
||||
#include "nsStyleTransformMatrix.h"
|
||||
#include "nsCSSValue.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsRuleNode.h"
|
||||
#include "nsSVGUtils.h"
|
||||
#include "nsCSSKeywords.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "gfxMatrix.h"
|
||||
@ -48,9 +50,44 @@ TransformReferenceBox::EnsureDimensionsAreCached()
|
||||
mIsCached = true;
|
||||
|
||||
if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
|
||||
// TODO: SVG needs to define what percentage translations resolve against.
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
if (!nsLayoutUtils::SVGTransformOriginEnabled()) {
|
||||
mX = -mFrame->GetPosition().x;
|
||||
mY = -mFrame->GetPosition().y;
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
} else
|
||||
if (mFrame->StyleDisplay()->mTransformBox ==
|
||||
NS_STYLE_TRANSFORM_BOX_FILL_BOX) {
|
||||
// Percentages in transforms resolve against the SVG bbox, and the
|
||||
// transform is relative to the top-left of the SVG bbox.
|
||||
gfxRect bbox = nsSVGUtils::GetBBox(const_cast<nsIFrame*>(mFrame));
|
||||
nsRect bboxInAppUnits =
|
||||
nsLayoutUtils::RoundGfxRectToAppRect(bbox,
|
||||
mFrame->PresContext()->AppUnitsPerCSSPixel());
|
||||
// The mRect of an SVG nsIFrame is its user space bounds *including*
|
||||
// stroke and markers, whereas bboxInAppUnits is its user space bounds
|
||||
// including fill only. We need to note the offset of the reference box
|
||||
// from the frame's mRect in mX/mY.
|
||||
mX = bboxInAppUnits.x - mFrame->GetPosition().x;
|
||||
mY = bboxInAppUnits.y - mFrame->GetPosition().y;
|
||||
mWidth = bboxInAppUnits.width;
|
||||
mHeight = bboxInAppUnits.height;
|
||||
} else {
|
||||
// The value 'border-box' is treated as 'view-box' for SVG content.
|
||||
MOZ_ASSERT(mFrame->StyleDisplay()->mTransformBox ==
|
||||
NS_STYLE_TRANSFORM_BOX_VIEW_BOX ||
|
||||
mFrame->StyleDisplay()->mTransformBox ==
|
||||
NS_STYLE_TRANSFORM_BOX_BORDER_BOX,
|
||||
"Unexpected value for 'transform-box'");
|
||||
// Percentages in transforms resolve against the width/height of the
|
||||
// nearest viewport (or it's viewBox if one is applied), and the
|
||||
// transform is relative to {0,0} in current user space.
|
||||
mX = -mFrame->GetPosition().x;
|
||||
mY = -mFrame->GetPosition().y;
|
||||
Size contextSize = nsSVGUtils::GetContextSize(mFrame);
|
||||
mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
|
||||
mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,6 +115,8 @@ TransformReferenceBox::EnsureDimensionsAreCached()
|
||||
}
|
||||
#endif
|
||||
|
||||
mX = 0;
|
||||
mY = 0;
|
||||
mWidth = rect.Width();
|
||||
mHeight = rect.Height();
|
||||
}
|
||||
@ -87,6 +126,8 @@ TransformReferenceBox::Init(const nsSize& aDimensions)
|
||||
{
|
||||
MOZ_ASSERT(!mFrame && !mIsCached);
|
||||
|
||||
mX = 0;
|
||||
mY = 0;
|
||||
mWidth = aDimensions.width;
|
||||
mHeight = aDimensions.height;
|
||||
mIsCached = true;
|
||||
|
@ -79,11 +79,28 @@ namespace nsStyleTransformMatrix {
|
||||
|
||||
void Init(const nsSize& aDimensions);
|
||||
|
||||
/**
|
||||
* The offset of the reference box from the nsIFrame's TopLeft(). This
|
||||
* is non-zero only in the case of SVG content. If we can successfully
|
||||
* implement UNIFIED_CONTINUATIONS at some point in the future then it
|
||||
* may also be non-zero for non-SVG content.
|
||||
*/
|
||||
nscoord X() {
|
||||
EnsureDimensionsAreCached();
|
||||
return mX;
|
||||
}
|
||||
nscoord Y() {
|
||||
EnsureDimensionsAreCached();
|
||||
return mY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The size of the reference box.
|
||||
*/
|
||||
nscoord Width() {
|
||||
EnsureDimensionsAreCached();
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
nscoord Height() {
|
||||
EnsureDimensionsAreCached();
|
||||
return mHeight;
|
||||
@ -98,7 +115,7 @@ namespace nsStyleTransformMatrix {
|
||||
void EnsureDimensionsAreCached();
|
||||
|
||||
const nsIFrame* mFrame;
|
||||
nscoord mWidth, mHeight;
|
||||
nscoord mX, mY, mWidth, mHeight;
|
||||
bool mIsCached;
|
||||
};
|
||||
|
||||
|
@ -3936,14 +3936,16 @@ SVGTextFrame::ReflowSVG()
|
||||
nsSVGEffects::UpdateEffects(this);
|
||||
}
|
||||
|
||||
// Now unset the various reflow bits. Do this before calling
|
||||
// FinishAndStoreOverflow since FinishAndStoreOverflow can require glyph
|
||||
// positions (to resolve transform-origin).
|
||||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
|
||||
nsOverflowAreas overflowAreas(overflow, overflow);
|
||||
FinishAndStoreOverflow(overflowAreas, mRect.Size());
|
||||
|
||||
// Now unset the various reflow bits:
|
||||
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
|
||||
NS_FRAME_HAS_DIRTY_CHILDREN);
|
||||
|
||||
// XXX nsSVGContainerFrame::ReflowSVG only looks at its nsISVGChildFrame
|
||||
// children, and calls ConsiderChildOverflow on them. Does it matter
|
||||
// that ConsiderChildOverflow won't be called on our children?
|
||||
@ -3977,10 +3979,22 @@ SVGTextFrame::GetBBoxContribution(const gfx::Matrix &aToBBoxUserspace,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
NS_ASSERTION(GetFirstPrincipalChild(), "must have a child frame");
|
||||
SVGBBox bbox;
|
||||
nsIFrame* kid = GetFirstPrincipalChild();
|
||||
if (kid && NS_SUBTREE_DIRTY(kid)) {
|
||||
// Return an empty bbox if our kid's subtree is dirty. This may be called
|
||||
// in that situation, e.g. when we're building a display list after an
|
||||
// interrupted reflow. This can also be called during reflow before we've
|
||||
// been reflowed, e.g. if an earlier sibling is calling FinishAndStoreOverflow and
|
||||
// needs our parent's perspective matrix, which depends on the SVG bbox
|
||||
// contribution of this frame. In the latter situation, when all siblings have
|
||||
// been reflowed, the parent will compute its perspective and rerun
|
||||
// FinishAndStoreOverflow for all its children.
|
||||
return bbox;
|
||||
}
|
||||
|
||||
UpdateGlyphPositioning();
|
||||
|
||||
SVGBBox bbox;
|
||||
nsPresContext* presContext = PresContext();
|
||||
|
||||
TextRenderedRunIterator it(this);
|
||||
|
@ -298,6 +298,22 @@ nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
|
||||
}
|
||||
}
|
||||
|
||||
Size
|
||||
nsSVGUtils::GetContextSize(const nsIFrame* aFrame)
|
||||
{
|
||||
Size size;
|
||||
|
||||
MOZ_ASSERT(aFrame->GetContent()->IsSVGElement(), "bad cast");
|
||||
const nsSVGElement* element = static_cast<nsSVGElement*>(aFrame->GetContent());
|
||||
|
||||
SVGSVGElement* ctx = element->GetCtx();
|
||||
if (ctx) {
|
||||
size.width = ctx->GetLength(SVGContentUtils::X);
|
||||
size.height = ctx->GetLength(SVGContentUtils::Y);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
float
|
||||
nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
|
||||
{
|
||||
|
@ -184,6 +184,7 @@ public:
|
||||
typedef mozilla::gfx::AntialiasMode AntialiasMode;
|
||||
typedef mozilla::gfx::FillRule FillRule;
|
||||
typedef mozilla::gfx::GeneralPattern GeneralPattern;
|
||||
typedef mozilla::gfx::Size Size;
|
||||
|
||||
static void Init();
|
||||
|
||||
@ -250,6 +251,14 @@ public:
|
||||
*/
|
||||
static void NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame);
|
||||
|
||||
/**
|
||||
* Percentage lengths in SVG are resolved against the width/height of the
|
||||
* nearest viewport (or its viewBox, if set). This helper returns the size
|
||||
* of this "context" for the given frame so that percentage values can be
|
||||
* resolved.
|
||||
*/
|
||||
static Size GetContextSize(const nsIFrame* aFrame);
|
||||
|
||||
/* Computes the input length in terms of object space coordinates.
|
||||
Input: rect - bounding box
|
||||
length - length to be converted
|
||||
|
@ -51,7 +51,7 @@ foreignObject {
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
/* Set |transform-origin:0% 0%;| for all SVG elements except outer-<svg>,
|
||||
/* Set |transform-origin:0 0;| for all SVG elements except outer-<svg>,
|
||||
noting that 'svg' as a child of 'foreignObject' counts as outer-<svg>.
|
||||
*/
|
||||
*:not(svg),
|
||||
|
Loading…
Reference in New Issue
Block a user