diff --git a/view/src/nsViewManager2.cpp b/view/src/nsViewManager2.cpp index bec6dd7f7067..5f4ee562a096 100755 --- a/view/src/nsViewManager2.cpp +++ b/view/src/nsViewManager2.cpp @@ -46,26 +46,16 @@ static NS_DEFINE_IID(kRenderingContextCID, NS_RENDERING_CONTEXT_CID); #define PUSH_CLIP 0x00000002 #define POP_CLIP 0x00000004 #define VIEW_TRANSPARENT 0x00000008 -#define VIEW_TRANSLUSCENT 0x00000010 +#define VIEW_TRANSLUCENT 0x00000010 // display list elements -struct DisplayListElement { +struct DisplayListElement2 { nsIView* mView; nsRect mClip; + nsRect mDirty; PRUint32 mFlags; }; -/** - * FrontToBackElements represent bundles of views that are traversed from - * front to back, but are individually rendered back to front. At worst - * there will be a single, opaque view in each bundle. - */ -struct FrontToBackElements { - DisplayListElement* mFirstElement; - PRInt32 mCount; - nsRect mClip; -}; - static void vm_timer_callback(nsITimer *aTimer, void *aClosure) { nsViewManager2 *vm = (nsViewManager2 *)aClosure; @@ -174,7 +164,7 @@ nsViewManager2::~nsViewManager2() { PRInt32 count = mDisplayList->Count(); for (PRInt32 index = 0; index < count; index++) { - DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(index); + DisplayListElement2* element = (DisplayListElement2*) mDisplayList->ElementAt(index); if (element != nsnull) delete element; } @@ -260,6 +250,8 @@ NS_IMETHODIMP nsViewManager2::Init(nsIDeviceContext* aContext) return NS_ERROR_ALREADY_INITIALIZED; } mContext = aContext; + mContext->GetAppUnitsToDevUnits(mTwipsToPixels); + mContext->GetDevUnitsToAppUnits(mPixelsToTwips); mDSBounds.Empty(); mTimer = nsnull; @@ -636,8 +628,14 @@ void nsViewManager2::RenderViews(nsIView *aRootView, nsIRenderingContext& aRC, c return; } } + + // initialize various counters. mDisplayListCount = 0; mOpaqueViewCount = 0; + mTranslucentViewCount = 0; + mTranslucentBounds.x = mTranslucentBounds.y = 0; + mTranslucentBounds.width = mTranslucentBounds.width = 0; + CreateDisplayList(mRootView, &mDisplayListCount, origin.x, origin.y, aRootView, &aRect); // now, partition this display list into "front-to-back" bundles, and then draw each bundle @@ -647,10 +645,11 @@ void nsViewManager2::RenderViews(nsIView *aRootView, nsIRenderingContext& aRC, c // draw all views in the display list, from back to front. for (PRInt32 i = mDisplayListCount - 1; i>= 0; --i) { - DisplayListElement* element = NS_STATIC_CAST(DisplayListElement*, mDisplayList->ElementAt(i)); + DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, mDisplayList->ElementAt(i)); if (element->mFlags & VIEW_RENDERED) { // typical case, just rendering a view. - RenderView(element->mView, aRC, aRect, element->mClip, aResult); + // RenderView(element->mView, aRC, aRect, element->mClip, aResult); + RenderDisplayListElement(element, aRC); } else { // special case, pushing or popping clipping. if (element->mFlags & PUSH_CLIP) { @@ -671,7 +670,7 @@ void nsViewManager2::RenderView(nsIView *aView, nsIRenderingContext &aRC, const { nsRect drect; - NS_ASSERTION(!(nsnull == aView), "no view"); + NS_ASSERTION((nsnull != aView), "no view"); aRC.PushState(); @@ -689,6 +688,172 @@ void nsViewManager2::RenderView(nsIView *aView, nsIRenderingContext &aRC, const aRC.PopState(aResult); } +void nsViewManager2::RenderDisplayListElement(DisplayListElement2* element, nsIRenderingContext &aRC) +{ + PRBool isTranslucent = (element->mFlags & VIEW_TRANSLUCENT) != 0; + if (!isTranslucent) { + aRC.PushState(); + + nscoord x = element->mClip.x, y = element->mClip.y; + aRC.Translate(x, y); + + nsRect drect(element->mDirty.x - x, element->mDirty.y - y, + element->mDirty.width, element->mDirty.height); + PRBool unused; + element->mView->Paint(aRC, drect, NS_VIEW_FLAG_JUST_PAINT, unused); + + aRC.PopState(unused); + } + + if (mTranslucentViewCount > 0 && (isTranslucent || mTranslucentBounds.Intersects(element->mDirty))) { + // transluscency case. if this view is transluscent, have to use the nsIBlender, otherwise, just + // render in the offscreen. when we reach the last transluscent view, then we flush the bits + // to the onscreen rendering context. + if (mTranslucentBounds.width > gBlendWidth || mTranslucentBounds.height > gBlendHeight) { + nsresult rv = CreateBlendingBuffers(aRC); + NS_ASSERTION((rv == NS_OK), "not enough memory to blend"); + if (NS_FAILED(rv)) { + // fall back by just rendering with transparency. + mTranslucentViewCount = 0; + RenderDisplayListElement(element, aRC); + return; + } + } + + // compute the origin of the view, relative to the blending buffer, which has the + // same dimensions as mTranslucentBounds. + nscoord viewX = element->mClip.x - mTranslucentBounds.x, viewY = element->mClip.y - mTranslucentBounds.y; + + nsRect damageRect(element->mDirty); + damageRect.IntersectRect(damageRect, mTranslucentBounds); + damageRect.x -= element->mClip.x, damageRect.y -= element->mClip.y; + + if (element->mFlags & VIEW_TRANSLUCENT) { + nsRect blendRect(element->mDirty.x - mTranslucentBounds.x, element->mDirty.y - mTranslucentBounds.y, + element->mDirty.width, element->mDirty.height); + + // paint the view twice, first in the red buffer, then the blue, the + // the blender will pick up the touched pixels only. + mRedCX->SetColor(NS_RGB(255, 0, 0)); + mRedCX->FillRect(blendRect); + PaintView(element->mView, *mRedCX, viewX, viewY, damageRect); + + mBlueCX->SetColor(NS_RGB(0, 0, 255)); + mBlueCX->FillRect(blendRect); + PaintView(element->mView, *mBlueCX, viewX, viewY, damageRect); + + float opacity; + element->mView->GetOpacity(opacity); + + // perform the blend itself. + blendRect *= mTwipsToPixels; + mBlender->Blend(blendRect.x, blendRect.y, + blendRect.width, blendRect.height, + mRedCX, mOffScreenCX, + blendRect.x, blendRect.y, + opacity, mBlueCX, + NS_RGB(255, 0, 0), NS_RGB(0, 0, 255)); + + --mTranslucentViewCount; + } else { + PaintView(element->mView, *mOffScreenCX, viewX, viewY, damageRect); + } + + // flush the bits back to screen. + if (mTranslucentViewCount == 0) { + aRC.CopyOffScreenBits(gOffScreen, 0, 0, mTranslucentBounds, + NS_COPYBITS_XFORM_DEST_VALUES | NS_COPYBITS_TO_BACK_BUFFER); + } + } +} + +void nsViewManager2::PaintView(nsIView *aView, nsIRenderingContext &aRC, nscoord x, nscoord y, + const nsRect &aDamageRect) +{ + aRC.PushState(); + aRC.Translate(x, y); + PRBool unused; + aView->Paint(aRC, aDamageRect, NS_VIEW_FLAG_JUST_PAINT, unused); + aRC.PopState(unused); +} + +inline PRInt32 nextPowerOf2(PRInt32 value) +{ + PRInt32 result = 1; + while (value > result) + result <<= 1; + return result; +} + +nsresult nsViewManager2::CreateBlendingBuffers(nsIRenderingContext &aRC) +{ + nsresult rv; + + // compute the new bounds of the blending buffers. should probably round the + // resulting + nsRect blenderBounds(0, 0, mTranslucentBounds.width, mTranslucentBounds.height); + blenderBounds.ScaleRoundOut(mTwipsToPixels); + + blenderBounds.width = nextPowerOf2(blenderBounds.width); + blenderBounds.height = nextPowerOf2(blenderBounds.height); + + NS_IF_RELEASE(mOffScreenCX); + NS_IF_RELEASE(mRedCX); + NS_IF_RELEASE(mBlueCX); + + if (nsnull != gOffScreen) { + aRC.DestroyDrawingSurface(gOffScreen); + gOffScreen = nsnull; + } + rv = aRC.CreateDrawingSurface(&blenderBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gOffScreen); + if (NS_FAILED(rv)) + return rv; + + if (nsnull != gRed) { + aRC.DestroyDrawingSurface(gRed); + gRed = nsnull; + } + aRC.CreateDrawingSurface(&blenderBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gRed); + if (NS_FAILED(rv)) + return rv; + + if (nsnull != gBlue) { + aRC.DestroyDrawingSurface(gBlue); + gBlue = nsnull; + } + aRC.CreateDrawingSurface(&blenderBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gBlue); + if (NS_FAILED(rv)) + return rv; + + // now create the blending offscreen rendering contexts. + rv = nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mOffScreenCX); + if (NS_FAILED(rv)) + return rv; + mOffScreenCX->Init(mContext, gOffScreen); + + rv = nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mRedCX); + if (NS_FAILED(rv)) + return rv; + mRedCX->Init(mContext, gRed); + + rv = nsComponentManager::CreateInstance(kRenderingContextCID, nsnull, NS_GET_IID(nsIRenderingContext), (void **)&mBlueCX); + if (NS_FAILED(rv)) + return rv; + mBlueCX->Init(mContext, gBlue); + + if (nsnull == mBlender) { + rv = nsComponentManager::CreateInstance(kBlenderCID, nsnull, NS_GET_IID(nsIBlender), (void **)&mBlender); + if (NS_FAILED(rv)) + return rv; + mBlender->Init(mContext); + } + + gBlendWidth = mTranslucentBounds.width * mPixelsToTwips; + gBlendHeight = mTranslucentBounds.height * mPixelsToTwips; + + return NS_OK; +} + void nsViewManager2::UpdateDirtyViews(nsIView *aView, nsRect *aParentRect) const { nsRect bounds; @@ -1892,7 +2057,7 @@ PRBool nsViewManager2::CreateDisplayList(nsIView *aView, PRInt32 *aIndex, lrect.x -= aOriginX; lrect.y -= aOriginY; - retval = AddToDisplayList(aIndex, aView, lrect, POP_CLIP); + retval = AddToDisplayList(aIndex, aView, lrect, lrect, POP_CLIP); if (retval) return retval; @@ -1924,7 +2089,7 @@ PRBool nsViewManager2::CreateDisplayList(nsIView *aView, PRInt32 *aIndex, // if (clipper) if (isClipView && (!hasWidget || (hasWidget && isParentView))) { if (childCount > 0) - retval = AddToDisplayList(aIndex, aView, lrect, PUSH_CLIP); + retval = AddToDisplayList(aIndex, aView, lrect, lrect, PUSH_CLIP); } else if (!retval) { nsViewVisibility visibility; float opacity; @@ -1946,8 +2111,8 @@ PRBool nsViewManager2::CreateDisplayList(nsIView *aView, PRInt32 *aIndex, if (transparent) flags |= VIEW_TRANSPARENT; if (opacity < 1.0f) - flags |= VIEW_TRANSLUSCENT; - retval = AddToDisplayList(aIndex, aView, lrect, flags); + flags |= VIEW_TRANSLUCENT; + retval = AddToDisplayList(aIndex, aView, lrect, irect, flags); if (retval || !transparent && (opacity == 1.0f) && (irect == *aDamageRect)) retval = PR_TRUE; @@ -1968,12 +2133,12 @@ PRBool nsViewManager2::CreateDisplayList(nsIView *aView, PRInt32 *aIndex, return retval; } -PRBool nsViewManager2::AddToDisplayList(PRInt32 *aIndex, nsIView *aView, nsRect &aRect, PRUint32 aFlags) +PRBool nsViewManager2::AddToDisplayList(PRInt32 *aIndex, nsIView *aView, nsRect &aClipRect, nsRect& aDirtyRect, PRUint32 aFlags) { PRInt32 index = (*aIndex)++; - DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(index); + DisplayListElement2* element = (DisplayListElement2*) mDisplayList->ElementAt(index); if (element == nsnull) { - element = new DisplayListElement; + element = new DisplayListElement2; if (element == nsnull) { *aIndex = index; return PR_TRUE; @@ -1982,12 +2147,26 @@ PRBool nsViewManager2::AddToDisplayList(PRInt32 *aIndex, nsIView *aView, nsRect } element->mView = aView; - element->mClip = aRect; + element->mClip = aClipRect; + element->mDirty = aDirtyRect; element->mFlags = aFlags; + // count number of opaque views. if (aFlags == VIEW_RENDERED) ++mOpaqueViewCount; + // count number of transluscent views, and + // accumulate a rectangle of all transluscent + // views. this will be used to determine which + // views need to be rendered into the blending + // buffers. + if (aFlags & VIEW_TRANSLUCENT) { + if (mTranslucentViewCount++ == 0) + mTranslucentBounds = aDirtyRect; + else + mTranslucentBounds.UnionRect(mTranslucentBounds, aDirtyRect); + } + return PR_FALSE; } @@ -1997,17 +2176,17 @@ nsresult nsViewManager2::OptimizeDisplayList(const nsRect& aDamageRect) PRInt32 count = mDisplayListCount; PRInt32 opaqueCount = mOpaqueViewCount; for (PRInt32 i = 0; i < count; ++i) { - DisplayListElement* element = NS_STATIC_CAST(DisplayListElement*, mDisplayList->ElementAt(i)); + DisplayListElement2* element = NS_STATIC_CAST(DisplayListElement2*, mDisplayList->ElementAt(i)); if (element->mFlags & VIEW_RENDERED) { // a view is opaque if it is neither transparent nor transluscent - if (!(element->mFlags & (VIEW_TRANSPARENT | VIEW_TRANSLUSCENT))) { + if (!(element->mFlags & (VIEW_TRANSPARENT | VIEW_TRANSLUCENT))) { nsRect opaqueRect; opaqueRect.IntersectRect(element->mClip, aDamageRect); nscoord top = opaqueRect.y, left = opaqueRect.x; nscoord bottom = top + opaqueRect.height, right = left + opaqueRect.width; // search for views behind this one, that are completely obscured by it. for (PRInt32 j = i + 1; j < count; ++j) { - DisplayListElement* lowerElement = NS_STATIC_CAST(DisplayListElement*, mDisplayList->ElementAt(j)); + DisplayListElement2* lowerElement = NS_STATIC_CAST(DisplayListElement2*, mDisplayList->ElementAt(j)); if (lowerElement->mFlags & VIEW_RENDERED) { nsRect lowerRect; lowerRect.IntersectRect(lowerElement->mClip, aDamageRect); @@ -2048,7 +2227,7 @@ void nsViewManager2::ShowDisplayList(PRInt32 flatlen) PRUint32 flags; PRInt32 zindex; - DisplayListElement* element = (DisplayListElement*) mDisplayList->ElementAt(cnt); + DisplayListElement2* element = (DisplayListElement2*) mDisplayList->ElementAt(cnt); view = element->mView; rect = element->mClip; flags = element->mFlags; diff --git a/view/src/nsViewManager2.h b/view/src/nsViewManager2.h index a80c34d7564d..cc90ee1dd5f8 100755 --- a/view/src/nsViewManager2.h +++ b/view/src/nsViewManager2.h @@ -36,6 +36,7 @@ #include "nsIBlender.h" class nsISupportsArray; +struct DisplayListElement2; class nsViewManager2 : public nsIViewManager { public: @@ -151,12 +152,21 @@ private: const nsRect *rect, PRUint32 aUpdateFlags); void RenderViews(nsIView *aRootView, nsIRenderingContext& aRC, const nsRect& aRect, PRBool &aResult); + void RenderView(nsIView *aView, nsIRenderingContext &aRC, const nsRect &aDamageRect, nsRect &aGlobalRect, PRBool &aResult); + + void RenderDisplayListElement(DisplayListElement2* element, nsIRenderingContext &aRC); + + void PaintView(nsIView *aView, nsIRenderingContext &aRC, nscoord x, nscoord y, + const nsRect &aDamageRect); + + nsresult CreateBlendingBuffers(nsIRenderingContext &aRC); + PRBool CreateDisplayList(nsIView *aView, PRInt32 *aIndex, nscoord aOriginX, nscoord aOriginY, nsIView *aRealView, const nsRect *aDamageRect = nsnull, nsIView *aTopView = nsnull, nscoord aX = 0, nscoord aY = 0); - PRBool AddToDisplayList(PRInt32 *aIndex, nsIView *aView, nsRect &aRect, PRUint32 aFlags); + PRBool AddToDisplayList(PRInt32 *aIndex, nsIView *aView, nsRect &aClipRect, nsRect& aDirtyRect, PRUint32 aFlags); nsresult OptimizeDisplayList(const nsRect& aDamageRect); void ShowDisplayList(PRInt32 flatlen); void ComputeViewOffset(nsIView *aView, nsPoint *aOrigin, PRInt32 aFlag); @@ -185,6 +195,8 @@ private: private: nsIDeviceContext *mContext; + float mTwipsToPixels; + float mPixelsToTwips; nsIViewObserver *mObserver; nsIWidget *mRootWindow; PRIntervalTime mLastRefresh; @@ -198,6 +210,8 @@ private: nsVoidArray *mDisplayList; PRInt32 mDisplayListCount; PRInt32 mOpaqueViewCount; + PRInt32 mTranslucentViewCount; + nsRect mTranslucentBounds; nsIScrollableView *mRootScrollable; //from here to public should be static and locked... MMP