--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -175,21 +175,16 @@ GetOffsetToBoundingBox(nsIFrame* aFrame)
{
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
// Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
// covered region relative to the nsSVGOuterSVGFrame, which is absolutely
// not what we want. SVG frames are always in user space, so they have
// no offset adjustment to make.
return nsPoint();
}
- // We could allow aFrame to be any continuation, but since that would require
- // a GetPrevContinuation() virtual call and conditional returns, and since
- // all our current consumers always pass in the first continuation, we don't
- // currently bother.
- NS_ASSERTION(!aFrame->GetPrevContinuation(), "Not first continuation");
// The GetAllInFlowRectsUnion() call gets the union of the frame border-box
// rects over all continuations, relative to the origin (top-left of the
// border box) of its second argument (here, aFrame, the first continuation).
return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft();
}
/* static */ nsSize
@@ -652,64 +647,81 @@ ValidateSVGFrame(const PaintFramesParams
*aResult = DrawResult::SUCCESS;
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.
+ * @param aClipCtx indicate whether clip aParams.ctx by visual overflow rect of
+ * aFrame or not.
+ */
static void
-SetupContextMatrix(const PaintFramesParams& aParams,
- nsPoint& aOffsetToBoundingBox,
- nsPoint& aToUserSpace,
- nsPoint& aOffsetToUserSpace)
+SetupContextMatrix(nsIFrame* aFrame, const PaintFramesParams& aParams,
+ nsPoint& aOffsetToBoundingBox, nsPoint& aOffsetToUserSpace,
+ bool aClipCtx)
{
- nsIFrame* frame = aParams.frame;
- nsIFrame* firstFrame =
- nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
-
- nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
- aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset;
- if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
+ aOffsetToBoundingBox = aParams.builder->ToReferenceFrame(aFrame) -
+ 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(
- frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
- frame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.x),
+ aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(aOffsetToBoundingBox.y));
}
// After applying only "aOffsetToBoundingBox", aCtx 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 aCtx 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(frame);
- aToUserSpace =
+ gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
+ nsPoint toUserSpace =
nsPoint(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
- aOffsetToUserSpace = aOffsetToBoundingBox - aToUserSpace;
+ aOffsetToUserSpace = aOffsetToBoundingBox - toUserSpace;
#ifdef DEBUG
- bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
+ bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
NS_ASSERTION(hasSVGLayout || aOffsetToBoundingBox == aOffsetToUserSpace,
"For non-SVG frames there shouldn't be any additional offset");
#endif
gfxPoint devPixelOffsetToUserSpace =
nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
- frame->PresContext()->AppUnitsPerDevPixel());
- aParams.ctx.SetMatrix(aParams.ctx.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
+ aFrame->PresContext()->AppUnitsPerDevPixel());
+ gfxContext& context = aParams.ctx;
+ context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
+
+ if (aClipCtx) {
+ nsRect clipRect =
+ aParams.frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
+ context.Clip(NSRectToSnappedRect(clipRect,
+ aFrame->PresContext()->AppUnitsPerDevPixel(),
+ *context.GetDrawTarget()));
+ }
}
static already_AddRefed<gfxContext>
CreateBlendTarget(const PaintFramesParams& aParams, IntPoint& aTargetOffset)
{
MOZ_ASSERT(aParams.frame->StyleEffects()->mMixBlendMode !=
NS_STYLE_BLEND_NORMAL);
@@ -778,21 +790,16 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
float opacity = ComputeOpacity(aParams);
if (opacity == 0.0f) {
return DrawResult::SUCCESS;
}
gfxContext& context = aParams.ctx;
gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
- nsPoint offsetToBoundingBox;
- nsPoint toUserSpace;
- nsPoint offsetToUserSpace;
- SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace,
- offsetToUserSpace);
/* Properties are added lazily and may have been removed by a restyle,
so make sure all applicable ones are set again. */
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
@@ -826,78 +833,92 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
#endif
bool shouldGenerateClipMaskLayer = clipPathFrame && !isTrivialClip;
bool shouldApplyClipPath = clipPathFrame && isTrivialClip;
bool shouldApplyBasicShape = !clipPathFrame && svgReset->HasClipPath();
MOZ_ASSERT_IF(shouldGenerateClipMaskLayer,
!shouldApplyClipPath && !shouldApplyBasicShape);
+ nsPoint offsetToBoundingBox;
+ nsPoint offsetToUserSpace;
+
// These are used if we require a temporary surface for a custom blend mode.
+ // Clip the source context first, so that we can generate a smaller temporary
+ // surface. (Since we will clip this context in SetupContextMatrix, a pair
+ // of save/restore is needed.)
+ context.Save();
+ SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
+ offsetToUserSpace, true);
IntPoint targetOffset;
RefPtr<gfxContext> target =
(aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
? RefPtr<gfxContext>(&aParams.ctx).forget()
: CreateBlendTarget(aParams, targetOffset);
+ context.Restore();
+
if (!target) {
return DrawResult::TEMPORARY_ERROR;
}
bool shouldGenerateMask = (opacity != 1.0f || shouldGenerateClipMaskLayer ||
shouldGenerateMaskLayer);
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (shouldGenerateMask) {
- context.Save();
- nsRect clipRect =
- frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
- context.Clip(NSRectToSnappedRect(clipRect,
- frame->PresContext()->AppUnitsPerDevPixel(),
- *context.GetDrawTarget()));
+ gfxContextMatrixAutoSaveRestore save(&context);
+ SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
+ offsetToUserSpace, true);
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface;
if (shouldGenerateMaskLayer) {
result = GenerateMaskSurface(aParams, opacity,
firstFrame->StyleContext(),
maskFrames, offsetToUserSpace,
maskTransform, maskSurface);
}
if (shouldGenerateMaskLayer && !maskSurface) {
// Entire surface is clipped out.
- context.Restore();
return result;
}
if (shouldGenerateClipMaskLayer) {
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface =
clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
&clippedMaskTransform, maskSurface,
maskTransform, &result);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
}
}
+ // Pop the clip pushed in SetupContextMatrix.
+ context.PopClip();
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
/* 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 (shouldApplyClipPath) {
+ if (shouldApplyClipPath || shouldApplyBasicShape) {
context.Save();
- clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
- } else if (shouldApplyBasicShape) {
- context.Save();
- nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
+ SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
+ offsetToUserSpace, false);
+
+ MOZ_ASSERT(!shouldApplyClipPath || !shouldApplyBasicShape);
+ if (shouldApplyClipPath) {
+ clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
+ } else {
+ nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
+ }
}
/* Paint the child */
target->SetMatrix(matrixAutoSaveRestore.Matrix());
BasicLayerManager* basic = static_cast<BasicLayerManager*>(aParams.layerManager);
RefPtr<gfxContext> oldCtx = basic->GetTarget();
basic->SetTarget(target);
aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
@@ -905,17 +926,16 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
basic->SetTarget(oldCtx);
if (shouldApplyClipPath || shouldApplyBasicShape) {
context.Restore();
}
if (shouldGenerateMask) {
target->PopGroupAndBlend();
- context.Restore();
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
MOZ_ASSERT(target != &aParams.ctx);
BlendToTarget(aParams, target, targetOffset);
}
return result;
@@ -936,75 +956,71 @@ nsSVGIntegrationUtils::PaintFilter(const
return result;
}
float opacity = ComputeOpacity(aParams);
if (opacity == 0.0f) {
return DrawResult::SUCCESS;
}
- gfxContext& context = aParams.ctx;
- gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);
- nsPoint offsetToBoundingBox;
- nsPoint toUserSpace;
- nsPoint offsetToUserSpace;
- SetupContextMatrix(aParams, offsetToBoundingBox, toUserSpace,
- offsetToUserSpace);
-
/* Properties are added lazily and may have been removed by a restyle,
so make sure all applicable ones are set again. */
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
if (!effectProperties.HasValidFilter()) {
return DrawResult::NOT_READY;
}
+ gfxContext& context = aParams.ctx;
+ nsPoint offsetToBoundingBox;
+ nsPoint offsetToUserSpace;
+
// These are used if we require a temporary surface for a custom blend mode.
+ // Clip the source context first, so that we can generate a smaller temporary
+ // surface. (Since we will clip this context in SetupContextMatrix, a pair
+ // of save/restore is needed.)
+ context.Save();
+ SetupContextMatrix(firstFrame, aParams, offsetToBoundingBox,
+ offsetToUserSpace, true);
IntPoint targetOffset;
RefPtr<gfxContext> target =
(aParams.frame->StyleEffects()->mMixBlendMode == NS_STYLE_BLEND_NORMAL)
? RefPtr<gfxContext>(&aParams.ctx).forget()
: CreateBlendTarget(aParams, targetOffset);
if (!target) {
+ context.Restore();
return DrawResult::TEMPORARY_ERROR;
}
if (opacity != 1.0f) {
- context.Save();
- nsRect clipRect =
- frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
- context.Clip(NSRectToSnappedRect(clipRect,
- frame->PresContext()->AppUnitsPerDevPixel(),
- *context.GetDrawTarget()));
-
target->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;
gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(),
tm, &callback, &dirtyRegion);
if (opacity != 1.0f) {
target->PopGroupAndBlend();
- context.Restore();
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
MOZ_ASSERT(target != &aParams.ctx);
BlendToTarget(aParams, target, targetOffset);
}
+ context.Restore();
return result;
}
gfxMatrix
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
{
int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
float devPxPerCSSPx =