Bug 1228280 - Part 5. Paint multiple SVG and image masks;

MozReview-Commit-ID: FOgcnR9cYlH
This commit is contained in:
CJKu 2016-05-19 15:44:07 +08:00
parent 936ab8c243
commit 27969c0e19

View File

@ -413,100 +413,108 @@ static bool
HasMaskToDraw(const nsStyleSVGReset* aSVGReset,
nsSVGEffects::EffectProperties& aEffectProperties)
{
bool isOK = true;
// Keep moving forward even if svgMaskFrame is nullptr or isOK is false.
// This source is not a svg mask, but it still can be a correct mask image.
nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetFirstMaskFrame(&isOK);
nsTArray<nsSVGMaskFrame*> svgMaskFrames = aEffectProperties.GetMaskFrames();
for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) {
nsSVGMaskFrame *maskFrame = svgMaskFrames[i];
// hasMaskToDraw is true means we have at least one drawable mask resource.
// We need to apply mask only if hasMaskToDraw is true.
bool hasMaskToDraw = (svgMaskFrame != nullptr);
if (!hasMaskToDraw) {
NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, aSVGReset->mMask) {
if (!aSVGReset->mMask.mLayers[i].mImage.IsEmpty()) {
hasMaskToDraw = true;
break;
}
// We found a SVG mask or an image mask.
if (maskFrame || !aSVGReset->mMask.mLayers[i].mImage.IsEmpty()) {
return true;
}
}
return hasMaskToDraw;
return false;
}
static void
GenerateMaskSurface(const nsSVGIntegrationUtils::PaintFramesParams& aParams,
float aOpacity, nsStyleContext* aSC,
nsSVGEffects::EffectProperties& aEffectProperties,
gfxMatrix aOriginMatrix, Matrix& aOutMaskTransform,
const gfxPoint& aOffest, Matrix& aOutMaskTransform,
RefPtr<SourceSurface>& aOutMaskSurface)
{
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
MOZ_ASSERT(HasMaskToDraw(svgReset, aEffectProperties));
// Keep moving forward even if svgMaskFrame is nullptr.
// This source is not a svg mask, but it still can be a correct mask image.
nsSVGMaskFrame *svgMaskFrame = aEffectProperties.GetFirstMaskFrame();
nsTArray<nsSVGMaskFrame *> svgMaskFrames = aEffectProperties.GetMaskFrames();
MOZ_ASSERT(svgMaskFrames.Length() == svgReset->mMask.mImageCount);
gfxMatrix cssPxToDevPxMatrix =
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
gfxContext& ctx = aParams.ctx;
ctx.Save();
ctx.SetMatrix(gfxMatrix());
gfxRect clipExtents = ctx.GetClipExtents();
IntRect maskSurfaceRect = RoundedOut(ToRect(clipExtents));
ctx.Restore();
if (svgMaskFrame) {
gfxMatrix cssPxToDevPxMatrix =
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
// Mask composition result on CoreGraphic::A8 surface is not correct
// when mask-mode is not add(source over). Switch to skia when CG backend
// detected.
RefPtr<DrawTarget> maskDT =
(ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS)
? Factory::CreateDrawTarget(BackendType::SKIA, maskSurfaceRect.Size(),
SurfaceFormat::A8)
: ctx.GetDrawTarget()->CreateSimilarDrawTarget(maskSurfaceRect.Size(),
SurfaceFormat::A8);
RefPtr<gfxContext> maskContext = gfxContext::ForDrawTarget(maskDT);
// Generate aOutMaskSurface from a SVG mask.
aOutMaskSurface = svgMaskFrame->GetMaskForMaskedFrame(&ctx,
aParams.frame,
cssPxToDevPxMatrix,
aOpacity,
&aOutMaskTransform,
svgReset->mMask.mLayers[0].mMaskMode);
} else {
// Create maskSuface.
gfxRect clipRect = ctx.GetClipExtents();
{
gfxContextMatrixAutoSaveRestore matRestore(&ctx);
// Set ctx's matrix on maskContext, offset by the maskSurfaceRect's position.
// This makes sure that we combine the masks in device space.
gfxMatrix maskSurfaceMatrix =
ctx.CurrentMatrix() * gfxMatrix::Translation(-maskSurfaceRect.TopLeft());
maskContext->SetMatrix(maskSurfaceMatrix);
ctx.SetMatrix(gfxMatrix());
clipRect = ctx.GetClipExtents();
// Multiple SVG masks interleave with image mask. Paint each layer onto maskDT
// one at a time.
for (int i = svgMaskFrames.Length() - 1; i >= 0 ; i--) {
nsSVGMaskFrame *maskFrame = svgMaskFrames[i];
// maskFrame != nullptr means we get a SVG mask.
// maskFrame == nullptr means we get an image mask.
if (maskFrame) {
Matrix svgMaskMatrix;
RefPtr<SourceSurface> svgMask =
maskFrame->GetMaskForMaskedFrame(maskContext, aParams.frame,
cssPxToDevPxMatrix, aOpacity,
&svgMaskMatrix,
svgReset->mMask.mLayers[i].mMaskMode);
if (svgMask) {
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
Rect drawRect = IntRectToRect(IntRect(IntPoint(0, 0), svgMask->GetSize()));
maskDT->DrawSurface(svgMask, drawRect, drawRect);
}
} else {
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(gfxMatrix::Translation(-aOffest));
CompositionOp compositionOp =
nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
nsRenderingContext rc(maskContext);
nsCSSRendering::PaintBGParams params =
nsCSSRendering::PaintBGParams::ForSingleLayer(*aParams.frame->PresContext(),
rc, aParams.dirtyRect,
aParams.borderArea,
aParams.frame,
aParams.builder->GetBackgroundPaintFlags() |
nsCSSRendering::PAINTBG_MASK_IMAGE,
i, compositionOp);
// FIXME We should use the return value, see bug 1258510.
Unused << nsCSSRendering::PaintBackgroundWithSC(params, aSC,
*aParams.frame->StyleBorder());
}
IntRect drawRect = RoundedOut(ToRect(clipRect));
// Mask composition result on CoreGraphic::A8 surface is not correct
// when mask-mode is not add(source over). Switch to skia when CG backend
// detected.
RefPtr<DrawTarget> targetDT =
(ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS) ?
Factory::CreateDrawTarget(BackendType::SKIA, drawRect.Size(),
SurfaceFormat::A8) :
ctx.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(),
SurfaceFormat::A8);
if (!targetDT || !targetDT->IsValid()) {
return;
}
RefPtr<gfxContext> target = gfxContext::ForDrawTarget(targetDT);
MOZ_ASSERT(target); // alrady checked the draw target above
target->SetMatrix(aOriginMatrix * gfxMatrix::Translation(-drawRect.TopLeft()));
// Compose all mask-images onto aOutMaskSurface.
nsRenderingContext rc(target);
nsCSSRendering::PaintBGParams params =
nsCSSRendering::PaintBGParams::ForAllLayers(*aParams.frame->PresContext(),
rc, aParams.dirtyRect,
aParams.borderArea,
aParams.frame,
aParams.builder->GetBackgroundPaintFlags() |
nsCSSRendering::PAINTBG_MASK_IMAGE);
// FIXME We should use the return value, see bug 1258510.
Unused << nsCSSRendering::PaintBackgroundWithSC(params, aSC,
*aParams.frame->StyleBorder());
aOutMaskSurface = targetDT->Snapshot();
// Compute mask transform.
Matrix mat = ToMatrix(ctx.CurrentMatrix());
mat.Invert();
aOutMaskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat;
}
aOutMaskTransform = ToMatrix(maskSurfaceMatrix);
if (!aOutMaskTransform.Invert()) {
return;
}
aOutMaskSurface = maskDT->Snapshot();
}
void
@ -632,7 +640,7 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams)
if (hasMaskToDraw) {
GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(),
effectProperties, matrixAutoSaveRestore.Matrix(),
effectProperties, devPixelOffsetToUserSpace,
maskTransform, maskSurface);
}