зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1210560 - Part 3: Convert more complex SVG usecases to PushGroupForBlendBack. r=jwatt r=jrmuizel
This commit is contained in:
Родитель
d4041dbed1
Коммит
36cd89f36b
|
@ -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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче