diff --git a/gfx/public/nsRegion.h b/gfx/public/nsRegion.h index d72e2528605d..4cb43f1fc175 100644 --- a/gfx/public/nsRegion.h +++ b/gfx/public/nsRegion.h @@ -44,7 +44,7 @@ // Rectangles in this list do not overlap and are sorted by (y, x) coordinates. #include "nsRect.h" - +#include "nsPoint.h" class NS_GFX nsRegion { @@ -155,7 +155,11 @@ public: } - void MoveBy (PRInt32 aXOffset, PRInt32 aYOffset); + void MoveBy (PRInt32 aXOffset, PRInt32 aYOffset) + { + MoveBy (nsPoint (aXOffset, aYOffset)); + } + void MoveBy (nsPoint aPt); void SetEmpty () { SetToElements (0); @@ -168,6 +172,19 @@ public: PRUint32 GetNumRects () const { return mRectCount; } const nsRect& GetBounds () const { return mBoundRect; } + /** + * Make sure the region has at most aMaxRects by adding area to it + * if necessary. The simplified region will be a superset of the + * original region. The simplified region's bounding box will be + * the same as for the current region. + */ + void SimplifyOutward (PRUint32 aMaxRects); + /** + * Make sure the region has at most aMaxRects by removing area from + * it if necessary. The simplified region will be a subset of the + * original region. + */ + void SimplifyInward (PRUint32 aMaxRects); private: PRUint32 mRectCount; diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index 541e47f51b58..54965e5976b4 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -1223,18 +1223,38 @@ PRBool nsRegion::IsEqual (const nsRegion& aRegion) const } -void nsRegion::MoveBy (PRInt32 aXOffset, PRInt32 aYOffset) +void nsRegion::MoveBy (nsPoint aPt) { - if (aXOffset || aYOffset) + if (aPt.x || aPt.y) { RgnRect* pRect = mRectListHead.next; while (pRect != &mRectListHead) { - pRect->MoveBy (aXOffset, aYOffset); + pRect->MoveBy (aPt.x, aPt.y); pRect = pRect->next; } - mBoundRect.MoveBy (aXOffset, aYOffset); + mBoundRect.MoveBy (aPt.x, aPt.y); } } + +void nsRegion::SimplifyOutward (PRUint32 aMaxRects) +{ + NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count"); + + if (mRectCount <= aMaxRects) + return; + + *this = GetBounds(); +} + +void nsRegion::SimplifyInward (PRUint32 aMaxRects) +{ + NS_ASSERTION(aMaxRects >= 1, "Invalid max rect count"); + + if (mRectCount <= aMaxRects) + return; + + SetEmpty(); +} diff --git a/view/src/nsScrollPortView.cpp b/view/src/nsScrollPortView.cpp index 33b779fba404..9265a45a6192 100644 --- a/view/src/nsScrollPortView.cpp +++ b/view/src/nsScrollPortView.cpp @@ -473,15 +473,19 @@ PRBool nsScrollPortView::CannotBitBlt(nsView* aScrolledView) } -void nsScrollPortView::Scroll(nsView *aScrolledView, PRInt32 aDx, PRInt32 aDy, float scale, PRUint32 aUpdateFlags) +void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsPoint aPixDelta, + float aT2P) { - if ((aDx != 0) || (aDy != 0)) + if (aTwipsDelta.x != 0 || aTwipsDelta.y != 0) { - // Since we keep track of the dirty region as absolute screen coordintes, + // Since we keep track of the dirty region as absolute coordinates, // we need to offset it by the amount we scrolled. - nsCOMPtr dirtyRegion; - GetDirtyRegion(*getter_AddRefs(dirtyRegion)); - dirtyRegion->Offset(aDx, aDy); + if (HasNonEmptyDirtyRegion()) { + nsRegion* rgn = GetDirtyRegion(); + if (rgn) { + rgn->MoveBy(aTwipsDelta); + } + } nsIWidget *scrollWidget = GetWidget(); @@ -494,7 +498,7 @@ void nsScrollPortView::Scroll(nsView *aScrolledView, PRInt32 aDx, PRInt32 aDy, f // may include area that's not supposed to be scrolled. We need // to invalidate to ensure that any such area is properly // repainted back to the right rendering. - AdjustChildWidgets(aScrolledView, offsetToWidget, scale, PR_TRUE); + AdjustChildWidgets(aScrolledView, offsetToWidget, aT2P, PR_TRUE); // If we don't have a scroll widget then we must just update. // We should call this after fixing up the widget positions to be // consistent with the view hierarchy. @@ -506,15 +510,15 @@ void nsScrollPortView::Scroll(nsView *aScrolledView, PRInt32 aDx, PRInt32 aDy, f nsRect bounds(GetBounds()); nsPoint topLeft(bounds.x, bounds.y); AdjustChildWidgets(aScrolledView, - GetPosition() - topLeft, scale, PR_FALSE); + GetPosition() - topLeft, aT2P, PR_FALSE); // We should call this after fixing up the widget positions to be // consistent with the view hierarchy. mViewManager->UpdateView(this, 0); } else { // if we can blit and have a scrollwidget then scroll. // Scroll the contents of the widget by the specfied amount, and scroll // the child widgets - scrollWidget->Scroll(aDx, aDy, nsnull); - mViewManager->UpdateViewAfterScroll(this, aDx, aDy); + scrollWidget->Scroll(aPixDelta.x, aPixDelta.y, nsnull); + mViewManager->UpdateViewAfterScroll(this, aTwipsDelta.x, aTwipsDelta.y); } } } @@ -618,11 +622,13 @@ NS_IMETHODIMP nsScrollPortView::ScrollToImpl(nscoord aX, nscoord aY, PRUint32 aU mOffsetXpx = aXpx; mOffsetYpx = aYpx; + nsPoint twipsDelta(aX - mOffsetX, aY - mOffsetY); + // store the new position mOffsetX = aX; mOffsetY = aY; - Scroll(scrolledView, dxPx, dyPx, t2p, 0); + Scroll(scrolledView, twipsDelta, nsPoint(dxPx, dyPx), t2p); mViewManager->SynthesizeMouseMove(PR_TRUE); diff --git a/view/src/nsScrollPortView.h b/view/src/nsScrollPortView.h index 438603ac107d..9322cfd436d3 100644 --- a/view/src/nsScrollPortView.h +++ b/view/src/nsScrollPortView.h @@ -106,7 +106,7 @@ protected: virtual ~nsScrollPortView(); //private - void Scroll(nsView *aScrolledView, PRInt32 aDx, PRInt32 aDy, float scale, PRUint32 aUpdateFlags); + void Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsPoint aPixDelta, float aT2P); PRBool CannotBitBlt(nsView* aScrolledView); nscoord mOffsetX, mOffsetY; diff --git a/view/src/nsView.cpp b/view/src/nsView.cpp index f712c8ac53c1..309e7bcca535 100644 --- a/view/src/nsView.cpp +++ b/view/src/nsView.cpp @@ -258,8 +258,7 @@ nsView::~nsView() mWindow->Destroy(); NS_RELEASE(mWindow); } - NS_IF_RELEASE(mDirtyRegion); - + delete mDirtyRegion; delete mClipRect; } @@ -787,19 +786,6 @@ nsIWidget* nsIView::GetNearestWidget(nsPoint* aOffset) return v->GetWidget(); } -nsresult nsView::GetDirtyRegion(nsIRegion*& aRegion) -{ - if (nsnull == mDirtyRegion) { - nsresult rv = GetViewManager()->CreateRegion(&mDirtyRegion); - if (NS_FAILED(rv)) - return rv; - } - - aRegion = mDirtyRegion; - NS_ADDREF(aRegion); - return NS_OK; -} - PRBool nsIView::IsRoot() const { NS_ASSERTION(mViewManager != nsnull," View manager is null in nsView::IsRoot()"); diff --git a/view/src/nsView.h b/view/src/nsView.h index 39377ca6d24f..70b2e603d1d0 100644 --- a/view/src/nsView.h +++ b/view/src/nsView.h @@ -40,6 +40,7 @@ #include "nsIView.h" #include "nsIWidget.h" +#include "nsRegion.h" #include "nsRect.h" #include "nsCRT.h" #include "nsIFactory.h" @@ -210,11 +211,6 @@ public: * @param aTransparent PR_TRUE if there are transparent areas, PR_FALSE otherwise. */ NS_IMETHOD SetContentTransparency(PRBool aTransparent); - /** - * Gets the dirty region associated with this view. Used by the view - * manager. - */ - nsresult GetDirtyRegion(nsIRegion*& aRegion); /** * Set the widget associated with this view. * @param aWidget widget to associate with view. It is an error @@ -256,6 +252,17 @@ public: // These are defined exactly the same in nsIView, but for now they have to be redeclared // here because of stupid C++ method hiding rules + PRBool HasNonEmptyDirtyRegion() { + return mDirtyRegion && !mDirtyRegion->IsEmpty(); + } + nsRegion* GetDirtyRegion() { + if (!mDirtyRegion) { + mDirtyRegion = new nsRegion(); + NS_ASSERTION(mDirtyRegion, "Out of memory!"); + } + return mDirtyRegion; + } + void InsertChild(nsView *aChild, nsView *aSibling); void RemoveChild(nsView *aChild); @@ -284,13 +291,12 @@ public: virtual ~nsView(); protected: - nsZPlaceholderView*mZParent; + nsZPlaceholderView* mZParent; // mClipRect is relative to the view's origin. - nsRect* mClipRect; - nsIRegion* mDirtyRegion; - PRPackedBool mChildRemoved; - + nsRect* mClipRect; + nsRegion* mDirtyRegion; + PRPackedBool mChildRemoved; }; #endif diff --git a/view/src/nsViewManager.cpp b/view/src/nsViewManager.cpp index 991082034a2b..a8bf28843530 100644 --- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -1564,13 +1564,6 @@ void nsViewManager::ProcessPendingUpdates(nsView* aView) if (aView->HasWidget()) { aView->ResetWidgetBounds(PR_FALSE, PR_FALSE, PR_TRUE); - - nsCOMPtr dirtyRegion; - aView->GetDirtyRegion(*getter_AddRefs(dirtyRegion)); - if (dirtyRegion && !dirtyRegion->IsEmpty()) { - aView->GetWidget()->InvalidateRegion(dirtyRegion, PR_FALSE); - dirtyRegion->Init(); - } } // process pending updates in child view. @@ -1578,6 +1571,17 @@ void nsViewManager::ProcessPendingUpdates(nsView* aView) childView = childView->GetNextSibling()) { ProcessPendingUpdates(childView); } + + if (aView->HasNonEmptyDirtyRegion()) { + // Push out updates after we've processed the children; ensures that + // damage is applied based on the final widget geometry + NS_ASSERTION(mRefreshEnabled, "Cannot process pending updates with refresh disabled"); + nsRegion* dirtyRegion = aView->GetDirtyRegion(); + if (dirtyRegion) { + UpdateWidgetArea(aView, *dirtyRegion, nsnull); + dirtyRegion->SetEmpty(); + } + } } NS_IMETHODIMP nsViewManager::Composite() @@ -1626,24 +1630,52 @@ nsViewManager::UpdateViewAfterScroll(nsIView *aView, PRInt32 aDX, PRInt32 aDY) return; } - UpdateWidgetArea(RootViewManager()->GetRootView(), damageRect, view); + UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damageRect), view); Composite(); } -// Returns true if this view's widget(s) completely cover the rectangle -// The specified rectangle, relative to aWidgetView, is invalidated in every widget child of aWidgetView, -// plus aWidgetView's own widget -// If non-null, the aIgnoreWidgetView's widget and its children are not updated. -PRBool nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamagedRect, nsView* aIgnoreWidgetView) +/** + * @param aDamagedRegion this region, relative to aWidgetView, is invalidated in + * every widget child of aWidgetView, plus aWidgetView's own widget + * @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its + * children are not updated. + * @param aCoveredRegion if non-null, is set to PR_TRUE whenever the aWidgetView's + * widget completely covers the region. Must be null when refresh is disabled. + */ +void +nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion, + nsView* aIgnoreWidgetView, PRBool* aCoveredRegion) { - // If the bounds don't overlap at all, there's nothing to do - nsRect bounds; - aWidgetView->GetDimensions(bounds); + if (!IsRefreshEnabled()) { + NS_ASSERTION(!aCoveredRegion, "aCoveredRegion is not computed when refresh is disabled"); - PRBool overlap = bounds.IntersectRect(bounds, aDamagedRect); - if (!overlap) { - return PR_FALSE; + // accumulate this rectangle in the view's dirty region, so we can + // process it later. + nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion(); + if (!dirtyRegion) return; + + dirtyRegion->Or(*dirtyRegion, aDamagedRegion); + // Don't let dirtyRegion grow beyond 8 rects + dirtyRegion->SimplifyOutward(8); + nsViewManager* rootVM = RootViewManager(); + rootVM->mHasPendingInvalidates = PR_TRUE; + rootVM->IncrementUpdateCount(); + return; + // this should only happen at the top level, and this result + // should not be consumed by top-level callers, so it doesn't + // really matter what we return + } + + if (aCoveredRegion) { + *aCoveredRegion = PR_FALSE; + } + + // If the bounds don't overlap at all, there's nothing to do + nsRegion intersection; + intersection.And(aWidgetView->GetDimensions(), aDamagedRegion); + if (intersection.IsEmpty()) { + return; } // If the widget is hidden, it don't cover nothing @@ -1657,14 +1689,16 @@ PRBool nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamag NS_ASSERTION(!visible, "View is hidden but widget is visible!"); } #endif - return PR_FALSE; + return; } - PRBool noCropping = bounds == aDamagedRect; if (aWidgetView == aIgnoreWidgetView) { // the widget for aIgnoreWidgetView (and its children) should be treated as already updated. // We still need to report whether this widget covers the rectangle. - return noCropping; + if (aCoveredRegion) { + *aCoveredRegion = intersection.IsEqual(aDamagedRegion); + } + return; } nsIWidget* widget = aWidgetView->GetNearestWidget(nsnull); @@ -1672,7 +1706,11 @@ PRBool nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamag // The root view or a scrolling view might not have a widget // (for example, during printing). We get here when we scroll // during printing to show selected options in a listbox, for example. - return PR_FALSE; + return; + } + + if (aCoveredRegion) { + *aCoveredRegion = intersection.IsEqual(aDamagedRegion); } PRBool childCovers = PR_FALSE; @@ -1681,15 +1719,19 @@ PRBool nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamag childWidget = childWidget->GetNextSibling()) { nsView* view = nsView::GetViewFor(childWidget); if (nsnull != view) { - nsRect damage = bounds; nsView* vp = view; + nsPoint offset(0, 0); while (vp != aWidgetView && nsnull != vp) { - vp->ConvertFromParentCoords(&damage.x, &damage.y); + offset -= vp->GetPosition(); vp = vp->GetParent(); } if (nsnull != vp) { // vp == nsnull means it's in a different hierarchy so we ignore it - if (UpdateWidgetArea(view, damage, aIgnoreWidgetView)) { + nsRegion damage = intersection; + damage.MoveBy(offset); + PRBool covers; + UpdateWidgetArea(view, damage, aIgnoreWidgetView, &covers); + if (covers) { childCovers = PR_TRUE; } } @@ -1697,21 +1739,15 @@ PRBool nsViewManager::UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamag } if (!childCovers) { - nsViewManager* vm = aWidgetView->GetViewManager(); - nsViewManager* rootVM = RootViewManager(); - rootVM->IncrementUpdateCount(); + NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope"); - if (!IsRefreshEnabled()) { - // accumulate this rectangle in the view's dirty region, so we can process it later. - vm->AddRectToDirtyRegion(aWidgetView, bounds); - rootVM->mHasPendingInvalidates = PR_TRUE; - } else { + const nsRect* r; + for (nsRegionRectIterator iter(intersection); (r = iter.Next());) { + nsRect bounds = *r; ViewToWidget(aWidgetView, aWidgetView, bounds); widget->Invalidate(bounds, PR_FALSE); } } - - return noCropping; } NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags) @@ -1752,7 +1788,7 @@ NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRU widgetParent = widgetParent->GetParent(); } - UpdateWidgetArea(widgetParent, damagedRect, nsnull); + UpdateWidgetArea(widgetParent, nsRegion(damagedRect), nsnull); } else { // Propagate the update to the root widget of the root view manager, since // iframes, for example, can overlap each other and be translucent. So we @@ -1760,7 +1796,7 @@ NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect, PRU // lying about. damagedRect.MoveBy(ComputeViewOffset(view)); - UpdateWidgetArea(RootViewManager()->GetRootView(), damagedRect, nsnull); + UpdateWidgetArea(RootViewManager()->GetRootView(), nsRegion(damagedRect), nsnull); } RootViewManager()->IncrementUpdateCount(); @@ -3206,25 +3242,6 @@ nsViewManager::CreateRenderingContext(nsView &aView) return cx; } -void nsViewManager::AddRectToDirtyRegion(nsView* aView, const nsRect &aRect) const -{ - // Find a view with an associated widget. We'll transform this rect from the - // current view's coordinate system to a "heavyweight" parent view, then convert - // the rect to pixel coordinates, and accumulate the rect into that view's dirty region. - nsView* widgetView = GetWidgetView(aView); - if (widgetView != nsnull) { - nsRect widgetRect = aRect; - ViewToWidget(aView, widgetView, widgetRect); - - // Get the dirty region associated with the widget view - nsCOMPtr dirtyRegion; - if (NS_SUCCEEDED(widgetView->GetDirtyRegion(*getter_AddRefs(dirtyRegion)))) { - // add this rect to the widget view's dirty region. - dirtyRegion->Union(widgetRect.x, widgetRect.y, widgetRect.width, widgetRect.height); - } - } -} - NS_IMETHODIMP nsViewManager::DisableRefresh(void) { if (!IsRootVM()) { diff --git a/view/src/nsViewManager.h b/view/src/nsViewManager.h index 9166125f21f6..cc14dab62499 100644 --- a/view/src/nsViewManager.h +++ b/view/src/nsViewManager.h @@ -267,9 +267,8 @@ private: void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget); void ReparentWidgets(nsIView* aView, nsIView *aParent); already_AddRefed CreateRenderingContext(nsView &aView); - void AddRectToDirtyRegion(nsView* aView, const nsRect &aRect) const; - - PRBool UpdateWidgetArea(nsView *aWidgetView, const nsRect &aDamagedRect, nsView* aIgnoreWidgetView); + void UpdateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion, + nsView* aIgnoreWidgetView, PRBool* aCoveredRegion = nsnull); void UpdateViews(nsView *aView, PRUint32 aUpdateFlags);