Bug 902525 - part 3: Create a layer for content that stores the blend mode. r=roc

This commit is contained in:
Rik Cabanier 2013-09-15 16:24:16 -04:00
parent b58954693a
commit 9f1afe3eea
7 changed files with 141 additions and 4 deletions

View File

@ -30,6 +30,7 @@ DECLARE_DISPLAY_ITEM_TYPE(FRAMESET_BLANK)
DECLARE_DISPLAY_ITEM_TYPE(HEADER_FOOTER)
DECLARE_DISPLAY_ITEM_TYPE(IMAGE)
DECLARE_DISPLAY_ITEM_TYPE(LIST_FOCUS)
DECLARE_DISPLAY_ITEM_TYPE(MIX_BLEND_MODE)
DECLARE_DISPLAY_ITEM_TYPE(OPACITY)
DECLARE_DISPLAY_ITEM_TYPE(OPTION_EVENT_GRABBER)
DECLARE_DISPLAY_ITEM_TYPE(OUTLINE)

View File

@ -3118,6 +3118,98 @@ bool nsDisplayOpacity::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* a
return true;
}
nsDisplayMixBlendMode::nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
uint32_t aFlags)
: nsDisplayWrapList(aBuilder, aFrame, aList) {
MOZ_COUNT_CTOR(nsDisplayMixBlendMode);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsDisplayMixBlendMode::~nsDisplayMixBlendMode() {
MOZ_COUNT_DTOR(nsDisplayMixBlendMode);
}
#endif
nsRegion nsDisplayMixBlendMode::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
bool* aSnap) {
*aSnap = false;
// We are never considered opaque
return nsRegion();
}
static gfxContext::GraphicsOperator GetGFXBlendMode(uint8_t mBlendMode) {
switch (mBlendMode) {
case NS_STYLE_BLEND_NORMAL: return gfxContext::OPERATOR_OVER;
case NS_STYLE_BLEND_MULTIPLY: return gfxContext::OPERATOR_MULTIPLY;
case NS_STYLE_BLEND_SCREEN: return gfxContext::OPERATOR_SCREEN;
case NS_STYLE_BLEND_OVERLAY: return gfxContext::OPERATOR_OVERLAY;
case NS_STYLE_BLEND_DARKEN: return gfxContext::OPERATOR_DARKEN;
case NS_STYLE_BLEND_LIGHTEN: return gfxContext::OPERATOR_LIGHTEN;
case NS_STYLE_BLEND_COLOR_DODGE: return gfxContext::OPERATOR_COLOR_DODGE;
case NS_STYLE_BLEND_COLOR_BURN: return gfxContext::OPERATOR_COLOR_BURN;
case NS_STYLE_BLEND_HARD_LIGHT: return gfxContext::OPERATOR_HARD_LIGHT;
case NS_STYLE_BLEND_SOFT_LIGHT: return gfxContext::OPERATOR_SOFT_LIGHT;
case NS_STYLE_BLEND_DIFFERENCE: return gfxContext::OPERATOR_DIFFERENCE;
case NS_STYLE_BLEND_EXCLUSION: return gfxContext::OPERATOR_EXCLUSION;
case NS_STYLE_BLEND_HUE: return gfxContext::OPERATOR_HUE;
case NS_STYLE_BLEND_SATURATION: return gfxContext::OPERATOR_SATURATION;
case NS_STYLE_BLEND_COLOR: return gfxContext::OPERATOR_COLOR;
case NS_STYLE_BLEND_LUMINOSITY: return gfxContext::OPERATOR_LUMINOSITY;
default: MOZ_ASSERT(false); return gfxContext::OPERATOR_OVER;
}
return gfxContext::OPERATOR_OVER;
}
// nsDisplayMixBlendMode uses layers for rendering
already_AddRefed<Layer>
nsDisplayMixBlendMode::BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) {
nsRefPtr<Layer> container = aManager->GetLayerBuilder()->
BuildContainerLayerFor(aBuilder, aManager, mFrame, this, mList,
aContainerParameters, nullptr);
if (!container) {
return nullptr;
}
container->SetMixBlendMode(GetGFXBlendMode(mFrame->StyleDisplay()->mMixBlendMode));
return container.forget();
}
bool nsDisplayMixBlendMode::ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
const nsRect& aAllowVisibleRegionExpansion) {
// Our children are need their backdrop so we should not allow them to subtract
// area from aVisibleRegion. We do need to find out what is visible under
// our children in the temporary compositing buffer, because if our children
// paint our entire bounds opaquely then we don't need an alpha channel in
// the temporary compositing buffer.
nsRect bounds = GetClippedBounds(aBuilder);
nsRegion visibleUnderChildren;
visibleUnderChildren.And(*aVisibleRegion, bounds);
nsRect allowExpansion = bounds.Intersect(aAllowVisibleRegionExpansion);
return
nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren,
allowExpansion);
}
bool nsDisplayMixBlendMode::TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) {
if (aItem->GetType() != TYPE_MIX_BLEND_MODE)
return false;
// items for the same content element should be merged into a single
// compositing group
// aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
if (aItem->Frame()->GetContent() != mFrame->GetContent())
return false;
if (aItem->GetClip() != GetClip())
return false;
MergeFromTrackingMergedFrames(static_cast<nsDisplayMixBlendMode*>(aItem));
return true;
}
nsDisplayBlendContainer::nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder,
nsIFrame* aFrame, nsDisplayList* aList,
uint32_t aFlags)

