From b360983fd33c1298acb434da1a387ca14588e13d Mon Sep 17 00:00:00 2001 From: Horia Iosif Olaru Date: Fri, 8 Nov 2013 10:08:03 -0500 Subject: [PATCH] Bug 841601 - Add background-blend-mode implementation. r=roc --- layout/base/nsCSSRendering.cpp | 36 ++++++++++++++++++++++++++++++++++ layout/base/nsCSSRendering.h | 6 +++++- layout/base/nsDisplayList.cpp | 27 +++++++++++++++++++++---- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 4fe63c1abc23..4cd365dd8f53 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -320,6 +320,30 @@ static nscolor MakeBevelColor(mozilla::css::Side whichSide, uint8_t style, nscolor aBackgroundColor, nscolor aBorderColor); +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; + } + + return gfxContext::OPERATOR_OVER; +} + static InlineBackgroundData* gInlineBGData = nullptr; // Initialize any static variables used by nsCSSRendering. @@ -2561,10 +2585,18 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nsBackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame, aFlags, aBorderArea, clipState.mBGClipArea, *bg, layer); if (!state.mFillArea.IsEmpty()) { + if (state.mCompositingOp != gfxContext::OPERATOR_OVER) { + NS_ASSERTION(ctx->CurrentOperator() == gfxContext::OPERATOR_OVER, + "It is assumed the initial operator is OPERATOR_OVER, when it is restored later"); + ctx->SetOperator(state.mCompositingOp); + } state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext, state.mDestArea, state.mFillArea, state.mAnchor + aBorderArea.TopLeft(), clipState.mDirtyRect); + if (state.mCompositingOp != gfxContext::OPERATOR_OVER) { + ctx->SetOperator(gfxContext::OPERATOR_OVER); + } } } } @@ -2856,6 +2888,7 @@ nsCSSRendering::PrepareBackgroundLayer(nsPresContext* aPresContext, * background-origin * background-size * background-break (-moz-background-inline-policy) + * background-blend-mode * * (background-color applies to the entire element and not to individual * layers, so it is irrelevant to this method.) @@ -2978,6 +3011,9 @@ nsCSSRendering::PrepareBackgroundLayer(nsPresContext* aPresContext, state.mFillArea.height = bgClipRect.height; } state.mFillArea.IntersectRect(state.mFillArea, bgClipRect); + + state.mCompositingOp = GetGFXBlendMode(aLayer.mBlendMode); + return state; } diff --git a/layout/base/nsCSSRendering.h b/layout/base/nsCSSRendering.h index 65b16ee0c122..e9c206b4f3ce 100644 --- a/layout/base/nsCSSRendering.h +++ b/layout/base/nsCSSRendering.h @@ -213,7 +213,7 @@ struct nsBackgroundLayerState { * @param aFlags some combination of nsCSSRendering::PAINTBG_* flags */ nsBackgroundLayerState(nsIFrame* aForFrame, const nsStyleImage* aImage, uint32_t aFlags) - : mImageRenderer(aForFrame, aImage, aFlags) {} + : mImageRenderer(aForFrame, aImage, aFlags), mCompositingOp(gfxContext::OPERATOR_OVER) {} /** * The nsImageRenderer that will be used to draw the background. @@ -237,6 +237,10 @@ struct nsBackgroundLayerState { * PrepareBackgroundLayer. */ nsPoint mAnchor; + /** + * The compositing operation that the image should use + */ + gfxContext::GraphicsOperator mCompositingOp; }; struct nsCSSRendering { diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 7391bc3c698c..5fbefcaaa480 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1739,11 +1739,15 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil drawBackgroundImage, drawBackgroundColor); } + // An auxiliary list is necessary in case we have background blending; if that + // is the case, background items need to be wrapped by a blend container to + // isolate blending to the background + nsDisplayList bgItemList; // Even if we don't actually have a background color to paint, we may still need // to create an item for hit testing. if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) || aBuilder->IsForEventDelivery()) { - aList->AppendNewToTop( + bgItemList.AppendNewToTop( new (aBuilder) nsDisplayBackgroundColor(aBuilder, aFrame, bg, drawBackgroundColor ? color : NS_RGBA(0, 0, 0, 0))); } @@ -1751,25 +1755,40 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil if (isThemed) { nsDisplayThemedBackground* bgItem = new (aBuilder) nsDisplayThemedBackground(aBuilder, aFrame); - aList->AppendNewToTop(bgItem); + bgItemList.AppendNewToTop(bgItem); + aList->AppendToTop(&bgItemList); return true; } if (!bg) { + aList->AppendToTop(&bgItemList); return false; } + bool needBlendContainer = false; + // Passing bg == nullptr in this macro will result in one iteration with // i = 0. NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) { if (bg->mLayers[i].mImage.IsEmpty()) { continue; } + + if (bg->mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { + needBlendContainer = true; + } + nsDisplayBackgroundImage* bgItem = new (aBuilder) nsDisplayBackgroundImage(aBuilder, aFrame, i, bg); - aList->AppendNewToTop(bgItem); + bgItemList.AppendNewToTop(bgItem); } + if (needBlendContainer) { + bgItemList.AppendNewToTop( + new (aBuilder) nsDisplayBlendContainer(aBuilder, aFrame, &bgItemList)); + } + + aList->AppendToTop(&bgItemList); return false; } @@ -2091,7 +2110,7 @@ nsDisplayBackgroundImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, if (mBackgroundStyle->mBackgroundInlinePolicy == NS_STYLE_BG_INLINE_POLICY_EACH_BOX || (!mFrame->GetPrevContinuation() && !mFrame->GetNextContinuation())) { const nsStyleBackground::Layer& layer = mBackgroundStyle->mLayers[mLayer]; - if (layer.mImage.IsOpaque()) { + if (layer.mImage.IsOpaque() && layer.mBlendMode == NS_STYLE_BLEND_NORMAL) { nsPresContext* presContext = mFrame->PresContext(); result = GetInsideClipRegion(this, presContext, layer.mClip, mBounds, aSnap); }