Bug 1301245 - Part 3. Impelment EffectProperties::GetMaskSource.
MozReview-Commit-ID: 3W1n5nj1J2b
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8363,19 +8363,19 @@ ComputeMaskGeometry(PaintFramesParams& a
// make sure all applicable ones are set again.
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aParams.frame);
const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
- nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
-
- if (maskFrames.Length() == 0) {
+ MaskSourceArray sources = effectProperties.GetMaskSource();
+
+ if (sources.Length() == 0) {
return;
}
gfxContext& ctx = aParams.ctx;
nsIFrame* frame = aParams.frame;
nsPoint offsetToUserSpace = ComputeOffsetToUserSpace(aParams);
gfxPoint devPixelOffsetToUserSpace =
@@ -8385,25 +8385,25 @@ ComputeMaskGeometry(PaintFramesParams& a
gfxContextMatrixAutoSaveRestore matSR(&ctx);
ctx.SetMatrix(ctx.CurrentMatrix().PreTranslate(devPixelOffsetToUserSpace));
// Convert boaderArea and dirtyRect to user space.
int32_t appUnitsPerDevPixel = frame->PresContext()->AppUnitsPerDevPixel();
nsRect userSpaceBorderArea = aParams.borderArea - offsetToUserSpace;
nsRect userSpaceDirtyRect = aParams.dirtyRect - offsetToUserSpace;
- // Union all mask layer rectangles in user space.
+ // Union all mask layer bounds in user space.
gfxRect maskInUserSpace;
- for (size_t i = 0; i < maskFrames.Length() ; i++) {
- nsSVGMaskFrame* maskFrame = maskFrames[i];
+ for (size_t i = 0; i < sources.Length() ; i++) {
+ MaskSource source = sources[i];
gfxRect currentMaskSurfaceRect;
- if (maskFrame) {
- currentMaskSurfaceRect = maskFrame->GetMaskArea(aParams.frame);
- } else {
+ if (source.first() == MaskSourceType::SVGMask) {
+ currentMaskSurfaceRect = source.second()->GetMaskArea(aParams.frame);
+ } else if (source.first() == MaskSourceType::ImageMask) {
nsCSSRendering::ImageLayerClipState clipState;
nsCSSRendering::GetImageLayerClip(svgReset->mMask.mLayers[i],
frame,
*frame->StyleBorder(),
userSpaceBorderArea,
userSpaceDirtyRect,
false, /* aWillPaintBorder */
appUnitsPerDevPixel,
@@ -8688,18 +8688,18 @@ nsDisplayMask::PrintEffects(nsACString&
if (style->HasClipPath() && !clipPathFrame) {
if (!first) {
aTo += ", ";
}
aTo += "clip(basic-shape)";
first = false;
}
- nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
- if (!masks.IsEmpty() && masks[0]) {
+ MaskSourceArray masks = effectProperties.GetMaskSource();
+ if (!masks.IsEmpty() && masks[0].first() == MaskSourceType::SVGMask) {
if (!first) {
aTo += ", ";
}
aTo += "mask";
}
aTo += ")";
}
#endif
--- a/layout/svg/nsSVGEffects.cpp
+++ b/layout/svg/nsSVGEffects.cpp
@@ -651,34 +651,49 @@ nsSVGEffects::EffectProperties::GetClipP
return nullptr;
nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>(
mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, nullptr));
return frame;
}
-nsTArray<nsSVGMaskFrame *>
-nsSVGEffects::EffectProperties::GetMaskFrames()
+MaskSourceArray
+nsSVGEffects::EffectProperties::GetMaskSource()
{
- nsTArray<nsSVGMaskFrame *> result;
+ MaskSourceArray result;
if (!mMask)
return result;
- bool ok = true;
const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
for (size_t i = 0; i < props.Length(); i++) {
+ nsSVGMaskFrame* maskFrame = nullptr;
+ MaskSourceType type = MaskSourceType::Uncertain;
+
if (props[i]) {
- nsSVGMaskFrame* maskFrame = static_cast<nsSVGMaskFrame*>(
+ bool ok = true;
+ maskFrame = static_cast<nsSVGMaskFrame*>(
props[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok));
MOZ_ASSERT(!maskFrame || ok);
- result.AppendElement(maskFrame);
+ MOZ_ASSERT_IF(!ok, !maskFrame);
+
+ if (!ok) {
+ type = MaskSourceType::ImageMask;
+ } else {
+ type = maskFrame ? MaskSourceType::SVGMask
+ : MaskSourceType::Uncertain;
+ }
} else {
- result.AppendElement(nullptr);
+ type = MaskSourceType::ImageMask;
}
+
+ MOZ_ASSERT_IF(type == MaskSourceType::SVGMask, maskFrame);
+ MOZ_ASSERT_IF(type == MaskSourceType::ImageMask ||
+ type == MaskSourceType::Uncertain, !maskFrame);
+ result.AppendElement(MakePair(type, maskFrame));
}
return result;
}
bool
nsSVGEffects::EffectProperties::HasNoOrValidEffects()
{
--- a/layout/svg/nsSVGEffects.h
+++ b/layout/svg/nsSVGEffects.h
@@ -418,16 +418,28 @@ public:
* to them.
*/
void RemoveAll();
private:
nsTHashtable<nsPtrHashKey<nsSVGRenderingObserver> > mObservers;
};
+enum class MaskSourceType: uint8_t {
+ Uncertain = 0, // Initial state, we don't know the tyep of this css
+ // mask yet. Keep stay in this state before we are certain
+ // the type of this mask source.
+ SVGMask = 1, // This css mask is an SVG mask.
+ ImageMask = 2 // This css mask is a mask other than an SVG mask.
+ // It can be an image mask or a gradient mask, for example.
+};
+
+typedef mozilla::Pair<MaskSourceType, nsSVGMaskFrame*> MaskSource;
+typedef nsTArray<MaskSource> MaskSourceArray;
+
class nsSVGEffects
{
public:
typedef mozilla::dom::Element Element;
typedef nsInterfaceHashtable<nsURIHashKey, nsIMutationObserver>
URIObserverHashtable;
using PaintingPropertyDescriptor =
@@ -474,19 +486,19 @@ public:
nsSVGPaintingProperty* mClipPath;
/**
* @return the clip-path frame, or null if there is no clip-path frame
*/
nsSVGClipPathFrame* GetClipPathFrame();
/**
- * @return an array which contains all SVG mask frames.
+ * @return an array which contains all mask sources.
*/
- nsTArray<nsSVGMaskFrame*> GetMaskFrames();
+ MaskSourceArray GetMaskSource();
/*
* @return true if all effects we have are valid or we have no effect
* at all.
*/
bool HasNoOrValidEffects();
/*
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -409,80 +409,90 @@ private:
typedef nsSVGIntegrationUtils::PaintFramesParams PaintFramesParams;
/**
* Paint css-positioned-mask onto a given target(aMaskDT).
*/
static void
PaintMaskSurface(const PaintFramesParams& aParams,
DrawTarget* aMaskDT, float aOpacity, nsStyleContext* aSC,
- const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
+ const MaskSourceArray& aSources,
const gfxMatrix& aMaskSurfaceMatrix,
const nsPoint& aOffsetToUserSpace)
{
- MOZ_ASSERT(aMaskFrames.Length() > 0);
+ MOZ_ASSERT(aSources.Length() > 0);
MOZ_ASSERT(aMaskDT->GetFormat() == SurfaceFormat::A8);
- MOZ_ASSERT(aOpacity == 1.0 || aMaskFrames.Length() == 1);
+ MOZ_ASSERT(aOpacity == 1.0 || aSources.Length() == 1);
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
gfxMatrix cssPxToDevPxMatrix =
nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
nsPresContext* presContext = aParams.frame->PresContext();
gfxPoint devPixelOffsetToUserSpace =
nsLayoutUtils::PointToGfxPoint(aOffsetToUserSpace,
presContext->AppUnitsPerDevPixel());
RefPtr<gfxContext> maskContext = gfxContext::CreateOrNull(aMaskDT);
MOZ_ASSERT(maskContext);
maskContext->SetMatrix(aMaskSurfaceMatrix);
// Multiple SVG masks interleave with image mask. Paint each layer onto
// aMaskDT one at a time.
- for (int i = aMaskFrames.Length() - 1; i >= 0 ; i--) {
- nsSVGMaskFrame *maskFrame = aMaskFrames[i];
- CompositionOp compositionOp = (i == int(aMaskFrames.Length() - 1))
+ for (int i = aSources.Length() - 1; i >= 0 ; i--) {
+ MaskSource source = aSources[i];
+ CompositionOp compositionOp = (i == int(aSources.Length() - 1))
? CompositionOp::OP_OVER
: nsCSSRendering::GetGFXCompositeMode(svgReset->mMask.mLayers[i].mComposite);
- // maskFrame != nullptr means we get a SVG mask.
- // maskFrame == nullptr means we get an image mask.
- if (maskFrame) {
+ if (source.first() == MaskSourceType::SVGMask) {
+ MOZ_ASSERT(source.second());
+
Matrix svgMaskMatrix;
nsSVGMaskFrame::MaskParams params(maskContext, aParams.frame,
cssPxToDevPxMatrix,
aOpacity, &svgMaskMatrix,
svgReset->mMask.mLayers[i].mMaskMode,
aParams.imgParams);
- RefPtr<SourceSurface> svgMask = maskFrame->GetMaskForMaskedFrame(params);
+ RefPtr<SourceSurface> svgMask =
+ source.second()->GetMaskForMaskedFrame(params);
if (svgMask) {
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(ThebesMatrix(svgMaskMatrix));
aMaskDT->MaskSurface(ColorPattern(Color(0.0, 0.0, 0.0, 1.0)), svgMask,
Point(0, 0),
DrawOptions(1.0, compositionOp));
}
- } else {
+ } else if (source.first() == MaskSourceType::ImageMask) {
+ MOZ_ASSERT(!source.second());
+
gfxContextMatrixAutoSaveRestore matRestore(maskContext);
maskContext->Multiply(gfxMatrix::Translation(-devPixelOffsetToUserSpace));
nsCSSRendering::PaintBGParams params =
nsCSSRendering::PaintBGParams::ForSingleLayer(*presContext,
aParams.dirtyRect,
aParams.borderArea,
aParams.frame,
aParams.builder->GetBackgroundPaintFlags() |
nsCSSRendering::PAINTBG_MASK_IMAGE,
i, compositionOp,
aOpacity);
aParams.imgParams.result &=
nsCSSRendering::PaintStyleImageLayerWithSC(params, *maskContext, aSC,
*aParams.frame->StyleBorder());
+ } else {
+ MOZ_ASSERT(source.first() == MaskSourceType::Uncertain);
+ MOZ_ASSERT(!source.second());
+
+ // We do not know the type of this mask yet. No bother to paint anything,
+ // return DrawResult::NOT_READY directly.
+ aParams.imgParams.result &= DrawResult::NOT_READY;
}
}
}
struct MaskPaintResult {
RefPtr<SourceSurface> maskSurface;
Matrix maskTransform;
bool transparentBlackMask;
@@ -492,36 +502,37 @@ struct MaskPaintResult {
: transparentBlackMask(false)
, opacityApplied(false)
{}
};
static MaskPaintResult
CreateAndPaintMaskSurface(const PaintFramesParams& aParams,
float aOpacity, nsStyleContext* aSC,
- const nsTArray<nsSVGMaskFrame*>& aMaskFrames,
+ const MaskSourceArray& aSources,
const nsPoint& aOffsetToUserSpace)
{
const nsStyleSVGReset *svgReset = aSC->StyleSVGReset();
- MOZ_ASSERT(aMaskFrames.Length() > 0);
+ MOZ_ASSERT(aSources.Length() > 0);
MaskPaintResult paintResult;
gfxContext& ctx = aParams.ctx;
// Optimization for single SVG mask.
- if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
+ if ((aSources.Length() == 1) &&
+ aSources[0].first() == MaskSourceType::SVGMask) {
gfxMatrix cssPxToDevPxMatrix =
nsSVGUtils::GetCSSPxToDevPxMatrix(aParams.frame);
paintResult.opacityApplied = true;
nsSVGMaskFrame::MaskParams params(&ctx, aParams.frame, cssPxToDevPxMatrix,
aOpacity, &paintResult.maskTransform,
svgReset->mMask.mLayers[0].mMaskMode,
aParams.imgParams);
paintResult.maskSurface =
- aMaskFrames[0]->GetMaskForMaskedFrame(params);
+ aSources[0].second()->GetMaskForMaskedFrame(params);
if (!paintResult.maskSurface) {
paintResult.transparentBlackMask = true;
}
return paintResult;
}
@@ -537,26 +548,26 @@ CreateAndPaintMaskSurface(const PaintFra
if (!maskDT || !maskDT->IsValid()) {
return paintResult;
}
// We can paint mask along with opacity only if
// 1. There is only one mask, or
// 2. No overlap among masks.
// Collision detect in #2 is not that trivial, we only accept #1 here.
- paintResult.opacityApplied = (aMaskFrames.Length() == 1);
+ paintResult.opacityApplied = (aSources.Length() == 1);
// Set context'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(-aParams.maskRect.TopLeft());
PaintMaskSurface(aParams, maskDT,
paintResult.opacityApplied ? aOpacity : 1.0,
- aSC, aMaskFrames, maskSurfaceMatrix,
+ aSC, aSources, maskSurfaceMatrix,
aOffsetToUserSpace);
if (aParams.imgParams.result != DrawResult::SUCCESS) {
// Now we know the status of mask resource since we used it while painting.
// According to the return value of PaintMaskSurface, we know whether mask
// resource is resolvable or not.
//
// For a HTML doc:
@@ -698,28 +709,27 @@ MoveContextOriginToUserSpace(nsIFrame* a
bool
nsSVGIntegrationUtils::IsMaskResourceReady(nsIFrame* aFrame)
{
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
- nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
+ MaskSourceArray sources = effectProperties.GetMaskSource();
const nsStyleSVGReset* svgReset = firstFrame->StyleSVGReset();
- for (uint32_t i = 0; i < maskFrames.Length(); i++) {
- // Refers to a valid SVG mask.
- if (maskFrames[i]) {
- continue;
- }
-
- // Refers to an external resource, which is not ready yet.
- if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
+ for (uint32_t i = 0; i < sources.Length(); i++) {
+ if (sources[i].first() == MaskSourceType::Uncertain) {
return false;
+ } else if (sources[i].first() == MaskSourceType::ImageMask) {
+ // Refers to an external resource, which is not ready yet.
+ if (!svgReset->mMask.mLayers[i].mImage.IsComplete()) {
+ return false;
+ }
}
}
// Either all mask resources are ready, or no mask resource is needed.
return true;
}
class AutoPopGroup
@@ -768,20 +778,20 @@ nsSVGIntegrationUtils::PaintMask(const P
//
// Create one extra draw target for drawing positioned mask, so that we do
// not have to copy the content of maskTarget before painting
// clip-path into it.
maskTarget = maskTarget->CreateSimilarDrawTarget(maskTarget->GetSize(),
SurfaceFormat::A8);
}
- nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
+ MaskSourceArray sources = effectProperties.GetMaskSource();
AutoPopGroup autoPop;
bool shouldPushOpacity = (maskUsage.opacity != 1.0) &&
- (maskFrames.Length() != 1);
+ (sources.Length() != 1);
if (shouldPushOpacity) {
ctx.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, maskUsage.opacity);
autoPop.SetContext(&ctx);
}
gfxContextMatrixAutoSaveRestore matSR;
// Paint clip-path-basic-shape onto ctx
@@ -806,17 +816,17 @@ nsSVGIntegrationUtils::PaintMask(const P
// Paint mask onto ctx.
if (maskUsage.shouldGenerateMaskLayer) {
matSR.Restore();
matSR.SetContext(&ctx);
EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
PaintMaskSurface(aParams, maskTarget,
shouldPushOpacity ? 1.0 : maskUsage.opacity,
- firstFrame->StyleContext(), maskFrames,
+ firstFrame->StyleContext(), sources,
ctx.CurrentMatrix(),
offsets.offsetToUserSpace);
}
// Paint clip-path onto ctx.
if (maskUsage.shouldGenerateClipMaskLayer || maskUsage.shouldApplyClipPath) {
matSR.Restore();
matSR.SetContext(&ctx);
@@ -875,17 +885,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
- nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
+ MaskSourceArray sources = effectProperties.GetMaskSource();
bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
maskUsage.shouldGenerateClipMaskLayer ||
maskUsage.shouldGenerateMaskLayer);
bool shouldPushMask = false;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
@@ -901,17 +911,17 @@ nsSVGIntegrationUtils::PaintMaskAndClipP
// For css-mask, we want to generate a mask for each continuation frame,
// so we setup context matrix by the position of the current frame,
// instead of the first continuation frame.
EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
MaskPaintResult paintResult =
CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
firstFrame->StyleContext(),
- maskFrames, offsets.offsetToUserSpace);
+ sources, offsets.offsetToUserSpace);
if (paintResult.transparentBlackMask) {
return;
}
maskSurface = paintResult.maskSurface;
if (maskSurface) {
shouldPushMask = true;
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -505,26 +505,27 @@ nsSVGUtils::DetermineMaskUsage(nsIFrame*
nsIFrame* firstFrame =
nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(firstFrame);
const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
- nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();
+ MaskSourceArray sources = effectProperties.GetMaskSource();
#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
- aUsage.shouldGenerateMaskLayer = (maskFrames.Length() > 0);
+ aUsage.shouldGenerateMaskLayer = (sources.Length() > 0);
#else
// Since we do not support image mask so far, we should treat any
// unresolvable mask as no mask. Otherwise, any object with a valid image
// mask, e.g. url("xxx.png"), will become invisible just because we can not
// handle image mask correctly. (See bug 1294171)
- aUsage.shouldGenerateMaskLayer = maskFrames.Length() == 1 && maskFrames[0];
+ aUsage.shouldGenerateMaskLayer =
+ sources.Length() == 1 && sources[0].second();
#endif
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
MOZ_ASSERT(!clipPathFrame ||
svgReset->mClipPath.GetType() == StyleShapeSourceType::URL);
switch (svgReset->mClipPath.GetType()) {
case StyleShapeSourceType::URL:
@@ -739,18 +740,19 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
nsSVGEffects::EffectProperties effectProperties =
nsSVGEffects::GetEffectProperties(aFrame);
if (effectProperties.HasInvalidEffects()) {
// Some resource is invalid. We shouldn't paint anything.
return;
}
nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();
- nsTArray<nsSVGMaskFrame*> masks = effectProperties.GetMaskFrames();
- nsSVGMaskFrame *maskFrame = masks.IsEmpty() ? nullptr : masks[0];
+ MaskSourceArray sources = effectProperties.GetMaskSource();
+ nsSVGMaskFrame *maskFrame = sources.IsEmpty() ? nullptr
+ : sources[0].second();
MixModeBlender blender(aFrame, &aContext);
gfxContext* target = blender.ShouldCreateDrawTargetForBlend()
? blender.CreateBlendTarget(aTransform) : &aContext;
if (!target) {
return;
}