Bug 1304011 - Part 3. Add clip function in SetupContextMatrix, and give a frame to this function as the source of offset computing. r=mstange

MozReview-Commit-ID: 2ry6c1cECvb

--HG--
extra : rebase_source : 54fc13e3948179ec543b0c7f4c8cf342108992fd
This commit is contained in:
cku 2016-09-21 16:49:27 +08:00
Родитель 22ac005328
Коммит 07e41f0497
1 изменённых файлов: 74 добавлений и 58 удалений

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

@ -180,11 +180,6 @@ GetOffsetToBoundingBox(nsIFrame* aFrame)
// 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
@ -657,24 +652,32 @@ ValidateSVGFrame(const PaintFramesParams& aParams, bool aHasSVGLayout,
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
@ -688,23 +691,32 @@ SetupContextMatrix(const PaintFramesParams& aParams,
// 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>
@ -783,11 +795,6 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
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. */
@ -831,12 +838,23 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
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;
}
@ -847,12 +865,9 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
/* 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) {
@ -864,7 +879,6 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
if (shouldGenerateMaskLayer && !maskSurface) {
// Entire surface is clipped out.
context.Restore();
return result;
}
@ -880,6 +894,8 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
maskTransform = clippedMaskTransform;
}
}
// Pop the clip pushed in SetupContextMatrix.
context.PopClip();
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
@ -887,12 +903,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
/* 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 */
@ -910,7 +931,6 @@ nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
if (shouldGenerateMask) {
target->PopGroupAndBlend();
context.Restore();
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
@ -941,14 +961,6 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
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 =
@ -960,24 +972,28 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
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());
}
@ -992,7 +1008,6 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
if (opacity != 1.0f) {
target->PopGroupAndBlend();
context.Restore();
}
if (aParams.frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
@ -1000,6 +1015,7 @@ nsSVGIntegrationUtils::PaintFilter(const PaintFramesParams& aParams)
BlendToTarget(aParams, target, targetOffset);
}
context.Restore();
return result;
}