From e7ff7b3a5fcaa29f3b5e3543d737e7af1cd2aefd 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 3: Change IsOpaque to GetOpaqueRegion so we can get useful opaque regions for content that uses border-radius. r=tnikkel,sr=dbaron,a=blocking --- gfx/src/nsRegion.cpp | 4 +- layout/base/FrameLayerBuilder.cpp | 57 ++++++++---- layout/base/FrameLayerBuilder.h | 5 ++ layout/base/nsDisplayList.cpp | 124 +++++++++++++-------------- layout/base/nsDisplayList.h | 53 ++++++------ layout/base/nsLayoutDebugger.cpp | 6 +- layout/base/nsLayoutUtils.cpp | 29 +++++++ layout/base/nsLayoutUtils.h | 9 ++ layout/generic/nsCanvasFrame.h | 9 +- layout/generic/nsHTMLCanvasFrame.cpp | 10 ++- layout/generic/nsObjectFrame.cpp | 14 +-- layout/generic/nsObjectFrame.h | 4 +- layout/generic/nsVideoFrame.cpp | 4 +- layout/style/nsStyleStruct.h | 2 +- 14 files changed, 209 insertions(+), 121 deletions(-) diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index bc246e7cad2..4a2bc06d594 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -1486,8 +1486,10 @@ namespace { nsRect nsRegion::GetLargestRectangle () const { nsRect bestRect; - if (!mRectCount) + if (mRectCount <= 1) { + bestRect = mBoundRect; return bestRect; + } AxisPartition xaxis, yaxis; diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index bbbf957c2af..19bb1a3918f 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -186,7 +186,8 @@ protected: void Accumulate(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, const nsIntRect& aVisibleRect, - const nsIntRect& aDrawRect); + const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip); nsIFrame* GetActiveScrolledRoot() { return mActiveScrolledRoot; } /** @@ -307,6 +308,7 @@ protected: already_AddRefed FindThebesLayerFor(nsDisplayItem* aItem, const nsIntRect& aVisibleRect, const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip, nsIFrame* aActiveScrolledRoot); ThebesLayerData* GetTopThebesLayerData() { @@ -922,7 +924,8 @@ void ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, const nsIntRect& aVisibleRect, - const nsIntRect& aDrawRect) + const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip) { nscolor uniformColor; if (aItem->IsUniform(aBuilder, &uniformColor)) { @@ -947,18 +950,25 @@ ContainerState::ThebesLayerData::Accumulate(nsDisplayListBuilder* aBuilder, mDrawRegion.SimplifyOutward(4); PRBool forceTransparentSurface = PR_FALSE; - if (aItem->IsOpaque(aBuilder, &forceTransparentSurface)) { - // We don't use SimplifyInward here since it's not defined exactly - // what it will discard. For our purposes the most important case - // is a large opaque background at the bottom of z-order (e.g., - // a canvas background), so we need to make sure that the first rect - // we see doesn't get discarded. - nsIntRegion tmp; - tmp.Or(mOpaqueRegion, aDrawRect); - if (tmp.GetNumRects() <= 4) { - mOpaqueRegion = tmp; + nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &forceTransparentSurface); + if (!opaque.IsEmpty()) { + nsRegionRectIterator iter(opaque); + nscoord appUnitsPerDevPixel = AppUnitsPerDevPixel(aItem); + for (const nsRect* r = iter.Next(); r; r = iter.Next()) { + // We don't use SimplifyInward here since it's not defined exactly + // what it will discard. For our purposes the most important case + // is a large opaque background at the bottom of z-order (e.g., + // a canvas background), so we need to make sure that the first rect + // we see doesn't get discarded. + nsIntRect rect = aClip.ApproximateIntersect(*r).ToNearestPixels(appUnitsPerDevPixel); + nsIntRegion tmp; + tmp.Or(mOpaqueRegion, rect); + if (tmp.GetNumRects() <= 4) { + mOpaqueRegion = tmp; + } } - } else if (aItem->HasText()) { + } + if (aItem->HasText()) { if (!mOpaqueRegion.Contains(aVisibleRect)) { if (SuppressComponentAlpha(aBuilder, aItem)) { aItem->DisableComponentAlpha(); @@ -974,6 +984,7 @@ already_AddRefed ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, const nsIntRect& aVisibleRect, const nsIntRect& aDrawRect, + const FrameLayerBuilder::Clip& aClip, nsIFrame* aActiveScrolledRoot) { PRInt32 i; @@ -1028,7 +1039,7 @@ ContainerState::FindThebesLayerFor(nsDisplayItem* aItem, layer = thebesLayerData->mLayer; } - thebesLayerData->Accumulate(mBuilder, aItem, aVisibleRect, aDrawRect); + thebesLayerData->Accumulate(mBuilder, aItem, aVisibleRect, aDrawRect, aClip); return layer.forget(); } @@ -1175,7 +1186,7 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList, } nsRefPtr thebesLayer = - FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, + FindThebesLayerFor(item, itemVisibleRect, itemDrawRect, aClip, activeScrolledRoot); InvalidateForLayerChange(item, thebesLayer); @@ -1814,4 +1825,20 @@ FrameLayerBuilder::Clip::ApplyTo(gfxContext* aContext, } } +nsRect +FrameLayerBuilder::Clip::ApproximateIntersect(const nsRect& aRect) const +{ + nsRect r = aRect; + if (mHaveClipRect) { + r.IntersectRect(r, mClipRect); + } + for (PRUint32 i = 0, iEnd = mRoundedClipRects.Length(); + i < iEnd; ++i) { + const Clip::RoundedRect &rr = mRoundedClipRects[i]; + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(rr.mRect, rr.mRadii, r); + r = rgn.GetLargestRectangle(); + } + return r; +} + } // namespace mozilla diff --git a/layout/base/FrameLayerBuilder.h b/layout/base/FrameLayerBuilder.h index 5fc3b34f767..e956ad988ef 100644 --- a/layout/base/FrameLayerBuilder.h +++ b/layout/base/FrameLayerBuilder.h @@ -317,6 +317,11 @@ public: // or clearing of other clips must be done by the caller. void ApplyTo(gfxContext* aContext, nsPresContext* aPresContext); + // Return a rectangle contained in the intersection of aRect with this + // clip region. Tries to return the largest possible rectangle, but may + // not succeed. + nsRect ApproximateIntersect(const nsRect& aRect) const; + bool operator==(const Clip& aOther) const { return mHaveClipRect == aOther.mHaveClipRect && (!mHaveClipRect || mClipRect == aOther.mClipRect) && diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 00212a103e5..b208c05949e 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -174,6 +174,9 @@ void nsDisplayListBuilder::SubtractFromVisibleRegion(nsRegion* aVisibleRegion, const nsRegion& aRegion) { + if (aRegion.IsEmpty()) + return; + nsRegion tmp; tmp.Sub(*aVisibleRegion, aRegion); // Don't let *aVisibleRegion get too complex, but don't let it fluff out @@ -310,19 +313,19 @@ nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder, return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds()); } -static PRBool +static nsRegion TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder, PRBool* aTransparentBackground) { - if (aItem->IsOpaque(aBuilder, aTransparentBackground)) - return PR_TRUE; + nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, aTransparentBackground); if (aBuilder->IsForPluginGeometry()) { // Treat all chrome items as opaque nsIFrame* f = aItem->GetUnderlyingFrame(); - if (f && f->PresContext()->IsChrome()) - return PR_TRUE; + if (f && f->PresContext()->IsChrome()) { + opaque = aItem->GetBounds(aBuilder); + } } - return PR_FALSE; + return opaque; } PRBool @@ -362,10 +365,9 @@ nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder, if (item->ComputeVisibility(aBuilder, aVisibleRegion)) { anyVisible = PR_TRUE; PRBool transparentBackground = PR_FALSE; - if (TreatAsOpaque(item, aBuilder, &transparentBackground)) { - // Subtract opaque item from the visible region - aBuilder->SubtractFromVisibleRegion(aVisibleRegion, nsRegion(bounds)); - } + nsRegion opaque = TreatAsOpaque(item, aBuilder, &transparentBackground); + // Subtract opaque item from the visible region + aBuilder->SubtractFromVisibleRegion(aVisibleRegion, opaque); forceTransparentSurface = forceTransparentSurface || transparentBackground; } AppendToBottom(item); @@ -686,9 +688,8 @@ PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder, return PR_FALSE; PRBool forceTransparentBackground; - if (TreatAsOpaque(this, aBuilder, &forceTransparentBackground)) { - aVisibleRegion->Sub(*aVisibleRegion, bounds); - } + nsRegion opaque = TreatAsOpaque(this, aBuilder, &forceTransparentBackground); + aVisibleRegion->Sub(*aVisibleRegion, opaque); return PR_TRUE; } @@ -834,25 +835,8 @@ RoundedBorderIntersectsRect(nsIFrame* aFrame, static PRBool RoundedRectContainsRect(const nsRect& aRoundedRect, const nscoord aRadii[8], const nsRect& aContainedRect) { - // rectFullHeight and rectFullWidth together will approximately contain - // the total area of the frame minus the rounded corners. - nsRect rectFullHeight = aRoundedRect; - nscoord xDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); - rectFullHeight.x += xDiff; - rectFullHeight.width -= NS_MAX(aRadii[NS_CORNER_TOP_RIGHT_X], - aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; - if (rectFullHeight.Contains(aContainedRect)) - return PR_TRUE; - - nsRect rectFullWidth = aRoundedRect; - nscoord yDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); - rectFullWidth.y += yDiff; - rectFullWidth.height -= NS_MAX(aRadii[NS_CORNER_BOTTOM_LEFT_Y], - aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; - if (rectFullWidth.Contains(aContainedRect)) - return PR_TRUE; - - return PR_FALSE; + nsRegion rgn = nsLayoutUtils::RoundedRectIntersectRect(aRoundedRect, aRadii, aContainedRect); + return rgn.Contains(aContainedRect); } void @@ -888,9 +872,10 @@ nsDisplayBackground::ComputeVisibility(nsDisplayListBuilder* aBuilder, nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC); } -PRBool -nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion +nsDisplayBackground::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { + nsRegion result; if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } @@ -901,27 +886,34 @@ nsDisplayBackground::IsOpaque(nsDisplayListBuilder* aBuilder, *aForceTransparentSurface = disp->mAppearance == NS_THEME_WIN_BORDERLESS_GLASS || disp->mAppearance == NS_THEME_WIN_GLASS; } - return mThemeTransparency == nsITheme::eOpaque; + if (mThemeTransparency == nsITheme::eOpaque) { + result = GetBounds(aBuilder); + } + return result; } nsStyleContext* bgSC; if (!nsCSSRendering::FindBackground(mFrame->PresContext(), mFrame, &bgSC)) - return PR_FALSE; + return result; const nsStyleBackground* bg = bgSC->GetStyleBackground(); - const nsStyleBackground::Layer& bottomLayer = bg->BottomLayer(); // bottom layer's clip is used for the color if (bottomLayer.mClip != NS_STYLE_BG_CLIP_BORDER || nsLayoutUtils::HasNonZeroCorner(mFrame->GetStyleBorder()->mBorderRadius)) - return PR_FALSE; + return result; if (NS_GET_A(bg->mBackgroundColor) == 255 && - !nsCSSRendering::IsCanvasFrame(mFrame)) - return PR_TRUE; + !nsCSSRendering::IsCanvasFrame(mFrame)) { + result = GetBounds(aBuilder); + return result; + } - return bottomLayer.mRepeat == NS_STYLE_BG_REPEAT_XY && - bottomLayer.mImage.IsOpaque(); + if (bottomLayer.mRepeat == NS_STYLE_BG_REPEAT_XY && + bottomLayer.mImage.IsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } PRBool @@ -1283,13 +1275,17 @@ nsDisplayWrapList::ComputeVisibility(nsDisplayListBuilder* aBuilder, mVisibleRect); } -PRBool -nsDisplayWrapList::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion +nsDisplayWrapList::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return mList.IsOpaque(); + nsRegion result; + if (mList.IsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } PRBool nsDisplayWrapList::IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { @@ -1427,14 +1423,14 @@ nsDisplayOpacity::~nsDisplayOpacity() { } #endif -PRBool nsDisplayOpacity::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) { +nsRegion nsDisplayOpacity::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } // We are never opaque, if our opacity was < 1 then we wouldn't have // been created. - return PR_FALSE; + return nsRegion(); } // nsDisplayOpacity uses layers for rendering @@ -1601,13 +1597,14 @@ nsDisplayClipRoundedRect::~nsDisplayClipRoundedRect() } #endif -PRBool nsDisplayClipRoundedRect::IsOpaque(nsDisplayListBuilder* aBuilder, +nsRegion +nsDisplayClipRoundedRect::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } void @@ -2021,8 +2018,8 @@ nsRect nsDisplayTransform::GetBounds(nsDisplayListBuilder *aBuilder) * mStoredList.GetVisibleRect().Contains(untransformedVisible), then it * certainly contains the actual (non-axis-aligned) untransformed rect. */ -PRBool nsDisplayTransform::IsOpaque(nsDisplayListBuilder *aBuilder, - PRBool* aForceTransparentSurface) +nsRegion nsDisplayTransform::GetOpaqueRegion(nsDisplayListBuilder *aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; @@ -2030,14 +2027,17 @@ PRBool nsDisplayTransform::IsOpaque(nsDisplayListBuilder *aBuilder, const nsStyleDisplay* disp = mFrame->GetStyleDisplay(); nsRect untransformedVisible = UntransformRect(mVisibleRect, mFrame, ToReferenceFrame()); - return disp->mTransform.GetMainMatrixEntry(1) == 0.0f && - disp->mTransform.GetMainMatrixEntry(2) == 0.0f && - mStoredList.GetVisibleRect().Contains(untransformedVisible) && - mStoredList.IsOpaque(aBuilder); + nsRegion result; + if (disp->mTransform.GetMainMatrixEntry(1) == 0.0f && + disp->mTransform.GetMainMatrixEntry(2) == 0.0f && + mStoredList.GetOpaqueRegion(aBuilder).Contains(untransformedVisible)) { + result = mVisibleRect; + } + return result; } /* The transform is uniform if it fills the entire bounding rect and the - * wrapped list is uniform. See IsOpaque for discussion of why this + * wrapped list is uniform. See GetOpaqueRegion for discussion of why this * works. */ PRBool nsDisplayTransform::IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor) @@ -2161,13 +2161,13 @@ nsDisplaySVGEffects::~nsDisplaySVGEffects() } #endif -PRBool nsDisplaySVGEffects::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) +nsRegion nsDisplaySVGEffects::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } void diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index bdb833ab739..cc90cdca85f 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -559,16 +559,16 @@ public: return nsRect(ToReferenceFrame(), GetUnderlyingFrame()->GetSize()); } /** - * @return PR_TRUE if the item is definitely opaque --- i.e., paints - * every pixel within its bounds opaquely + * @return a region of the item that is opaque --- every pixel painted + * with an opaque color. */ - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return PR_FALSE; + return nsRegion(); } /** * If this returns true, then aColor is set to the uniform color @@ -641,11 +641,10 @@ public: * On entry, aVisibleRegion contains the region (relative to ReferenceFrame()) * which may be visible. If the display item opaquely covers an area, it * can remove that area from aVisibleRegion before returning. - * nsDisplayList::ComputeVisibility automatically subtracts the bounds - * of items that return true from IsOpaque(), and automatically - * removes items whose bounds do not intersect the visible area, - * so implementations of nsDisplayItem::ComputeVisibility do not - * need to do these things. + * nsDisplayList::ComputeVisibility automatically subtracts the region + * returned by GetOpaqueRegion, and automatically removes items whose bounds + * do not intersect the visible area, so implementations of + * nsDisplayItem::ComputeVisibility do not need to do these things. * nsDisplayList::ComputeVisibility will already have set mVisibleRect on * this item to the intersection of *aVisibleRegion and this item's bounds. * We rely on that, so this should only be called by @@ -1360,12 +1359,16 @@ public: virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { return mBounds; } - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aOutTransparentBackground = nsnull) { + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aOutTransparentBackground = nsnull) { if (aOutTransparentBackground) { *aOutTransparentBackground = PR_FALSE; } - return (NS_GET_A(mColor) == 255); + nsRegion result; + if (NS_GET_A(mColor) == 255) { + result = GetBounds(aBuilder); + } + return result; } virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) @@ -1400,8 +1403,8 @@ public: HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor); @@ -1536,8 +1539,8 @@ public: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor); virtual PRBool IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame); @@ -1620,8 +1623,8 @@ public: virtual ~nsDisplayOpacity(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual already_AddRefed BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, @@ -1718,8 +1721,8 @@ public: virtual ~nsDisplayClipRoundedRect(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, @@ -1788,8 +1791,8 @@ public: virtual ~nsDisplaySVGEffects(); #endif - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { @@ -1847,8 +1850,8 @@ public: virtual void HitTest(nsDisplayListBuilder *aBuilder, const nsRect& aRect, HitTestState *aState, nsTArray *aOutFrames); virtual nsRect GetBounds(nsDisplayListBuilder *aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder *aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder *aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual PRBool IsUniform(nsDisplayListBuilder *aBuilder, nscolor* aColor); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager); diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 037974b7558..5939b07377d 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -178,11 +178,15 @@ PrintDisplayListTo(nsDisplayListBuilder* aBuilder, const nsDisplayList& aList, nscolor color; nsRect vis = i->GetVisibleRect(); nsDisplayList* list = i->GetList(); + nsRegion opaque; + if (!list || list->DidComputeVisibility()) { + opaque = i->GetOpaqueRegion(aBuilder); + } fprintf(aOutput, "%s %p(%s) (%d,%d,%d,%d)(%d,%d,%d,%d)%s%s\n", i->Name(), (void*)f, NS_ConvertUTF16toUTF8(fName).get(), rect.x, rect.y, rect.width, rect.height, vis.x, vis.y, vis.width, vis.height, - ((!list || list->DidComputeVisibility()) && i->IsOpaque(aBuilder)) ? " opaque" : "", + opaque.IsEmpty() ? "" : " opaque", i->IsUniform(aBuilder, &color) ? " uniform" : ""); if (list) { PrintDisplayListTo(aBuilder, *list, aIndent + 4, aOutput); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 94e41b8a2c9..cd1069ab183 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -923,6 +923,35 @@ nsLayoutUtils::RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor) nscoord(scaledRect.size.width), nscoord(scaledRect.size.height)); } + +nsRegion +nsLayoutUtils::RoundedRectIntersectRect(const nsRect& aRoundedRect, + const nscoord aRadii[8], + const nsRect& aContainedRect) +{ + // rectFullHeight and rectFullWidth together will approximately contain + // the total area of the frame minus the rounded corners. + nsRect rectFullHeight = aRoundedRect; + nscoord xDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_X], aRadii[NS_CORNER_BOTTOM_LEFT_X]); + rectFullHeight.x += xDiff; + rectFullHeight.width -= NS_MAX(aRadii[NS_CORNER_TOP_RIGHT_X], + aRadii[NS_CORNER_BOTTOM_RIGHT_X]) + xDiff; + nsRect r1; + r1.IntersectRect(rectFullHeight, aContainedRect); + + nsRect rectFullWidth = aRoundedRect; + nscoord yDiff = NS_MAX(aRadii[NS_CORNER_TOP_LEFT_Y], aRadii[NS_CORNER_TOP_RIGHT_Y]); + rectFullWidth.y += yDiff; + rectFullWidth.height -= NS_MAX(aRadii[NS_CORNER_BOTTOM_LEFT_Y], + aRadii[NS_CORNER_BOTTOM_RIGHT_Y]) + yDiff; + nsRect r2; + r2.IntersectRect(rectFullWidth, aContainedRect); + + nsRegion result; + result.Or(r1, r2); + return result; +} + nsRect nsLayoutUtils::MatrixTransformRect(const nsRect &aBounds, const gfxMatrix &aMatrix, float aFactor) diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 893766d8124..f10f1c7627a 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -512,6 +512,15 @@ public: */ static nsRect RoundGfxRectToAppRect(const gfxRect &aRect, float aFactor); + /** + * Returns a subrectangle of aContainedRect that is entirely inside the rounded + * rect. Complex cases are handled conservatively by returning a smaller + * rect than necessary. + */ + static nsRegion RoundedRectIntersectRect(const nsRect& aRoundedRect, + const nscoord aRadii[8], + const nsRect& aContainedRect); + enum { PAINT_IN_TRANSFORM = 0x01, PAINT_SYNC_DECODE_IMAGES = 0x02, diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index 12f55cc16ff..2674a76b0d0 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -177,14 +177,15 @@ public: return NS_GET_A(mExtraBackgroundColor) > 0 || nsDisplayBackground::ComputeVisibility(aBuilder, aVisibleRegion); } - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } - return NS_GET_A(mExtraBackgroundColor) == 255 || - nsDisplayBackground::IsOpaque(aBuilder); + if (NS_GET_A(mExtraBackgroundColor) == 255) + return nsRegion(GetBounds(aBuilder)); + return nsDisplayBackground::GetOpaqueRegion(aBuilder); } virtual PRBool IsUniform(nsDisplayListBuilder* aBuilder, nscolor* aColor) { diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp index cb4ae47213b..0fdba89b610 100644 --- a/layout/generic/nsHTMLCanvasFrame.cpp +++ b/layout/generic/nsHTMLCanvasFrame.cpp @@ -77,14 +77,18 @@ public: NS_DISPLAY_DECL_NAME("nsDisplayCanvas", TYPE_CANVAS) - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull) { + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } nsIFrame* f = GetUnderlyingFrame(); nsHTMLCanvasElement *canvas = CanvasElementFromContent(f->GetContent()); - return canvas->GetIsOpaque(); + nsRegion result; + if (canvas->GetIsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) { diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 519afafeb47..01fbdcbae26 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1290,13 +1290,14 @@ nsDisplayPlugin::ComputeVisibility(nsDisplayListBuilder* aBuilder, return nsDisplayItem::ComputeVisibility(aBuilder, aVisibleRegion); } -PRBool -nsDisplayPlugin::IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface) +nsRegion +nsDisplayPlugin::GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface) { if (aForceTransparentSurface) { *aForceTransparentSurface = PR_FALSE; } + nsRegion result; nsObjectFrame* f = static_cast(mFrame); if (!aBuilder->IsForPluginGeometry()) { nsIWidget* widget = f->GetWidget(); @@ -1310,11 +1311,14 @@ nsDisplayPlugin::IsOpaque(nsDisplayListBuilder* aBuilder, // Something has clipped us unexpectedly. Perhaps there is a translucent // chrome element overlaying us that forced us to be clipped away. Treat // us as non-opaque since we may have holes. - return PR_FALSE; + return result; } } } - return f->IsOpaque(); + if (f->IsOpaque()) { + result = GetBounds(aBuilder); + } + return result; } void diff --git a/layout/generic/nsObjectFrame.h b/layout/generic/nsObjectFrame.h index cc84f43cf8c..bf92e1c7053 100644 --- a/layout/generic/nsObjectFrame.h +++ b/layout/generic/nsObjectFrame.h @@ -316,8 +316,8 @@ public: #endif virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder); - virtual PRBool IsOpaque(nsDisplayListBuilder* aBuilder, - PRBool* aForceTransparentSurface = nsnull); + virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, + PRBool* aForceTransparentSurface = nsnull); virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx); virtual PRBool ComputeVisibility(nsDisplayListBuilder* aBuilder, diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index f9d50484b38..062bfb320b3 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -375,10 +375,10 @@ public: NS_DISPLAY_DECL_NAME("Video", TYPE_VIDEO) - // It would be great if we could override IsOpaque to return false here, + // It would be great if we could override GetOpaqueRegion to return nonempty here, // but it's probably not safe to do so in general. Video frames are // updated asynchronously from decoder threads, and it's possible that - // we might have an opaque video frame when IsOpaque is called, but + // we might have an opaque video frame when GetOpaqueRegion is called, but // when we come to paint, the video frame is transparent or has gone // away completely (e.g. because of a decoder error). The problem would // be especially acute if we have off-main-thread rendering. diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 80868998cbc..a050c5e7c30 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -280,7 +280,7 @@ struct nsStyleImage { // rect is non-trivial since each side value can be specified with // percentage unit, which can not be evaluated until the source image size // is available. Therefore, we currently postpone the evaluation of crop - // rect until the actual rendering time --- alternatively until IsOpaque() + // rect until the actual rendering time --- alternatively until GetOpaqueRegion() // is called. return mType == eStyleImageType_Null; }