Bug 113232. Support translucent chrome. Relanding, fingers crossed. r+sr=bzbarsky

This commit is contained in:
roc+%cs.cmu.edu 2003-04-06 02:49:40 +00:00
parent 20cbf4913d
commit d21b664b22
15 changed files with 898 additions and 263 deletions

View File

@ -95,6 +95,15 @@ public:
nsIRenderingContext *aDest, PRInt32 aDX, PRInt32 aDY, float aSrcOpacity,
nsIRenderingContext *aSecondSrc = nsnull, nscolor aSrcBackColor = NS_RGB(0, 0, 0),
nscolor aSecondSrcBackColor = NS_RGB(0, 0, 0)) = 0;
/**
GetAlphas computes an array of alpha values for a rectangle of pixels, using
the drawn-onto-black and the drawn-onto-white images. The pixels are
returned in a new'ed array of aRect.width*aRect.height elements, in row-major
order. This array must be freed by the caller.
*/
NS_IMETHOD GetAlphas(const nsRect& aRect, nsDrawingSurface aBlack,
nsDrawingSurface aWhite, PRUint8** aAlphas) = 0;
};
#endif

View File

@ -198,7 +198,8 @@ public:
NS_IMETHOD IsVisibleRect(const nsRect& aRect, PRBool &aIsVisible) = 0;
/**
* Sets the clipping for the RenderingContext to the passed in rectangle
* Sets the clipping for the RenderingContext to the passed in rectangle.
* The rectangle is in app units!
* @param aRect The rectangle to set the clipping rectangle to
* @param aCombine how to combine this rect with the current clip region.
* see the bottom of nsIRenderingContext.h
@ -207,7 +208,8 @@ public:
NS_IMETHOD SetClipRect(const nsRect& aRect, nsClipCombine aCombine, PRBool &aClipEmpty) = 0;
/**
* Gets the bounds of the clip region of the RenderingContext
* Gets the bounds of the clip region of the RenderingContext. The bounds are returned
* in device units!
* @param aRect out parameter to contain the clip region bounds
* for the RenderingContext
* @return PR_TRUE if the rendering context has a local cliprect set
@ -245,7 +247,8 @@ public:
/**
* Sets the clipping for the RenderingContext to the passed in region
* Sets the clipping for the RenderingContext to the passed in region.
* The region is in device coordinates!
* @param aRegion The region to set the clipping area to, IN DEVICE COORDINATES
* @param aCombine how to combine this region with the current clip region.
* see the bottom of nsIRenderingContext.h
@ -255,6 +258,7 @@ public:
/**
* Gets a copy of the current clipping region for the RenderingContext
* The region is in device coordinates!
* @param aRegion inout parameter representing the clip region.
* if SetClipRegion() is called, do not assume that GetClipRegion()
* will return the same object.
@ -263,6 +267,7 @@ public:
/**
* Gets the current clipping region for the RenderingContext
* The region is in device coordinates!
* @param aRegion out parameter representing the clip region.
* if SetClipRegion() is called, do not assume that GetClipRegion()
* will return the same object.

View File

@ -75,6 +75,7 @@ NS_IMPL_ISUPPORTS1(nsBlender, nsIBlender);
#define BLEND_RED_SHIFT 7
#define BLEND_GREEN_SHIFT 2
#define BLEND_BLUE_SHIFT 3
#define BLEND_GREEN_BITS 5
#else // XP_WIN, XP_UNIX, ???
@ -87,6 +88,7 @@ NS_IMPL_ISUPPORTS1(nsBlender, nsIBlender);
#define BLEND_RED_SHIFT 8
#define BLEND_GREEN_SHIFT 3
#define BLEND_BLUE_SHIFT 3
#define BLEND_GREEN_BITS 6
#endif
@ -102,6 +104,10 @@ nsBlender::Init(nsIDeviceContext *aContext)
return NS_OK;
}
#define RED16(x) (((x) & BLEND_RED_MASK) >> BLEND_RED_SHIFT)
#define GREEN16(x) (((x) & BLEND_GREEN_MASK) >> BLEND_GREEN_SHIFT)
#define BLUE16(x) (((x) & BLEND_BLUE_MASK) << BLEND_BLUE_SHIFT)
static void rangeCheck(nsIDrawingSurface* surface, PRInt32& aX, PRInt32& aY, PRInt32& aWidth, PRInt32& aHeight)
{
PRUint32 width, height;
@ -228,6 +234,174 @@ NS_IMETHODIMP nsBlender::Blend(PRInt32 aSX, PRInt32 aSY, PRInt32 aWidth, PRInt32
aSecondSrcBackColor);
}
#ifndef MOZ_XUL
NS_IMETHODIMP nsBlender::GetAlphas(const nsRect& aRect, nsDrawingSurface aBlack,
nsDrawingSurface aWhite, PRUint8** aAlphas) {
NS_ERROR("GetAlphas not implemented because XUL support not built");
return NS_ERROR_NOT_IMPLEMENTED;
}
#else
/**
* Let A be the unknown source pixel's alpha value and let C be its (unknown) color.
* Let S be the value painted onto black and T be the value painted onto white.
* Then S = C*(A/255) and T = 255*(1 - A/255) + C*(A/255).
* Therefore A = 255 - (T - S)
* This is true no matter what color component we look at.
*/
static void ComputeAlphasByByte(PRInt32 aNumLines, PRInt32 aBytesPerLine,
PRInt32 aBytesPerPixel,
PRUint8 *aOnBlackImage, PRUint8 *aOnWhiteImage,
PRInt32 aBytesLineSpan, PRUint8 *aAlphas,
PRUint32 aAlphasSize)
{
NS_ASSERTION(aBytesPerPixel == 3 || aBytesPerPixel == 4,
"Only 24 or 32 bits per pixel supported here");
PRIntn y;
PRUint8* alphas = aAlphas;
for (y = 0; y < aNumLines; y++) {
// Look at component #1. It must be a real color no matter what
// RGBA ordering is used.
PRUint8 *s1 = aOnBlackImage + 1;
PRUint8 *s2 = aOnWhiteImage + 1;
PRIntn i;
for (i = 1; i < aBytesPerLine; i += aBytesPerPixel) {
*alphas++ = (PRUint8)(255 - (*s2 - *s1));
s1 += aBytesPerPixel;
s2 += aBytesPerPixel;
}
aOnBlackImage += aBytesLineSpan;
aOnWhiteImage += aBytesLineSpan;
}
NS_ASSERTION(alphas - aAlphas == aAlphasSize, "alpha24/32 calculation error");
}
/** Use the green channel to work out the alpha value,
since green has the most bits in most divisions of 16-bit color.
The green values range from 0 to (1 << BLEND_GREEN_BITS) - 1.
Therefore we multiply a green value by 255/((1 << BLEND_GREEN_BITS) - 1)
to get a real alpha value.
*/
static void ComputeAlphas16(PRInt32 aNumLines, PRInt32 aBytesPerLine,
PRUint8 *aOnBlackImage, PRUint8 *aOnWhiteImage,
PRInt32 aBytesLineSpan, PRUint8 *aAlphas,
PRUint32 aAlphasSize)
{
PRIntn y;
PRUint8* alphas = aAlphas;
for (y = 0; y < aNumLines; y++) {
PRUint16 *s1 = (PRUint16*)aOnBlackImage;
PRUint16 *s2 = (PRUint16*)aOnWhiteImage;
// GREEN16 returns a value between 0 and 255 representing the
// green value of the pixel. It only has BLEND_GREEN_BITS of
// precision (so the values are typically 0, 8, 16, ..., 248).
// If we just used the GREEN16 values
// directly in the same equations that we use for the 24-bit case,
// we'd lose because (e.g.) a completely transparent pixel would
// have GREEN16(pix1) = 0, GREEN16(pix2) = 248, and the resulting
// alpha value would just be 248, but we need 255. So we need to
// do some rescaling.
const PRUint32 SCALE_DENOMINATOR = // usually 248
((1 << BLEND_GREEN_BITS) - 1) << (8 - BLEND_GREEN_BITS);
PRIntn i;
for (i = 0; i < aBytesPerLine; i += 2) {
PRUint32 pix1 = GREEN16(*s1);
PRUint32 pix2 = GREEN16(*s2);
*alphas++ = (PRUint8)(255 - ((pix2 - pix1)*255)/SCALE_DENOMINATOR);
s1++;
s2++;
}
aOnBlackImage += aBytesLineSpan;
aOnWhiteImage += aBytesLineSpan;
}
NS_ASSERTION(alphas - aAlphas == aAlphasSize, "alpha16 calculation error");
}
static void ComputeAlphas(PRInt32 aNumLines, PRInt32 aBytesPerLine,
PRInt32 aDepth,
PRUint8 *aOnBlackImage, PRUint8 *aOnWhiteImage,
PRInt32 aBytesLineSpan, PRUint8 *aAlphas,
PRUint32 aAlphasSize)
{
switch (aDepth) {
case 32:
case 24:
ComputeAlphasByByte(aNumLines, aBytesPerLine, aDepth/8,
aOnBlackImage, aOnWhiteImage,
aBytesLineSpan, aAlphas, aAlphasSize);
break;
case 16:
ComputeAlphas16(aNumLines, aBytesPerLine, aOnBlackImage, aOnWhiteImage,
aBytesLineSpan, aAlphas, aAlphasSize);
break;
default:
NS_ERROR("Unknown depth for alpha calculation");
// make them all opaque
memset(aAlphas, 255, aAlphasSize);
}
}
NS_IMETHODIMP nsBlender::GetAlphas(const nsRect& aRect, nsDrawingSurface aBlack,
nsDrawingSurface aWhite, PRUint8** aAlphas) {
nsresult result;
nsIDrawingSurface* blackSurface = (nsIDrawingSurface *)aBlack;
nsIDrawingSurface* whiteSurface = (nsIDrawingSurface *)aWhite;
nsRect r = aRect;
rangeCheck(blackSurface, r.x, r.y, r.width, r.height);
rangeCheck(whiteSurface, r.x, r.y, r.width, r.height);
PRUint8* blackBytes = nsnull;
PRUint8* whiteBytes = nsnull;
PRInt32 blackSpan, whiteSpan;
PRInt32 blackBytesPerLine, whiteBytesPerLine;
result = blackSurface->Lock(r.x, r.y, r.width, r.height,
(void**)&blackBytes, &blackSpan,
&blackBytesPerLine, NS_LOCK_SURFACE_READ_ONLY);
if (NS_SUCCEEDED(result)) {
result = whiteSurface->Lock(r.x, r.y, r.width, r.height,
(void**)&whiteBytes, &whiteSpan,
&whiteBytesPerLine, NS_LOCK_SURFACE_READ_ONLY);
if (NS_SUCCEEDED(result)) {
NS_ASSERTION(blackSpan == whiteSpan &&
blackBytesPerLine == whiteBytesPerLine,
"Mismatched bitmap formats (black/white) in Blender");
if (blackSpan == whiteSpan && blackBytesPerLine == whiteBytesPerLine) {
*aAlphas = new PRUint8[r.width*r.height];
if (*aAlphas) {
PRUint32 depth;
mContext->GetDepth(depth);
ComputeAlphas(r.height, blackBytesPerLine, depth,
blackBytes, whiteBytes, blackSpan,
*aAlphas, r.width*r.height);
} else {
result = NS_ERROR_FAILURE;
}
}
whiteSurface->Unlock();
}
blackSurface->Unlock();
}
return result;
}
#endif // MOZ_XUL
/** ---------------------------------------------------
* See documentation in nsBlender.h
* @update 2/25/00 dwc
@ -526,10 +700,6 @@ nsBlender::Do24Blend(float aOpacity, PRInt32 aNumLines, PRInt32 aNumBytes,
#define RED16(x) (((x) & BLEND_RED_MASK) >> BLEND_RED_SHIFT)
#define GREEN16(x) (((x) & BLEND_GREEN_MASK) >> BLEND_GREEN_SHIFT)
#define BLUE16(x) (((x) & BLEND_BLUE_MASK) << BLEND_BLUE_SHIFT)
#define MAKE16(r, g, b) \
(PRUint16)(((r) & BLEND_RED_SET_MASK) << BLEND_RED_SHIFT) \
| (((g) & BLEND_GREEN_SET_MASK) << BLEND_GREEN_SHIFT) \
@ -573,7 +743,6 @@ nsBlender::Do16Blend(float aOpacity, PRInt32 aNumLines, PRInt32 aNumBytes,
*d2 = MAKE16(destPixR + (((RED16(srcPix) - destPixR)*opacity256) >> 8),
destPixG + (((GREEN16(srcPix) - destPixG)*opacity256) >> 8),
destPixB + (((BLUE16(srcPix) - destPixB)*opacity256) >> 8));
d2++;
s2++;
}

View File

@ -60,6 +60,9 @@ public:
nsIRenderingContext *aSecondSrc = nsnull, nscolor aSrcBackColor = NS_RGB(0, 0, 0),
nscolor aSecondSrcBackColor = NS_RGB(0, 0, 0));
NS_IMETHOD GetAlphas(const nsRect& aRect, nsDrawingSurface aBlack,
nsDrawingSurface aWhite, PRUint8** aAlphas);
protected:
/** --------------------------------------------------------------------------

View File

@ -2615,14 +2615,32 @@ nsCSSRendering::FindNonTransparentBackground(nsStyleContext* aContext,
* canvas.
*/
inline PRBool
IsCanvasFrame(nsIFrame *aFrame)
// Returns nsnull if aFrame is not a canvas frame.
// Otherwise, it returns the frame we should look for the background on.
// This is normally aFrame but if aFrame is the viewport, we need to
// look for the background starting at the scroll root (which shares
// style context with the document root) or the document root itself.
// We need to treat the viewport as canvas because, even though
// it does not actually paint a background, we need to get the right
// background style so we correctly detect transparent documents.
inline nsIFrame*
IsCanvasFrame(nsIPresContext* aPresContext, nsIFrame *aFrame)
{
nsCOMPtr<nsIAtom> frameType;
aFrame->GetFrameType(getter_AddRefs(frameType));
return (frameType == nsLayoutAtoms::canvasFrame ||
frameType == nsLayoutAtoms::rootFrame ||
frameType == nsLayoutAtoms::pageFrame);
if (frameType == nsLayoutAtoms::canvasFrame ||
frameType == nsLayoutAtoms::rootFrame ||
frameType == nsLayoutAtoms::pageFrame) {
return aFrame;
} else if (frameType == nsLayoutAtoms::viewportFrame) {
nsIFrame* firstChild;
aFrame->FirstChild(aPresContext, nsnull, &firstChild);
if (firstChild) {
return firstChild;
}
}
return nsnull;
}
inline PRBool
@ -2719,7 +2737,7 @@ FindElementBackground(nsIPresContext* aPresContext,
nsIFrame *parentFrame;
aForFrame->GetParent(&parentFrame);
// XXXldb We shouldn't have to null-check |parentFrame| here.
if (parentFrame && IsCanvasFrame(parentFrame)) {
if (parentFrame && IsCanvasFrame(aPresContext, parentFrame) == parentFrame) {
// Check that we're really the root (rather than in another child list).
nsIFrame *childFrame;
parentFrame->FirstChild(aPresContext, nsnull, &childFrame);
@ -2761,10 +2779,10 @@ nsCSSRendering::FindBackground(nsIPresContext* aPresContext,
const nsStyleBackground** aBackground,
PRBool* aIsCanvas)
{
PRBool isCanvas = IsCanvasFrame(aForFrame);
*aIsCanvas = isCanvas;
return isCanvas
? FindCanvasBackground(aPresContext, aForFrame, aBackground)
nsIFrame* canvasFrame = IsCanvasFrame(aPresContext, aForFrame);
*aIsCanvas = canvasFrame != nsnull;
return canvasFrame
? FindCanvasBackground(aPresContext, canvasFrame, aBackground)
: FindElementBackground(aPresContext, aForFrame, aBackground);
}
@ -2836,11 +2854,21 @@ nsCSSRendering::PaintBackground(nsIPresContext* aPresContext,
vm->GetRootView(rootView);
nsIView* rootParent;
rootView->GetParent(rootParent);
if (nsnull == rootParent) {
// Ensure that we always paint a color for the root (in case there's
// no background at all or a partly transparent image).
canvasColor.mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
aPresContext->GetDefaultBackgroundColor(&canvasColor.mBackgroundColor);
if (!rootParent) {
PRBool widgetIsTranslucent = PR_FALSE;
nsCOMPtr<nsIWidget> rootWidget;
rootView->GetWidget(*getter_AddRefs(rootWidget));
if (rootWidget) {
rootWidget->GetWindowTranslucency(widgetIsTranslucent);
}
if (!widgetIsTranslucent) {
// Ensure that we always paint a color for the root (in case there's
// no background at all or a partly transparent image).
canvasColor.mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
aPresContext->GetDefaultBackgroundColor(&canvasColor.mBackgroundColor);
}
}
}

View File

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsContainerFrame.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsStyleContext.h"
@ -558,15 +559,42 @@ SyncFrameViewGeometryDependentProperties(nsIPresContext* aPresContext,
(bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) ||
!aFrame->CanPaintBackground() ||
HasNonZeroBorderRadius(aStyleContext);
if (isCanvas && viewHasTransparentContent) {
if (isCanvas) {
nsIView* rootView;
vm->GetRootView(rootView);
nsIView* rootParent;
rootView->GetParent(rootParent);
if (nsnull == rootParent) {
if (!rootParent) {
// We're the root of a view manager hierarchy. We will have to
// paint something. NOTE: this can be overridden below.
viewHasTransparentContent = PR_FALSE;
}
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
nsCOMPtr<nsIDocument> doc;
shell->GetDocument(getter_AddRefs(doc));
if (doc) {
nsCOMPtr<nsIDocument> parentDoc;
doc->GetParentDocument(getter_AddRefs(parentDoc));
nsCOMPtr<nsIContent> rootElem;
doc->GetRootContent(getter_AddRefs(rootElem));
if (!parentDoc && rootElem && rootElem->IsContentOfType(nsIContent::eXUL)) {
// we're XUL at the root of the document hierarchy. Try to make our
// window translucent.
nsCOMPtr<nsIWidget> widget;
aView->GetWidget(*getter_AddRefs(widget));
// don't proceed unless this is the root view
// (sometimes the non-root-view is a canvas)
if (widget && aView == rootView) {
viewHasTransparentContent = hasBG && (bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT);
widget->SetWindowTranslucency(viewHasTransparentContent);
}
}
}
}
// XXX we should also set widget transparency for XUL popups
const nsStyleDisplay* display;
::GetStyleData(aStyleContext, &display);

View File

@ -37,6 +37,7 @@
* ***** END LICENSE BLOCK ***** */
#include "nsContainerFrame.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsStyleContext.h"
@ -558,15 +559,42 @@ SyncFrameViewGeometryDependentProperties(nsIPresContext* aPresContext,
(bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT) ||
!aFrame->CanPaintBackground() ||
HasNonZeroBorderRadius(aStyleContext);
if (isCanvas && viewHasTransparentContent) {
if (isCanvas) {
nsIView* rootView;
vm->GetRootView(rootView);
nsIView* rootParent;
rootView->GetParent(rootParent);
if (nsnull == rootParent) {
if (!rootParent) {
// We're the root of a view manager hierarchy. We will have to
// paint something. NOTE: this can be overridden below.
viewHasTransparentContent = PR_FALSE;
}
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
nsCOMPtr<nsIDocument> doc;
shell->GetDocument(getter_AddRefs(doc));
if (doc) {
nsCOMPtr<nsIDocument> parentDoc;
doc->GetParentDocument(getter_AddRefs(parentDoc));
nsCOMPtr<nsIContent> rootElem;
doc->GetRootContent(getter_AddRefs(rootElem));
if (!parentDoc && rootElem && rootElem->IsContentOfType(nsIContent::eXUL)) {
// we're XUL at the root of the document hierarchy. Try to make our
// window translucent.
nsCOMPtr<nsIWidget> widget;
aView->GetWidget(*getter_AddRefs(widget));
// don't proceed unless this is the root view
// (sometimes the non-root-view is a canvas)
if (widget && aView == rootView) {
viewHasTransparentContent = hasBG && (bg->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT);
widget->SetWindowTranslucency(viewHasTransparentContent);
}
}
}
}
// XXX we should also set widget transparency for XUL popups
const nsStyleDisplay* display;
::GetStyleData(aStyleContext, &display);

View File

@ -2615,14 +2615,32 @@ nsCSSRendering::FindNonTransparentBackground(nsStyleContext* aContext,
* canvas.
*/
inline PRBool
IsCanvasFrame(nsIFrame *aFrame)
// Returns nsnull if aFrame is not a canvas frame.
// Otherwise, it returns the frame we should look for the background on.
// This is normally aFrame but if aFrame is the viewport, we need to
// look for the background starting at the scroll root (which shares
// style context with the document root) or the document root itself.
// We need to treat the viewport as canvas because, even though
// it does not actually paint a background, we need to get the right
// background style so we correctly detect transparent documents.
inline nsIFrame*
IsCanvasFrame(nsIPresContext* aPresContext, nsIFrame *aFrame)
{
nsCOMPtr<nsIAtom> frameType;
aFrame->GetFrameType(getter_AddRefs(frameType));
return (frameType == nsLayoutAtoms::canvasFrame ||
frameType == nsLayoutAtoms::rootFrame ||
frameType == nsLayoutAtoms::pageFrame);
if (frameType == nsLayoutAtoms::canvasFrame ||
frameType == nsLayoutAtoms::rootFrame ||
frameType == nsLayoutAtoms::pageFrame) {
return aFrame;
} else if (frameType == nsLayoutAtoms::viewportFrame) {
nsIFrame* firstChild;
aFrame->FirstChild(aPresContext, nsnull, &firstChild);
if (firstChild) {
return firstChild;
}
}
return nsnull;
}
inline PRBool
@ -2719,7 +2737,7 @@ FindElementBackground(nsIPresContext* aPresContext,
nsIFrame *parentFrame;
aForFrame->GetParent(&parentFrame);
// XXXldb We shouldn't have to null-check |parentFrame| here.
if (parentFrame && IsCanvasFrame(parentFrame)) {
if (parentFrame && IsCanvasFrame(aPresContext, parentFrame) == parentFrame) {
// Check that we're really the root (rather than in another child list).
nsIFrame *childFrame;
parentFrame->FirstChild(aPresContext, nsnull, &childFrame);
@ -2761,10 +2779,10 @@ nsCSSRendering::FindBackground(nsIPresContext* aPresContext,
const nsStyleBackground** aBackground,
PRBool* aIsCanvas)
{
PRBool isCanvas = IsCanvasFrame(aForFrame);
*aIsCanvas = isCanvas;
return isCanvas
? FindCanvasBackground(aPresContext, aForFrame, aBackground)
nsIFrame* canvasFrame = IsCanvasFrame(aPresContext, aForFrame);
*aIsCanvas = canvasFrame != nsnull;
return canvasFrame
? FindCanvasBackground(aPresContext, canvasFrame, aBackground)
: FindElementBackground(aPresContext, aForFrame, aBackground);
}
@ -2836,11 +2854,21 @@ nsCSSRendering::PaintBackground(nsIPresContext* aPresContext,
vm->GetRootView(rootView);
nsIView* rootParent;
rootView->GetParent(rootParent);
if (nsnull == rootParent) {
// Ensure that we always paint a color for the root (in case there's
// no background at all or a partly transparent image).
canvasColor.mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
aPresContext->GetDefaultBackgroundColor(&canvasColor.mBackgroundColor);
if (!rootParent) {
PRBool widgetIsTranslucent = PR_FALSE;
nsCOMPtr<nsIWidget> rootWidget;
rootView->GetWidget(*getter_AddRefs(rootWidget));
if (rootWidget) {
rootWidget->GetWindowTranslucency(widgetIsTranslucent);
}
if (!widgetIsTranslucent) {
// Ensure that we always paint a color for the root (in case there's
// no background at all or a partly transparent image).
canvasColor.mBackgroundFlags &= ~NS_STYLE_BG_COLOR_TRANSPARENT;
aPresContext->GetDefaultBackgroundColor(&canvasColor.mBackgroundColor);
}
}
}

View File

@ -99,6 +99,33 @@ static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
#define SUPPORT_TRANSLUCENT_VIEWS
/*
This class encapsulates all the offscreen buffers we might want to
render into. mOffscreen is the basic buffer for doing
double-buffering. mBlack and mWhite are buffers for collecting an
individual view drawn onto black and drawn onto white, from which we
can recover the alpha values of the view's pixels. mOffscreenWhite
is only for when the widget being rendered is transparent; it's an
alternative double-buffer buffer that starts off white instead of
black, and allows us to recover alpha values for the widget.
*/
class BlendingBuffers {
public:
BlendingBuffers(nsIRenderingContext* aCleanupContext);
~BlendingBuffers();
nsCOMPtr<nsIRenderingContext> mCleanupContext;
nsCOMPtr<nsIRenderingContext> mOffScreenCX;
nsCOMPtr<nsIRenderingContext> mOffScreenWhiteCX;
nsCOMPtr<nsIRenderingContext> mBlackCX;
nsCOMPtr<nsIRenderingContext> mWhiteCX;
nsDrawingSurface mOffScreen;
nsDrawingSurface mOffScreenWhite;
nsDrawingSurface mBlack;
nsDrawingSurface mWhite;
};
// A DisplayListElement2 records the information needed to paint one view.
// Note that child views get their own DisplayListElement2s; painting a view
// paints that view's frame and all its child frames EXCEPT for the child frames
@ -291,10 +318,6 @@ nsViewManager::PostInvalidateEvent()
PRInt32 nsViewManager::mVMCount = 0;
nsIRenderingContext* nsViewManager::gCleanupContext = nsnull;
nsDrawingSurface nsViewManager::gOffScreen = nsnull;
nsDrawingSurface nsViewManager::gBlack = nsnull;
nsDrawingSurface nsViewManager::gWhite = nsnull;
nsSize nsViewManager::gOffScreenSize = nsSize(0, 0);
// Weakly held references to all of the view managers
nsVoidArray* nsViewManager::gViewManagers = nsnull;
@ -366,37 +389,16 @@ nsViewManager::~nsViewManager()
if (gCleanupContext) {
gCleanupContext->DestroyCachedBackbuffer();
if (nsnull != gOffScreen)
gCleanupContext->DestroyDrawingSurface(gOffScreen);
if (nsnull != gWhite)
gCleanupContext->DestroyDrawingSurface(gWhite);
if (nsnull != gBlack)
gCleanupContext->DestroyDrawingSurface(gBlack);
} else {
NS_ASSERTION(PR_FALSE, "Cleanup of drawing surfaces + offscreen buffer failed");
}
gOffScreen = nsnull;
gWhite = nsnull;
gBlack = nsnull;
gOffScreenSize.SizeTo(0, 0);
NS_IF_RELEASE(gCleanupContext);
}
mObserver = nsnull;
mContext = nsnull;
NS_IF_RELEASE(mBlender);
NS_IF_RELEASE(mOffScreenCX);
NS_IF_RELEASE(mBlackCX);
NS_IF_RELEASE(mWhiteCX);
if (nsnull != mCompositeListeners) {
mCompositeListeners->Clear();
NS_RELEASE(mCompositeListeners);
@ -637,15 +639,15 @@ static void ConvertNativeRegionToAppRegion(nsIRegion* aIn, nsRegion* aOut,
float p2t;
context->GetDevUnitsToAppUnits(p2t);
// roc says:
// we mustn't introduce any off by one errors here, so stick to integer arithmetic
// this assumes that p2t is an integer, but if it isn't, all kinds of other stuff breaks anyway
int pt2i = (int)floor(p2t+0.5);
PRUint32 i;
for (i = 0; i < rects->mNumRects; i++) {
const nsRegionRect& inR = rects->mRects[i];
nsRect outR(inR.x*pt2i, inR.y*pt2i, inR.width*pt2i, inR.height*pt2i);
nsRect outR;
outR.x = NSToIntRound(inR.x * p2t);
outR.y = NSToIntRound(inR.y * p2t);
outR.width = NSToIntRound(inR.width * p2t);
outR.height = NSToIntRound(inR.height * p2t);
aOut->Or(*aOut, outR);
}
@ -1028,13 +1030,11 @@ static void PushStateAndClip(nsIRenderingContext **aRCs, PRInt32 aRCCount, nsRec
PRInt32 aDX, PRInt32 aDY) {
PRBool clipEmpty;
nsRect rect = aRect;
rect.x -= aDX;
rect.y -= aDY;
for (PRInt32 i = 0; i < aRCCount; i++) {
aRCs[i]->PushState();
if (i == 1) {
rect.x -= aDX;
rect.y -= aDY;
}
aRCs[i]->SetClipRect(rect, nsClipCombine_kIntersect, clipEmpty);
aRCs[i]->SetClipRect(i == 0 ? aRect : rect, nsClipCombine_kIntersect, clipEmpty);
}
}
@ -1150,15 +1150,30 @@ void nsViewManager::RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
// We keep a list of all the rendering contexts whose clip rects
// need to be updated.
nsIRenderingContext* RCList[4];
nsIRenderingContext* RCList[5];
PRInt32 RCCount = 1;
RCList[0] = &aRC;
nsCOMPtr<nsIWidget> widget;
aRootView->GetWidget(*getter_AddRefs(widget));
PRBool translucentWindow = PR_FALSE;
if (widget) {
widget->GetWindowTranslucency(translucentWindow);
if (translucentWindow) {
NS_WARNING("Transparent window enabled");
// for a translucent window, we'll pretend the whole area is
// translucent.
mTranslucentArea = aRegion.GetBounds();
}
}
BlendingBuffers* buffers = nsnull;
// create blending buffers, if necessary.
if (mTranslucentViewCount > 0) {
nsresult rv = CreateBlendingBuffers(aRC);
NS_ASSERTION(NS_SUCCEEDED(rv), "not enough memory to blend");
if (NS_FAILED(rv)) {
if (mTranslucentViewCount > 0 || translucentWindow) {
buffers = CreateBlendingBuffers(&aRC, translucentWindow);
NS_ASSERTION(buffers, "not enough memory to blend");
if (!buffers) {
// fall back by just rendering with transparency.
mTranslucentViewCount = 0;
for (PRInt32 i = mDisplayListCount - 1; i>= 0; --i) {
@ -1166,18 +1181,23 @@ void nsViewManager::RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
element->mFlags &= ~VIEW_TRANSLUCENT;
}
} else {
RCCount = 4;
RCList[1] = mBlackCX;
RCList[2] = mWhiteCX;
RCList[3] = mOffScreenCX;
RCList[RCCount++] = buffers->mOffScreenCX;
if (translucentWindow) {
RCList[RCCount++] = buffers->mOffScreenWhiteCX;
}
if (mTranslucentViewCount > 0) {
RCList[RCCount++] = buffers->mBlackCX;
RCList[RCCount++] = buffers->mWhiteCX;
}
if (!translucentWindow && !finalTransparentRect.IsEmpty()) {
// There are some bits that aren't going to be completely painted, so
// make sure we don't leave garbage in the offscreen context
buffers->mOffScreenCX->SetColor(NS_RGB(128, 128, 128));
buffers->mOffScreenCX->FillRect(nsRect(0, 0, mTranslucentArea.width, mTranslucentArea.height));
}
}
if (!finalTransparentRect.IsEmpty()) {
// There are some bits that aren't going to be completely painted, so
// make sure we don't leave garbage in the offscreen context
mOffScreenCX->SetColor(NS_RGB(128, 128, 128));
mOffScreenCX->FillRect(nsRect(0, 0, gOffScreenSize.width, gOffScreenSize.height));
}
// DEBUGGING: fill in complete offscreen image in green, to see if we've got a blending bug.
// mOffScreenCX->SetColor(NS_RGB(0, 255, 0));
// mOffScreenCX->FillRect(nsRect(0, 0, gOffScreenSize.width, gOffScreenSize.height));
@ -1192,10 +1212,10 @@ void nsViewManager::RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
if (element->mFlags & VIEW_CLIPPED) {
//Render the view using the clip rect set by it's ancestors
PushStateAndClip(RCList, RCCount, element->mBounds, mTranslucentArea.x, mTranslucentArea.y);
RenderDisplayListElement(element, aRC);
RenderDisplayListElement(element, aRC, buffers);
PopState(RCList, RCCount);
} else {
RenderDisplayListElement(element, aRC);
RenderDisplayListElement(element, aRC, buffers);
}
} else {
@ -1214,31 +1234,52 @@ void nsViewManager::RenderViews(nsView *aRootView, nsIRenderingContext& aRC,
// flush bits back to screen.
// Must flush back when no clipping is in effect.
if (mTranslucentViewCount > 0) {
if (buffers) {
if (translucentWindow) {
// Set window alphas
nsRect r = mTranslucentArea;
r *= mTwipsToPixels;
nsRect bufferRect(0, 0, r.width, r.height);
PRUint8* alphas = nsnull;
nsresult rv = mBlender->GetAlphas(bufferRect, buffers->mOffScreen,
buffers->mOffScreenWhite, &alphas);
if (NS_SUCCEEDED(rv)) {
widget->UpdateTranslucentWindowAlpha(r, alphas);
}
delete[] alphas;
}
// DEBUG: is this getting through?
// mOffScreenCX->SetColor(NS_RGB(177, 177, 0));
// mOffScreenCX->FillRect(nsRect(1, 1, mTranslucentArea.width-2, mTranslucentArea.height-2));
aRC.CopyOffScreenBits(gOffScreen, 0, 0, mTranslucentArea,
aRC.CopyOffScreenBits(buffers->mOffScreen, 0, 0, mTranslucentArea,
NS_COPYBITS_XFORM_DEST_VALUES |
NS_COPYBITS_TO_BACK_BUFFER);
// DEBUG: what rectangle are we blitting?
// aRC.SetColor(NS_RGB(0, 177, 177));
// aRC.DrawRect(mTranslucentArea);
delete buffers;
}
mDisplayList.Clear();
}
void nsViewManager::RenderDisplayListElement(DisplayListElement2* element, nsIRenderingContext &aRC)
void nsViewManager::RenderDisplayListElement(DisplayListElement2* element,
nsIRenderingContext &aRC,
BlendingBuffers* aBuffers)
{
PRBool isTranslucent = (element->mFlags & VIEW_TRANSLUCENT) != 0;
PRBool clipEmpty;
nsRect r;
nsView* view = element->mView;
view->GetDimensions(r);
if (!isTranslucent) {
// if this element is contained by the translucent area, then
// there's no point in drawing it here because it will be
// overwritten by the final copy of the composited offscreen area
if (!aBuffers || !mTranslucentArea.Contains(element->mBounds)) {
aRC.PushState();
nscoord x = element->mAbsX - r.x, y = element->mAbsY - r.y;
@ -1253,11 +1294,7 @@ void nsViewManager::RenderDisplayListElement(DisplayListElement2* element, nsIRe
}
#if defined(SUPPORT_TRANSLUCENT_VIEWS)
if (mTranslucentViewCount > 0 && (isTranslucent || mTranslucentArea.Intersects(element->mBounds))) {
// 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 (aBuffers && mTranslucentArea.Intersects(element->mBounds)) {
// compute the origin of the view, relative to the offscreen buffer, which has the
// same dimensions as mTranslucentArea.
nscoord x = element->mAbsX - r.x, y = element->mAbsY - r.y;
@ -1267,16 +1304,21 @@ void nsViewManager::RenderDisplayListElement(DisplayListElement2* element, nsIRe
damageRect.IntersectRect(damageRect, mTranslucentArea);
// -> coordinates relative to element->mView origin
damageRect.x -= x, damageRect.y -= y;
// the element must be rendered into mOffscreenCX and also
// mOffScreenWhiteCX if the window is transparent/translucent.
nsIRenderingContext* targets[2] =
{ aBuffers->mOffScreenCX, aBuffers->mOffScreenWhiteCX };
if (element->mFlags & VIEW_TRANSLUCENT) {
// paint the view twice, first in the black buffer, then the white;
// the blender will pick up the touched pixels only.
PaintView(view, *mBlackCX, viewX, viewY, damageRect);
PaintView(view, *aBuffers->mBlackCX, viewX, viewY, damageRect);
// DEBUGGING ONLY
//aRC.CopyOffScreenBits(gBlack, 0, 0, element->mBounds,
// NS_COPYBITS_XFORM_DEST_VALUES | NS_COPYBITS_TO_BACK_BUFFER);
PaintView(view, *mWhiteCX, viewX, viewY, damageRect);
PaintView(view, *aBuffers->mWhiteCX, viewX, viewY, damageRect);
// DEBUGGING ONLY
//aRC.CopyOffScreenBits(gWhite, 0, 0, element->mBounds,
// NS_COPYBITS_XFORM_DEST_VALUES | NS_COPYBITS_TO_BACK_BUFFER);
@ -1293,17 +1335,22 @@ void nsViewManager::RenderDisplayListElement(DisplayListElement2* element, nsIRe
nsRect damageRectInPixels = damageRect;
damageRectInPixels *= mTwipsToPixels;
if (damageRectInPixels.width > 0 && damageRectInPixels.height > 0) {
nsresult rv = mBlender->Blend(damageRectInPixels.x, damageRectInPixels.y,
damageRectInPixels.width, damageRectInPixels.height,
mBlackCX, mOffScreenCX,
damageRectInPixels.x, damageRectInPixels.y,
opacity, mWhiteCX,
NS_RGB(0, 0, 0), NS_RGB(255, 255, 255));
if (NS_FAILED(rv)) {
NS_WARNING("Blend failed!");
// let's paint SOMETHING. Paint opaquely
damageRect.MoveBy(-viewX, -viewY);
PaintView(view, *mOffScreenCX, viewX, viewY, damageRect);
PRIntn i;
for (i = 0; i < 2; i++) {
if (targets[i]) {
nsresult rv = mBlender->Blend(damageRectInPixels.x, damageRectInPixels.y,
damageRectInPixels.width, damageRectInPixels.height,
aBuffers->mBlackCX, targets[i],
damageRectInPixels.x, damageRectInPixels.y,
opacity, aBuffers->mWhiteCX,
NS_RGB(0, 0, 0), NS_RGB(255, 255, 255));
if (NS_FAILED(rv)) {
NS_WARNING("Blend failed!");
// let's paint SOMETHING. Paint opaquely
damageRect.MoveBy(-viewX, -viewY);
PaintView(view, *targets[i], viewX, viewY, damageRect);
}
}
}
}
@ -1311,12 +1358,17 @@ void nsViewManager::RenderDisplayListElement(DisplayListElement2* element, nsIRe
// We do that here because we know that whatever the clip region is,
// everything we just painted is within the clip region so we are
// sure to be able to overwrite it now.
mBlackCX->SetColor(NS_RGB(0, 0, 0));
mBlackCX->FillRect(damageRect);
mWhiteCX->SetColor(NS_RGB(255, 255, 255));
mWhiteCX->FillRect(damageRect);
aBuffers->mBlackCX->SetColor(NS_RGB(0, 0, 0));
aBuffers->mBlackCX->FillRect(damageRect);
aBuffers->mWhiteCX->SetColor(NS_RGB(255, 255, 255));
aBuffers->mWhiteCX->FillRect(damageRect);
} else {
PaintView(view, *mOffScreenCX, viewX, viewY, damageRect);
PRIntn i;
for (i = 0; i < 2; i++) {
if (targets[i]) {
PaintView(view, *targets[i], viewX, viewY, damageRect);
}
}
}
}
#endif
@ -1332,14 +1384,6 @@ void nsViewManager::PaintView(nsView *aView, nsIRenderingContext &aRC, nscoord x
aRC.PopState(unused);
}
inline PRInt32 nextPowerOf2(PRInt32 value)
{
PRInt32 result = 1;
while (value > result)
result <<= 1;
return result;
}
static nsresult NewOffscreenContext(nsIDeviceContext* deviceContext, nsDrawingSurface surface,
const nsSize& size, nsIRenderingContext* *aResult)
{
@ -1359,87 +1403,119 @@ static nsresult NewOffscreenContext(nsIDeviceContext* deviceContext, nsDrawingSu
return NS_OK;
}
nsresult nsViewManager::CreateBlendingBuffers(nsIRenderingContext &aRC)
BlendingBuffers::BlendingBuffers(nsIRenderingContext* aCleanupContext) {
mCleanupContext = aCleanupContext;
mOffScreen = nsnull;
mOffScreenWhite = nsnull;
mWhite = nsnull;
mBlack = nsnull;
}
BlendingBuffers::~BlendingBuffers() {
if (mOffScreen)
mCleanupContext->DestroyDrawingSurface(mOffScreen);
if (mOffScreenWhite)
mCleanupContext->DestroyDrawingSurface(mOffScreenWhite);
if (mWhite)
mCleanupContext->DestroyDrawingSurface(mWhite);
if (mBlack)
mCleanupContext->DestroyDrawingSurface(mBlack);
}
BlendingBuffers* nsViewManager::CreateBlendingBuffers(nsIRenderingContext *aRC,
PRBool aTranslucentWindow)
{
nsresult rv = NS_OK;
nsresult rv;
// create a blender, if none exists already.
if (nsnull == mBlender) {
rv = nsComponentManager::CreateInstance(kBlenderCID, nsnull, NS_GET_IID(nsIBlender), (void **)&mBlender);
if (!mBlender) {
mBlender = do_CreateInstance(kBlenderCID, &rv);
if (NS_FAILED(rv))
return rv;
return nsnull;
rv = mBlender->Init(mContext);
if (NS_FAILED(rv))
return rv;
return nsnull;
}
// ensure that the global drawing surfaces are large enough.
if (mTranslucentArea.width > gOffScreenSize.width || mTranslucentArea.height > gOffScreenSize.height) {
nsRect offscreenBounds(0, 0, mTranslucentArea.width, mTranslucentArea.height);
offscreenBounds.ScaleRoundOut(mTwipsToPixels);
offscreenBounds.width = nextPowerOf2(offscreenBounds.width);
offscreenBounds.height = nextPowerOf2(offscreenBounds.height);
BlendingBuffers* buffers = new BlendingBuffers(aRC);
if (!buffers)
return nsnull;
NS_IF_RELEASE(mOffScreenCX);
NS_IF_RELEASE(mBlackCX);
NS_IF_RELEASE(mWhiteCX);
nsRect offscreenBounds(0, 0, mTranslucentArea.width, mTranslucentArea.height);
offscreenBounds.ScaleRoundOut(mTwipsToPixels);
nsSize offscreenSize(mTranslucentArea.width, mTranslucentArea.height);
if (nsnull != gOffScreen) {
aRC.DestroyDrawingSurface(gOffScreen);
gOffScreen = nsnull;
rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mOffScreen);
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
rv = NewOffscreenContext(mContext, buffers->mOffScreen, offscreenSize, getter_AddRefs(buffers->mOffScreenCX));
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
if (mTranslucentViewCount > 0) {
rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mBlack);
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
rv = aRC.CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gOffScreen);
if (NS_FAILED(rv))
return rv;
if (nsnull != gBlack) {
aRC.DestroyDrawingSurface(gBlack);
gBlack = nsnull;
rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mWhite);
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
rv = aRC.CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gBlack);
if (NS_FAILED(rv))
return rv;
if (nsnull != gWhite) {
aRC.DestroyDrawingSurface(gWhite);
gWhite = nsnull;
rv = NewOffscreenContext(mContext, buffers->mBlack, offscreenSize, getter_AddRefs(buffers->mBlackCX));
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
rv = aRC.CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, gWhite);
if (NS_FAILED(rv))
return rv;
offscreenBounds.ScaleRoundIn(mPixelsToTwips);
gOffScreenSize.width = offscreenBounds.width;
gOffScreenSize.height = offscreenBounds.height;
rv = NewOffscreenContext(mContext, buffers->mWhite, offscreenSize, getter_AddRefs(buffers->mWhiteCX));
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
nsRect fillArea(0, 0, mTranslucentArea.width, mTranslucentArea.height);
buffers->mBlackCX->SetColor(NS_RGB(0, 0, 0));
buffers->mBlackCX->FillRect(fillArea);
buffers->mWhiteCX->SetColor(NS_RGB(255, 255, 255));
buffers->mWhiteCX->FillRect(fillArea);
}
// recreate local offscreen & blending contexts, if necessary.
if (mOffScreenCX == nsnull) {
rv = NewOffscreenContext(mContext, gOffScreen, gOffScreenSize, &mOffScreenCX);
if (NS_FAILED(rv))
return rv;
}
if (mBlackCX == nsnull) {
rv = NewOffscreenContext(mContext, gBlack, gOffScreenSize, &mBlackCX);
if (NS_FAILED(rv))
return rv;
}
if (mWhiteCX == nsnull) {
rv = NewOffscreenContext(mContext, gWhite, gOffScreenSize, &mWhiteCX);
if (NS_FAILED(rv))
return rv;
if (aTranslucentWindow) {
rv = aRC->CreateDrawingSurface(offscreenBounds, NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS, buffers->mOffScreenWhite);
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
rv = NewOffscreenContext(mContext, buffers->mOffScreenWhite, offscreenSize, getter_AddRefs(buffers->mOffScreenWhiteCX));
if (NS_FAILED(rv)) {
delete buffers;
return nsnull;
}
nsRect fillArea(0, 0, mTranslucentArea.width, mTranslucentArea.height);
buffers->mOffScreenCX->SetColor(NS_RGB(0, 0, 0));
buffers->mOffScreenCX->FillRect(fillArea);
buffers->mOffScreenWhiteCX->SetColor(NS_RGB(255, 255, 255));
buffers->mOffScreenWhiteCX->FillRect(fillArea);
}
nsRect fillArea = mTranslucentArea;
fillArea.x = 0;
fillArea.y = 0;
mBlackCX->SetColor(NS_RGB(0, 0, 0));
mBlackCX->FillRect(fillArea);
mWhiteCX->SetColor(NS_RGB(255, 255, 255));
mWhiteCX->FillRect(fillArea);
return NS_OK;
return buffers;
}
void nsViewManager::ProcessPendingUpdates(nsView* aView)

View File

@ -60,6 +60,7 @@ class nsIPresContext;
class nsISupportsArray;
struct DisplayListElement2;
struct DisplayZTreeNode;
class BlendingBuffers;
//Uncomment the following line to enable generation of viewmanager performance data.
#ifdef MOZ_PERF_METRICS
@ -255,17 +256,20 @@ private:
void RenderViews(nsView *aRootView, nsIRenderingContext& aRC, const nsRegion& aRegion,
PRBool &aResult);
void RenderDisplayListElement(DisplayListElement2* element, nsIRenderingContext &aRC);
void RenderDisplayListElement(DisplayListElement2* element,
nsIRenderingContext &aRC,
BlendingBuffers* aBuffers);
void PaintView(nsView *aView, nsIRenderingContext &aRC, nscoord x, nscoord y,
const nsRect &aDamageRect);
const nsRect &aDamageRect);
void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, PRUint32 aUpdateFlags);
void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, PRBool aInCutOut);
nsresult CreateBlendingBuffers(nsIRenderingContext &aRC);
BlendingBuffers* CreateBlendingBuffers(nsIRenderingContext *aRC,
PRBool aTranslucentWindow);
void ReparentViews(DisplayZTreeNode* aNode);
void BuildDisplayList(nsView* aView, const nsRect& aRect, PRBool aEventProcessing, PRBool aCaptured);
void BuildEventTargetList(nsAutoVoidArray &aTargets, nsView* aView, nsGUIEvent* aEvent, PRBool aCaptured);
@ -392,23 +396,13 @@ private:
//from here to public should be static and locked... MMP
static PRInt32 mVMCount; //number of viewmanagers
//blending buffers
static nsDrawingSurface gOffScreen;
static nsDrawingSurface gBlack;
static nsDrawingSurface gWhite;
static nsSize gOffScreenSize;
//Rendering context used to cleanup the blending buffers
static nsIRenderingContext* gCleanupContext;
//list of view managers
static nsVoidArray *gViewManagers;
nsIBlender *mBlender;
nsIRenderingContext *mOffScreenCX;
nsIRenderingContext *mBlackCX;
nsIRenderingContext *mWhiteCX;
nsCOMPtr<nsIBlender> mBlender;
nsISupportsArray *mCompositeListeners;
void DestroyZTreeNode(DisplayZTreeNode* aNode);

View File

@ -593,6 +593,44 @@ class nsIWidget : public nsISupports {
*/
NS_IMETHOD GetWindowType(nsWindowType& aWindowType) = 0;
/**
* Set the translucency of the top-level window containing this widget.
* So, e.g., if you call this on the widget for an IFRAME, the top level
* browser window containing the IFRAME actually gets set. Be careful.
*
* This can fail if the platform doesn't support
* transparency/translucency. By default widgets are not
* transparent. This will also fail if the toplevel window is not
* a Mozilla window, e.g., if the widget is in an embedded
* context.
*
* After translucency has been enabled, the initial alpha channel
* value for all pixels is 1, i.e., opaque.
* If the window is resized then the alpha channel values for
* all pixels are reset to 1.
* @param aTranslucent true if the window may have translucent
* or transparent pixels
*/
NS_IMETHOD SetWindowTranslucency(PRBool aTranslucent) = 0;
/**
* Get the translucency of the top-level window that contains this
* widget.
* @param aTranslucent true if the window may have translucent or
* transparent pixels
*/
NS_IMETHOD GetWindowTranslucency(PRBool& aTranslucent) = 0;
/**
* Update the alpha channel for some pixels of the top-level window
* that contains this widget.
* The window must have been made translucent using SetWindowTranslucency.
* @param aRect the rect to update
* @param aAlphas the alpha values, in w x h array, row-major order,
* in units of 1/255. nsBlender::GetAlphas is a good way to compute this array.
*/
NS_IMETHOD UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas) = 0;
/**
* Hide window chrome (borders, buttons) for this widget.
*

View File

@ -266,6 +266,9 @@ nsWindow::nsWindow()
mIMECompositionUniString = nsnull;
mIMECompositionUniStringSize = 0;
mIsTranslucent = PR_FALSE;
mTransparencyBitmap = nsnull;
#ifdef USE_XIM
mIMEEnable = PR_TRUE; //currently will not be used
mIMEShellWindow = 0;
@ -354,6 +357,9 @@ nsWindow::~nsWindow()
Destroy();
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
if (mIsUpdating)
UnqueueDraw();
}
@ -809,12 +815,23 @@ nsWindow::DoPaint (nsIRegion *aClipRegion)
nsRect boundsRect;
aClipRegion->GetBoundingBox(&boundsRect.x, &boundsRect.y, &boundsRect.width, &boundsRect.height);
event.rect = &boundsRect;
event.region = aClipRegion;
event.region = nsnull; // aClipRegion;
// Don't paint anything if our window isn't visible.
if (!mSuperWin || mSuperWin->visibility == GDK_VISIBILITY_FULLY_OBSCURED)
if (!mSuperWin)
return;
if (mSuperWin->visibility == GDK_VISIBILITY_FULLY_OBSCURED) {
// if our window has translucent pixels, then we can't trust the obscured
// check; it's possible that no pixels of the window are *currently* showing,
// but maybe after the paint new non-transparent pixels will appear in visible
// positions.
PRBool isTranslucent;
GetWindowTranslucency(isTranslucent);
if (!isTranslucent)
return;
}
event.renderingContext = GetRenderingContext();
if (!event.renderingContext)
return;
@ -2724,6 +2741,8 @@ NS_IMETHODIMP nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, PRBool aRepaint)
aWidth, aHeight);
#endif
ResizeTransparencyBitmap(aWidth, aHeight);
mBounds.width = aWidth;
mBounds.height = aHeight;
@ -4216,15 +4235,210 @@ NS_IMETHODIMP nsWindow::ResetInputState()
return NS_OK;
}
nsWindow* nsWindow::FindTopLevelWindow() {
if (!mShell) {
GtkWidget *top_mozarea = GetOwningWidget();
void *data = gtk_object_get_data(GTK_OBJECT(top_mozarea), "nsWindow");
return NS_STATIC_CAST(nsWindow *, data);
} else {
return nsnull;
}
}
static void
gdk_wmspec_change_state (gboolean add,
GdkWindow *window,
GdkAtom state1,
GdkAtom state2)
{
XEvent xev;
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define _NET_WM_STATE_ADD 1 /* add/set property */
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = GDK_WINDOW_XWINDOW(window);
xev.xclient.message_type = gdk_atom_intern("_NET_WM_STATE", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xev.xclient.data.l[1] = state1;
xev.xclient.data.l[2] = state2;
XSendEvent(gdk_display, GDK_ROOT_WINDOW(), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
}
#ifndef MOZ_XUL
void nsWindow::ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight) {
}
#else
NS_IMETHODIMP nsWindow::SetWindowTranslucency(PRBool aTranslucent) {
nsWindow* top = FindTopLevelWindow();
if (top) {
return top->SetWindowTranslucency(aTranslucent);
}
if (!mShell) {
// we must be embedded
NS_WARNING("Trying to use transparent chrome in an embedded context");
return NS_ERROR_FAILURE;
}
if (mIsTranslucent == aTranslucent)
return NS_OK;
if (!aTranslucent) {
if (mTransparencyBitmap) {
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
gtk_widget_reset_shapes(mShell);
}
} // else the new default alpha values are "all 1", so we don't
// need to change anything yet
mIsTranslucent = aTranslucent;
return NS_OK;
}
NS_IMETHODIMP nsWindow::GetWindowTranslucency(PRBool& aTranslucent) {
nsWindow* top = FindTopLevelWindow();
if (top) {
return top->GetWindowTranslucency(aTranslucent);
}
aTranslucent = mIsTranslucent;
return NS_OK;
}
void nsWindow::ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight) {
if (!mTransparencyBitmap)
return;
PRInt32 newSize = ((aNewWidth+7)/8)*aNewHeight;
gchar* newBits = new gchar[newSize];
if (!newBits) {
delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull;
return;
}
// fill new mask with "opaque", first
memset(newBits, 255, newSize);
// Now copy the intersection of the old and new areas into the new mask
PRInt32 copyWidth = PR_MIN(aNewWidth, mBounds.width);
PRInt32 copyHeight = PR_MIN(aNewHeight, mBounds.height);
PRInt32 oldRowBytes = (mBounds.width+7)/8;
PRInt32 newRowBytes = (aNewWidth+7)/8;
PRInt32 copyBytes = (copyWidth+7)/8;
PRInt32 i;
gchar* fromPtr = mTransparencyBitmap;
gchar* toPtr = newBits;
for (i = 0; i < copyHeight; i++) {
memcpy(toPtr, fromPtr, copyBytes);
fromPtr += oldRowBytes;
toPtr += newRowBytes;
}
delete[] mTransparencyBitmap;
mTransparencyBitmap = newBits;
}
static PRBool ChangedMaskBits(gchar* aMaskBits, PRInt32 aMaskWidth, PRInt32 aMaskHeight,
const nsRect& aRect, PRUint8* aAlphas) {
PRInt32 x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
PRInt32 maskBytesPerRow = (aMaskWidth + 7)/8;
for (y = aRect.y; y < yMax; y++) {
gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
for (x = aRect.x; x < xMax; x++) {
PRBool newBit = *aAlphas > 0;
aAlphas++;
gchar maskByte = maskBytes[x >> 3];
PRBool maskBit = (maskByte & (1 << (x & 7))) != 0;
if (maskBit != newBit) {
return PR_TRUE;
}
}
}
return PR_FALSE;
}
static void UpdateMaskBits(gchar* aMaskBits, PRInt32 aMaskWidth, PRInt32 aMaskHeight,
const nsRect& aRect, PRUint8* aAlphas) {
PRInt32 x, y, xMax = aRect.XMost(), yMax = aRect.YMost();
PRInt32 maskBytesPerRow = (aMaskWidth + 7)/8;
for (y = aRect.y; y < yMax; y++) {
gchar* maskBytes = aMaskBits + y*maskBytesPerRow;
for (x = aRect.x; x < xMax; x++) {
PRBool newBit = *aAlphas > 0;
aAlphas++;
gchar mask = 1 << (x & 7);
gchar maskByte = maskBytes[x >> 3];
// Note: '-newBit' turns 0 into 00...00 and 1 into 11...11
maskBytes[x >> 3] = (maskByte & ~mask) | (-newBit & mask);
}
}
}
NS_IMETHODIMP nsWindow::UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas) {
nsWindow* top = FindTopLevelWindow();
if (top) {
return top->UpdateTranslucentWindowAlpha(aRect, aAlphas);
}
NS_ASSERTION(mIsTranslucent, "Window is not transparent");
if (mTransparencyBitmap == nsnull) {
PRInt32 size = ((mBounds.width+7)/8)*mBounds.height;
mTransparencyBitmap = new gchar[size];
if (mTransparencyBitmap == nsnull)
return NS_ERROR_FAILURE;
memset(mTransparencyBitmap, 255, size);
}
NS_ASSERTION(aRect.x >= 0 && aRect.y >= 0
&& aRect.XMost() <= mBounds.width && aRect.YMost() <= mBounds.height,
"Rect is out of window bounds");
if (!ChangedMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, aRect, aAlphas))
// skip the expensive stuff if the mask bits haven't changed; hopefully
// this is the common case
return NS_OK;
UpdateMaskBits(mTransparencyBitmap, mBounds.width, mBounds.height, aRect, aAlphas);
gtk_widget_reset_shapes(mShell);
GdkBitmap* maskBitmap = gdk_bitmap_create_from_data(mShell->window,
mTransparencyBitmap,
mBounds.width, mBounds.height);
if (!maskBitmap)
return NS_ERROR_FAILURE;
gtk_widget_shape_combine_mask(mShell, maskBitmap, 0, 0);
gdk_bitmap_unref(maskBitmap);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::HideWindowChrome(PRBool aShouldHide)
{
nsWindow* top = FindTopLevelWindow();
if (top) {
return top->HideWindowChrome(aShouldHide);
}
if (!mShell) {
// Pass the request to the toplevel window.
GtkWidget *top_mozarea = GetOwningWidget();
void *data = gtk_object_get_data(GTK_OBJECT(top_mozarea), "nsWindow");
nsWindow *mozAreaWindow = NS_STATIC_CAST(nsWindow *, data);
return mozAreaWindow->HideWindowChrome(aShouldHide);
// we must be embedded
NS_WARNING("Trying to hide window decorations in an embedded context");
return NS_ERROR_FAILURE;
}
// Sawfish, metacity, and presumably other window managers get
@ -4255,41 +4469,18 @@ nsWindow::HideWindowChrome(PRBool aShouldHide)
return NS_OK;
}
static void
gdk_wmspec_change_state (gboolean add,
GdkWindow *window,
GdkAtom state1,
GdkAtom state2)
{
XEvent xev;
#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
#define _NET_WM_STATE_ADD 1 /* add/set property */
#define _NET_WM_STATE_TOGGLE 2 /* toggle property */
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.window = GDK_WINDOW_XWINDOW(window);
xev.xclient.message_type = gdk_atom_intern("_NET_WM_STATE", FALSE);
xev.xclient.format = 32;
xev.xclient.data.l[0] = add ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE;
xev.xclient.data.l[1] = state1;
xev.xclient.data.l[2] = state2;
XSendEvent(gdk_display, GDK_ROOT_WINDOW(), False,
SubstructureRedirectMask | SubstructureNotifyMask, &xev);
}
NS_IMETHODIMP
nsWindow::MakeFullScreen(PRBool aFullScreen)
{
nsWindow* top = FindTopLevelWindow();
if (top) {
return top->MakeFullScreen(aFullScreen);
}
if (!mShell) {
// Pass the request to the toplevel window.
GtkWidget *top_mozarea = GetOwningWidget();
void *data = gtk_object_get_data(GTK_OBJECT(top_mozarea), "nsWindow");
nsWindow *mozAreaWindow = NS_STATIC_CAST(nsWindow *, data);
return mozAreaWindow->MakeFullScreen(aFullScreen);
// we must be embedded
NS_WARNING("Trying to go fullscreen in an embedded context");
return NS_ERROR_FAILURE;
}
gdk_wmspec_change_state(aFullScreen, mShell->window,
@ -4297,6 +4488,7 @@ nsWindow::MakeFullScreen(PRBool aFullScreen)
GDK_NONE);
return NS_OK;
}
#endif
PRBool PR_CALLBACK
nsWindow::IconEntryMatches(PLDHashTable* aTable,

View File

@ -115,8 +115,15 @@ public:
NS_IMETHOD SetFocus(PRBool aRaise);
NS_IMETHOD GetAttention(void);
NS_IMETHOD Destroy();
nsWindow* FindTopLevelWindow(); // returns nsnull if WE are top level
void ResizeTransparencyBitmap(PRInt32 aNewWidth, PRInt32 aNewHeight);
#ifdef INCLUDE_XUL
NS_IMETHOD SetWindowTranslucency(PRBool aTransparent);
NS_IMETHOD GetWindowTranslucency(PRBool& aTransparent);
NS_IMETHOD UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas);
NS_IMETHOD HideWindowChrome(PRBool aShouldHide);
NS_IMETHOD MakeFullScreen(PRBool aFullScreen);
#endif
NS_IMETHOD SetIcon(const nsAString& aIcon);
GdkCursor *GtkCreateCursor(nsCursor aCursorType);
virtual void LoseFocus(void);
@ -337,14 +344,14 @@ protected:
#ifdef USE_XIM
protected:
PRBool mIMEEnable;
static GdkFont *gPreeditFontset;
static GdkFont *gStatusFontset;
static GdkIMStyle gInputStyle;
static PLDHashTable gXICLookupTable;
PRBool mIMECallComposeStart;
PRBool mIMECallComposeEnd;
PRBool mIMEIsBeingActivate;
PRPackedBool mIMEEnable;
PRPackedBool mIMECallComposeStart;
PRPackedBool mIMECallComposeEnd;
PRPackedBool mIMEIsBeingActivate;
nsWindow* mIMEShellWindow;
void SetXICSpotLocation(nsIMEGtkIC* aXIC, nsPoint aPoint);
void SetXICBaseFontSize(nsIMEGtkIC* aXIC, int height);
@ -356,18 +363,18 @@ protected:
int mXICFontSize;
public:
nsIMEGtkIC* IMEGetInputContext(PRBool aCreate);
nsIMEGtkIC* IMEGetInputContext(PRBool aCreate);
void ime_preedit_start();
void ime_preedit_draw(nsIMEGtkIC* aXIC);
void ime_preedit_done();
void ime_status_draw();
void ime_preedit_start();
void ime_preedit_draw(nsIMEGtkIC* aXIC);
void ime_preedit_done();
void ime_status_draw();
void IMEUnsetFocusWindow();
void IMESetFocusWindow();
void IMEGetShellWindow();
void IMEDestroyIC();
void IMEBeingActivate(PRBool aActive);
void IMEUnsetFocusWindow();
void IMESetFocusWindow();
void IMEGetShellWindow();
void IMEDestroyIC();
void IMEBeingActivate(PRBool aActive);
#endif // USE_XIM
protected:
@ -389,17 +396,24 @@ private:
GdkBitmap *window_mask);
nsresult SetIcon(GdkPixmap *window_pixmap,
GdkBitmap *window_mask);
PRBool mLastGrabFailed;
void NativeGrab(PRBool aGrab);
PRBool mIsUpdating;
PRPackedBool mLastGrabFailed;
PRPackedBool mIsUpdating;
PRPackedBool mLeavePending;
PRPackedBool mRestoreFocus;
PRPackedBool mIsTranslucent;
// This bitmap tracks which pixels are transparent. We don't support
// full translucency at this time; each pixel is either fully opaque
// or fully transparent.
gchar* mTransparencyBitmap;
void DestroyNativeChildren(void);
GtkWindow *mTransientParent;
PRBool mLeavePending;
PRBool mRestoreFocus;
};
//

View File

@ -482,6 +482,26 @@ NS_IMETHODIMP nsBaseWidget::SetWindowType(nsWindowType aWindowType)
return NS_OK;
}
//-------------------------------------------------------------------------
//
// Window transparency methods
//
//-------------------------------------------------------------------------
NS_IMETHODIMP nsBaseWidget::SetWindowTranslucency(PRBool aTranslucent) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP nsBaseWidget::GetWindowTranslucency(PRBool& aTranslucent) {
aTranslucent = PR_FALSE;
return NS_OK;
}
NS_IMETHODIMP nsBaseWidget::UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas) {
NS_ASSERTION(PR_FALSE, "Window is not translucent");
return NS_ERROR_NOT_IMPLEMENTED;
}
//-------------------------------------------------------------------------
//
// Hide window borders/decorations for this widget

View File

@ -99,6 +99,9 @@ public:
NS_IMETHOD SetCursor(nsCursor aCursor);
NS_IMETHOD GetWindowType(nsWindowType& aWindowType);
NS_IMETHOD SetWindowType(nsWindowType aWindowType);
NS_IMETHOD SetWindowTranslucency(PRBool aTranslucent);
NS_IMETHOD GetWindowTranslucency(PRBool& aTranslucent);
NS_IMETHOD UpdateTranslucentWindowAlpha(const nsRect& aRect, PRUint8* aAlphas);
NS_IMETHOD HideWindowChrome(PRBool aShouldHide);
NS_IMETHOD MakeFullScreen(PRBool aFullScreen);
virtual nsIRenderingContext* GetRenderingContext();