Bug 544099 - Highly optimise -moz-box-shadow for common cases by allowing layout to specify an area where blurring is not needed r=roc,joe

--HG--
rename : gfx/thebes/src/gfxBlur.cpp => gfx/src/thebes/utils/gfxBlur.cpp
rename : gfx/thebes/public/gfxBlur.h => gfx/src/thebes/utils/gfxBlur.h
extra : rebase_source : d7ad7a1ee2ac9e25937133dba95f0f8d7347d612
This commit is contained in:
Michael Ventnor 2010-04-18 13:13:10 +10:00
parent b5f0594b55
commit 3e049f0588
22 changed files with 447 additions and 86 deletions

View File

@ -1586,7 +1586,7 @@ nsCanvasRenderingContext2D::ShadowInitialize(const gfxRect& extents, gfxAlphaBox
blurRadius.height, blurRadius.width);
drawExtents = drawExtents.Intersect(clipExtents - CurrentState().shadowOffset);
gfxContext* ctx = blur.Init(drawExtents, blurRadius, nsnull);
gfxContext* ctx = blur.Init(drawExtents, blurRadius, nsnull, nsnull);
if (!ctx)
return nsnull;

View File

@ -235,6 +235,20 @@ struct NS_GFX nsIntRect {
height += aMargin.top + aMargin.bottom;
}
// Deflate the rect by the specified width/height or margin
void Deflate(PRInt32 aDx, PRInt32 aDy) {
x += aDx;
y += aDy;
width -= aDx*2;
height -= aDy*2;
}
void Deflate(const nsIntMargin &aMargin) {
x += aMargin.left;
y += aMargin.top;
width -= (aMargin.left + aMargin.right);
height -= (aMargin.top + aMargin.bottom);
}
// Overloaded operators. Note that '=' isn't defined so we'll get the
// compiler generated default assignment operator.
PRBool operator==(const nsIntRect& aRect) const {

View File

@ -52,6 +52,7 @@ GRE_MODULE = 1
LIBXUL_LIBRARY = 1
DIRS = utils
CPPSRCS = \
nsThebesDeviceContext.cpp \

View File

@ -0,0 +1,70 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is thebes gfx
#
# The Initial Developer of the Original Code is
# mozilla.org.
# Portions created by the Initial Developer are Copyright (C) 2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Michael Ventnor <m.ventnor@gmail.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
# This library can link against both old-gfx and thebes,
# and is not a component
MODULE = gfx
LIBRARY_NAME = gfxutils
EXPORT_LIBRARY = 1
LIBXUL_LIBRARY = 1
CPPSRCS = \
gfxThebesUtils.cpp \
gfxBlur.cpp \
$(NULL)
EXTRA_DSO_LIBS = gkgfx thebes
EXPORTS += \
gfxThebesUtils.h \
gfxBlur.h \
$(NULL)
EXTRA_DSO_LDOPTS += \
$(EXTRA_DSO_LIBS) \
$(MOZ_COMPONENT_LIBS) \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -55,7 +55,8 @@ gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
gfxContext*
gfxAlphaBoxBlur::Init(const gfxRect& aRect,
const gfxIntSize& aBlurRadius,
const gfxRect* aDirtyRect)
const gfxRect* aDirtyRect,
const gfxRect* aSkipRect)
{
mBlurRadius = aBlurRadius;
@ -80,6 +81,25 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
mHasDirtyRect = PR_FALSE;
}
if (aSkipRect) {
// If we get passed a skip rect, we can lower the amount of
// blurring we need to do. We convert it to nsIntRect to avoid
// expensive int<->float conversions if we were to use gfxRect instead.
gfxRect skipRect = *aSkipRect;
skipRect.RoundIn();
mSkipRect = gfxThebesUtils::GfxRectToIntRect(skipRect);
nsIntRect shadowIntRect = gfxThebesUtils::GfxRectToIntRect(rect);
mSkipRect.Deflate(aBlurRadius.width, aBlurRadius.height);
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
if (mSkipRect == shadowIntRect)
return nsnull;
mSkipRect -= shadowIntRect.TopLeft();
} else {
mSkipRect = nsIntRect(0, 0, 0, 0);
}
// Make an alpha-only surface to draw on. We will play with the data after
// everything is drawn to create a blur effect.
mImageSurface = new gfxImageSurface(gfxIntSize(static_cast<PRInt32>(rect.Width()), static_cast<PRInt32>(rect.Height())),
@ -117,36 +137,68 @@ gfxAlphaBoxBlur::PremultiplyAlpha(gfxFloat alpha)
* @param aOutput The output buffer.
* @param aLeftLobe The number of pixels to blend on the left.
* @param aRightLobe The number of pixels to blend on the right.
* @param aStride The stride of the buffers.
* @param aWidth The number of columns in the buffers.
* @param aRows The number of rows in the buffers.
* @param aSkipRect An area to skip blurring in.
*/
static void
BoxBlurHorizontal(unsigned char* aInput,
unsigned char* aOutput,
PRInt32 aLeftLobe,
PRInt32 aRightLobe,
PRInt32 aStride,
PRInt32 aRows)
PRInt32 aWidth,
PRInt32 aRows,
const nsIntRect& aSkipRect)
{
PRInt32 boxSize = aLeftLobe + aRightLobe + 1;
PRBool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
aWidth - 1 <= aSkipRect.XMost();
for (PRInt32 y = 0; y < aRows; y++) {
// Check whether the skip rect intersects this row. If the skip
// rect covers the whole surface in this row, we can avoid
// this row entirely (and any others along the skip rect).
PRBool inSkipRectY = y >= aSkipRect.y &&
y < aSkipRect.YMost();
if (inSkipRectY && skipRectCoversWholeRow) {
y = aSkipRect.YMost() - 1;
continue;
}
PRInt32 alphaSum = 0;
for (PRInt32 i = 0; i < boxSize; i++) {
PRInt32 pos = i - aLeftLobe;
pos = PR_MAX(pos, 0);
pos = PR_MIN(pos, aStride - 1);
alphaSum += aInput[aStride * y + pos];
pos = NS_MAX(pos, 0);
pos = NS_MIN(pos, aWidth - 1);
alphaSum += aInput[aWidth * y + pos];
}
for (PRInt32 x = 0; x < aStride; x++) {
for (PRInt32 x = 0; x < aWidth; x++) {
// Check whether we are within the skip rect. If so, go
// to the next point outside the skip rect.
if (inSkipRectY && x >= aSkipRect.x &&
x < aSkipRect.XMost()) {
x = aSkipRect.XMost();
if (x >= aWidth)
break;
// Recalculate the neighbouring alpha values for
// our new point on the surface.
alphaSum = 0;
for (PRInt32 i = 0; i < boxSize; i++) {
PRInt32 pos = x + i - aLeftLobe;
pos = NS_MAX(pos, 0);
pos = NS_MIN(pos, aWidth - 1);
alphaSum += aInput[aWidth * y + pos];
}
}
PRInt32 tmp = x - aLeftLobe;
PRInt32 last = PR_MAX(tmp, 0);
PRInt32 next = PR_MIN(tmp + boxSize, aStride - 1);
PRInt32 last = NS_MAX(tmp, 0);
PRInt32 next = NS_MIN(tmp + boxSize, aWidth - 1);
aOutput[aStride * y + x] = alphaSum/boxSize;
aOutput[aWidth * y + x] = alphaSum/boxSize;
alphaSum += aInput[aStride * y + next] -
aInput[aStride * y + last];
alphaSum += aInput[aWidth * y + next] -
aInput[aWidth * y + last];
}
}
}
@ -160,28 +212,52 @@ BoxBlurVertical(unsigned char* aInput,
unsigned char* aOutput,
PRInt32 aTopLobe,
PRInt32 aBottomLobe,
PRInt32 aStride,
PRInt32 aRows)
PRInt32 aWidth,
PRInt32 aRows,
const nsIntRect& aSkipRect)
{
PRInt32 boxSize = aTopLobe + aBottomLobe + 1;
PRBool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
aRows - 1 <= aSkipRect.YMost();
for (PRInt32 x = 0; x < aWidth; x++) {
PRBool inSkipRectX = x >= aSkipRect.x &&
x < aSkipRect.XMost();
if (inSkipRectX && skipRectCoversWholeColumn) {
x = aSkipRect.XMost() - 1;
continue;
}
for (PRInt32 x = 0; x < aStride; x++) {
PRInt32 alphaSum = 0;
for (PRInt32 i = 0; i < boxSize; i++) {
PRInt32 pos = i - aTopLobe;
pos = PR_MAX(pos, 0);
pos = PR_MIN(pos, aRows - 1);
alphaSum += aInput[aStride * pos + x];
pos = NS_MAX(pos, 0);
pos = NS_MIN(pos, aRows - 1);
alphaSum += aInput[aWidth * pos + x];
}
for (PRInt32 y = 0; y < aRows; y++) {
if (inSkipRectX && y >= aSkipRect.y &&
y < aSkipRect.YMost()) {
y = aSkipRect.YMost();
if (y >= aRows)
break;
alphaSum = 0;
for (PRInt32 i = 0; i < boxSize; i++) {
PRInt32 pos = y + i - aTopLobe;
pos = NS_MAX(pos, 0);
pos = NS_MIN(pos, aRows - 1);
alphaSum += aInput[aWidth * pos + x];
}
}
PRInt32 tmp = y - aTopLobe;
PRInt32 last = PR_MAX(tmp, 0);
PRInt32 next = PR_MIN(tmp + boxSize, aRows - 1);
PRInt32 last = NS_MAX(tmp, 0);
PRInt32 next = NS_MIN(tmp + boxSize, aRows - 1);
aOutput[aStride * y + x] = alphaSum/boxSize;
aOutput[aWidth * y + x] = alphaSum/boxSize;
alphaSum += aInput[aStride * next + x] -
aInput[aStride * last + x];
alphaSum += aInput[aWidth * next + x] -
aInput[aWidth * last + x];
}
}
}
@ -249,17 +325,17 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
if (mBlurRadius.width > 0) {
PRInt32 lobes[3][2];
ComputeLobes(mBlurRadius.width, lobes);
BoxBlurHorizontal(boxData, tmpData, lobes[0][0], lobes[0][1], stride, rows);
BoxBlurHorizontal(tmpData, boxData, lobes[1][0], lobes[1][1], stride, rows);
BoxBlurHorizontal(boxData, tmpData, lobes[2][0], lobes[2][1], stride, rows);
BoxBlurHorizontal(boxData, tmpData, lobes[0][0], lobes[0][1], stride, rows, mSkipRect);
BoxBlurHorizontal(tmpData, boxData, lobes[1][0], lobes[1][1], stride, rows, mSkipRect);
BoxBlurHorizontal(boxData, tmpData, lobes[2][0], lobes[2][1], stride, rows, mSkipRect);
}
if (mBlurRadius.height > 0) {
PRInt32 lobes[3][2];
ComputeLobes(mBlurRadius.height, lobes);
BoxBlurVertical(tmpData, boxData, lobes[0][0], lobes[0][1], stride, rows);
BoxBlurVertical(boxData, tmpData, lobes[1][0], lobes[1][1], stride, rows);
BoxBlurVertical(tmpData, boxData, lobes[2][0], lobes[2][1], stride, rows);
BoxBlurVertical(tmpData, boxData, lobes[0][0], lobes[0][1], stride, rows, mSkipRect);
BoxBlurVertical(boxData, tmpData, lobes[1][0], lobes[1][1], stride, rows, mSkipRect);
BoxBlurVertical(tmpData, boxData, lobes[2][0], lobes[2][1], stride, rows, mSkipRect);
}
}

