From b81ded822534acecffc97a317c5ec8e81c61a922 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Mon, 3 Jan 2011 14:48:09 +1300 Subject: [PATCH] Bug 602757. Part 2: Detect display items over the transparent part of a window, and disable usage of component alpha (i.e., subpixel antialiasing) for those items. r=tnikkel,a=blocking --- layout/base/FrameLayerBuilder.cpp | 29 +++++++++++++++++++++++++- layout/base/nsDisplayList.cpp | 1 + layout/base/nsDisplayList.h | 19 +++++++++++++++++ layout/base/nsLayoutUtils.cpp | 9 ++++---- layout/generic/nsTextFrameThebes.cpp | 14 ++++++++++--- layout/xul/base/src/nsTextBoxFrame.cpp | 10 ++++++++- 6 files changed, 73 insertions(+), 9 deletions(-) diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index e85d9f92bf5b..bbbf957c2af4 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -895,6 +895,29 @@ ContainerState::PopThebesLayerData() mThebesLayerDataStack.RemoveElementAt(lastIndex); } +static PRBool +SuppressComponentAlpha(nsDisplayListBuilder* aBuilder, + nsDisplayItem* aItem) +{ + const nsRegion* windowTransparentRegion = aBuilder->GetFinalTransparentRegion(); + if (!windowTransparentRegion || windowTransparentRegion->IsEmpty()) + return PR_FALSE; + + // Suppress component alpha for items in the toplevel window that are over + // the window translucent area + nsIFrame* f = aItem->GetUnderlyingFrame(); + nsIFrame* ref = aBuilder->ReferenceFrame(); + if (f->PresContext() != ref->PresContext()) + return PR_FALSE; + + for (nsIFrame* t = f; t; t = t->GetParent()) { + if (t->IsTransformed()) + return PR_FALSE; + } + + return windowTransparentRegion->Intersects(aItem->GetBounds(aBuilder)); +} + void ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, @@ -937,7 +960,11 @@ ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, } } else if (aItem->HasText()) { if (!mOpaqueRegion.Contains(aVisibleRect)) { - mNeedComponentAlpha = PR_TRUE; + if (SuppressComponentAlpha(aBuilder, aItem)) { + aItem->DisableComponentAlpha(); + } else { + mNeedComponentAlpha = PR_TRUE; + } } } mForceTransparentSurface = mForceTransparentSurface || forceTransparentSurface; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 2c3ba8175d1d..00212a103e54 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -73,6 +73,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame, : mReferenceFrame(aReferenceFrame), mIgnoreScrollFrame(nsnull), mCurrentTableItem(nsnull), + mFinalTransparentRegion(nsnull), mMode(aMode), mBuildCaret(aBuildCaret), mIgnoreSuppression(PR_FALSE), diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index e736f5790d66..bdb833ab7395 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -338,6 +338,19 @@ public: */ FrameLayerBuilder* LayerBuilder() { return &mLayerBuilder; } + /** + * Get the area of the final transparent region. + */ + const nsRegion* GetFinalTransparentRegion() { return mFinalTransparentRegion; } + /** + * Record the area of the final transparent region after all visibility + * calculations were performed. + */ + void SetFinalTransparentRegion(const nsRegion& aFinalTransparentRegion) + { + mFinalTransparentRegion = &aFinalTransparentRegion; + } + /** * Returns true if we need to descend into this frame when building * the display list, even though it doesn't intersect the dirty @@ -422,6 +435,7 @@ private: nsAutoTArray mPresShellStates; nsAutoTArray mFramesMarkedForDisplay; nsDisplayTableItem* mCurrentTableItem; + const nsRegion* mFinalTransparentRegion; Mode mMode; PRPackedBool mBuildCaret; PRPackedBool mIgnoreSuppression; @@ -702,6 +716,11 @@ public: */ virtual PRBool HasText() { return PR_FALSE; } + /** + * Disable usage of component alpha. Currently only relevant for items that have text. + */ + virtual void DisableComponentAlpha() {} + protected: friend class nsDisplayList; diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 33f658f20195..94e41b8a2c9a 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1409,11 +1409,8 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra PRUint32 flags = nsDisplayList::PAINT_DEFAULT; if (aFlags & PAINT_WIDGET_LAYERS) { flags |= nsDisplayList::PAINT_USE_WIDGET_LAYERS; - nsIWidget *widget = aFrame->GetNearestWidget(); - PRInt32 pixelRatio = presContext->AppUnitsPerDevPixel(); - nsIntRegion visibleWindowRegion(visibleRegion.ToOutsidePixels(pixelRatio)); - nsIntRegion dirtyWindowRegion(aDirtyRegion.ToOutsidePixels(pixelRatio)); + nsIWidget *widget = aFrame->GetNearestWidget(); if (willFlushRetainedLayers) { // The caller wanted to paint from retained layers, but set up // the paint in such a way that we can't use them. We're going @@ -1426,6 +1423,10 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra } else if (widget && !(aFlags & PAINT_DOCUMENT_RELATIVE)) { // XXX we should simplify this API now that dirtyWindowRegion always // covers the entire window + PRInt32 pixelRatio = presContext->AppUnitsPerDevPixel(); + nsIntRegion visibleWindowRegion(visibleRegion.ToOutsidePixels(pixelRatio)); + nsIntRegion dirtyWindowRegion(aDirtyRegion.ToOutsidePixels(pixelRatio)); + builder.SetFinalTransparentRegion(visibleRegion); widget->UpdatePossiblyTransparentRegion(dirtyWindowRegion, visibleWindowRegion); } } diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 57349f9c1b9b..d1250fa20f1e 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -4039,7 +4039,8 @@ nsTextFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) class nsDisplayText : public nsDisplayItem { public: nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame) : - nsDisplayItem(aBuilder, aFrame) { + nsDisplayItem(aBuilder, aFrame), + mDisableSubpixelAA(PR_FALSE) { MOZ_COUNT_CTOR(nsDisplayText); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -4062,6 +4063,10 @@ public: NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT) virtual PRBool HasText() { return PR_TRUE; } + + virtual void DisableComponentAlpha() { mDisableSubpixelAA = PR_TRUE; } + + PRPackedBool mDisableSubpixelAA; }; void @@ -4073,8 +4078,11 @@ nsDisplayText::Paint(nsDisplayListBuilder* aBuilder, nsRect extraVisible = mVisibleRect; nscoord appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel(); extraVisible.Inflate(appUnitsPerDevPixel, appUnitsPerDevPixel); - static_cast(mFrame)-> - PaintText(aCtx, ToReferenceFrame(), extraVisible); + nsTextFrame* f = static_cast(mFrame); + + gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(), + mDisableSubpixelAA); + f->PaintText(aCtx, ToReferenceFrame(), extraVisible); } NS_IMETHODIMP diff --git a/layout/xul/base/src/nsTextBoxFrame.cpp b/layout/xul/base/src/nsTextBoxFrame.cpp index a9f4df1c6c5c..852954a4e5e0 100644 --- a/layout/xul/base/src/nsTextBoxFrame.cpp +++ b/layout/xul/base/src/nsTextBoxFrame.cpp @@ -333,7 +333,9 @@ class nsDisplayXULTextBox : public nsDisplayItem { public: nsDisplayXULTextBox(nsDisplayListBuilder* aBuilder, nsTextBoxFrame* aFrame) : - nsDisplayItem(aBuilder, aFrame) { + nsDisplayItem(aBuilder, aFrame), + mDisableSubpixelAA(PR_FALSE) + { MOZ_COUNT_CTOR(nsDisplayXULTextBox); } #ifdef NS_BUILD_REFCNT_LOGGING @@ -348,12 +350,18 @@ public: NS_DISPLAY_DECL_NAME("XULTextBox", TYPE_XUL_TEXT_BOX) virtual PRBool HasText() { return PR_TRUE; } + + virtual void DisableComponentAlpha() { mDisableSubpixelAA = PR_TRUE; } + + PRPackedBool mDisableSubpixelAA; }; void nsDisplayXULTextBox::Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx) { + gfxContextAutoDisableSubpixelAntialiasing disable(aCtx->ThebesContext(), + mDisableSubpixelAA); static_cast(mFrame)-> PaintTitle(*aCtx, mVisibleRect, ToReferenceFrame()); }