Bug 1210560 - Part 3: Convert more complex SVG usecases to PushGroupForBlendBack. r=jwatt r=jrmuizel

This commit is contained in:
Bas Schouten 2015-11-11 16:15:39 +01:00
Родитель d4041dbed1
Коммит 36cd89f36b
5 изменённых файлов: 201 добавлений и 98 удалений

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

@ -444,8 +444,6 @@ public:
* group back to the current surface with some alpha applied will give
* the correct results and using an opaque pushed surface gives better
* quality and performance.
* This API really only makes sense if you do a PopGroupToSource and
* immediate Paint with OP_OVER.
*/
void PushGroupAndCopyBackground(gfxContentType content = gfxContentType::COLOR);
already_AddRefed<gfxPattern> PopGroup();

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

@ -95,7 +95,10 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext,
if (referencedClipIsTrivial) {
clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix);
} else {
aContext.PushGroup(gfxContentType::ALPHA);
Matrix maskTransform;
RefPtr<SourceSurface> mask = clipPathFrame->GetClipMask(aContext, aClippedFrame, aMatrix, &maskTransform);
aContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform);
}
}
@ -121,7 +124,10 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext,
if (isTrivial) {
clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix);
} else {
aContext.PushGroup(gfxContentType::ALPHA);
Matrix maskTransform;
RefPtr<SourceSurface> mask = clipPathFrame->GetClipMask(aContext, aClippedFrame, aMatrix, &maskTransform);
aContext.PushGroupForBlendBack(gfxContentType::ALPHA, 1.0, mask, maskTransform);
}
}
@ -138,17 +144,7 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext,
if (clipPathFrame) {
if (!isTrivial) {
aContext.PopGroupToSource();
aContext.PushGroup(gfxContentType::ALPHA);
clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix);
Matrix maskTransform;
RefPtr<SourceSurface> clipMaskSurface = aContext.PopGroupToSurface(&maskTransform);
if (clipMaskSurface) {
aContext.Mask(clipMaskSurface, maskTransform);
}
aContext.PopGroupAndBlend();
}
aContext.Restore();
}
@ -157,17 +153,7 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext,
if (clipPathFrame) {
if (!referencedClipIsTrivial) {
aContext.PopGroupToSource();
aContext.PushGroup(gfxContentType::ALPHA);
clipPathFrame->ApplyClipOrPaintClipMask(aContext, aClippedFrame, aMatrix);
Matrix maskTransform;
RefPtr<SourceSurface> clipMaskSurface = aContext.PopGroupToSurface(&maskTransform);
if (clipMaskSurface) {
aContext.Mask(clipMaskSurface, maskTransform);
}
aContext.PopGroupAndBlend();
}
aContext.Restore();
}
@ -175,6 +161,50 @@ nsSVGClipPathFrame::ApplyClipOrPaintClipMask(gfxContext& aContext,
return NS_OK;
}
already_AddRefed<SourceSurface>
nsSVGClipPathFrame::GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame,
const gfxMatrix& aMatrix, Matrix* aMaskTransform,
SourceSurface* aInputMask, const Matrix& aInputMaskTransform)
{
if (IsTrivial()) {
return nullptr;
}
IntRect intRect;
{
gfxContextMatrixAutoSaveRestore autoRestoreMatrix(&aReferenceContext);
aReferenceContext.SetMatrix(gfxMatrix());
gfxRect rect = aReferenceContext.GetClipExtents();
intRect = RoundedOut(ToRect(rect));
}
RefPtr<DrawTarget> maskDT = aReferenceContext.GetDrawTarget()->CreateSimilarDrawTarget(intRect.Size(), SurfaceFormat::A8);
gfxMatrix mat =
aReferenceContext.CurrentMatrix() * gfxMatrix::Translation(-intRect.TopLeft());
{
RefPtr<gfxContext> ctx = new gfxContext(maskDT);
ctx->SetMatrix(mat);
ApplyClipOrPaintClipMask(*ctx, aClippedFrame, aMatrix);
}
mat.Invert();
if (aInputMask) {
MOZ_ASSERT(!aInputMaskTransform.HasNonTranslation());
RefPtr<SourceSurface> currentMask = maskDT->Snapshot();
maskDT->SetTransform(Matrix());
maskDT->ClearRect(Rect(0, 0, intRect.width, intRect.height));
maskDT->MaskSurface(SurfacePattern(currentMask, ExtendMode::CLAMP), aInputMask,
Point(aInputMaskTransform._31 - intRect.x, aInputMaskTransform._32 - intRect.y));
}
*aMaskTransform = ToMatrix(mat);
return maskDT->Snapshot();
}
bool
nsSVGClipPathFrame::PointIsInsideClipPath(nsIFrame* aClippedFrame,
const gfxPoint &aPoint)

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