View File

@ -40,8 +40,8 @@
#include "gfxContext.h"
#include "gfxImageSurface.h"
#include "gfxRect.h"
#include "gfxTypes.h"
#include "gfxThebesUtils.h"
/**
* Implementation of a box blur approximation of a Gaussian blur.
@ -69,10 +69,15 @@ public:
*
* @param aDirtyRect A pointer to a dirty rect, measured in device units, if available.
* This will be used for optimizing the blur operation. It is safe to pass NULL here.
*
* @param aSkipRect A pointer to a rect, measured in device units, that represents an area
* where blurring is unnecessary and shouldn't be done for speed reasons. It is safe to
* pass NULL here.
*/
gfxContext* Init(const gfxRect& aRect,
const gfxIntSize& aBlurRadius,
const gfxRect* aDirtyRect);
const gfxRect* aDirtyRect,
const gfxRect* aSkipRect);
/**
* Returns the context that should be drawn to supply the alpha mask to be
@ -126,7 +131,13 @@ protected:
* mHasDirtyRect is TRUE.
*/
gfxRect mDirtyRect;
PRBool mHasDirtyRect;
/**
* A rect indicating the area where blurring is unnecessary, and the blur
* algorithm should skip over it.
*/
nsIntRect mSkipRect;
PRPackedBool mHasDirtyRect;
};
#endif /* GFX_BLUR_H */

