mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 847120: Move SVGFEGaussianBlueElement to its own file r=Ms2ger
--HG-- rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFEGaussianBlurElement.cpp rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFEGaussianBlurElement.h
This commit is contained in:
parent
66b44c2f73
commit
ce074ad1ae
@ -84,6 +84,7 @@ CPPSRCS = \
|
||||
SVGFECompositeElement.cpp \
|
||||
SVGFEDistantLightElement.cpp \
|
||||
SVGFEFloodElement.cpp \
|
||||
SVGFEGaussianBlurElement.cpp \
|
||||
SVGFEImageElement.cpp \
|
||||
SVGFEMergeElement.cpp \
|
||||
SVGFEMergeNodeElement.cpp \
|
||||
@ -189,6 +190,7 @@ EXPORTS_mozilla/dom = \
|
||||
SVGFECompositeElement.h \
|
||||
SVGFEDistantLightElement.h \
|
||||
SVGFEFloodElement.h \
|
||||
SVGFEGaussianBlurElement.h \
|
||||
SVGFEImageElement.h \
|
||||
SVGFEMergeElement.h \
|
||||
SVGFEMergeNodeElement.h \
|
||||
|
427
content/svg/content/src/SVGFEGaussianBlurElement.cpp
Normal file
427
content/svg/content/src/SVGFEGaussianBlurElement.cpp
Normal file
@ -0,0 +1,427 @@
|
||||
/* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/SVGFEGaussianBlurElement.h"
|
||||
|
||||
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FEGaussianBlur)
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
nsSVGElement::NumberPairInfo nsSVGFEGaussianBlurElement::sNumberPairInfo[1] =
|
||||
{
|
||||
{ &nsGkAtoms::stdDeviation, 0, 0 }
|
||||
};
|
||||
|
||||
nsSVGElement::StringInfo nsSVGFEGaussianBlurElement::sStringInfo[2] =
|
||||
{
|
||||
{ &nsGkAtoms::result, kNameSpaceID_None, true },
|
||||
{ &nsGkAtoms::in, kNameSpaceID_None, true }
|
||||
};
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISupports methods
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsSVGFEGaussianBlurElement,nsSVGFEGaussianBlurElementBase)
|
||||
NS_IMPL_RELEASE_INHERITED(nsSVGFEGaussianBlurElement,nsSVGFEGaussianBlurElementBase)
|
||||
|
||||
DOMCI_NODE_DATA(SVGFEGaussianBlurElement, nsSVGFEGaussianBlurElement)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD(nsSVGFEGaussianBlurElement)
|
||||
NS_NODE_INTERFACE_TABLE5(nsSVGFEGaussianBlurElement, nsIDOMNode,
|
||||
nsIDOMElement, nsIDOMSVGElement,
|
||||
nsIDOMSVGFilterPrimitiveStandardAttributes,
|
||||
nsIDOMSVGFEGaussianBlurElement)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEGaussianBlurElement)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsSVGFEGaussianBlurElementBase)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMNode methods
|
||||
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEGaussianBlurElement)
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMSVGFEGaussianBlurElement methods
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedString in1; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
|
||||
{
|
||||
return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationX; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationX(nsIDOMSVGAnimatedNumber * *aX)
|
||||
{
|
||||
return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aX, nsSVGNumberPair::eFirst, this);
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationY; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationY(nsIDOMSVGAnimatedNumber * *aY)
|
||||
{
|
||||
return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aY, nsSVGNumberPair::eSecond, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
|
||||
{
|
||||
NS_ENSURE_FINITE2(stdDeviationX, stdDeviationY, NS_ERROR_ILLEGAL_VALUE);
|
||||
mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to speed up 1/N integer divisions --- integer division is
|
||||
* often rather slow.
|
||||
* We know that our input numerators V are constrained to be <= 255*N,
|
||||
* so the result of dividing by N always fits in 8 bits.
|
||||
* So we can try approximating the division V/N as V*K/(2^24) (integer
|
||||
* division, 32-bit multiply). Dividing by 2^24 is a simple shift so it's
|
||||
* fast. The main problem is choosing a value for K; this function returns
|
||||
* K's value.
|
||||
*
|
||||
* If the result is correct for the extrema, V=0 and V=255*N, then we'll
|
||||
* be in good shape since both the original function and our approximation
|
||||
* are linear. V=0 always gives 0 in both cases, no problem there.
|
||||
* For V=255*N, let's choose the largest K that doesn't cause overflow
|
||||
* and ensure that it gives the right answer. The constraints are
|
||||
* (1) 255*N*K < 2^32
|
||||
* and (2) 255*N*K >= 255*(2^24)
|
||||
*
|
||||
* From (1) we find the best value of K is floor((2^32 - 1)/(255*N)).
|
||||
* (2) tells us when this will be valid:
|
||||
* N*floor((2^32 - 1)/(255*N)) >= 2^24
|
||||
* Now, floor(X) > X - 1, so (2) holds if
|
||||
* N*((2^32 - 1)/(255*N) - 1) >= 2^24
|
||||
* (2^32 - 1)/255 - 2^24 >= N
|
||||
* N <= 65793
|
||||
*
|
||||
* If all that math confuses you, this should convince you:
|
||||
* > perl -e 'for($N=1;(255*$N*int(0xFFFFFFFF/(255*$N)))>>24==255;++$N){}print"$N\n"'
|
||||
* 66052
|
||||
*
|
||||
* So this is fine for all reasonable values of N. For larger values of N
|
||||
* we may as well just use the same approximation and accept the fact that
|
||||
* the output channel values will be a little low.
|
||||
*/
|
||||
static uint32_t ComputeScaledDivisor(uint32_t aDivisor)
|
||||
{
|
||||
return UINT32_MAX/(255*aDivisor);
|
||||
}
|
||||
|
||||
static void
|
||||
BoxBlur(const uint8_t *aInput, uint8_t *aOutput,
|
||||
int32_t aStrideMinor, int32_t aStartMinor, int32_t aEndMinor,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, bool aAlphaOnly)
|
||||
{
|
||||
int32_t boxSize = aLeftLobe + aRightLobe + 1;
|
||||
int32_t scaledDivisor = ComputeScaledDivisor(boxSize);
|
||||
int32_t sums[4] = {0, 0, 0, 0};
|
||||
|
||||
for (int32_t i=0; i < boxSize; i++) {
|
||||
int32_t pos = aStartMinor - aLeftLobe + i;
|
||||
pos = std::max(pos, aStartMinor);
|
||||
pos = std::min(pos, aEndMinor - 1);
|
||||
#define SUM(j) sums[j] += aInput[aStrideMinor*pos + j];
|
||||
SUM(0); SUM(1); SUM(2); SUM(3);
|
||||
#undef SUM
|
||||
}
|
||||
|
||||
aOutput += aStrideMinor*aStartMinor;
|
||||
if (aStartMinor + int32_t(boxSize) <= aEndMinor) {
|
||||
const uint8_t *lastInput = aInput + aStartMinor*aStrideMinor;
|
||||
const uint8_t *nextInput = aInput + (aStartMinor + aRightLobe + 1)*aStrideMinor;
|
||||
#define OUTPUT(j) aOutput[j] = (sums[j]*scaledDivisor) >> 24;
|
||||
#define SUM(j) sums[j] += nextInput[j] - lastInput[j];
|
||||
// process pixels in B, G, R, A order because that's 0, 1, 2, 3 for x86
|
||||
#define OUTPUT_PIXEL() \
|
||||
if (!aAlphaOnly) { OUTPUT(GFX_ARGB32_OFFSET_B); \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_G); \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_R); } \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_A);
|
||||
#define SUM_PIXEL() \
|
||||
if (!aAlphaOnly) { SUM(GFX_ARGB32_OFFSET_B); \
|
||||
SUM(GFX_ARGB32_OFFSET_G); \
|
||||
SUM(GFX_ARGB32_OFFSET_R); } \
|
||||
SUM(GFX_ARGB32_OFFSET_A);
|
||||
for (int32_t minor = aStartMinor;
|
||||
minor < aStartMinor + aLeftLobe;
|
||||
minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
nextInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
}
|
||||
for (int32_t minor = aStartMinor + aLeftLobe;
|
||||
minor < aEndMinor - aRightLobe - 1;
|
||||
minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
lastInput += aStrideMinor;
|
||||
nextInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
}
|
||||
// nextInput is now aInput + aEndMinor*aStrideMinor. Set it back to
|
||||
// aInput + (aEndMinor - 1)*aStrideMinor so we read the last pixel in every
|
||||
// iteration of the next loop.
|
||||
nextInput -= aStrideMinor;
|
||||
for (int32_t minor = aEndMinor - aRightLobe - 1; minor < aEndMinor; minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
lastInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
#undef SUM_PIXEL
|
||||
#undef SUM
|
||||
}
|
||||
} else {
|
||||
for (int32_t minor = aStartMinor; minor < aEndMinor; minor++) {
|
||||
int32_t tmp = minor - aLeftLobe;
|
||||
int32_t last = std::max(tmp, aStartMinor);
|
||||
int32_t next = std::min(tmp + int32_t(boxSize), aEndMinor - 1);
|
||||
|
||||
OUTPUT_PIXEL();
|
||||
#define SUM(j) sums[j] += aInput[aStrideMinor*next + j] - \
|
||||
aInput[aStrideMinor*last + j];
|
||||
if (!aAlphaOnly) { SUM(GFX_ARGB32_OFFSET_B);
|
||||
SUM(GFX_ARGB32_OFFSET_G);
|
||||
SUM(GFX_ARGB32_OFFSET_R); }
|
||||
SUM(GFX_ARGB32_OFFSET_A);
|
||||
aOutput += aStrideMinor;
|
||||
#undef SUM
|
||||
#undef OUTPUT_PIXEL
|
||||
#undef OUTPUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetBlurBoxSize(double aStdDev)
|
||||
{
|
||||
NS_ASSERTION(aStdDev >= 0, "Negative standard deviations not allowed");
|
||||
|
||||
double size = aStdDev*3*sqrt(2*M_PI)/4;
|
||||
// Doing super-large blurs accurately isn't very important.
|
||||
uint32_t max = 1024;
|
||||
if (size > max)
|
||||
return max;
|
||||
return uint32_t(floor(size + 0.5));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGFEGaussianBlurElement::GetDXY(uint32_t *aDX, uint32_t *aDY,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
float stdX = aInstance.GetPrimitiveNumber(SVGContentUtils::X,
|
||||
&mNumberPairAttributes[STD_DEV],
|
||||
nsSVGNumberPair::eFirst);
|
||||
float stdY = aInstance.GetPrimitiveNumber(SVGContentUtils::Y,
|
||||
&mNumberPairAttributes[STD_DEV],
|
||||
nsSVGNumberPair::eSecond);
|
||||
if (stdX < 0 || stdY < 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// If the box size is greater than twice the temporary surface size
|
||||
// in an axis, then each pixel will be set to the average of all the
|
||||
// other pixel values.
|
||||
*aDX = GetBlurBoxSize(stdX);
|
||||
*aDY = GetBlurBoxSize(stdY);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
AreAllColorChannelsZero(const nsSVGFE::Image* aTarget)
|
||||
{
|
||||
return aTarget->mConstantColorChannels &&
|
||||
aTarget->mImage->GetDataSize() >= 4 &&
|
||||
(*reinterpret_cast<uint32_t*>(aTarget->mImage->Data()) & 0x00FFFFFF) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::GaussianBlur(const Image *aSource,
|
||||
const Image *aTarget,
|
||||
const nsIntRect& aDataRect,
|
||||
uint32_t aDX, uint32_t aDY)
|
||||
{
|
||||
NS_ASSERTION(nsIntRect(0, 0, aTarget->mImage->Width(), aTarget->mImage->Height()).Contains(aDataRect),
|
||||
"aDataRect out of bounds");
|
||||
|
||||
nsAutoArrayPtr<uint8_t> tmp(new uint8_t[aTarget->mImage->GetDataSize()]);
|
||||
if (!tmp)
|
||||
return;
|
||||
memset(tmp, 0, aTarget->mImage->GetDataSize());
|
||||
|
||||
bool alphaOnly = AreAllColorChannelsZero(aTarget);
|
||||
|
||||
const uint8_t* sourceData = aSource->mImage->Data();
|
||||
uint8_t* targetData = aTarget->mImage->Data();
|
||||
uint32_t stride = aTarget->mImage->Stride();
|
||||
|
||||
if (aDX == 0) {
|
||||
CopyDataRect(tmp, sourceData, stride, aDataRect);
|
||||
} else {
|
||||
int32_t longLobe = aDX/2;
|
||||
int32_t shortLobe = (aDX & 1) ? longLobe : longLobe - 1;
|
||||
for (int32_t major = aDataRect.y; major < aDataRect.YMost(); ++major) {
|
||||
int32_t ms = major*stride;
|
||||
BoxBlur(sourceData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, shortLobe, alphaOnly);
|
||||
BoxBlur(tmp + ms, targetData + ms, 4, aDataRect.x, aDataRect.XMost(), shortLobe, longLobe, alphaOnly);
|
||||
BoxBlur(targetData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, longLobe, alphaOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if (aDY == 0) {
|
||||
CopyDataRect(targetData, tmp, stride, aDataRect);
|
||||
} else {
|
||||
int32_t longLobe = aDY/2;
|
||||
int32_t shortLobe = (aDY & 1) ? longLobe : longLobe - 1;
|
||||
for (int32_t major = aDataRect.x; major < aDataRect.XMost(); ++major) {
|
||||
int32_t ms = major*4;
|
||||
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, shortLobe, alphaOnly);
|
||||
BoxBlur(targetData + ms, tmp + ms, stride, aDataRect.y, aDataRect.YMost(), shortLobe, longLobe, alphaOnly);
|
||||
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, longLobe, alphaOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
InflateRectForBlurDXY(nsIntRect* aRect, uint32_t aDX, uint32_t aDY)
|
||||
{
|
||||
aRect->Inflate(3*(aDX/2), 3*(aDY/2));
|
||||
}
|
||||
|
||||
static void
|
||||
ClearRect(gfxImageSurface* aSurface, int32_t aX, int32_t aY,
|
||||
int32_t aXMost, int32_t aYMost)
|
||||
{
|
||||
NS_ASSERTION(aX <= aXMost && aY <= aYMost, "Invalid rectangle");
|
||||
NS_ASSERTION(aX >= 0 && aY >= 0 && aXMost <= aSurface->Width() && aYMost <= aSurface->Height(),
|
||||
"Rectangle out of bounds");
|
||||
|
||||
if (aX == aXMost || aY == aYMost)
|
||||
return;
|
||||
for (int32_t y = aY; y < aYMost; ++y) {
|
||||
memset(aSurface->Data() + aSurface->Stride()*y + aX*4, 0, (aXMost - aX)*4);
|
||||
}
|
||||
}
|
||||
|
||||
// Clip aTarget's image to its filter primitive subregion.
|
||||
// aModifiedRect contains all the pixels which might not be RGBA(0,0,0,0),
|
||||
// it's relative to the surface data.
|
||||
static void
|
||||
ClipTarget(nsSVGFilterInstance* aInstance, const nsSVGFE::Image* aTarget,
|
||||
const nsIntRect& aModifiedRect)
|
||||
{
|
||||
nsIntPoint surfaceTopLeft = aInstance->GetSurfaceRect().TopLeft();
|
||||
|
||||
NS_ASSERTION(aInstance->GetSurfaceRect().Contains(aModifiedRect + surfaceTopLeft),
|
||||
"Modified data area overflows the surface?");
|
||||
|
||||
nsIntRect clip = aModifiedRect;
|
||||
nsSVGUtils::ClipToGfxRect(&clip,
|
||||
aTarget->mFilterPrimitiveSubregion - gfxPoint(surfaceTopLeft.x, surfaceTopLeft.y));
|
||||
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, aModifiedRect.y, aModifiedRect.XMost(), clip.y);
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, clip.y, clip.x, clip.YMost());
|
||||
ClearRect(aTarget->mImage, clip.XMost(), clip.y, aModifiedRect.XMost(), clip.YMost());
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, clip.YMost(), aModifiedRect.XMost(), aModifiedRect.YMost());
|
||||
}
|
||||
|
||||
static void
|
||||
ClipComputationRectToSurface(nsSVGFilterInstance* aInstance,
|
||||
nsIntRect* aDataRect)
|
||||
{
|
||||
aDataRect->IntersectRect(*aDataRect,
|
||||
nsIntRect(nsIntPoint(0, 0), aInstance->GetSurfaceRect().Size()));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGFEGaussianBlurElement::Filter(nsSVGFilterInstance* aInstance,
|
||||
const nsTArray<const Image*>& aSources,
|
||||
const Image* aTarget,
|
||||
const nsIntRect& rect)
|
||||
{
|
||||
uint32_t dx, dy;
|
||||
nsresult rv = GetDXY(&dx, &dy, *aInstance);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIntRect computationRect = rect;
|
||||
InflateRectForBlurDXY(&computationRect, dx, dy);
|
||||
ClipComputationRectToSurface(aInstance, &computationRect);
|
||||
GaussianBlur(aSources[0], aTarget, computationRect, dx, dy);
|
||||
ClipTarget(aInstance, aTarget, computationRect);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute) const
|
||||
{
|
||||
return nsSVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
|
||||
(aNameSpaceID == kNameSpaceID_None &&
|
||||
(aAttribute == nsGkAtoms::in ||
|
||||
aAttribute == nsGkAtoms::stdDeviation));
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
|
||||
{
|
||||
aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::InflateRectForBlur(const nsIntRect& aRect,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
uint32_t dX, dY;
|
||||
nsresult rv = GetDXY(&dX, &dY, aInstance);
|
||||
nsIntRect result = aRect;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
InflateRectForBlurDXY(&result, dX, dY);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
return InflateRectForBlur(aSourceBBoxes[0], aInstance);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
|
||||
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
aSourceBBoxes[0] = InflateRectForBlur(aTargetBBox, aInstance);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
return InflateRectForBlur(aSourceChangeBoxes[0], aInstance);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
nsSVGElement::NumberPairAttributesInfo
|
||||
nsSVGFEGaussianBlurElement::GetNumberPairInfo()
|
||||
{
|
||||
return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
|
||||
ArrayLength(sNumberPairInfo));
|
||||
}
|
||||
|
||||
nsSVGElement::StringAttributesInfo
|
||||
nsSVGFEGaussianBlurElement::GetStringInfo()
|
||||
{
|
||||
return StringAttributesInfo(mStringAttributes, sStringInfo,
|
||||
ArrayLength(sStringInfo));
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
84
content/svg/content/src/SVGFEGaussianBlurElement.h
Normal file
84
content/svg/content/src/SVGFEGaussianBlurElement.h
Normal file
@ -0,0 +1,84 @@
|
||||
/* a*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_SVGFEGaussianBlurElement_h
|
||||
#define mozilla_dom_SVGFEGaussianBlurElement_h
|
||||
|
||||
#include "nsSVGFilters.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
typedef nsSVGFE nsSVGFEGaussianBlurElementBase;
|
||||
|
||||
class nsSVGFEGaussianBlurElement : public nsSVGFEGaussianBlurElementBase,
|
||||
public nsIDOMSVGFEGaussianBlurElement
|
||||
{
|
||||
friend nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
|
||||
already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
protected:
|
||||
nsSVGFEGaussianBlurElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsSVGFEGaussianBlurElementBase(aNodeInfo) {}
|
||||
|
||||
public:
|
||||
// interfaces:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// FE Base
|
||||
NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEGaussianBlurElementBase::)
|
||||
|
||||
virtual nsresult Filter(nsSVGFilterInstance* aInstance,
|
||||
const nsTArray<const Image*>& aSources,
|
||||
const Image* aTarget,
|
||||
const nsIntRect& aDataRect);
|
||||
virtual bool AttributeAffectsRendering(
|
||||
int32_t aNameSpaceID, nsIAtom* aAttribute) const;
|
||||
virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
|
||||
virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources);
|
||||
virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
|
||||
const nsSVGFilterInstance& aInstance);
|
||||
virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
|
||||
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
|
||||
virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
|
||||
const nsSVGFilterInstance& aInstance);
|
||||
|
||||
// Gaussian
|
||||
NS_DECL_NSIDOMSVGFEGAUSSIANBLURELEMENT
|
||||
|
||||
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEGaussianBlurElementBase::)
|
||||
|
||||
NS_FORWARD_NSIDOMNODE_TO_NSINODE
|
||||
NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
|
||||
virtual nsIDOMNode* AsDOMNode() { return this; }
|
||||
protected:
|
||||
virtual NumberPairAttributesInfo GetNumberPairInfo();
|
||||
virtual StringAttributesInfo GetStringInfo();
|
||||
|
||||
enum { STD_DEV };
|
||||
nsSVGNumberPair mNumberPairAttributes[1];
|
||||
static NumberPairInfo sNumberPairInfo[1];
|
||||
|
||||
enum { RESULT, IN1 };
|
||||
nsSVGString mStringAttributes[2];
|
||||
static StringInfo sStringInfo[2];
|
||||
|
||||
private:
|
||||
nsresult GetDXY(uint32_t *aDX, uint32_t *aDY, const nsSVGFilterInstance& aInstance);
|
||||
nsIntRect InflateRectForBlur(const nsIntRect& aRect, const nsSVGFilterInstance& aInstance);
|
||||
|
||||
void GaussianBlur(const Image *aSource, const Image *aTarget,
|
||||
const nsIntRect& aDataRect,
|
||||
uint32_t aDX, uint32_t aDY);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_SVGFEGaussianBlurElement_h
|
@ -350,490 +350,6 @@ nsSVGFE::GetLengthInfo()
|
||||
ArrayLength(sLengthInfo));
|
||||
}
|
||||
|
||||
//---------------------Gaussian Blur------------------------
|
||||
|
||||
typedef nsSVGFE nsSVGFEGaussianBlurElementBase;
|
||||
|
||||
class nsSVGFEGaussianBlurElement : public nsSVGFEGaussianBlurElementBase,
|
||||
public nsIDOMSVGFEGaussianBlurElement
|
||||
{
|
||||
friend nsresult NS_NewSVGFEGaussianBlurElement(nsIContent **aResult,
|
||||
already_AddRefed<nsINodeInfo> aNodeInfo);
|
||||
protected:
|
||||
nsSVGFEGaussianBlurElement(already_AddRefed<nsINodeInfo> aNodeInfo)
|
||||
: nsSVGFEGaussianBlurElementBase(aNodeInfo) {}
|
||||
|
||||
public:
|
||||
// interfaces:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// FE Base
|
||||
NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFEGaussianBlurElementBase::)
|
||||
|
||||
virtual nsresult Filter(nsSVGFilterInstance* aInstance,
|
||||
const nsTArray<const Image*>& aSources,
|
||||
const Image* aTarget,
|
||||
const nsIntRect& aDataRect);
|
||||
virtual bool AttributeAffectsRendering(
|
||||
int32_t aNameSpaceID, nsIAtom* aAttribute) const;
|
||||
virtual nsSVGString& GetResultImageName() { return mStringAttributes[RESULT]; }
|
||||
virtual void GetSourceImageNames(nsTArray<nsSVGStringInfo >& aSources);
|
||||
virtual nsIntRect ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
|
||||
const nsSVGFilterInstance& aInstance);
|
||||
virtual void ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
|
||||
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance);
|
||||
virtual nsIntRect ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
|
||||
const nsSVGFilterInstance& aInstance);
|
||||
|
||||
// Gaussian
|
||||
NS_DECL_NSIDOMSVGFEGAUSSIANBLURELEMENT
|
||||
|
||||
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFEGaussianBlurElementBase::)
|
||||
|
||||
NS_FORWARD_NSIDOMNODE_TO_NSINODE
|
||||
NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
|
||||
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
||||
virtual nsXPCClassInfo* GetClassInfo();
|
||||
|
||||
virtual nsIDOMNode* AsDOMNode() { return this; }
|
||||
protected:
|
||||
virtual NumberPairAttributesInfo GetNumberPairInfo();
|
||||
virtual StringAttributesInfo GetStringInfo();
|
||||
|
||||
enum { STD_DEV };
|
||||
nsSVGNumberPair mNumberPairAttributes[1];
|
||||
static NumberPairInfo sNumberPairInfo[1];
|
||||
|
||||
enum { RESULT, IN1 };
|
||||
nsSVGString mStringAttributes[2];
|
||||
static StringInfo sStringInfo[2];
|
||||
|
||||
private:
|
||||
nsresult GetDXY(uint32_t *aDX, uint32_t *aDY, const nsSVGFilterInstance& aInstance);
|
||||
nsIntRect InflateRectForBlur(const nsIntRect& aRect, const nsSVGFilterInstance& aInstance);
|
||||
|
||||
void GaussianBlur(const Image *aSource, const Image *aTarget,
|
||||
const nsIntRect& aDataRect,
|
||||
uint32_t aDX, uint32_t aDY);
|
||||
};
|
||||
|
||||
nsSVGElement::NumberPairInfo nsSVGFEGaussianBlurElement::sNumberPairInfo[1] =
|
||||
{
|
||||
{ &nsGkAtoms::stdDeviation, 0, 0 }
|
||||
};
|
||||
|
||||
nsSVGElement::StringInfo nsSVGFEGaussianBlurElement::sStringInfo[2] =
|
||||
{
|
||||
{ &nsGkAtoms::result, kNameSpaceID_None, true },
|
||||
{ &nsGkAtoms::in, kNameSpaceID_None, true }
|
||||
};
|
||||
|
||||
NS_IMPL_NS_NEW_SVG_ELEMENT(FEGaussianBlur)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsISupports methods
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(nsSVGFEGaussianBlurElement,nsSVGFEGaussianBlurElementBase)
|
||||
NS_IMPL_RELEASE_INHERITED(nsSVGFEGaussianBlurElement,nsSVGFEGaussianBlurElementBase)
|
||||
|
||||
DOMCI_NODE_DATA(SVGFEGaussianBlurElement, nsSVGFEGaussianBlurElement)
|
||||
|
||||
NS_INTERFACE_TABLE_HEAD(nsSVGFEGaussianBlurElement)
|
||||
NS_NODE_INTERFACE_TABLE5(nsSVGFEGaussianBlurElement, nsIDOMNode,
|
||||
nsIDOMElement, nsIDOMSVGElement,
|
||||
nsIDOMSVGFilterPrimitiveStandardAttributes,
|
||||
nsIDOMSVGFEGaussianBlurElement)
|
||||
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFEGaussianBlurElement)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsSVGFEGaussianBlurElementBase)
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMNode methods
|
||||
|
||||
|
||||
NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFEGaussianBlurElement)
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsIDOMSVGFEGaussianBlurElement methods
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedString in1; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
|
||||
{
|
||||
return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationX; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationX(nsIDOMSVGAnimatedNumber * *aX)
|
||||
{
|
||||
return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aX, nsSVGNumberPair::eFirst, this);
|
||||
}
|
||||
|
||||
/* readonly attribute nsIDOMSVGAnimatedNumber stdDeviationY; */
|
||||
NS_IMETHODIMP nsSVGFEGaussianBlurElement::GetStdDeviationY(nsIDOMSVGAnimatedNumber * *aY)
|
||||
{
|
||||
return mNumberPairAttributes[STD_DEV].ToDOMAnimatedNumber(aY, nsSVGNumberPair::eSecond, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSVGFEGaussianBlurElement::SetStdDeviation(float stdDeviationX, float stdDeviationY)
|
||||
{
|
||||
NS_ENSURE_FINITE2(stdDeviationX, stdDeviationY, NS_ERROR_ILLEGAL_VALUE);
|
||||
mNumberPairAttributes[STD_DEV].SetBaseValues(stdDeviationX, stdDeviationY, this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* We want to speed up 1/N integer divisions --- integer division is
|
||||
* often rather slow.
|
||||
* We know that our input numerators V are constrained to be <= 255*N,
|
||||
* so the result of dividing by N always fits in 8 bits.
|
||||
* So we can try approximating the division V/N as V*K/(2^24) (integer
|
||||
* division, 32-bit multiply). Dividing by 2^24 is a simple shift so it's
|
||||
* fast. The main problem is choosing a value for K; this function returns
|
||||
* K's value.
|
||||
*
|
||||
* If the result is correct for the extrema, V=0 and V=255*N, then we'll
|
||||
* be in good shape since both the original function and our approximation
|
||||
* are linear. V=0 always gives 0 in both cases, no problem there.
|
||||
* For V=255*N, let's choose the largest K that doesn't cause overflow
|
||||
* and ensure that it gives the right answer. The constraints are
|
||||
* (1) 255*N*K < 2^32
|
||||
* and (2) 255*N*K >= 255*(2^24)
|
||||
*
|
||||
* From (1) we find the best value of K is floor((2^32 - 1)/(255*N)).
|
||||
* (2) tells us when this will be valid:
|
||||
* N*floor((2^32 - 1)/(255*N)) >= 2^24
|
||||
* Now, floor(X) > X - 1, so (2) holds if
|
||||
* N*((2^32 - 1)/(255*N) - 1) >= 2^24
|
||||
* (2^32 - 1)/255 - 2^24 >= N
|
||||
* N <= 65793
|
||||
*
|
||||
* If all that math confuses you, this should convince you:
|
||||
* > perl -e 'for($N=1;(255*$N*int(0xFFFFFFFF/(255*$N)))>>24==255;++$N){}print"$N\n"'
|
||||
* 66052
|
||||
*
|
||||
* So this is fine for all reasonable values of N. For larger values of N
|
||||
* we may as well just use the same approximation and accept the fact that
|
||||
* the output channel values will be a little low.
|
||||
*/
|
||||
static uint32_t ComputeScaledDivisor(uint32_t aDivisor)
|
||||
{
|
||||
return UINT32_MAX/(255*aDivisor);
|
||||
}
|
||||
|
||||
static void
|
||||
BoxBlur(const uint8_t *aInput, uint8_t *aOutput,
|
||||
int32_t aStrideMinor, int32_t aStartMinor, int32_t aEndMinor,
|
||||
int32_t aLeftLobe, int32_t aRightLobe, bool aAlphaOnly)
|
||||
{
|
||||
int32_t boxSize = aLeftLobe + aRightLobe + 1;
|
||||
int32_t scaledDivisor = ComputeScaledDivisor(boxSize);
|
||||
int32_t sums[4] = {0, 0, 0, 0};
|
||||
|
||||
for (int32_t i=0; i < boxSize; i++) {
|
||||
int32_t pos = aStartMinor - aLeftLobe + i;
|
||||
pos = std::max(pos, aStartMinor);
|
||||
pos = std::min(pos, aEndMinor - 1);
|
||||
#define SUM(j) sums[j] += aInput[aStrideMinor*pos + j];
|
||||
SUM(0); SUM(1); SUM(2); SUM(3);
|
||||
#undef SUM
|
||||
}
|
||||
|
||||
aOutput += aStrideMinor*aStartMinor;
|
||||
if (aStartMinor + int32_t(boxSize) <= aEndMinor) {
|
||||
const uint8_t *lastInput = aInput + aStartMinor*aStrideMinor;
|
||||
const uint8_t *nextInput = aInput + (aStartMinor + aRightLobe + 1)*aStrideMinor;
|
||||
#define OUTPUT(j) aOutput[j] = (sums[j]*scaledDivisor) >> 24;
|
||||
#define SUM(j) sums[j] += nextInput[j] - lastInput[j];
|
||||
// process pixels in B, G, R, A order because that's 0, 1, 2, 3 for x86
|
||||
#define OUTPUT_PIXEL() \
|
||||
if (!aAlphaOnly) { OUTPUT(GFX_ARGB32_OFFSET_B); \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_G); \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_R); } \
|
||||
OUTPUT(GFX_ARGB32_OFFSET_A);
|
||||
#define SUM_PIXEL() \
|
||||
if (!aAlphaOnly) { SUM(GFX_ARGB32_OFFSET_B); \
|
||||
SUM(GFX_ARGB32_OFFSET_G); \
|
||||
SUM(GFX_ARGB32_OFFSET_R); } \
|
||||
SUM(GFX_ARGB32_OFFSET_A);
|
||||
for (int32_t minor = aStartMinor;
|
||||
minor < aStartMinor + aLeftLobe;
|
||||
minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
nextInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
}
|
||||
for (int32_t minor = aStartMinor + aLeftLobe;
|
||||
minor < aEndMinor - aRightLobe - 1;
|
||||
minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
lastInput += aStrideMinor;
|
||||
nextInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
}
|
||||
// nextInput is now aInput + aEndMinor*aStrideMinor. Set it back to
|
||||
// aInput + (aEndMinor - 1)*aStrideMinor so we read the last pixel in every
|
||||
// iteration of the next loop.
|
||||
nextInput -= aStrideMinor;
|
||||
for (int32_t minor = aEndMinor - aRightLobe - 1; minor < aEndMinor; minor++) {
|
||||
OUTPUT_PIXEL();
|
||||
SUM_PIXEL();
|
||||
lastInput += aStrideMinor;
|
||||
aOutput += aStrideMinor;
|
||||
#undef SUM_PIXEL
|
||||
#undef SUM
|
||||
}
|
||||
} else {
|
||||
for (int32_t minor = aStartMinor; minor < aEndMinor; minor++) {
|
||||
int32_t tmp = minor - aLeftLobe;
|
||||
int32_t last = std::max(tmp, aStartMinor);
|
||||
int32_t next = std::min(tmp + int32_t(boxSize), aEndMinor - 1);
|
||||
|
||||
OUTPUT_PIXEL();
|
||||
#define SUM(j) sums[j] += aInput[aStrideMinor*next + j] - \
|
||||
aInput[aStrideMinor*last + j];
|
||||
if (!aAlphaOnly) { SUM(GFX_ARGB32_OFFSET_B);
|
||||
SUM(GFX_ARGB32_OFFSET_G);
|
||||
SUM(GFX_ARGB32_OFFSET_R); }
|
||||
SUM(GFX_ARGB32_OFFSET_A);
|
||||
aOutput += aStrideMinor;
|
||||
#undef SUM
|
||||
#undef OUTPUT_PIXEL
|
||||
#undef OUTPUT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GetBlurBoxSize(double aStdDev)
|
||||
{
|
||||
NS_ASSERTION(aStdDev >= 0, "Negative standard deviations not allowed");
|
||||
|
||||
double size = aStdDev*3*sqrt(2*M_PI)/4;
|
||||
// Doing super-large blurs accurately isn't very important.
|
||||
uint32_t max = 1024;
|
||||
if (size > max)
|
||||
return max;
|
||||
return uint32_t(floor(size + 0.5));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGFEGaussianBlurElement::GetDXY(uint32_t *aDX, uint32_t *aDY,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
float stdX = aInstance.GetPrimitiveNumber(SVGContentUtils::X,
|
||||
&mNumberPairAttributes[STD_DEV],
|
||||
nsSVGNumberPair::eFirst);
|
||||
float stdY = aInstance.GetPrimitiveNumber(SVGContentUtils::Y,
|
||||
&mNumberPairAttributes[STD_DEV],
|
||||
nsSVGNumberPair::eSecond);
|
||||
if (stdX < 0 || stdY < 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// If the box size is greater than twice the temporary surface size
|
||||
// in an axis, then each pixel will be set to the average of all the
|
||||
// other pixel values.
|
||||
*aDX = GetBlurBoxSize(stdX);
|
||||
*aDY = GetBlurBoxSize(stdY);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
AreAllColorChannelsZero(const nsSVGFE::Image* aTarget)
|
||||
{
|
||||
return aTarget->mConstantColorChannels &&
|
||||
aTarget->mImage->GetDataSize() >= 4 &&
|
||||
(*reinterpret_cast<uint32_t*>(aTarget->mImage->Data()) & 0x00FFFFFF) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::GaussianBlur(const Image *aSource,
|
||||
const Image *aTarget,
|
||||
const nsIntRect& aDataRect,
|
||||
uint32_t aDX, uint32_t aDY)
|
||||
{
|
||||
NS_ASSERTION(nsIntRect(0, 0, aTarget->mImage->Width(), aTarget->mImage->Height()).Contains(aDataRect),
|
||||
"aDataRect out of bounds");
|
||||
|
||||
nsAutoArrayPtr<uint8_t> tmp(new uint8_t[aTarget->mImage->GetDataSize()]);
|
||||
if (!tmp)
|
||||
return;
|
||||
memset(tmp, 0, aTarget->mImage->GetDataSize());
|
||||
|
||||
bool alphaOnly = AreAllColorChannelsZero(aTarget);
|
||||
|
||||
const uint8_t* sourceData = aSource->mImage->Data();
|
||||
uint8_t* targetData = aTarget->mImage->Data();
|
||||
uint32_t stride = aTarget->mImage->Stride();
|
||||
|
||||
if (aDX == 0) {
|
||||
CopyDataRect(tmp, sourceData, stride, aDataRect);
|
||||
} else {
|
||||
int32_t longLobe = aDX/2;
|
||||
int32_t shortLobe = (aDX & 1) ? longLobe : longLobe - 1;
|
||||
for (int32_t major = aDataRect.y; major < aDataRect.YMost(); ++major) {
|
||||
int32_t ms = major*stride;
|
||||
BoxBlur(sourceData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, shortLobe, alphaOnly);
|
||||
BoxBlur(tmp + ms, targetData + ms, 4, aDataRect.x, aDataRect.XMost(), shortLobe, longLobe, alphaOnly);
|
||||
BoxBlur(targetData + ms, tmp + ms, 4, aDataRect.x, aDataRect.XMost(), longLobe, longLobe, alphaOnly);
|
||||
}
|
||||
}
|
||||
|
||||
if (aDY == 0) {
|
||||
CopyDataRect(targetData, tmp, stride, aDataRect);
|
||||
} else {
|
||||
int32_t longLobe = aDY/2;
|
||||
int32_t shortLobe = (aDY & 1) ? longLobe : longLobe - 1;
|
||||
for (int32_t major = aDataRect.x; major < aDataRect.XMost(); ++major) {
|
||||
int32_t ms = major*4;
|
||||
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, shortLobe, alphaOnly);
|
||||
BoxBlur(targetData + ms, tmp + ms, stride, aDataRect.y, aDataRect.YMost(), shortLobe, longLobe, alphaOnly);
|
||||
BoxBlur(tmp + ms, targetData + ms, stride, aDataRect.y, aDataRect.YMost(), longLobe, longLobe, alphaOnly);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
InflateRectForBlurDXY(nsIntRect* aRect, uint32_t aDX, uint32_t aDY)
|
||||
{
|
||||
aRect->Inflate(3*(aDX/2), 3*(aDY/2));
|
||||
}
|
||||
|
||||
static void
|
||||
ClearRect(gfxImageSurface* aSurface, int32_t aX, int32_t aY,
|
||||
int32_t aXMost, int32_t aYMost)
|
||||
{
|
||||
NS_ASSERTION(aX <= aXMost && aY <= aYMost, "Invalid rectangle");
|
||||
NS_ASSERTION(aX >= 0 && aY >= 0 && aXMost <= aSurface->Width() && aYMost <= aSurface->Height(),
|
||||
"Rectangle out of bounds");
|
||||
|
||||
if (aX == aXMost || aY == aYMost)
|
||||
return;
|
||||
for (int32_t y = aY; y < aYMost; ++y) {
|
||||
memset(aSurface->Data() + aSurface->Stride()*y + aX*4, 0, (aXMost - aX)*4);
|
||||
}
|
||||
}
|
||||
|
||||
// Clip aTarget's image to its filter primitive subregion.
|
||||
// aModifiedRect contains all the pixels which might not be RGBA(0,0,0,0),
|
||||
// it's relative to the surface data.
|
||||
static void
|
||||
ClipTarget(nsSVGFilterInstance* aInstance, const nsSVGFE::Image* aTarget,
|
||||
const nsIntRect& aModifiedRect)
|
||||
{
|
||||
nsIntPoint surfaceTopLeft = aInstance->GetSurfaceRect().TopLeft();
|
||||
|
||||
NS_ASSERTION(aInstance->GetSurfaceRect().Contains(aModifiedRect + surfaceTopLeft),
|
||||
"Modified data area overflows the surface?");
|
||||
|
||||
nsIntRect clip = aModifiedRect;
|
||||
nsSVGUtils::ClipToGfxRect(&clip,
|
||||
aTarget->mFilterPrimitiveSubregion - gfxPoint(surfaceTopLeft.x, surfaceTopLeft.y));
|
||||
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, aModifiedRect.y, aModifiedRect.XMost(), clip.y);
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, clip.y, clip.x, clip.YMost());
|
||||
ClearRect(aTarget->mImage, clip.XMost(), clip.y, aModifiedRect.XMost(), clip.YMost());
|
||||
ClearRect(aTarget->mImage, aModifiedRect.x, clip.YMost(), aModifiedRect.XMost(), aModifiedRect.YMost());
|
||||
}
|
||||
|
||||
static void
|
||||
ClipComputationRectToSurface(nsSVGFilterInstance* aInstance,
|
||||
nsIntRect* aDataRect)
|
||||
{
|
||||
aDataRect->IntersectRect(*aDataRect,
|
||||
nsIntRect(nsIntPoint(0, 0), aInstance->GetSurfaceRect().Size()));
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGFEGaussianBlurElement::Filter(nsSVGFilterInstance* aInstance,
|
||||
const nsTArray<const Image*>& aSources,
|
||||
const Image* aTarget,
|
||||
const nsIntRect& rect)
|
||||
{
|
||||
uint32_t dx, dy;
|
||||
nsresult rv = GetDXY(&dx, &dy, *aInstance);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIntRect computationRect = rect;
|
||||
InflateRectForBlurDXY(&computationRect, dx, dy);
|
||||
ClipComputationRectToSurface(aInstance, &computationRect);
|
||||
GaussianBlur(aSources[0], aTarget, computationRect, dx, dy);
|
||||
ClipTarget(aInstance, aTarget, computationRect);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsSVGFEGaussianBlurElement::AttributeAffectsRendering(int32_t aNameSpaceID,
|
||||
nsIAtom* aAttribute) const
|
||||
{
|
||||
return nsSVGFEGaussianBlurElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
|
||||
(aNameSpaceID == kNameSpaceID_None &&
|
||||
(aAttribute == nsGkAtoms::in ||
|
||||
aAttribute == nsGkAtoms::stdDeviation));
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
|
||||
{
|
||||
aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::InflateRectForBlur(const nsIntRect& aRect,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
uint32_t dX, dY;
|
||||
nsresult rv = GetDXY(&dX, &dY, aInstance);
|
||||
nsIntRect result = aRect;
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
InflateRectForBlurDXY(&result, dX, dY);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
return InflateRectForBlur(aSourceBBoxes[0], aInstance);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGFEGaussianBlurElement::ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
|
||||
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
aSourceBBoxes[0] = InflateRectForBlur(aTargetBBox, aInstance);
|
||||
}
|
||||
|
||||
nsIntRect
|
||||
nsSVGFEGaussianBlurElement::ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
|
||||
const nsSVGFilterInstance& aInstance)
|
||||
{
|
||||
return InflateRectForBlur(aSourceChangeBoxes[0], aInstance);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// nsSVGElement methods
|
||||
|
||||
nsSVGElement::NumberPairAttributesInfo
|
||||
nsSVGFEGaussianBlurElement::GetNumberPairInfo()
|
||||
{
|
||||
return NumberPairAttributesInfo(mNumberPairAttributes, sNumberPairInfo,
|
||||
ArrayLength(sNumberPairInfo));
|
||||
}
|
||||
|
||||
nsSVGElement::StringAttributesInfo
|
||||
nsSVGFEGaussianBlurElement::GetStringInfo()
|
||||
{
|
||||
return StringAttributesInfo(mStringAttributes, sStringInfo,
|
||||
ArrayLength(sStringInfo));
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user