@ -43,9 +43,7 @@ public:
* calling this method simply pushes a clip path onto the DrawTarget. If the
* SVG clipPath is not simple then calling this method will paint the
* clipPath's contents (geometry being filled only, with opaque black) to the
* DrawTarget. In this latter case callers are expected to first push a
* group before calling this method, then pop the group after calling and use
* it as a mask to mask the clipped frame.
* DrawTarget.
*
* XXXjwatt Maybe split this into two methods.
*/
@ -53,6 +51,20 @@ public:
nsIFrame* aClippedFrame,
const gfxMatrix &aMatrix);
/**
* If the SVG clipPath is simple (as determined by the IsTrivial() method),
* calling this method simply returns null. If the SVG clipPath is not
* simple then calling this method will return a mask surface containing
* the clipped geometry. The reference context will be used to determine the
* backend for the SourceSurface as well as the size, which will be limited
* to the device clip extents on the context.
*/
already_AddRefed<mozilla::gfx::SourceSurface>
GetClipMask(gfxContext& aReferenceContext, nsIFrame* aClippedFrame,
const gfxMatrix& aMatrix, Matrix* aMaskTransform,
mozilla::gfx::SourceSurface* aInputMask = nullptr,
const mozilla::gfx::Matrix& aInputMaskTransform = mozilla::gfx::Matrix());
/**
* aPoint is expected to be in aClippedFrame's SVG user space.
*/

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

@ -511,18 +511,68 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext,
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);
bool complexEffects = false;
// These are used if we require a temporary surface for a custom blend mode.
RefPtr<gfxContext> target = &aContext;
IntPoint targetOffset;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
|| aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
complexEffects = true;
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface =
maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
aFrame, cssPxToDevPxMatrix, opacity, &maskTransform)
: nullptr;
if (maskFrame && !maskSurface) {
// Entire surface is clipped out.
return;
}
aContext.Save();
nsRect clipRect =
aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
aContext.Clip(NSRectToSnappedRect(clipRect,
aFrame->PresContext()->AppUnitsPerDevPixel(),
*drawTarget));
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
// Create a temporary context to draw to so we can blend it back with
// another operator.
gfxRect clipRect;
{
gfxContextMatrixAutoSaveRestore matRestore(&aContext);
aContext.SetMatrix(gfxMatrix());
clipRect = aContext.GetClipExtents();
}
IntRect drawRect = RoundedOut(ToRect(clipRect));
RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
target = new gfxContext(targetDT);
target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
targetOffset = drawRect.TopLeft();
}
if (clipPathFrame && !isTrivialClip) {
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, cssPxToDevPxMatrix,
&clippedMaskTransform, maskSurface, maskTransform);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
}
}
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
@ -540,11 +590,14 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext,
nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
nsFilterInstance::PaintFilteredFrame(aFrame, aContext, tm, &callback, &dirtyRegion);
nsFilterInstance::PaintFilteredFrame(aFrame, *target, tm, &callback, &dirtyRegion);
} else {
aContext.SetMatrix(matrixAutoSaveRestore.Matrix());
target->SetMatrix(matrixAutoSaveRestore.Matrix());
BasicLayerManager* basic = static_cast<BasicLayerManager*>(aLayerManager);
RefPtr<gfxContext> oldCtx = basic->GetTarget();
basic->SetTarget(target);
aLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
basic->SetTarget(oldCtx);
}
if (clipPathFrame && isTrivialClip) {
@ -556,39 +609,19 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext,
return;
}
aContext.PopGroupToSource();
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface =
maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
aFrame, cssPxToDevPxMatrix,
opacity, &maskTransform)
: nullptr;
if (clipPathFrame && !isTrivialClip) {
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
nsresult rv = clipPathFrame->ApplyClipOrPaintClipMask(aContext, aFrame, cssPxToDevPxMatrix);
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface = aContext.PopGroupToSurface(&clippedMaskTransform);
if (NS_SUCCEEDED(rv) && clipMaskSurface) {
// Still more set after clipping, so clip to another surface
if (maskSurface || opacity != 1.0f) {
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
aContext.Mask(clipMaskSurface, clippedMaskTransform);
aContext.PopGroupToSource();
} else {
aContext.Mask(clipMaskSurface, clippedMaskTransform);
}
}
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
target->PopGroupAndBlend();
}
if (maskSurface) {
aContext.Mask(maskSurface, maskTransform);
} else if (opacity != 1.0f ||
aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
aContext.Paint(opacity);
if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
target = nullptr;
RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();
aContext.SetMatrix(gfxMatrix()); // This will be restored right after.
RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
aContext.SetPattern(pattern);
aContext.Paint();
}
aContext.Restore();

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