View File

@ -0,0 +1,49 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is gfx thebes code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Ventnor <m.ventnor@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxThebesUtils.h"
// Converts a gfxRect to an nsIntRect for speed
nsIntRect
gfxThebesUtils::GfxRectToIntRect(const gfxRect& aIn)
{
nsIntRect result(PRInt32(aIn.X()), PRInt32(aIn.Y()),
PRInt32(aIn.Width()), PRInt32(aIn.Height()));
NS_ASSERTION(gfxRect(result.x, result.y, result.width, result.height) == aIn,
"The given gfxRect isn't rounded properly!");
return result;
}

View File

@ -0,0 +1,53 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is gfx thebes code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Ventnor <m.ventnor@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "gfxRect.h"
#include "nsRect.h"
#ifndef GFX_THEBES_UTILS_H
#define GFX_THEBES_UTILS_H
class THEBES_API gfxThebesUtils
{
public:
/**
* Converts a gfxRect into nsIntRect for speed reasons.
*/
static nsIntRect GfxRectToIntRect(const gfxRect& aIn);
};
#endif /* GFX_THEBES_UTILS_H */

View File

@ -12,7 +12,6 @@ MODULE = thebes
EXPORTS = gfx3DMatrix.h \
gfxASurface.h \
gfxAlphaRecovery.h \
gfxBlur.h \
gfxColor.h \
gfxContext.h \
gfxFont.h \

View File

@ -150,6 +150,10 @@ struct THEBES_API gfxRect {
// new |RoundAwayFromZero()| method.
void Round();
// Snap the rectangle edges to integer coordinates, such that the
// original rectangle contains the resulting rectangle.
void RoundIn();
// Snap the rectangle edges to integer coordinates, such that the
// resulting rectangle contains the original rectangle.
void RoundOut();

View File

@ -15,7 +15,6 @@ EXPORT_LIBRARY = 1
CPPSRCS = \
gfxASurface.cpp \
gfxAlphaRecovery.cpp \
gfxBlur.cpp \
gfxContext.cpp \
gfxImageSurface.cpp \
gfxFont.cpp \

View File

@ -103,6 +103,21 @@ gfxRect::Round()
size.height = y1 - y0;
}
void
gfxRect::RoundIn()
{
gfxFloat x0 = NS_ceil(X());
gfxFloat y0 = NS_ceil(Y());
gfxFloat x1 = NS_floor(XMost());
gfxFloat y1 = NS_floor(YMost());
pos.x = x0;
pos.y = y0;
size.width = x1 - x0;
size.height = y1 - y0;
}
void
gfxRect::RoundOut()
{

View File

@ -1186,6 +1186,14 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
gfxRect frameGfxRect = RectToGfxRect(aFrameArea, twipsPerPixel);
frameGfxRect.Round();
// We don't show anything that intersects with the frame we're blurring on. So tell the
// blurrer not to do unnecessary work there.
gfxRect skipGfxRect = frameGfxRect;
if (hasBorderRadius) {
skipGfxRect.Inset(PR_MAX(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
PR_MAX(borderRadii[C_BL].height, borderRadii[C_BR].height), 0);
}
for (PRUint32 i = shadows->Length(); i > 0; --i) {
nsCSSShadowItem* shadowItem = shadows->ShadowAt(i - 1);
if (shadowItem->mInset)
@ -1210,7 +1218,8 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
nsRefPtr<gfxContext> shadowContext;
nsContextBoxBlur blurringArea;
shadowContext = blurringArea.Init(shadowRect, blurRadius, twipsPerPixel, renderContext, aDirtyRect);
shadowContext = blurringArea.Init(shadowRect, blurRadius, twipsPerPixel, renderContext,
aDirtyRect, &skipGfxRect);
if (!shadowContext)
continue;
@ -1337,46 +1346,9 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
shadowClipRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
shadowClipRect.Deflate(shadowItem->mSpread, shadowItem->mSpread);
gfxContext* renderContext = aRenderingContext.ThebesContext();
nsRefPtr<gfxContext> shadowContext;
nsContextBoxBlur blurringArea;
shadowContext = blurringArea.Init(shadowPaintRect, blurRadius, twipsPerPixel, renderContext, aDirtyRect);
if (!shadowContext)
continue;
// Set the shadow color; if not specified, use the foreground color
nscolor shadowColor;
if (shadowItem->mHasColor)
shadowColor = shadowItem->mColor;
else
shadowColor = aForFrame->GetStyleColor()->mColor;
renderContext->Save();
renderContext->SetColor(gfxRGBA(shadowColor));
// Clip the context to the area of the frame's padding rect, so no part of the
// shadow is painted outside
gfxRect shadowGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
shadowGfxRect.Round();
renderContext->NewPath();
if (hasBorderRadius)
renderContext->RoundedRectangle(shadowGfxRect, innerRadii, PR_FALSE);
else
renderContext->Rectangle(shadowGfxRect);
renderContext->Clip();
// Fill the temporary surface minus the area within the frame that we should
// not paint in, and blur and apply it
gfxRect shadowPaintGfxRect = RectToGfxRect(shadowPaintRect, twipsPerPixel);
gfxRect shadowClipGfxRect = RectToGfxRect(shadowClipRect, twipsPerPixel);
shadowPaintGfxRect.RoundOut();
shadowClipGfxRect.Round();
shadowContext->NewPath();
shadowContext->Rectangle(shadowPaintGfxRect);
gfxCornerSizes clipRectRadii;
if (hasBorderRadius) {
// Calculate the radii the inner clipping rect will have
gfxCornerSizes clipRectRadii;
gfxFloat spreadDistance = shadowItem->mSpread / twipsPerPixel;
gfxFloat borderSizes[4] = {0, 0, 0, 0};
@ -1399,10 +1371,63 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
nsCSSBorderRenderer::ComputeInnerRadii(innerRadii, borderSizes,
&clipRectRadii);
shadowContext->RoundedRectangle(shadowClipGfxRect, clipRectRadii, PR_FALSE);
} else {
shadowContext->Rectangle(shadowClipGfxRect);
}
// Set the "skip rect" to the area within the frame that we don't paint in,
// including after blurring. We also use this for clipping later on.
nsRect skipRect = shadowClipRect;
skipRect.Deflate(blurRadius, blurRadius);
gfxRect skipGfxRect = RectToGfxRect(skipRect, twipsPerPixel);
if (hasBorderRadius) {
skipGfxRect.Inset(PR_MAX(clipRectRadii[C_TL].height, clipRectRadii[C_TR].height), 0,
PR_MAX(clipRectRadii[C_BL].height, clipRectRadii[C_BR].height), 0);
}
gfxContext* renderContext = aRenderingContext.ThebesContext();
nsRefPtr<gfxContext> shadowContext;
nsContextBoxBlur blurringArea;
shadowContext = blurringArea.Init(shadowPaintRect, blurRadius, twipsPerPixel, renderContext,
aDirtyRect, &skipGfxRect);
if (!shadowContext)
continue;
// Set the shadow color; if not specified, use the foreground color
nscolor shadowColor;
if (shadowItem->mHasColor)
shadowColor = shadowItem->mColor;
else
shadowColor = aForFrame->GetStyleColor()->mColor;
renderContext->Save();
renderContext->SetColor(gfxRGBA(shadowColor));
// Clip the context to the area of the frame's padding rect, so no part of the
// shadow is painted outside. Also cut out anything beyond where the inset shadow
// will be.
gfxRect shadowGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
shadowGfxRect.Round();
renderContext->NewPath();
if (hasBorderRadius)
renderContext->RoundedRectangle(shadowGfxRect, innerRadii, PR_FALSE);
else
renderContext->Rectangle(shadowGfxRect);
renderContext->Rectangle(skipGfxRect);
renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
renderContext->Clip();
// Fill the temporary surface minus the area within the frame that we should
// not paint in, and blur and apply it
gfxRect shadowPaintGfxRect = RectToGfxRect(shadowPaintRect, twipsPerPixel);
shadowPaintGfxRect.RoundOut();
gfxRect shadowClipGfxRect = RectToGfxRect(shadowClipRect, twipsPerPixel);
shadowClipGfxRect.Round();
shadowContext->NewPath();
shadowContext->Rectangle(shadowPaintGfxRect);
if (hasBorderRadius)
shadowContext->RoundedRectangle(shadowClipGfxRect, clipRectRadii, PR_FALSE);
else
shadowContext->Rectangle(shadowClipGfxRect);
shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
shadowContext->Fill();
@ -3708,7 +3733,8 @@ gfxContext*
nsContextBoxBlur::Init(const nsRect& aRect, nscoord aBlurRadius,
PRInt32 aAppUnitsPerDevPixel,
gfxContext* aDestinationCtx,
const nsRect& aDirtyRect)
const nsRect& aDirtyRect,
const gfxRect* aSkipRect)
{
if (aRect.IsEmpty()) {
mContext = nsnull;
@ -3732,7 +3758,8 @@ nsContextBoxBlur::Init(const nsRect& aRect, nscoord aBlurRadius,
dirtyRect.RoundOut();
// Create the temporary surface for blurring
mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius), &dirtyRect);
mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius),
&dirtyRect, aSkipRect);
return mContext;
}

