From ebb082aaed85cac88f01df7dddd8f71905a67ecb Mon Sep 17 00:00:00 2001 From: "kmcclusk%netscape.com" Date: Fri, 21 Apr 2000 23:02:02 +0000 Subject: [PATCH] Offscreen drawing surface now shrinks and expands in discrete increments of the screen size. b=20741 r=rods@netscape.com. tested on WIN32, Mac, Linux Applied second half of patch to prevent invisible views from being painted (patch from Tomi.Leppikangas@oulu.fi) b=34466 r=rods@netscape tested on WIN32, Mac, Linux --- view/src/nsViewManager2.cpp | 189 ++++++++++++++++++++++++++++++------ view/src/nsViewManager2.h | 56 +++++++++++ 2 files changed, 217 insertions(+), 28 deletions(-) diff --git a/view/src/nsViewManager2.cpp b/view/src/nsViewManager2.cpp index 0db806fafd41..25a20616193f 100755 --- a/view/src/nsViewManager2.cpp +++ b/view/src/nsViewManager2.cpp @@ -123,17 +123,28 @@ nsDrawingSurface nsViewManager2::gBlue = nsnull; nsSize nsViewManager2::gBlendSize = nsSize(0, 0); nsSize nsViewManager2::gOffScreenSize = nsSize(0, 0); +// Weakly held references to all of the view managers +nsVoidArray* nsViewManager2::gViewManagers = nsnull; + static NS_DEFINE_IID(knsViewManagerIID, NS_IVIEWMANAGER_IID); nsViewManager2::nsViewManager2() { NS_INIT_REFCNT(); + + if (mVMCount == 0) { + //Create a vector to hold each view manager + gViewManagers = new nsVoidArray; + } + gViewManagers->AppendElement(this); + mVMCount++; // NOTE: we use a zeroing operator new, so all data members are // assumed to be cleared here. mX = 0; mY = 0; mCachingWidgetChanges = 0; + } nsViewManager2::~nsViewManager2() @@ -152,10 +163,16 @@ nsViewManager2::~nsViewManager2() NS_ASSERTION((mVMCount > 0), "underflow of viewmanagers"); --mVMCount; + PRBool removed = gViewManagers->RemoveElement(this); + NS_ASSERTION(removed, "Viewmanager instance not was not in the global list of viewmanagers"); + if ((0 == mVMCount) && ((nsnull != mDrawingSurface) || (nsnull != gOffScreen) || (nsnull != gRed) || (nsnull != gBlue))) { + delete gViewManagers; + gViewManagers = nsnull; + nsCOMPtr rc; nsresult rv = nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, @@ -1260,7 +1277,7 @@ NS_IMETHODIMP nsViewManager2::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *a // XXX rods updateFlags |= NS_VMREFRESH_DOUBLE_BUFFER; - //printf("refreshing: view: %x, %d, %d, %d, %d\n", view, trect.x, trect.y, trect.width, trect.height); + //printf("refreshing: view: %x, %d, %d, %d, %d\n", view, damrect.x, damrect.y, damrect.width, damrect.height); // Refresh the view Refresh(view, ((nsPaintEvent*)aEvent)->renderingContext, &damrect, updateFlags); } @@ -1578,22 +1595,20 @@ NS_IMETHODIMP nsViewManager2::ResizeView(nsIView *aView, nscoord width, nscoord else parentView = aView; - // resize the view. - aView->SetDimensions(width, height, PR_TRUE); + // resize the view. + nsViewVisibility visibility; + aView->GetVisibility(visibility); -#if 1 - // refresh the bounding box of old and new areas. - nscoord maxWidth = (oldWidth < width ? width : oldWidth); - nscoord maxHeight = (oldHeight < height ? height : oldHeight); - nsRect boundingArea(x, y, maxWidth, maxHeight); - UpdateView(parentView, boundingArea, NS_VMREFRESH_NO_SYNC); -#else - // brute force, invalidate old and new areas. I don't understand - // why just refreshing the bounding box is insufficient. - nsRect oldBounds(x, y, oldWidth, oldHeight); - UpdateView(parentView, oldBounds, NS_VMREFRESH_NO_SYNC); - UpdateView(parentView, NS_VMREFRESH_NO_SYNC); -#endif + // Prevent Invalidation of hidden views + if (visibility == nsViewVisibility_kHide) { + aView->SetDimensions(width, height, PR_FALSE); + } else { + aView->SetDimensions(width, height, PR_TRUE); + nscoord maxWidth = (oldWidth < width ? width : oldWidth); + nscoord maxHeight = (oldHeight < height ? height : oldHeight); + nsRect boundingArea(x, y, maxWidth, maxHeight); + UpdateView(parentView, boundingArea, NS_VMREFRESH_NO_SYNC); + } } return NS_OK; @@ -1814,17 +1829,132 @@ NS_IMETHODIMP nsViewManager2::GetDeviceContext(nsIDeviceContext *&aContext) return NS_OK; } -nsDrawingSurface nsViewManager2::GetDrawingSurface(nsIRenderingContext &aContext, nsRect& aBounds) +void nsViewManager2::GetMaxWidgetBounds(nsRect& aMaxWidgetBounds) const { - if ((nsnull == mDrawingSurface) - || (mDSBounds.width < aBounds.width) - || (mDSBounds.height < aBounds.height)) - { - nsRect newBounds; - newBounds.MoveTo(aBounds.x, aBounds.y); - newBounds.width = max(aBounds.width, mDSBounds.width); - newBounds.height = max(aBounds.height, mDSBounds.height); + // Go through the list of viewmanagers and get the maximum width and + // height of their widgets + aMaxWidgetBounds.width = 0; + aMaxWidgetBounds.height = 0; + PRInt32 index = 0; + for (index = 0; index < mVMCount; index++) { + nsIViewManager* vm = (nsIViewManager*)gViewManagers->ElementAt(index); + nsCOMPtr rootWidget; + + vm->GetWidget(getter_AddRefs(rootWidget)); + + nsRect widgetBounds; + rootWidget->GetBounds(widgetBounds); + aMaxWidgetBounds.width = max(aMaxWidgetBounds.width, widgetBounds.width); + aMaxWidgetBounds.height = max(aMaxWidgetBounds.height, widgetBounds.height); + + } + +// printf("WIDGET BOUNDS %d %d\n", aMaxWidgetBounds.width, aMaxWidgetBounds.height); +} + +PRBool nsViewManager2::RectFitsInside(nsRect& aRect, PRInt32 aWidth, PRInt32 aHeight) const +{ + if (aRect.width > aWidth) + return (PR_FALSE); + + if (aRect.height > aHeight) + return (PR_FALSE); + + return PR_TRUE; +} + +PRBool nsViewManager2::BothRectsFitInside(nsRect& aRect1, nsRect& aRect2, PRInt32 aWidth, PRInt32 aHeight, nsRect& aNewSize) const +{ + if (PR_FALSE == RectFitsInside(aRect1, aWidth, aHeight)) { + return PR_FALSE; + } + + if (PR_FALSE == RectFitsInside(aRect2, aWidth, aHeight)) { + return PR_FALSE; + } + + aNewSize.width = aWidth; + aNewSize.height = aHeight; + + return PR_TRUE; +} + + +PRBool nsViewManager2::CalculateDiscreteSurfaceSize(nsRect& aRequestedSize, nsRect& aSurfaceSize) const +{ + nsRect aMaxWidgetSize; + GetMaxWidgetBounds(aMaxWidgetSize); + + // Get the height and width of the screen + PRInt32 height; + PRInt32 width; + NS_ASSERTION(mContext != nsnull, "The device context is null"); + mContext->GetDeviceSurfaceDimensions(width, height); + + float devUnits; + mContext->GetDevUnitsToAppUnits(devUnits); + PRInt32 screenHeight = NSToIntRound(float( height) / devUnits ); + PRInt32 screenWidth = NSToIntRound(float( width) / devUnits ); + + // These tests must go from smallest rectangle to largest rectangle. + + // 1/8 screen + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, screenWidth / 8, screenHeight / 8, aSurfaceSize)) { + return PR_TRUE; + } + + // 1/4 screen + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, screenWidth / 4, screenHeight / 4, aSurfaceSize)) { + return PR_TRUE; + } + + // 1/2 screen + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, screenWidth / 2, screenHeight / 2, aSurfaceSize)) { + return PR_TRUE; + } + + // 3/4 screen + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, (screenWidth * 3) / 4, (screenHeight * 3) / 4, aSurfaceSize)) { + return PR_TRUE; + } + + // 3/4 screen width full screen height + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, (screenWidth * 3) / 4, screenHeight, aSurfaceSize)) { + return PR_TRUE; + } + + // Full screen + if (BothRectsFitInside(aRequestedSize, aMaxWidgetSize, screenWidth, screenHeight, aSurfaceSize)) { + return PR_TRUE; + } + + return PR_FALSE; +} + +void nsViewManager2::GetDrawingSurfaceSize(nsRect& aRequestedSize, nsRect& aNewSize) const +{ + if (PR_FALSE == CalculateDiscreteSurfaceSize(aRequestedSize, aNewSize)) { + NS_ASSERTION(PR_FALSE, "CalculateDiscreteSize failed"); + // Wasn't able to find any fixed size to render inside of + // so just expand the size of the offscreen using the requested size. + aNewSize.width = max(aRequestedSize.width, mDSBounds.width); + aNewSize.height = max(aRequestedSize.height, mDSBounds.height); + } + + aNewSize.MoveTo(aRequestedSize.x, aRequestedSize.y); +} + + +nsDrawingSurface nsViewManager2::GetDrawingSurface(nsIRenderingContext &aContext, nsRect& aBounds) +{ + nsRect newBounds; + GetDrawingSurfaceSize(aBounds, newBounds); + + if ((nsnull == mDrawingSurface) + || (mDSBounds.width != newBounds.width) + || (mDSBounds.height != newBounds.height)) + { if (mDrawingSurface) { //destroy existing DS aContext.DestroyDrawingSurface(mDrawingSurface); @@ -1832,7 +1962,7 @@ nsDrawingSurface nsViewManager2::GetDrawingSurface(nsIRenderingContext &aContext } nsresult rv = aContext.CreateDrawingSurface(&newBounds, 0, mDrawingSurface); - +// printf("Allocating a new drawing surface %d %d\n", newBounds.width, newBounds.height); if (NS_SUCCEEDED(rv)) { mDSBounds = newBounds; aContext.SelectOffScreenDrawingSurface(mDrawingSurface); @@ -1851,8 +1981,11 @@ nsDrawingSurface nsViewManager2::GetDrawingSurface(nsIRenderingContext &aContext PRBool clipEmpty; aContext.SetClipRect(bounds, nsClipCombine_kReplace, clipEmpty); - nscolor col = NS_RGB(255,255,255); - aContext.SetColor(col); + // This is not be needed. Only the part of the offscreen that has been + // rendered to should be displayed so there no need to + // clear it out. + //nscolor col = NS_RGB(255,255,255); + //aContext.SetColor(col); //aContext.FillRect(bounds); } diff --git a/view/src/nsViewManager2.h b/view/src/nsViewManager2.h index 3dde87c5c7cb..8f794f6ca3d0 100755 --- a/view/src/nsViewManager2.h +++ b/view/src/nsViewManager2.h @@ -243,6 +243,59 @@ private: nsresult ProcessWidgetChanges(nsIView* aView); + // Utilities used to size the offscreen drawing surface + + /** + * Determine the maximum and width and height of all of the + * view manager's widgets. + * + * @param aMaxWidgetBounds the maximum width and height of all view managers + * widgets on exit. + */ + void GetMaxWidgetBounds(nsRect& aMaxWidgetBounds) const; + + /** + * Determine if a rect's width and height will fit within a specified width and height + * @param aRect rectangle to test + * @param aWidth width to determine if the rectangle's width will fit within + * @param aHeight height to determine if the rectangles height will fit within + * @returns PR_TRUE if the rect width and height fits with aWidth, aHeight, PR_FALSE + * otherwise. + */ + PRBool RectFitsInside(nsRect& aRect, PRInt32 aWidth, PRInt32 aHeight) const; + + /** + * Determine if two rectangles width and height will fit within a specified width and height + * @param aRect1 first rectangle to test + * @param aRect1 second rectangle to test + * @param aWidth width to determine if both rectangle's width will fit within + * @param aHeight height to determine if both rectangles height will fit within + * @returns PR_TRUE if the rect1's and rect2's width and height fits with aWidth, + * aHeight, PR_FALSE otherwise. + */ + PRBool BothRectsFitInside(nsRect& aRect1, nsRect& aRect2, PRInt32 aWidth, PRInt32 aHeight, nsRect& aNewSize) const; + + /** + * Return an offscreen surface size from a set of discrete surface sizes. + * The smallest discrete surface size that can enclose both the Maximum widget + * size (@see GetMaxWidgetBounds) and the requested size is returned. + * + * @param aRequestedSize Requested size for the offscreen. + * @param aSurfaceSize contains the surface size if the method returns PR_TRUE. + * It is unmodified if the method returns PR_FALSE; + * @returns PR_TRUE if it was able to calculate a discrete surface size, PR_FALSE + * otherwise. + */ + PRBool CalculateDiscreteSurfaceSize(nsRect& aRequestedSize, nsRect& aSize) const; + + /** + * Get the size of the offscreen drawing surface.. + * + * @param aRequestedSize Desired size for the offscreen. + * @param aSurfaceSize Offscreen adjusted to a discrete size which encloses aRequestedSize. + */ + void GetDrawingSurfaceSize(nsRect& aRequestedSize, nsRect& aSurfaceSize) const; + private: nsIDeviceContext *mContext; float mTwipsToPixels; @@ -278,6 +331,9 @@ private: static nsSize gOffScreenSize; static nsSize gBlendSize; + //list of view managers + static nsVoidArray *gViewManagers; + //compositor regions nsIRegion *mTransRgn; nsIRegion *mOpaqueRgn;