--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -648,71 +648,96 @@ ValidateSVGFrame(nsIFrame* aFrame)
// The SVG spec says not to draw _anything_
return false;
}
}
return true;
}
-/**
- * Setup transform matrix of a gfx context by a specific frame. Depend on
- * aClipCtx, this function may clip that context by the visual overflow area
- * of aFrame.
- *
- * @param aFrame is the target frame.
- * @param aOffsetToBoundingBox returns the offset between the reference frame
- * and the bounding box of aFrame.
- * @oaram aOffsetToUserSpace returns the offset between the reference frame and
- * the user space coordinate of aFrame.
- */
-static void
-SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
- nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace)
+struct EffectOffsets {
+ // The offset between the reference frame and the bounding box of the
+ // target frame in app unit.
+ nsPoint offsetToBoundingBox;
+ // The offset between the reference frame and the bounding box of the
+ // target frame in device unit.
+ gfxPoint offsetToBoundingBoxInDevPx;
+ // The offset between the reference frame and the bounding box of the
+ // target frame in app unit.
+ nsPoint offsetToUserSpace;
+ // The offset between the reference frame and the bounding box of the
+ // target frame in device unit.
+ gfxPoint offsetToUserSpaceInDevPx;
+};
+
+EffectOffsets
+ComputeEffectOffset(nsIFrame* aFrame, const PaintFramesParams& aParams)
{
- aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
- nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
+ EffectOffsets result;
+
+ result.offsetToBoundingBox =
+ aParams.builder->ToReferenceFrame(aFrame) -
+ nsSVGIntegrationUtils::GetOffsetToBoundingBox(aFrame);
if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
/* Snap the offset if the reference frame is not a SVG frame,
* since other frames will be snapped to pixel when rendering. */
- aOffsetToBoundingBox = nsPoint(
- aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
- aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
+ result.offsetToBoundingBox =
+ nsPoint(
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.x),
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(result.offsetToBoundingBox.y));
}
// After applying only "aOffsetToBoundingBox", aParams.ctx would have its
// origin at the top left corner of frame's bounding box (over all
// continuations).
// However, SVG painting needs the origin to be located at the origin of the
// SVG frame's "user space", i.e. the space in which, for example, the
// frame's BBox lives.
// SVG geometry frames and foreignObject frames apply their own offsets, so
// their position is relative to their user space. So for these frame types,
// if we want aParams.ctx to be in user space, we first need to subtract the
// frame's position so that SVG painting can later add it again and the
// frame is painted in the right place.
-
gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
nsPoint toUserSpace =
nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
- aOffsetToUserSpace = aOffsetToBoundingBox - toUserSpace;
+ result.offsetToUserSpace = result.offsetToBoundingBox - toUserSpace;
#ifdef DEBUG
bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
- NS_ASSERTION(hasSVGLayout || aOffsetToBoundingBox == aOffsetToUserSpace,
+ NS_ASSERTION(hasSVGLayout ||
+ result.offsetToBoundingBox == result.offsetToUserSpace,
"For non-SVG frames there shouldn't be any additional offset");
#endif
- gfxPoint devPixelOffsetToUserSpace =
- nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
+ result.offsetToUserSpaceInDevPx =
+ nsLayoutUtils::PointToGfxPoint(result.offsetToUserSpace,
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+ result.offsetToBoundingBoxInDevPx =
+ nsLayoutUtils::PointToGfxPoint(result.offsetToBoundingBox,
aFrame->PresContext()->AppUnitsPerDevPixel());
- gfxContext& context = aParams.ctx;
- context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
+
+ return result;
+}
+
+/**
+ * Setup transform matrix of a gfx context by a specific frame. Move the
+ * origin of aParams.ctx to the user space of aFrame.
+ */
+static EffectOffsets
+MoveContextOriginToUserSpace(nsIFrame* aFrame, const PaintFramesParams& aParams)
+{
+ EffectOffsets offset = ComputeEffectOffset(aFrame, aParams);
+
+ aParams.ctx.SetMatrix(
+ aParams.ctx.CurrentMatrix().Translate(offset.offsetToUserSpaceInDevPx));
+
+ return offset;
}
bool
nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
{
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
@@ -793,26 +818,23 @@ nsSVGIntegrationUtils::PaintMask(const P
bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
(maskFrames.Length() != 1);
if (shouldPushOpacity) {
ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
autoPop.SetContext(&ctx);
}
gfxContextMatrixAutoSaveRestore matSR;
- nsPoint offsetToBoundingBox;
- nsPoint offsetToUserSpace;
// Paint clip-path-basic-shape onto ctx
gfxContextAutoSaveRestore basicShapeSR;
if (maskUsage.shouldApplyBasicShape) {
matSR.SetContext(&ctx);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ MoveContextOriginToUserSpace(firstFrame, aParams);
basicShapeSR.SetContext(&ctx);
nsCSSClipPathInstance::ApplyBasicShapeClip(ctx, frame);
if (!maskUsage.shouldGenerateMaskLayer) {
// Only have basic-shape clip-path effect. Fill clipped region by
// opaque white.
ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0));
ctx.Fill();
@@ -821,34 +843,33 @@ nsSVGIntegrationUtils::PaintMask(const P
}
}
// Paint mask onto ctx.
if (maskUsage.shouldGenerateMaskLayer) {
matSR.Restore();
matSR.SetContext(&ctx);
- SetupContextMatrix(frame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
result = PaintMaskSurface(aParams, maskTarget,
shouldPushOpacity ? 1.0 : maskUsage.opacity,
firstFrame->StyleContext(), maskFrames,
- ctx.CurrentMatrix(), offsetToUserSpace);
+ ctx.CurrentMatrix(),
+ offsets.offsetToUserSpace);
if (result != DrawResult::SUCCESS) {
return result;
}
}
// Paint clip-path onto ctx.
if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) {
matSR.Restore();
matSR.SetContext(&ctx);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ MoveContextOriginToUserSpace(firstFrame, aParams);
Matrix clipMaskTransform;
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
RefPtr<SourceSurface> maskSurface =
maskUsage.shouldGenerateMaskLayer ? maskTarget->Snapshot() : nullptr;
result =
clipPathFrame->PaintClipMask(ctx, frame, cssPxToDevPxMatrix,
@@ -903,19 +924,16 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
- nsPoint offsetToBoundingBox;
- nsPoint offsetToUserSpace;
-
bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
maskUsage.shouldGenerateClipMaskLayer ||
maskUsage.shouldGenerateMaskLayer);
bool shouldPushMask = false;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (shouldGenerateMask) {
@@ -926,22 +944,21 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
bool opacityApplied = false;
if (maskUsage.shouldGenerateMaskLayer) {
matSR.SetContext(&context);
// For css-mask, we want to generate a mask for each continuation frame,
// so we setup context matrix by the position of the current frame,
// instead of the first continuation frame.
- SetupContextMatrix(frame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
MaskPaintResult paintResult =
CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
firstFrame->StyleContext(),
- maskFrames, offsetToUserSpace);
+ maskFrames, offsets.offsetToUserSpace);
if (paintResult.transparentBlackMask) {
return paintResult.result;
}
result &= paintResult.result;
maskSurface = paintResult.maskSurface;
if (maskSurface) {
@@ -951,18 +968,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
opacityApplied = paintResult.opacityApplied;
}
}
if (maskUsage.shouldGenerateClipMaskLayer) {
matSR.Restore();
matSR.SetContext(&context);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ MoveContextOriginToUserSpace(firstFrame, aParams);
Matrix clipMaskTransform;
DrawResult clipMaskResult;
RefPtr<SourceSurface> clipMaskSurface;
Tie(clipMaskResult, clipMaskSurface) =
clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
&clipMaskTransform, maskSurface,
maskTransform);
@@ -979,18 +995,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
}
// opacity != 1.0f.
if (!maskUsage.shouldGenerateClipMaskLayer &&
!maskUsage.shouldGenerateMaskLayer) {
MOZ_ASSERT(maskUsage.opacity != 1.0f);
matSR.SetContext(&context);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ MoveContextOriginToUserSpace(firstFrame, aParams);
shouldPushMask = true;
}
if (shouldPushMask) {
if (aParams.layerManager->GetRoot()->GetContentFlags() &
Layer::CONTENT_COMPONENT_ALPHA) {
context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
opacityApplied
@@ -1006,18 +1021,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
* we can just do normal painting and get it clipped appropriately.
*/
if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
gfxContextMatrixAutoSaveRestore matSR(&context);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ MoveContextOriginToUserSpace(firstFrame, aParams);
MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
!maskUsage.shouldApplyBasicShape);
if (maskUsage.shouldApplyClipPath) {
clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
} else {
nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
}
@@ -1093,32 +1107,29 @@ nsSVGIntegrationUtils::PaintFilter(const
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
if (effectProperties.HasInvalidFilter()) {
return DrawResult::NOT_READY;
}
gfxContext& context = aParams.ctx;
- nsPoint offsetToBoundingBox;
- nsPoint offsetToUserSpace;
gfxContextAutoSaveRestore autoSR(&context);
- SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
- offsetToUserSpace);
+ EffectOffsets offsets = MoveContextOriginToUserSpace(firstFrame, aParams);
if (opacity != 1.0f) {
context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
nullptr, Matrix());
}
/* Paint the child and apply filters */
RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
- offsetToUserSpace);
- nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
+ offsets.offsetToUserSpace);
+ nsRegion dirtyRegion = aParams.dirtyRect - offsets.offsetToBoundingBox;
gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
DrawResult result =
nsFilterInstance::PaintFilteredFrame(frame, context.GetDrawTarget(),
tm, &callback, &dirtyRegion);
if (opacity != 1.0f) {
context.PopGroupAndBlend();
}