--- a/gfx/thebes/src/gfxBlur.cpp
+++ b/gfx/thebes/src/gfxBlur.cpp
@@ -181,54 +181,90 @@ BoxBlurVertical(unsigned char* aInput,
aOutput[aStride * y + x] = alphaSum/boxSize;
alphaSum += aInput[aStride * next + x] -
aInput[aStride * last + x];
}
}
}
+static void ComputeLobes(PRInt32 aRadius, PRInt32 aLobes[3][2])
+{
+ PRInt32 major, minor, final;
+
+ /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
+ * some notes about approximating the Gaussian blur with box-blurs.
+ * The comments below are in the terminology of that page.
+ */
+ PRInt32 z = aRadius/3;
+ switch (aRadius % 3) {
+ case 0:
+ // aRadius = z*3; choose d = 2*z + 1
+ major = minor = final = z;
+ break;
+ case 1:
+ // aRadius = z*3 + 1
+ // This is a tricky case since there is no value of d which will
+ // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
+ // for some integer k, then the radius will be 3*k. If d is even,
+ // i.e. d=2*k, then the radius will be 3*k - 1.
+ // So we have to choose values that don't match the standard
+ // algorithm.
+ major = z + 1;
+ minor = final = z;
+ break;
+ case 2:
+ // aRadius = z*3 + 2; choose d = 2*z + 2
+ major = final = z + 1;
+ minor = z;
+ break;
+ }
+ NS_ASSERTION(major + minor + final == aRadius,
+ "Lobes don't sum to the right length");
+
+ aLobes[0][0] = major;
+ aLobes[0][1] = minor;
+ aLobes[1][0] = minor;
+ aLobes[1][1] = major;
+ aLobes[2][0] = final;
+ aLobes[2][1] = final;
+}
+
void
gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
{
if (!mContext)
return;
unsigned char* boxData = mImageSurface->Data();
// no need to do all this if not blurring
if (mBlurRadius.width != 0 || mBlurRadius.height != 0) {
- // A blur radius of 1 achieves nothing (1/2 = 0 in int terms),
- // but we still want a blur!
- // XXX this may not be appropriate... perhaps just use actuall Gaussian here?
- mBlurRadius.width = PR_MAX(mBlurRadius.width, 2);
- mBlurRadius.height = PR_MAX(mBlurRadius.height, 2);
-
nsTArray<unsigned char> tempAlphaDataBuf;
if (!tempAlphaDataBuf.SetLength(mImageSurface->GetDataSize()))
return; // OOM
unsigned char* tmpData = tempAlphaDataBuf.Elements();
PRInt32 stride = mImageSurface->Stride();
PRInt32 rows = mImageSurface->Height();
if (mBlurRadius.width > 0) {
- PRInt32 longLobe = mBlurRadius.width / 2;
- PRInt32 shortLobe = (mBlurRadius.width & 1) ? longLobe : longLobe - 1;
- BoxBlurHorizontal(boxData, tmpData, longLobe, shortLobe, stride, rows);
- BoxBlurHorizontal(tmpData, boxData, shortLobe, longLobe, stride, rows);
- BoxBlurHorizontal(boxData, tmpData, longLobe, longLobe, stride, rows);
+ 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);
}
if (mBlurRadius.height > 0) {
- PRInt32 longLobe = mBlurRadius.height / 2;
- PRInt32 shortLobe = (mBlurRadius.height & 1) ? longLobe : longLobe - 1;
- BoxBlurVertical(tmpData, boxData, longLobe, shortLobe, stride, rows);
- BoxBlurVertical(boxData, tmpData, shortLobe, longLobe, stride, rows);
- BoxBlurVertical(tmpData, boxData, longLobe, longLobe, stride, rows);
+ 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);
}
}
// Avoid a semi-expensive clip operation if we can, otherwise
// clip to the dirty rect
if (mHasDirtyRect) {
aDestinationCtx->Save();
aDestinationCtx->NewPath();
@@ -236,16 +272,17 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDest
aDestinationCtx->Clip();
aDestinationCtx->Mask(mImageSurface, offset);
aDestinationCtx->Restore();
} else {
aDestinationCtx->Mask(mImageSurface, offset);
}
}
-static const gfxFloat GAUSSIAN_SCALE_FACTOR = 3 * sqrt(2 * M_PI) / 4;
+// Blur radius is approximately 3/2 times the box-blur size
+static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * (3/2);
gfxIntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd)
{
return gfxIntSize(
static_cast<PRInt32>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
static_cast<PRInt32>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
}