зеркало из https://github.com/mozilla/pjs.git
Bug 685767 - Factor blurring out into its own class, and use it from gfxAlphaBoxBlur. r=mattwoodrow
This commit is contained in:
Родитель
14906f6012
Коммит
2a96c7591e
|
@ -0,0 +1,526 @@
|
|||
/* ***** 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 Mozilla gfx.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* 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 <algorithm>
|
||||
#include <math.h>
|
||||
|
||||
#include "CheckedInt.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
#include "mozilla/gfx/Blur.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Box blur involves looking at one pixel, and setting its value to the average
|
||||
* of its neighbouring pixels.
|
||||
* @param aInput The input buffer.
|
||||
* @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 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,
|
||||
unsigned char* aOutput,
|
||||
int32_t aLeftLobe,
|
||||
int32_t aRightLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aWidth > 0);
|
||||
|
||||
int32_t boxSize = aLeftLobe + aRightLobe + 1;
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
|
||||
for (int32_t 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).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
for (int32_t 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 (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = x + i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
}
|
||||
int32_t tmp = x - aLeftLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = alphaSum / boxSize;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
unsigned char* aOutput,
|
||||
int32_t aTopLobe,
|
||||
int32_t aBottomLobe,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
MOZ_ASSERT(aRows > 0);
|
||||
|
||||
int32_t boxSize = aTopLobe + aBottomLobe + 1;
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
for (int32_t y = 0; y < aRows; y++) {
|
||||
if (inSkipRectX && y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost()) {
|
||||
y = aSkipRect.YMost();
|
||||
if (y >= aRows)
|
||||
break;
|
||||
|
||||
alphaSum = 0;
|
||||
for (int32_t i = 0; i < boxSize; i++) {
|
||||
int32_t pos = y + i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = max(pos, 0);
|
||||
pos = min(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
}
|
||||
int32_t tmp = y - aTopLobe;
|
||||
int32_t last = max(tmp, 0);
|
||||
int32_t next = min(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = alphaSum/boxSize;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * last + x];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeLobes(int32_t aRadius, int32_t aLobes[3][2])
|
||||
{
|
||||
int32_t 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.
|
||||
*/
|
||||
int32_t 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;
|
||||
default:
|
||||
// Mathematical impossibility!
|
||||
MOZ_ASSERT(false);
|
||||
major = minor = final = 0;
|
||||
}
|
||||
MOZ_ASSERT(major + minor + final == aRadius);
|
||||
|
||||
aLobes[0][0] = major;
|
||||
aLobes[0][1] = minor;
|
||||
aLobes[1][0] = minor;
|
||||
aLobes[1][1] = major;
|
||||
aLobes[2][0] = final;
|
||||
aLobes[2][1] = final;
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadHorizontal(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
for (int32_t 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).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t 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;
|
||||
}
|
||||
|
||||
int32_t sMin = max(x - aRadius, 0);
|
||||
int32_t sMax = min(x + aRadius, aWidth - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * y + s]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
SpreadVertical(unsigned char* aInput,
|
||||
unsigned char* aOutput,
|
||||
int32_t aRadius,
|
||||
int32_t aWidth,
|
||||
int32_t aRows,
|
||||
int32_t aStride,
|
||||
const IntRect& aSkipRect)
|
||||
{
|
||||
if (aRadius == 0) {
|
||||
memcpy(aOutput, aInput, aStride * aRows);
|
||||
return;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
for (int32_t x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int32_t 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;
|
||||
}
|
||||
|
||||
int32_t sMin = max(y - aRadius, 0);
|
||||
int32_t sMax = min(y + aRadius, aRows - 1);
|
||||
int32_t v = 0;
|
||||
for (int32_t s = sMin; s <= sMax; ++s) {
|
||||
v = max<int32_t>(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static CheckedInt<int32_t>
|
||||
RoundUpToMultipleOf4(int32_t aVal)
|
||||
{
|
||||
CheckedInt<int32_t> val(aVal);
|
||||
|
||||
val += 3;
|
||||
val /= 4;
|
||||
val *= 4;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
AlphaBoxBlur::AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect)
|
||||
: mSpreadRadius(aSpreadRadius),
|
||||
mBlurRadius(aBlurRadius),
|
||||
mData(NULL)
|
||||
{
|
||||
Rect rect(aRect);
|
||||
rect.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect.RoundOut();
|
||||
|
||||
if (aDirtyRect) {
|
||||
// If we get passed a dirty rect from layout, we can minimize the
|
||||
// shadow size and make painting faster.
|
||||
mHasDirtyRect = true;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
Rect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Inflate(Size(aBlurRadius + aSpreadRadius));
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = false;
|
||||
}
|
||||
|
||||
if (rect.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aSkipRect) {
|
||||
// If we get passed a skip rect, we can lower the amount of
|
||||
// blurring/spreading we need to do. We convert it to IntRect to avoid
|
||||
// expensive int<->float conversions if we were to use Rect instead.
|
||||
Rect skipRect = *aSkipRect;
|
||||
skipRect.RoundIn();
|
||||
skipRect.Deflate(Size(aBlurRadius + aSpreadRadius));
|
||||
mSkipRect = IntRect(skipRect.x, skipRect.y, skipRect.width, skipRect.height);
|
||||
|
||||
IntRect shadowIntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
|
||||
|
||||
if (mSkipRect.IsEqualInterior(shadowIntRect))
|
||||
return;
|
||||
|
||||
mSkipRect -= shadowIntRect.TopLeft();
|
||||
} else {
|
||||
mSkipRect = IntRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
mRect = IntRect(rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
CheckedInt<int32_t> stride = RoundUpToMultipleOf4(mRect.width);
|
||||
if (stride.valid()) {
|
||||
mStride = stride.value();
|
||||
|
||||
CheckedInt<int32_t> size = CheckedInt<int32_t>(mStride) * mRect.height *
|
||||
sizeof(unsigned char);
|
||||
if (size.valid()) {
|
||||
mData = static_cast<unsigned char*>(malloc(size.value()));
|
||||
memset(mData, 0, size.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AlphaBoxBlur::~AlphaBoxBlur()
|
||||
{
|
||||
free(mData);
|
||||
}
|
||||
|
||||
unsigned char*
|
||||
AlphaBoxBlur::GetData()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::GetSize()
|
||||
{
|
||||
IntSize size(mRect.width, mRect.height);
|
||||
return size;
|
||||
}
|
||||
|
||||
int32_t
|
||||
AlphaBoxBlur::GetStride()
|
||||
{
|
||||
return mStride;
|
||||
}
|
||||
|
||||
IntRect
|
||||
AlphaBoxBlur::GetRect()
|
||||
{
|
||||
return mRect;
|
||||
}
|
||||
|
||||
Rect*
|
||||
AlphaBoxBlur::GetDirtyRect()
|
||||
{
|
||||
if (mHasDirtyRect) {
|
||||
return &mDirtyRect;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
AlphaBoxBlur::Blur()
|
||||
{
|
||||
if (!mData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no need to do all this if not blurring or spreading
|
||||
if (mBlurRadius != IntSize(0,0) || mSpreadRadius != IntSize(0,0)) {
|
||||
int32_t stride = GetStride();
|
||||
|
||||
// No need to use CheckedInt here - we have validated it in the constructor.
|
||||
size_t szB = stride * GetSize().height * sizeof(unsigned char);
|
||||
unsigned char* tmpData = static_cast<unsigned char*>(malloc(szB));
|
||||
if (!tmpData)
|
||||
return; // OOM
|
||||
|
||||
memset(tmpData, 0, szB);
|
||||
|
||||
if (mSpreadRadius.width > 0 || mSpreadRadius.height > 0) {
|
||||
SpreadHorizontal(mData, tmpData, mSpreadRadius.width, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
SpreadVertical(tmpData, mData, mSpreadRadius.height, GetSize().width, GetSize().height, stride, mSkipRect);
|
||||
}
|
||||
|
||||
if (mBlurRadius.width > 0) {
|
||||
int32_t lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.width, lobes);
|
||||
BoxBlurHorizontal(mData, tmpData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(tmpData, mData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurHorizontal(mData, tmpData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
memcpy(tmpData, mData, stride * GetSize().height);
|
||||
}
|
||||
|
||||
if (mBlurRadius.height > 0) {
|
||||
int32_t lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.height, lobes);
|
||||
BoxBlurVertical(tmpData, mData, lobes[0][0], lobes[0][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(mData, tmpData, lobes[1][0], lobes[1][1], stride, GetSize().height, mSkipRect);
|
||||
BoxBlurVertical(tmpData, mData, lobes[2][0], lobes[2][1], stride, GetSize().height, mSkipRect);
|
||||
} else {
|
||||
memcpy(mData, tmpData, stride * GetSize().height);
|
||||
}
|
||||
|
||||
free(tmpData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the box blur size (which we're calling the blur radius) from
|
||||
* the standard deviation.
|
||||
*
|
||||
* Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
|
||||
* approximating a Gaussian using box blurs. This yields quite a good
|
||||
* approximation for a Gaussian. Then we multiply this by 1.5 since our
|
||||
* code wants the radius of the entire triple-box-blur kernel instead of
|
||||
* the diameter of an individual box blur. For more details, see:
|
||||
* http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
|
||||
*/
|
||||
static const Float GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||
|
||||
IntSize
|
||||
AlphaBoxBlur::CalculateBlurRadius(const Point& aStd)
|
||||
{
|
||||
IntSize size(static_cast<int32_t>(floor(aStd.x * GAUSSIAN_SCALE_FACTOR + 0.5)),
|
||||
static_cast<int32_t>(floor(aStd.y * GAUSSIAN_SCALE_FACTOR + 0.5)));
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/* ***** 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 Mozilla gfx.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* 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 "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* Implementation of a triple box blur approximation of a Gaussian blur.
|
||||
*
|
||||
* A Gaussian blur is good for blurring because, when done independently
|
||||
* in the horizontal and vertical directions, it matches the result that
|
||||
* would be obtained using a different (rotated) set of axes. A triple
|
||||
* box blur is a very close approximation of a Gaussian.
|
||||
*
|
||||
* Creates an 8-bit alpha channel context for callers to draw in,
|
||||
* spreads the contents of that context, and blurs the contents.
|
||||
*
|
||||
* 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
|
||||
* calls Paint to apply the blurred content as an alpha mask.
|
||||
*/
|
||||
class AlphaBoxBlur
|
||||
{
|
||||
public:
|
||||
|
||||
/** Constructs a box blur and initializes the backing surface.
|
||||
*
|
||||
* @param aRect The coordinates of the surface to create in device units.
|
||||
*
|
||||
* @param aBlurRadius The blur radius in pixels. This is the radius of the
|
||||
* entire (triple) kernel function. Each individual box blur has radius
|
||||
* approximately 1/3 this value, or diameter approximately 2/3 this value.
|
||||
* This parameter should nearly always be computed using CalculateBlurRadius,
|
||||
* below.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
AlphaBoxBlur(const Rect& aRect,
|
||||
const IntSize& aSpreadRadius,
|
||||
const IntSize& aBlurRadius,
|
||||
const Rect* aDirtyRect,
|
||||
const Rect* aSkipRect);
|
||||
|
||||
~AlphaBoxBlur();
|
||||
|
||||
/**
|
||||
* Return the pointer to memory allocated by the constructor for the 8-bit
|
||||
* alpha surface you need to be blurred. After you draw to this surface, call
|
||||
* Blur(), below, to have its contents blurred.
|
||||
*/
|
||||
unsigned char* GetData();
|
||||
|
||||
/**
|
||||
* Return the size, in pixels, of the 8-bit alpha surface backed by the
|
||||
* pointer returned by GetData().
|
||||
*/
|
||||
IntSize GetSize();
|
||||
|
||||
/**
|
||||
* Return the stride, in bytes, of the 8-bit alpha surface backed by the
|
||||
* pointer returned by GetData().
|
||||
*/
|
||||
int32_t GetStride();
|
||||
|
||||
/**
|
||||
* Returns the device-space rectangle the 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect GetRect();
|
||||
|
||||
/**
|
||||
* Return a pointer to a dirty rect, as passed in to the constructor, or NULL
|
||||
* if none was passed in.
|
||||
*/
|
||||
Rect* GetDirtyRect();
|
||||
|
||||
/**
|
||||
* Perform the blur in-place on the surface backed by the pointer returned by
|
||||
* GetData().
|
||||
*/
|
||||
void Blur();
|
||||
|
||||
/**
|
||||
* Calculates a blur radius that, when used with box blur, approximates a
|
||||
* Gaussian blur with the given standard deviation. The result of this
|
||||
* function should be used as the aBlurRadius parameter to AlphaBoxBlur's
|
||||
* constructor, above.
|
||||
*/
|
||||
static IntSize CalculateBlurRadius(const Point& aStandardDeviation);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* A rect indicating the area where blurring is unnecessary, and the blur
|
||||
* algorithm should skip over it.
|
||||
*/
|
||||
IntRect mSkipRect;
|
||||
|
||||
/**
|
||||
* The device-space rectangle the the backing 8-bit alpha surface covers.
|
||||
*/
|
||||
IntRect mRect;
|
||||
|
||||
/**
|
||||
* A copy of the dirty rect passed to the constructor. This will only be valid if
|
||||
* mHasDirtyRect is true.
|
||||
*/
|
||||
Rect mDirtyRect;
|
||||
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
IntSize mSpreadRadius;
|
||||
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
IntSize mBlurRadius;
|
||||
|
||||
/**
|
||||
* A pointer to the backing 8-bit alpha surface.
|
||||
*/
|
||||
unsigned char* mData;
|
||||
|
||||
/**
|
||||
* The stride of the data contained in mData.
|
||||
*/
|
||||
int32_t mStride;
|
||||
|
||||
/**
|
||||
* Whether mDirtyRect contains valid data.
|
||||
*/
|
||||
bool mHasDirtyRect;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
|
@ -56,6 +56,7 @@ EXPORTS_mozilla/gfx = \
|
|||
BaseMargin.h \
|
||||
BaseRect.h \
|
||||
BaseSize.h \
|
||||
Blur.h \
|
||||
PathHelpers.h \
|
||||
Point.h \
|
||||
Matrix.h \
|
||||
|
@ -68,6 +69,7 @@ CPPSRCS = \
|
|||
Matrix.cpp \
|
||||
DrawTargetCairo.cpp \
|
||||
SourceSurfaceCairo.cpp \
|
||||
Blur.cpp \
|
||||
$(NULL)
|
||||
|
||||
|
||||
|
|
|
@ -37,14 +37,12 @@
|
|||
|
||||
#include "gfxBlur.h"
|
||||
|
||||
#include "nsMathUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/gfx/Blur.h"
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
gfxAlphaBoxBlur::gfxAlphaBoxBlur()
|
||||
: mBlur(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -59,418 +57,63 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect,
|
|||
const gfxRect* aDirtyRect,
|
||||
const gfxRect* aSkipRect)
|
||||
{
|
||||
mSpreadRadius = aSpreadRadius;
|
||||
mBlurRadius = aBlurRadius;
|
||||
|
||||
gfxRect rect(aRect);
|
||||
rect.Inflate(aBlurRadius + aSpreadRadius);
|
||||
rect.RoundOut();
|
||||
|
||||
Rect rect(aRect.x, aRect.y, aRect.width, aRect.height);
|
||||
IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height);
|
||||
IntSize blurRadius(aBlurRadius.width, aBlurRadius.height);
|
||||
nsAutoPtr<Rect> dirtyRect;
|
||||
if (aDirtyRect) {
|
||||
// If we get passed a dirty rect from layout, we can minimize the
|
||||
// shadow size and make painting faster.
|
||||
mHasDirtyRect = true;
|
||||
mDirtyRect = *aDirtyRect;
|
||||
gfxRect requiredBlurArea = mDirtyRect.Intersect(rect);
|
||||
requiredBlurArea.Inflate(aBlurRadius + aSpreadRadius);
|
||||
rect = requiredBlurArea.Intersect(rect);
|
||||
} else {
|
||||
mHasDirtyRect = false;
|
||||
dirtyRect = new Rect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height);
|
||||
}
|
||||
|
||||
// Check rect empty after accounting for aDirtyRect, since that may have
|
||||
// make the rectangle empty. BoxBlurVertical and BoxBlurHorizontal require
|
||||
// that we have a nonzero number of rows and columns.
|
||||
if (rect.IsEmpty())
|
||||
return nsnull;
|
||||
|
||||
nsAutoPtr<Rect> skipRect;
|
||||
if (aSkipRect) {
|
||||
// If we get passed a skip rect, we can lower the amount of
|
||||
// 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.Deflate(aBlurRadius + aSpreadRadius);
|
||||
gfxUtils::GfxRectToIntRect(skipRect, &mSkipRect);
|
||||
nsIntRect shadowIntRect;
|
||||
gfxUtils::GfxRectToIntRect(rect, &shadowIntRect);
|
||||
mSkipRect.IntersectRect(mSkipRect, shadowIntRect);
|
||||
if (mSkipRect.IsEqualInterior(shadowIntRect))
|
||||
return nsnull;
|
||||
|
||||
mSkipRect -= shadowIntRect.TopLeft();
|
||||
} else {
|
||||
mSkipRect = nsIntRect(0, 0, 0, 0);
|
||||
skipRect = new Rect(aSkipRect->x, aSkipRect->y, aSkipRect->width, aSkipRect->height);
|
||||
}
|
||||
|
||||
mBlur = new AlphaBoxBlur(rect, spreadRadius, blurRadius, dirtyRect, skipRect);
|
||||
|
||||
unsigned char* data = mBlur->GetData();
|
||||
if (!data)
|
||||
return nsnull;
|
||||
|
||||
IntSize size = mBlur->GetSize();
|
||||
// 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())),
|
||||
mImageSurface = new gfxImageSurface(data, gfxIntSize(size.width, size.height),
|
||||
mBlur->GetStride(),
|
||||
gfxASurface::ImageFormatA8);
|
||||
if (!mImageSurface || mImageSurface->CairoStatus())
|
||||
if (mImageSurface->CairoStatus())
|
||||
return nsnull;
|
||||
|
||||
IntRect irect = mBlur->GetRect();
|
||||
gfxPoint topleft(irect.TopLeft().x, irect.TopLeft().y);
|
||||
|
||||
// Use a device offset so callers don't need to worry about translating
|
||||
// coordinates, they can draw as if this was part of the destination context
|
||||
// at the coordinates of rect.
|
||||
mImageSurface->SetDeviceOffset(-rect.TopLeft());
|
||||
mImageSurface->SetDeviceOffset(-topleft);
|
||||
|
||||
mContext = new gfxContext(mImageSurface);
|
||||
|
||||
return mContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Box blur involves looking at one pixel, and setting its value to the average
|
||||
* of its neighbouring pixels.
|
||||
* @param aInput The input buffer.
|
||||
* @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 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,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aLeftLobe,
|
||||
PRInt32 aRightLobe,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
NS_ASSERTION(aWidth > 0, "Can't handle zero width here");
|
||||
|
||||
PRInt32 boxSize = aLeftLobe + aRightLobe + 1;
|
||||
bool skipRectCoversWholeRow = 0 >= aSkipRect.x &&
|
||||
aWidth <= aSkipRect.XMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
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).
|
||||
bool inSkipRectY = y >= aSkipRect.y &&
|
||||
y < aSkipRect.YMost();
|
||||
if (inSkipRectY && skipRectCoversWholeRow) {
|
||||
y = aSkipRect.YMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aLeftLobe;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
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;
|
||||
// See assertion above; if aWidth is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aWidth - 1);
|
||||
alphaSum += aInput[aWidth * y + pos];
|
||||
}
|
||||
}
|
||||
PRInt32 tmp = x - aLeftLobe;
|
||||
PRInt32 last = NS_MAX(tmp, 0);
|
||||
PRInt32 next = NS_MIN(tmp + boxSize, aWidth - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * y + next] -
|
||||
aInput[aWidth * y + last];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
unsigned char* aOutput,
|
||||
PRInt32 aTopLobe,
|
||||
PRInt32 aBottomLobe,
|
||||
PRInt32 aWidth,
|
||||
PRInt32 aRows,
|
||||
const nsIntRect& aSkipRect)
|
||||
{
|
||||
NS_ASSERTION(aRows > 0, "Can't handle zero rows here");
|
||||
|
||||
PRInt32 boxSize = aTopLobe + aBottomLobe + 1;
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
if (boxSize == 1) {
|
||||
memcpy(aOutput, aInput, aWidth*aRows);
|
||||
return;
|
||||
}
|
||||
PRUint32 reciprocal = (PRUint64(1) << 32)/boxSize;
|
||||
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
bool inSkipRectX = x >= aSkipRect.x &&
|
||||
x < aSkipRect.XMost();
|
||||
if (inSkipRectX && skipRectCoversWholeColumn) {
|
||||
x = aSkipRect.XMost() - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
PRUint32 alphaSum = 0;
|
||||
for (PRInt32 i = 0; i < boxSize; i++) {
|
||||
PRInt32 pos = i - aTopLobe;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
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;
|
||||
// See assertion above; if aRows is zero, then we would have no
|
||||
// valid position to clamp to.
|
||||
pos = NS_MAX(pos, 0);
|
||||
pos = NS_MIN(pos, aRows - 1);
|
||||
alphaSum += aInput[aWidth * pos + x];
|
||||
}
|
||||
}
|
||||
PRInt32 tmp = y - aTopLobe;
|
||||
PRInt32 last = NS_MAX(tmp, 0);
|
||||
PRInt32 next = NS_MIN(tmp + boxSize, aRows - 1);
|
||||
|
||||
aOutput[aWidth * y + x] = (PRUint64(alphaSum)*reciprocal) >> 32;
|
||||
|
||||
alphaSum += aInput[aWidth * next + x] -
|
||||
aInput[aWidth * 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;
|
||||
default:
|
||||
NS_ERROR("Mathematical impossibility.");
|
||||
major = minor = final = 0;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool 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).
|
||||
bool 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 = NS_MAX(x - aRadius, 0);
|
||||
PRInt32 sMax = NS_MIN(x + aRadius, aWidth - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = NS_MAX<PRInt32>(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;
|
||||
}
|
||||
|
||||
bool skipRectCoversWholeColumn = 0 >= aSkipRect.y &&
|
||||
aRows <= aSkipRect.YMost();
|
||||
for (PRInt32 x = 0; x < aWidth; x++) {
|
||||
bool 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 = NS_MAX(y - aRadius, 0);
|
||||
PRInt32 sMax = NS_MIN(y + aRadius, aRows - 1);
|
||||
PRInt32 v = 0;
|
||||
for (PRInt32 s = sMin; s <= sMax; ++s) {
|
||||
v = NS_MAX<PRInt32>(v, aInput[aStride * s + x]);
|
||||
}
|
||||
aOutput[aStride * y + x] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
||||
{
|
||||
if (!mContext)
|
||||
return;
|
||||
|
||||
unsigned char* boxData = mImageSurface->Data();
|
||||
mBlur->Blur();
|
||||
|
||||
// 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))
|
||||
return; // OOM
|
||||
|
||||
unsigned char* tmpData = tempAlphaDataBuf.Elements();
|
||||
// .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.
|
||||
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];
|
||||
ComputeLobes(mBlurRadius.width, lobes);
|
||||
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) {
|
||||
PRInt32 lobes[3][2];
|
||||
ComputeLobes(mBlurRadius.height, lobes);
|
||||
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);
|
||||
}
|
||||
}
|
||||
Rect* dirtyrect = mBlur->GetDirtyRect();
|
||||
|
||||
// Avoid a semi-expensive clip operation if we can, otherwise
|
||||
// clip to the dirty rect
|
||||
if (mHasDirtyRect) {
|
||||
if (dirtyrect) {
|
||||
aDestinationCtx->Save();
|
||||
aDestinationCtx->NewPath();
|
||||
aDestinationCtx->Rectangle(mDirtyRect);
|
||||
gfxRect dirty(dirtyrect->x, dirtyrect->y, dirtyrect->width, dirtyrect->height);
|
||||
aDestinationCtx->Rectangle(dirty);
|
||||
aDestinationCtx->Clip();
|
||||
aDestinationCtx->Mask(mImageSurface, offset);
|
||||
aDestinationCtx->Restore();
|
||||
|
@ -479,23 +122,9 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the box blur size (which we're calling the blur radius) from
|
||||
* the standard deviation.
|
||||
*
|
||||
* Much of this, the 3 * sqrt(2 * pi) / 4, is the known value for
|
||||
* approximating a Gaussian using box blurs. This yields quite a good
|
||||
* approximation for a Gaussian. Then we multiply this by 1.5 since our
|
||||
* code wants the radius of the entire triple-box-blur kernel instead of
|
||||
* the diameter of an individual box blur. For more details, see:
|
||||
* http://www.w3.org/TR/SVG11/filters.html#feGaussianBlurElement
|
||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=590039#c19
|
||||
*/
|
||||
static const gfxFloat GAUSSIAN_SCALE_FACTOR = (3 * sqrt(2 * M_PI) / 4) * 1.5;
|
||||
|
||||
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)));
|
||||
Point std(aStd.x, aStd.y);
|
||||
IntSize size = AlphaBoxBlur::CalculateBlurRadius(std);
|
||||
return gfxIntSize(size.width, size.height);
|
||||
}
|
||||
|
|
|
@ -42,7 +42,12 @@
|
|||
#include "gfxImageSurface.h"
|
||||
#include "gfxTypes.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "nsRect.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class AlphaBoxBlur;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of a triple box blur approximation of a Gaussian blur.
|
||||
|
@ -122,15 +127,6 @@ public:
|
|||
static gfxIntSize CalculateBlurRadius(const gfxPoint& aStandardDeviation);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The spread radius, in pixels.
|
||||
*/
|
||||
gfxIntSize mSpreadRadius;
|
||||
/**
|
||||
* The blur radius, in pixels.
|
||||
*/
|
||||
gfxIntSize mBlurRadius;
|
||||
|
||||
/**
|
||||
* The context of the temporary alpha surface.
|
||||
*/
|
||||
|
@ -141,18 +137,10 @@ protected:
|
|||
*/
|
||||
nsRefPtr<gfxImageSurface> mImageSurface;
|
||||
|
||||
/**
|
||||
* A copy of the dirty rect passed to Init(). This will only be valid if
|
||||
* mHasDirtyRect is TRUE.
|
||||
*/
|
||||
gfxRect mDirtyRect;
|
||||
/**
|
||||
* A rect indicating the area where blurring is unnecessary, and the blur
|
||||
* algorithm should skip over it.
|
||||
*/
|
||||
nsIntRect mSkipRect;
|
||||
|
||||
bool mHasDirtyRect;
|
||||
/**
|
||||
* The object that actually does the blurring for us.
|
||||
*/
|
||||
nsAutoPtr<mozilla::gfx::AlphaBoxBlur> mBlur;
|
||||
};
|
||||
|
||||
#endif /* GFX_BLUR_H */
|
||||
|
|
Загрузка…
Ссылка в новой задаче