View File

@ -2507,6 +2507,38 @@ public:
bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
};
class nsDisplayMixBlendMode : public nsDisplayWrapList {
public:
nsDisplayMixBlendMode(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
nsDisplayList* aList, uint32_t aFlags = 0);
#ifdef NS_BUILD_REFCNT_LOGGING
virtual ~nsDisplayMixBlendMode();
#endif
nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap);
virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aContainerParameters) MOZ_OVERRIDE;
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion* aInvalidRegion) MOZ_OVERRIDE
{
// We don't need to compute an invalidation region since we have LayerTreeInvalidation
}
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,
const ContainerParameters& aParameters) MOZ_OVERRIDE
{
return mozilla::LAYER_INACTIVE;
}
virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
const nsRect& aAllowVisibleRegionExpansion) MOZ_OVERRIDE;
virtual bool TryMerge(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) MOZ_OVERRIDE;
NS_DISPLAY_DECL_NAME("MixBlendMode", TYPE_MIX_BLEND_MODE)
};
class nsDisplayBlendContainer : public nsDisplayWrapList {
public:
nsDisplayBlendContainer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,

View File

@ -1997,6 +1997,16 @@ nsIFrame::BuildDisplayListForStackingContext(nsDisplayListBuilder* aBuilder,
new (aBuilder) nsDisplayTransform(aBuilder, this, &resultList));
}
}
/* If there's blending, wrap up the list in a blend-mode item. Note
* that opacity can be applied before blending as the blend color is
* not affected by foreground opacity (only background alpha).
*/
if (useBlendMode && !resultList.IsEmpty()) {
resultList.AppendNewToTop(
new (aBuilder) nsDisplayMixBlendMode(aBuilder, this, &resultList));
}
if (aBuilder->ContainsBlendMode()) {
resultList.AppendNewToTop(

View File

@ -342,7 +342,7 @@ nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext,
opacity = StyleDisplay()->mOpacity;
}
if (opacity != 1.0f) {
if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
ctx->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
}
@ -396,7 +396,7 @@ nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext,
drawFlags);
}
if (opacity != 1.0f) {
if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
ctx->PopGroupToSource();
ctx->SetOperator(gfxContext::OPERATOR_OVER);
ctx->Paint(opacity);

View File

@ -494,7 +494,8 @@ nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
bool complexEffects = false;
/* 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)) {
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
|| aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
complexEffects = true;
gfx->Save();
aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +

View File

@ -846,7 +846,8 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
/* 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)) {
if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
|| aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
complexEffects = true;
gfx->Save();
if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {