mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 581222. Extend gfxAlphaBlur to support "spread radii". r=vlad
--HG-- extra : rebase_source : e644ae08007e52c524c3237d336441f1413f846b
This commit is contained in:
parent
747fa70120
commit
79b8d06302
@ -1887,7 +1887,7 @@ nsCanvasRenderingContext2D::ShadowInitialize(const gfxRect& extents, gfxAlphaBox
|
||||
blurRadius.height, blurRadius.width);
|
||||
drawExtents = drawExtents.Intersect(clipExtents - CurrentState().shadowOffset);
|
||||
|
||||
gfxContext* ctx = blur.Init(drawExtents, blurRadius, nsnull, nsnull);
|
||||
gfxContext* ctx = blur.Init(drawExtents, gfxIntSize(0,0), blurRadius, nsnull, nsnull);
|
||||
|
||||
if (!ctx)
|
||||
return nsnull;
|
||||
|
@ -54,15 +54,16 @@ gfxAlphaBoxBlur::~gfxAlphaBoxBlur()
|
||||
|
||||
gfxContext*
|
||||
gfxAlphaBoxBlur::Init(const gfxRect& aRect,
|
||||
const gfxIntSize& aSpreadRadius,
|
||||
const gfxIntSize& aBlurRadius,
|
||||
const gfxRect* aDirtyRect,
|
||||
const gfxRect* aSkipRect)
|
||||
{
|
||||
mSpreadRadius = aSpreadRadius;
|
||||
mBlurRadius = aBlurRadius;
|
||||
|
||||
gfxRect rect(aRect);
|
||||
rect.Outset(aBlurRadius.height, aBlurRadius.width,
|
||||
aBlurRadius.height, aBlurRadius.width);
|
||||
rect.Outset(aBlurRadius + aSpreadRadius);
|
||||
rect.RoundOut();
|
||||
|
||||
if (rect.IsEmpty())
|
||||
@ -74,8 +75,7 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
|
||||
mHasDirtyRect = PR_TRUE;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
gfxRect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Outset(aBlurRadius.height, aBlurRadius.width,
|
||||
aBlurRadius.height, aBlurRadius.width);
|
||||
requiredBlurArea.Outset(aBlurRadius + aSpreadRadius);
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = PR_FALSE;
|
||||
@ -83,14 +83,13 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
|
||||
|
||||
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
|
||||
// blurring/spreading 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();
|
||||
skipRect.Inset(aBlurRadius + aSpreadRadius);
|
||||
mSkipRect = gfxThebesUtils::GfxRectToIntRect(skipRect);
|
||||
nsIntRect shadowIntRect = gfxThebesUtils::GfxRectToIntRect(rect);
|
||||
|
||||
mSkipRect.Deflate(aBlurRadius.width, aBlurRadius.height);
|
||||
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
|
||||
if (mSkipRect == shadowIntRect)
|
||||
return nsnull;
|
||||
@ -140,6 +139,7 @@ gfxAlphaBoxBlur::PremultiplyAlpha(gfxFloat alpha)
|
||||
* @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.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurHorizontal(unsigned char* aInput,
|
||||
@ -152,7 +152,7 @@ BoxBlurHorizontal(unsigned char* aInput,
|
||||
{
|
||||
PRInt32 boxSize = aLeftLobe + aRightLobe + 1;
|
||||
PRBool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth - 1 <= aSkipRect.XMost();
|
||||
aWidth <= aSkipRect.XMost();
|
||||
|
||||
for (PRInt32 y = 0; y < aRows; y++) {
|
||||
// Check whether the skip rect intersects this row. If the skip
|
||||
@ -206,6 +206,7 @@ BoxBlurHorizontal(unsigned char* aInput,
|
||||
/**
|
||||
* Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
|
||||
* left and right.
|
||||
* XXX shouldn't we pass stride in separately here?
|
||||
*/
|
||||
static void
|
||||
BoxBlurVertical(unsigned char* aInput,
|
||||
@ -218,7 +219,7 @@ BoxBlurVertical(unsigned char* aInput,
|
||||
{
|
||||
PRInt32 boxSize = aTopLobe + aBottomLobe + 1;
|
||||
PRBool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows - 1 <= aSkipRect.YMost();
|
||||
aRows <= aSkipRect.YMost();
|
||||
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
PRBool inSkipRectX = x >= aSkipRect.x &&
|
||||
@ -304,6 +305,99 @@ static void ComputeLobes(PRInt32 aRadius, PRInt32 aLobes[3][2])
|
||||
aLobes[2][1] = final;
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aRadius,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
PRInt32 aStride,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride*aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
PRInt32 sMin = PR_MAX(x - aRadius, 0);
|
||||
PRInt32 sMax = PR_MIN(x + aRadius, aWidth - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = PR_MAX(v, aInput[aStride * y + s]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aRadius,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
PRInt32 aStride,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride*aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
PRBool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= 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 y = 0; y < aRows; y++) {
|
||||
// Check whether we are within the skip rect. If so, go
|
||||
// to the next point outside the skip rect.
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
}
|
||||
|
||||
PRInt32 sMin = PR_MAX(y - aRadius, 0);
|
||||
PRInt32 sMax = PR_MIN(y + aRadius, aRows - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = PR_MAX(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
{
|
||||
@ -312,8 +406,8 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
|
||||
unsigned char* boxData = mImageSurface->Data();
|
||||
|
||||
// no need to do all this if not blurring
|
||||
if (mBlurRadius.width != 0 || mBlurRadius.height != 0) {
|
||||
// no need to do all this if not blurring or spreading
|
||||
if (mBlurRadius != gfxIntSize(0,0) || mSpreadRadius != gfxIntSize(0,0)) {
|
||||
nsTArray<unsigned char> tempAlphaDataBuf;
|
||||
PRSize szB = mImageSurface->GetDataSize();
|
||||
if (!tempAlphaDataBuf.SetLength(szB))
|
||||
@ -323,11 +417,16 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
// .SetLength above doesn't initialise the new elements since
|
||||
// they are unsigned chars and so have no default constructor.
|
||||
// So we have to initialise them by hand.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=582668#c10
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
PRInt32 stride = mImageSurface->Stride();
|
||||
PRInt32 rows = mImageSurface->Height();
|
||||
PRInt32 width = mImageSurface->Width();
|
||||
|
||||
if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
|
||||
SpreadHorizontal(boxData, tmpData, mSpreadRadius.width, width, rows, stride, mSkipRect);
|
||||
SpreadVertical(tmpData, boxData, mSpreadRadius.height, width, rows, stride, mSkipRect);
|
||||
}
|
||||
|
||||
if (mBlurRadius.width > 0) {
|
||||
PRInt32 lobes[3][2];
|
||||
@ -335,6 +434,8 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
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);
|
||||
} else {
|
||||
memcpy(tmpData, boxData, stride*rows);
|
||||
}
|
||||
|
||||
if (mBlurRadius.height > 0) {
|
||||
@ -343,6 +444,8 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
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);
|
||||
} else {
|
||||
memcpy(boxData, tmpData, stride*rows);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,9 +46,12 @@
|
||||
/**
|
||||
* Implementation of a box blur approximation of a Gaussian blur.
|
||||
*
|
||||
* Creates an 8-bit alpha channel context for callers to draw in, blurs the
|
||||
* contents of that context and applies it as an alpha mask on a
|
||||
* different existing context.
|
||||
* Creates an 8-bit alpha channel context for callers to draw in,
|
||||
* spreads the contents of that context, blurs the contents, and applies
|
||||
* it as an alpha mask on a different existing context.
|
||||
*
|
||||
* A spread N makes each output pixel the maximum value of all source
|
||||
* pixels within a square of side length 2N+1 centered on the output pixel.
|
||||
*
|
||||
* A temporary surface is created in the Init function. The caller then draws
|
||||
* any desired content onto the context acquired through GetContext, and lastly
|
||||
@ -75,6 +78,7 @@ public:
|
||||
* pass NULL here.
|
||||
*/
|
||||
gfxContext* Init(const gfxRect& aRect,
|
||||
const gfxIntSize& aSpreadRadius,
|
||||
const gfxIntSize& aBlurRadius,
|
||||
const gfxRect* aDirtyRect,
|
||||
const gfxRect* aSkipRect);
|
||||
@ -95,8 +99,8 @@ public:
|
||||
void PremultiplyAlpha(gfxFloat alpha);
|
||||
|
||||
/**
|
||||
* Does the actual blurring and mask applying. Users of this object
|
||||
* must have drawn whatever they want to be blurred onto the internal
|
||||
* Does the actual blurring/spreading and mask applying. Users of this
|
||||
* object must have drawn whatever they want to be blurred onto the internal
|
||||
* gfxContext returned by GetContext before calling this.
|
||||
*
|
||||
* @param aDestinationCtx The graphics context on which to apply the
|
||||
@ -111,6 +115,10 @@ public:
|
||||
static gfxIntSize CalculateBlurRadius(const gfxPoint& aStandardDeviation);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
gfxIntSize mSpreadRadius;
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
|
@ -137,6 +137,10 @@ struct THEBES_API gfxRect {
|
||||
Inset(sides[0], sides[1], sides[2], sides[3]);
|
||||
}
|
||||
|
||||
void Inset(const gfxIntSize& size) {
|
||||
Inset(size.height, size.width, size.height, size.width);
|
||||
}
|
||||
|
||||
void Outset(gfxFloat k) {
|
||||
pos.x -= k;
|
||||
pos.y -= k;
|
||||
@ -155,6 +159,10 @@ struct THEBES_API gfxRect {
|
||||
Outset(sides[0], sides[1], sides[2], sides[3]);
|
||||
}
|
||||
|
||||
void Outset(const gfxIntSize& size) {
|
||||
Outset(size.height, size.width, size.height, size.width);
|
||||
}
|
||||
|
||||
// Round the rectangle edges to integer coordinates, such that the rounded
|
||||
// rectangle has the same set of pixel centers as the original rectangle.
|
||||
// Edges at offset 0.5 round up.
|
||||
|
@ -1227,7 +1227,7 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
|
||||
nsRefPtr<gfxContext> shadowContext;
|
||||
nsContextBoxBlur blurringArea;
|
||||
|
||||
shadowContext = blurringArea.Init(shadowRect, blurRadius, twipsPerPixel, renderContext,
|
||||
shadowContext = blurringArea.Init(shadowRect, 0, blurRadius, twipsPerPixel, renderContext,
|
||||
aDirtyRect, &skipGfxRect);
|
||||
if (!shadowContext)
|
||||
continue;
|
||||
@ -1398,7 +1398,7 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
|
||||
nsRefPtr<gfxContext> shadowContext;
|
||||
nsContextBoxBlur blurringArea;
|
||||
|
||||
shadowContext = blurringArea.Init(shadowPaintRect, blurRadius, twipsPerPixel, renderContext,
|
||||
shadowContext = blurringArea.Init(shadowPaintRect, 0, blurRadius, twipsPerPixel, renderContext,
|
||||
aDirtyRect, &skipGfxRect);
|
||||
if (!shadowContext)
|
||||
continue;
|
||||
@ -3827,16 +3827,19 @@ ImageRenderer::Draw(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
#define MAX_BLUR_RADIUS 300
|
||||
#define MAX_SPREAD_RADIUS 50
|
||||
|
||||
// -----
|
||||
// nsContextBoxBlur
|
||||
// -----
|
||||
gfxContext*
|
||||
nsContextBoxBlur::Init(const nsRect& aRect, nscoord aBlurRadius,
|
||||
nsContextBoxBlur::Init(const nsRect& aRect, nscoord aSpreadRadius,
|
||||
nscoord aBlurRadius,
|
||||
PRInt32 aAppUnitsPerDevPixel,
|
||||
gfxContext* aDestinationCtx,
|
||||
const nsRect& aDirtyRect,
|
||||
const gfxRect* aSkipRect)
|
||||
const gfxRect* aSkipRect,
|
||||
PRUint32 aFlags)
|
||||
{
|
||||
if (aRect.IsEmpty()) {
|
||||
mContext = nsnull;
|
||||
@ -3845,10 +3848,12 @@ nsContextBoxBlur::Init(const nsRect& aRect, nscoord aBlurRadius,
|
||||
|
||||
PRInt32 blurRadius = static_cast<PRInt32>(aBlurRadius / aAppUnitsPerDevPixel);
|
||||
blurRadius = PR_MIN(blurRadius, MAX_BLUR_RADIUS);
|
||||
PRInt32 spreadRadius = static_cast<PRInt32>(aSpreadRadius / aAppUnitsPerDevPixel);
|
||||
spreadRadius = PR_MIN(spreadRadius, MAX_BLUR_RADIUS);
|
||||
mDestinationCtx = aDestinationCtx;
|
||||
|
||||
// If not blurring, draw directly onto the destination device
|
||||
if (blurRadius <= 0) {
|
||||
if (blurRadius <= 0 && spreadRadius <= 0 && !(aFlags & FORCE_MASK)) {
|
||||
mContext = aDestinationCtx;
|
||||
return mContext;
|
||||
}
|
||||
@ -3860,7 +3865,8 @@ nsContextBoxBlur::Init(const nsRect& aRect, nscoord aBlurRadius,
|
||||
dirtyRect.RoundOut();
|
||||
|
||||
// Create the temporary surface for blurring
|
||||
mContext = blur.Init(rect, gfxIntSize(blurRadius, blurRadius),
|
||||
mContext = blur.Init(rect, gfxIntSize(spreadRadius, spreadRadius),
|
||||
gfxIntSize(blurRadius, blurRadius),
|
||||
&dirtyRect, aSkipRect);
|
||||
return mContext;
|
||||
}
|
||||
|
@ -391,6 +391,9 @@ protected:
|
||||
*/
|
||||
class nsContextBoxBlur {
|
||||
public:
|
||||
enum {
|
||||
FORCE_MASK = 0x01
|
||||
};
|
||||
/**
|
||||
* Prepares a gfxContext to draw on. Do not call this twice; if you want
|
||||
* to get the gfxContext again use GetContext().
|
||||
@ -422,6 +425,10 @@ public:
|
||||
* @param aSkipRect An area in device pixels (NOT app units!) to avoid
|
||||
* blurring over, to prevent unnecessary work.
|
||||
*
|
||||
* @param aFlags FORCE_MASK to ensure that the content drawn to the
|
||||
* returned gfxContext is used as a mask, and not
|
||||
* drawn directly to aDestinationCtx.
|
||||
*
|
||||
* @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
|
||||
@ -435,9 +442,18 @@ public:
|
||||
* should prepare the destination context as if you were going to draw
|
||||
* directly on it instead of any temporary surface created in this class.
|
||||
*/
|
||||
gfxContext* Init(const nsRect& aRect, nscoord aBlurRadius,
|
||||
gfxContext* Init(const nsRect& aRect, nscoord aSpreadRadius,
|
||||
nscoord aBlurRadius,
|
||||
PRInt32 aAppUnitsPerDevPixel, gfxContext* aDestinationCtx,
|
||||
const nsRect& aDirtyRect, const gfxRect* aSkipRect);
|
||||
const nsRect& aDirtyRect, const gfxRect* aSkipRect,
|
||||
PRUint32 aFlags = 0);
|
||||
|
||||
/**
|
||||
* Does the actual blurring/spreading. Users of this object *must*
|
||||
* have called Init() first, then have drawn whatever they want to be
|
||||
* blurred onto the internal gfxContext before calling this.
|
||||
*/
|
||||
void DoEffects();
|
||||
|
||||
/**
|
||||
* Does the actual blurring and mask applying. Users of this object *must*
|
||||
|
@ -293,7 +293,7 @@ nsDisplayTextShadow::Paint(nsDisplayListBuilder* aBuilder,
|
||||
|
||||
// Create our shadow surface, then paint the text decorations onto it
|
||||
nsContextBoxBlur contextBoxBlur;
|
||||
gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, shadow->mRadius,
|
||||
gfxContext* shadowCtx = contextBoxBlur.Init(shadowRect, 0, shadow->mRadius,
|
||||
presContext->AppUnitsPerDevPixel(),
|
||||
thebesCtx, mVisibleRect, nsnull);
|
||||
if (!shadowCtx) {
|
||||
|
@ -4548,7 +4548,7 @@ nsTextFrame::PaintOneShadow(PRUint32 aOffset, PRUint32 aLength,
|
||||
shadowGfxRect.Width(), shadowGfxRect.Height());
|
||||
|
||||
nsContextBoxBlur contextBoxBlur;
|
||||
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
|
||||
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
|
||||
PresContext()->AppUnitsPerDevPixel(),
|
||||
aCtx, aDirtyRect, nsnull);
|
||||
if (!shadowContext)
|
||||
|
@ -582,7 +582,7 @@ void nsTextBoxFrame::PaintOneShadow(gfxContext* aCtx,
|
||||
shadowRect.MoveBy(shadowOffset);
|
||||
|
||||
nsContextBoxBlur contextBoxBlur;
|
||||
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, blurRadius,
|
||||
gfxContext* shadowContext = contextBoxBlur.Init(shadowRect, 0, blurRadius,
|
||||
PresContext()->AppUnitsPerDevPixel(),
|
||||
aCtx, aDirtyRect, nsnull);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user