@ -585,11 +585,27 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
return;
}
// These are used if we require a temporary surface for a custom blend mode.
RefPtr<gfxContext> target = &aContext;
IntPoint targetOffset;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
|| aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
complexEffects = true;
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface =
maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
aFrame, aTransform, opacity, &maskTransform)
: nullptr;
if (maskFrame && !maskSurface) {
// Entire surface is clipped out.
return;
}
aContext.Save();
if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
// aFrame has a valid visual overflow rect, so clip to it before calling
@ -607,7 +623,40 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
aFrame->PresContext()->AppUnitsPerDevPixel(),
*drawTarget));
}
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
// Create a temporary context to draw to so we can blend it back with
// another operator.
gfxRect clipRect;
{
gfxContextMatrixAutoSaveRestore matRestore(&aContext);
aContext.SetMatrix(gfxMatrix());
clipRect = aContext.GetClipExtents();
}
IntRect drawRect = RoundedOut(ToRect(clipRect));
RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
target = new gfxContext(targetDT);
target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
targetOffset = drawRect.TopLeft();
}
if (clipPathFrame && !isTrivialClip) {
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
&clippedMaskTransform, maskSurface, maskTransform);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
}
}
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
@ -641,10 +690,10 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
dirtyRegion = &tmpDirtyRegion;
}
SVGPaintCallback paintCallback;
nsFilterInstance::PaintFilteredFrame(aFrame, aContext, aTransform,
nsFilterInstance::PaintFilteredFrame(aFrame, *target, aTransform,
&paintCallback, dirtyRegion);
} else {
svgChildFrame->PaintSVG(aContext, aTransform, aDirtyRect);
svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
}
if (clipPathFrame && isTrivialClip) {
@ -654,39 +703,20 @@ nsSVGUtils::PaintFrameWithEffects(nsIFrame *aFrame,
/* No more effects, we're done. */
if (!complexEffects)
return;
aContext.PopGroupToSource();
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface =
maskFrame ? maskFrame->GetMaskForMaskedFrame(&aContext,
aFrame, aTransform, opacity, &maskTransform)
: nullptr;
if (clipPathFrame && !isTrivialClip) {
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
nsresult rv = clipPathFrame->ApplyClipOrPaintClipMask(aContext, aFrame, aTransform);
Matrix clippedMaskTransform;
RefPtr<SourceSurface> clipMaskSurface = aContext.PopGroupToSurface(&clippedMaskTransform);
if (NS_SUCCEEDED(rv) && clipMaskSurface) {
// Still more set after clipping, so clip to another surface
if (maskSurface || opacity != 1.0f) {
aContext.PushGroup(gfxContentType::COLOR_ALPHA);
aContext.Mask(clipMaskSurface, clippedMaskTransform);
aContext.PopGroupToSource();
} else {
aContext.Mask(clipMaskSurface, clippedMaskTransform);
}
}
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)) {
target->PopGroupAndBlend();
}
if (maskSurface) {
aContext.Mask(maskSurface, maskTransform);
} else if (opacity != 1.0f ||
aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
aContext.Paint(opacity);
if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
target = nullptr;
RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();
aContext.SetMatrix(gfxMatrix()); // This will be restored right after.
RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
aContext.SetPattern(pattern);
aContext.Paint();
}
aContext.Restore();