View File

@ -428,6 +428,9 @@ public:
* @param aDirtyRect The absolute dirty rect in app units. Used to
* optimize the temporary surface size and speed up blur.
*
* @param aSkipRect An area in device pixels (NOT app units!) to avoid
* blurring over, to prevent unnecessary work.
*
* @return A blank 8-bit alpha-channel-only graphics context to
* draw on, or null on error. Must not be freed. The
* context has a device offset applied to it given by
@ -443,7 +446,7 @@ public:
*/
gfxContext* Init(const nsRect& aRect, nscoord aBlurRadius,
PRInt32 aAppUnitsPerDevPixel, gfxContext* aDestinationCtx,
const nsRect& aDirtyRect);
const nsRect& aDirtyRect, const gfxRect* aSkipRect);
/**
* Does the actual blurring and mask applying. Users of this object *must*

View File

@ -59,7 +59,7 @@ GRE_MODULE = 1
LIBXUL_LIBRARY = 1
ifndef MOZ_ENABLE_LIBXUL
EXTRA_DSO_LIBS = gkgfx
EXTRA_DSO_LIBS = gkgfx gfxutils
endif
EXTRA_DSO_LIBS += thebes layers

View File

@ -264,7 +264,7 @@ nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder,
nsContextBoxBlur contextBoxBlur;
gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, mBlurRadius,
presContext->AppUnitsPerDevPixel(),
thebesCtx, mVisibleRect);
thebesCtx, mVisibleRect, nsnull);
if (!shadowCtx)
return;

View File

@ -4481,12 +4481,12 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
gfxRect shadowGfxRect = shadowMetrics.mBoundingBox +
gfxPoint(aFramePt.x, aTextBaselinePt.y) + shadowOffset;
nsRect shadowRect(shadowGfxRect.X(), shadowGfxRect.Y(),
shadowGfxRect.Width(), shadowGfxRect.Height());;
shadowGfxRect.Width(), shadowGfxRect.Height());
nsContextBoxBlur contextBoxBlur;
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
PresContext()->AppUnitsPerDevPixel(),
aCtx, aDirtyRect);
aCtx, aDirtyRect, nsnull);
if (!shadowContext)
return;

View File

@ -0,0 +1,24 @@
<!DOCTYPE HTML>
<style>
#thediv {
width: 400px;
height: 250px;
position: absolute;
top: -500px;
left: -500px;
-moz-box-shadow: 540px 540px 15px black;
}
#blankdiv {
width: 400px;
height: 250px;
background-color: white;
position: absolute;
top: 40px;
left: 40px;
}
</style>
<div id="thediv">&nbsp;</div>
<div id="blankdiv">&nbsp;</div>

View File

@ -0,0 +1,14 @@
<!DOCTYPE HTML>
<style>
#thediv {
width: 400px;
height: 250px;
position: absolute;
top: 40px;
left: 40px;
-moz-box-shadow: 0px 0px 15px black;
}
</style>
<div id="thediv">&nbsp;</div>

View File

@ -13,3 +13,4 @@
== boxshadow-rounded-spread.html boxshadow-rounded-spread-ref.html
HTTP(..) == boxshadow-dynamic.xul boxshadow-dynamic-ref.xul
== boxshadow-onecorner.html boxshadow-onecorner-ref.html
== boxshadow-skiprect.html boxshadow-skiprect-ref.html

View File

@ -579,7 +579,7 @@ void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx,
nsContextBoxBlur contextBoxBlur;
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
PresContext()->AppUnitsPerDevPixel(),
aCtx, aDirtyRect);
aCtx, aDirtyRect, nsnull);
if (!shadowContext)
return;

View File

@ -115,6 +115,7 @@ STATIC_LIBS += \
xpcom_core \
ucvutil_s \
gkgfx \
gfxutils \
$(NULL)
ifdef MOZ_IPC