Bug 847120: Move SVGFETileElement to its own file r=Ms2ger

--HG--
rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFETileElement.cpp
rename : content/svg/content/src/nsSVGFilters.cpp => content/svg/content/src/SVGFETileElement.h
This commit is contained in:
David Zbarsky 2013-03-11 03:26:23 -04:00
Родитель edb9c7cfbf
Коммит 9aedd27a38
4 изменённых файлов: 525 добавлений и 493 удалений

Просмотреть файл

@ -84,6 +84,7 @@ CPPSRCS = \
SVGFEImageElement.cpp \
SVGFEMergeElement.cpp \
SVGFEMergeNodeElement.cpp \
SVGFETileElement.cpp \
SVGFilterElement.cpp \
SVGForeignObjectElement.cpp \
SVGFragmentIdentifier.cpp \
@ -180,6 +181,7 @@ EXPORTS_mozilla/dom = \
SVGFEImageElement.h \
SVGFEMergeElement.h \
SVGFEMergeNodeElement.h \
SVGFETileElement.h \
SVGFilterElement.h \
SVGForeignObjectElement.h \
SVGGElement.h \

Просмотреть файл

@ -0,0 +1,447 @@
/* -*- 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/SVGFETileElement.h"
DOMCI_NODE_DATA(SVGFETileElement, nsSVGFETileElement)
NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(FETile)
namespace mozilla {
namespace dom {
nsSVGElement::StringInfo nsSVGFETileElement::sStringInfo[2] =
{
{ &nsGkAtoms::result, kNameSpaceID_None, true },
{ &nsGkAtoms::in, kNameSpaceID_None, true }
};
//----------------------------------------------------------------------
// nsISupports methods
NS_IMPL_ADDREF_INHERITED(nsSVGFETileElement,nsSVGFETileElementBase)
NS_IMPL_RELEASE_INHERITED(nsSVGFETileElement,nsSVGFETileElementBase)
NS_INTERFACE_TABLE_HEAD(nsSVGFETileElement)
NS_NODE_INTERFACE_TABLE5(nsSVGFETileElement, nsIDOMNode, nsIDOMElement,
nsIDOMSVGElement,
nsIDOMSVGFilterPrimitiveStandardAttributes,
nsIDOMSVGFETileElement)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFETileElement)
NS_INTERFACE_MAP_END_INHERITING(nsSVGFETileElementBase)
//----------------------------------------------------------------------
// nsIDOMNode methods
NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFETileElement)
//----------------------------------------------------------------------
// nsSVGFETileElement methods
/* readonly attribute nsIDOMSVGAnimatedString in1; */
NS_IMETHODIMP nsSVGFETileElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
{
return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
}
void
nsSVGFETileElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
{
aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
}
nsIntRect
nsSVGFETileElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
const nsSVGFilterInstance& aInstance)
{
return GetMaxRect();
}
void
nsSVGFETileElement::ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance)
{
// Just assume we need the entire source bounding box, so do nothing.
}
nsIntRect
nsSVGFETileElement::ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
const nsSVGFilterInstance& aInstance)
{
return GetMaxRect();
}
static int32_t WrapInterval(int32_t aVal, int32_t aMax)
{
aVal = aVal % aMax;
return aVal < 0 ? aMax + aVal : aVal;
}
//----------------------------------------------------------------------
// nsSVGElement methods
/*
* This function computes the size of partial match on either side of the tile.
* eg: If we are talking about the X-axis direction, then it computes, the
* size of the tile that would be copied to the lesser X-axis side (usually
* left), the higher X-axis side (usualy right) and the centre.
* This is needed because often, the tile doesn't exactly align to the target
* region and is partially copied on the edges. This function computes the
* dimensions of the partially copied regions in one axis.
*
* OUTPUT:
* aLesserSidePartialMatchSize: The size of the partial match on the lesser
* side of the axis being considered.
* eg: for X-axis, usually left side and
* for Y-axis, usually top
* aHigherSidePartialMatchSize: The size of the partial match on the higher
* side of the axis being considered.
* eg: for X-axis, usually right side and
* for Y-axis, usually bottom
* aCentreSize: The size of the target area where the tile is copied in full.
* This lies between the lesser and higher side partial matches.
* (the partially matched areas may be of zero width)
*
* INPUT:
* aLesserTargetExtent: Edge of the target area on the axis being considered
* on the lesser side. (eg: usually left on the X-axis)
* aTargetSize: Size of the target area on the axis being considered (eg:
* usually width for X-axis)
* aLesserTileExtent: Edge of the tile on the axis being considered on the
* lesser side.
* aTileSize: Size of the tile on the axis being considered.
*/
static inline void
ComputePartialTileExtents(int32_t *aLesserSidePartialMatchSize,
int32_t *aHigherSidePartialMatchSize,
int32_t *aCentreSize,
int32_t aLesserTargetExtent,
int32_t aTargetSize,
int32_t aLesserTileExtent,
int32_t aTileSize)
{
int32_t targetExtentMost = aLesserTargetExtent + aTargetSize;
int32_t tileExtentMost = aLesserTileExtent + aTileSize;
int32_t lesserSidePartialMatchSize;
if (aLesserTileExtent < aLesserTargetExtent) {
lesserSidePartialMatchSize = tileExtentMost - aLesserTargetExtent;
} else {
lesserSidePartialMatchSize = (aLesserTileExtent - aLesserTargetExtent) %
aTileSize;
}
int32_t higherSidePartialMatchSize;
if (lesserSidePartialMatchSize > aTargetSize) {
lesserSidePartialMatchSize = aTargetSize;
higherSidePartialMatchSize = 0;
} else if (tileExtentMost > targetExtentMost) {
higherSidePartialMatchSize = targetExtentMost - aLesserTileExtent;
} else {
higherSidePartialMatchSize = (targetExtentMost - tileExtentMost) %
aTileSize;
}
if (lesserSidePartialMatchSize + higherSidePartialMatchSize >
aTargetSize) {
higherSidePartialMatchSize = aTargetSize - lesserSidePartialMatchSize;
}
/*
* To understand the conditon below, let us consider the X-Axis:
* Lesser side is left and the Higher side is right.
* This implies:
* aTargetSize is rect.width.
* lesserSidePartialMatchSize would mean leftPartialTileWidth.
* higherSidePartialMatchSize would mean rightPartialTileWidth.
*
* leftPartialTileWidth == rect.width only happens when the tile entirely
* overlaps with the target area in the X-axis and exceeds its bounds by at
* least one pixel on the lower X-Axis side.
*
* leftPartialTileWidth + rightPartialTileWidth == rect.width only happens
* when the tile overlaps the target area in such a way that the edge of the
* tile on the higher X-Axis side cuts through the target area and there is no
* space for a complete tile in the X-Axis in the target area on either side
* of that edge. In this scenario, centre will be of zero width and the
* partial widths on left and right will add up to the width of the rect. In
* case the tile is bigger than the rect in the X-axis, it will get clipped
* and remain equal to rect.width.
*
* Therefore, those two conditions are separate cases which lead to centre
* being of zero width.
*
* The condition below is the same logic as above expressed independent of
* the axis in consideration.
*/
int32_t centreSize;
if (lesserSidePartialMatchSize == aTargetSize ||
lesserSidePartialMatchSize + higherSidePartialMatchSize ==
aTargetSize) {
centreSize = 0;
} else {
centreSize = aTargetSize -
(lesserSidePartialMatchSize + higherSidePartialMatchSize);
}
*aLesserSidePartialMatchSize = lesserSidePartialMatchSize;
*aHigherSidePartialMatchSize = higherSidePartialMatchSize;
*aCentreSize = centreSize;
}
static inline void
TilePixels(uint8_t *aTargetData,
const uint8_t *aSourceData,
const nsIntRect &targetRegion,
const nsIntRect &aTile,
uint32_t aStride)
{
if (targetRegion.IsEmpty()) {
return;
}
uint32_t tileRowCopyMemSize = aTile.width * 4;
uint32_t numTimesToCopyTileRows = targetRegion.width / aTile.width;
uint8_t *targetFirstRowOffset = aTargetData + 4 * targetRegion.x;
const uint8_t *tileFirstRowOffset = aSourceData + 4 * aTile.x;
int32_t tileYOffset = 0;
for (int32_t targetY = targetRegion.y;
targetY < targetRegion.YMost();
++targetY) {
uint8_t *targetRowOffset = targetFirstRowOffset + aStride * targetY;
const uint8_t *tileRowOffset = tileFirstRowOffset +
aStride * (aTile.y + tileYOffset);
for (uint32_t i = 0; i < numTimesToCopyTileRows; ++i) {
memcpy(targetRowOffset + i * tileRowCopyMemSize,
tileRowOffset,
tileRowCopyMemSize);
}
tileYOffset = (tileYOffset + 1) % aTile.height;
}
}
nsresult
nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
const nsTArray<const Image*>& aSources,
const Image* aTarget,
const nsIntRect& rect)
{
// XXX This code depends on the surface rect containing the filter
// primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes
// and ComputeChangeBBox are all pessimal, so that will normally be OK,
// but nothing clips mFilterPrimitiveSubregion so this should be changed.
nsIntRect tile;
bool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion,
&tile);
NS_ENSURE_TRUE(res, NS_ERROR_FAILURE); // asserts on failure (not
if (tile.IsEmpty())
return NS_OK;
const nsIntRect &surfaceRect = instance->GetSurfaceRect();
if (!tile.Intersects(surfaceRect)) {
// nothing to draw
return NS_OK;
}
// clip tile
tile = tile.Intersect(surfaceRect);
// Get it into surface space
tile -= surfaceRect.TopLeft();
uint8_t* sourceData = aSources[0]->mImage->Data();
uint8_t* targetData = aTarget->mImage->Data();
uint32_t stride = aTarget->mImage->Stride();
/*
* priority: left before right before centre
* and
* top before bottom before centre
*
* eg: If we have a target area which is 1.5 times the width of a tile,
* then, based on alignment, we get:
* 'left and right'
* or
* 'left and centre'
*
*/
int32_t leftPartialTileWidth;
int32_t rightPartialTileWidth;
int32_t centreWidth;
ComputePartialTileExtents(&leftPartialTileWidth,
&rightPartialTileWidth,
&centreWidth,
rect.x,
rect.width,
tile.x,
tile.width);
int32_t topPartialTileHeight;
int32_t bottomPartialTileHeight;
int32_t centreHeight;
ComputePartialTileExtents(&topPartialTileHeight,
&bottomPartialTileHeight,
&centreHeight,
rect.y,
rect.height,
tile.y,
tile.height);
/* We have nine regions of the target area which have to be tiled differetly:
*
* Top Left, Top Middle, Top Right,
* Left Middle, Centre, Right Middle,
* Bottom Left, Bottom Middle, Bottom Right
*
* + Centre is tiled by repeating the tiled image in full.
* + Top Left, Top Middle and Top Right:
* Some of the rows from the top of the tile will be clipped here.
* + Bottom Left, Bottom Middle and Bottom Right:
* Some of the rows from the bottom of the tile will be clipped here.
* + Top Left, Left Middle and Bottom left:
* Some of the columns from the Left of the tile will be clipped here.
* + Top Right, Right Middle and Bottom Right:
* Some of the columns from the right of the tile will be clipped here.
*
* If the sizes and positions of the target and tile are such that the tile
* aligns exactly on any (or all) of the edges, then some (or all) of the
* regions above (except Centre) will be zero sized.
*/
nsIntRect targetRects[] = {
// Top Left
nsIntRect(rect.x, rect.y, leftPartialTileWidth, topPartialTileHeight),
// Top Middle
nsIntRect(rect.x + leftPartialTileWidth,
rect.y,
centreWidth,
topPartialTileHeight),
// Top Right
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.y,
rightPartialTileWidth,
topPartialTileHeight),
// Left Middle
nsIntRect(rect.x,
rect.y + topPartialTileHeight,
leftPartialTileWidth,
centreHeight),
// Centre
nsIntRect(rect.x + leftPartialTileWidth,
rect.y + topPartialTileHeight,
centreWidth,
centreHeight),
// Right Middle
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.y + topPartialTileHeight,
rightPartialTileWidth,
centreHeight),
// Bottom Left
nsIntRect(rect.x,
rect.YMost() - bottomPartialTileHeight,
leftPartialTileWidth,
bottomPartialTileHeight),
// Bottom Middle
nsIntRect(rect.x + leftPartialTileWidth,
rect.YMost() - bottomPartialTileHeight,
centreWidth,
bottomPartialTileHeight),
// Bottom Right
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.YMost() - bottomPartialTileHeight,
rightPartialTileWidth,
bottomPartialTileHeight)
};
nsIntRect tileRects[] = {
// Top Left
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.YMost() - topPartialTileHeight,
leftPartialTileWidth,
topPartialTileHeight),
// Top Middle
nsIntRect(tile.x,
tile.YMost() - topPartialTileHeight,
tile.width,
topPartialTileHeight),
// Top Right
nsIntRect(tile.x,
tile.YMost() - topPartialTileHeight,
rightPartialTileWidth,
topPartialTileHeight),
// Left Middle
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.y,
leftPartialTileWidth,
tile.height),
// Centre
nsIntRect(tile.x,
tile.y,
tile.width,
tile.height),
// Right Middle
nsIntRect(tile.x,
tile.y,
rightPartialTileWidth,
tile.height),
// Bottom Left
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.y,
leftPartialTileWidth,
bottomPartialTileHeight),
// Bottom Middle
nsIntRect(tile.x,
tile.y,
tile.width,
bottomPartialTileHeight),
// Bottom Right
nsIntRect(tile.x,
tile.y,
rightPartialTileWidth,
bottomPartialTileHeight)
};
for (uint32_t i = 0; i < ArrayLength(targetRects); ++i) {
TilePixels(targetData,
sourceData,
targetRects[i],
tileRects[i],
stride);
}
return NS_OK;
}
bool
nsSVGFETileElement::AttributeAffectsRendering(int32_t aNameSpaceID,
nsIAtom* aAttribute) const
{
return nsSVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID,
aAttribute) ||
(aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in);
}
//----------------------------------------------------------------------
// nsSVGElement methods
nsSVGElement::StringAttributesInfo
nsSVGFETileElement::GetStringInfo()
{
return StringAttributesInfo(mStringAttributes, sStringInfo,
ArrayLength(sStringInfo));
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,76 @@
/* -*- 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_SVGFETileElement_h
#define mozilla_dom_SVGFETileElement_h
#include "nsSVGFilters.h"
nsresult NS_NewSVGFETileElement(nsIContent **aResult,
already_AddRefed<nsINodeInfo> aNodeInfo);
namespace mozilla {
namespace dom {
typedef nsSVGFE nsSVGFETileElementBase;
class nsSVGFETileElement : public nsSVGFETileElementBase,
public nsIDOMSVGFETileElement
{
friend nsresult (::NS_NewSVGFETileElement(nsIContent **aResult,
already_AddRefed<nsINodeInfo> aNodeInfo));
protected:
nsSVGFETileElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsSVGFETileElementBase(aNodeInfo) {}
public:
virtual bool SubregionIsUnionOfRegions() { return false; }
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
// FE Base
NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFETileElementBase::)
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);
// Tile
NS_DECL_NSIDOMSVGFETILEELEMENT
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFETileElementBase::)
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 StringAttributesInfo GetStringInfo();
enum { RESULT, IN1 };
nsSVGString mStringAttributes[2];
static StringInfo sStringInfo[2];
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_SVGFETileElement_h

Просмотреть файл

@ -2192,499 +2192,6 @@ nsSVGFEOffsetElement::GetStringInfo()
ArrayLength(sStringInfo));
}
//---------------------Tile------------------------
typedef nsSVGFE nsSVGFETileElementBase;
class nsSVGFETileElement : public nsSVGFETileElementBase,
public nsIDOMSVGFETileElement
{
friend nsresult NS_NewSVGFETileElement(nsIContent **aResult,
already_AddRefed<nsINodeInfo> aNodeInfo);
protected:
nsSVGFETileElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsSVGFETileElementBase(aNodeInfo) {}
public:
virtual bool SubregionIsUnionOfRegions() { return false; }
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
// FE Base
NS_FORWARD_NSIDOMSVGFILTERPRIMITIVESTANDARDATTRIBUTES(nsSVGFETileElementBase::)
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);
// Tile
NS_DECL_NSIDOMSVGFETILEELEMENT
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGFETileElementBase::)
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 StringAttributesInfo GetStringInfo();
enum { RESULT, IN1 };
nsSVGString mStringAttributes[2];
static StringInfo sStringInfo[2];
};
nsSVGElement::StringInfo nsSVGFETileElement::sStringInfo[2] =
{
{ &nsGkAtoms::result, kNameSpaceID_None, true },
{ &nsGkAtoms::in, kNameSpaceID_None, true }
};
NS_IMPL_NS_NEW_SVG_ELEMENT(FETile)
//----------------------------------------------------------------------
// nsISupports methods
NS_IMPL_ADDREF_INHERITED(nsSVGFETileElement,nsSVGFETileElementBase)
NS_IMPL_RELEASE_INHERITED(nsSVGFETileElement,nsSVGFETileElementBase)
DOMCI_NODE_DATA(SVGFETileElement, nsSVGFETileElement)
NS_INTERFACE_TABLE_HEAD(nsSVGFETileElement)
NS_NODE_INTERFACE_TABLE5(nsSVGFETileElement, nsIDOMNode, nsIDOMElement,
nsIDOMSVGElement,
nsIDOMSVGFilterPrimitiveStandardAttributes,
nsIDOMSVGFETileElement)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(SVGFETileElement)
NS_INTERFACE_MAP_END_INHERITING(nsSVGFETileElementBase)
//----------------------------------------------------------------------
// nsIDOMNode methods
NS_IMPL_ELEMENT_CLONE_WITH_INIT(nsSVGFETileElement)
//----------------------------------------------------------------------
// nsSVGFETileElement methods
/* readonly attribute nsIDOMSVGAnimatedString in1; */
NS_IMETHODIMP nsSVGFETileElement::GetIn1(nsIDOMSVGAnimatedString * *aIn)
{
return mStringAttributes[IN1].ToDOMAnimatedString(aIn, this);
}
void
nsSVGFETileElement::GetSourceImageNames(nsTArray<nsSVGStringInfo>& aSources)
{
aSources.AppendElement(nsSVGStringInfo(&mStringAttributes[IN1], this));
}
nsIntRect
nsSVGFETileElement::ComputeTargetBBox(const nsTArray<nsIntRect>& aSourceBBoxes,
const nsSVGFilterInstance& aInstance)
{
return GetMaxRect();
}
void
nsSVGFETileElement::ComputeNeededSourceBBoxes(const nsIntRect& aTargetBBox,
nsTArray<nsIntRect>& aSourceBBoxes, const nsSVGFilterInstance& aInstance)
{
// Just assume we need the entire source bounding box, so do nothing.
}
nsIntRect
nsSVGFETileElement::ComputeChangeBBox(const nsTArray<nsIntRect>& aSourceChangeBoxes,
const nsSVGFilterInstance& aInstance)
{
return GetMaxRect();
}
static int32_t WrapInterval(int32_t aVal, int32_t aMax)
{
aVal = aVal % aMax;
return aVal < 0 ? aMax + aVal : aVal;
}
//----------------------------------------------------------------------
// nsSVGElement methods
/*
* This function computes the size of partial match on either side of the tile.
* eg: If we are talking about the X-axis direction, then it computes, the
* size of the tile that would be copied to the lesser X-axis side (usually
* left), the higher X-axis side (usualy right) and the centre.
* This is needed because often, the tile doesn't exactly align to the target
* region and is partially copied on the edges. This function computes the
* dimensions of the partially copied regions in one axis.
*
* OUTPUT:
* aLesserSidePartialMatchSize: The size of the partial match on the lesser
* side of the axis being considered.
* eg: for X-axis, usually left side and
* for Y-axis, usually top
* aHigherSidePartialMatchSize: The size of the partial match on the higher
* side of the axis being considered.
* eg: for X-axis, usually right side and
* for Y-axis, usually bottom
* aCentreSize: The size of the target area where the tile is copied in full.
* This lies between the lesser and higher side partial matches.
* (the partially matched areas may be of zero width)
*
* INPUT:
* aLesserTargetExtent: Edge of the target area on the axis being considered
* on the lesser side. (eg: usually left on the X-axis)
* aTargetSize: Size of the target area on the axis being considered (eg:
* usually width for X-axis)
* aLesserTileExtent: Edge of the tile on the axis being considered on the
* lesser side.
* aTileSize: Size of the tile on the axis being considered.
*/
static inline void
ComputePartialTileExtents(int32_t *aLesserSidePartialMatchSize,
int32_t *aHigherSidePartialMatchSize,
int32_t *aCentreSize,
int32_t aLesserTargetExtent,
int32_t aTargetSize,
int32_t aLesserTileExtent,
int32_t aTileSize)
{
int32_t targetExtentMost = aLesserTargetExtent + aTargetSize;
int32_t tileExtentMost = aLesserTileExtent + aTileSize;
int32_t lesserSidePartialMatchSize;
if (aLesserTileExtent < aLesserTargetExtent) {
lesserSidePartialMatchSize = tileExtentMost - aLesserTargetExtent;
} else {
lesserSidePartialMatchSize = (aLesserTileExtent - aLesserTargetExtent) %
aTileSize;
}
int32_t higherSidePartialMatchSize;
if (lesserSidePartialMatchSize > aTargetSize) {
lesserSidePartialMatchSize = aTargetSize;
higherSidePartialMatchSize = 0;
} else if (tileExtentMost > targetExtentMost) {
higherSidePartialMatchSize = targetExtentMost - aLesserTileExtent;
} else {
higherSidePartialMatchSize = (targetExtentMost - tileExtentMost) %
aTileSize;
}
if (lesserSidePartialMatchSize + higherSidePartialMatchSize >
aTargetSize) {
higherSidePartialMatchSize = aTargetSize - lesserSidePartialMatchSize;
}
/*
* To understand the conditon below, let us consider the X-Axis:
* Lesser side is left and the Higher side is right.
* This implies:
* aTargetSize is rect.width.
* lesserSidePartialMatchSize would mean leftPartialTileWidth.
* higherSidePartialMatchSize would mean rightPartialTileWidth.
*
* leftPartialTileWidth == rect.width only happens when the tile entirely
* overlaps with the target area in the X-axis and exceeds its bounds by at
* least one pixel on the lower X-Axis side.
*
* leftPartialTileWidth + rightPartialTileWidth == rect.width only happens
* when the tile overlaps the target area in such a way that the edge of the
* tile on the higher X-Axis side cuts through the target area and there is no
* space for a complete tile in the X-Axis in the target area on either side
* of that edge. In this scenario, centre will be of zero width and the
* partial widths on left and right will add up to the width of the rect. In
* case the tile is bigger than the rect in the X-axis, it will get clipped
* and remain equal to rect.width.
*
* Therefore, those two conditions are separate cases which lead to centre
* being of zero width.
*
* The condition below is the same logic as above expressed independent of
* the axis in consideration.
*/
int32_t centreSize;
if (lesserSidePartialMatchSize == aTargetSize ||
lesserSidePartialMatchSize + higherSidePartialMatchSize ==
aTargetSize) {
centreSize = 0;
} else {
centreSize = aTargetSize -
(lesserSidePartialMatchSize + higherSidePartialMatchSize);
}
*aLesserSidePartialMatchSize = lesserSidePartialMatchSize;
*aHigherSidePartialMatchSize = higherSidePartialMatchSize;
*aCentreSize = centreSize;
}
static inline void
TilePixels(uint8_t *aTargetData,
const uint8_t *aSourceData,
const nsIntRect &targetRegion,
const nsIntRect &aTile,
uint32_t aStride)
{
if (targetRegion.IsEmpty()) {
return;
}
uint32_t tileRowCopyMemSize = aTile.width * 4;
uint32_t numTimesToCopyTileRows = targetRegion.width / aTile.width;
uint8_t *targetFirstRowOffset = aTargetData + 4 * targetRegion.x;
const uint8_t *tileFirstRowOffset = aSourceData + 4 * aTile.x;
int32_t tileYOffset = 0;
for (int32_t targetY = targetRegion.y;
targetY < targetRegion.YMost();
++targetY) {
uint8_t *targetRowOffset = targetFirstRowOffset + aStride * targetY;
const uint8_t *tileRowOffset = tileFirstRowOffset +
aStride * (aTile.y + tileYOffset);
for (uint32_t i = 0; i < numTimesToCopyTileRows; ++i) {
memcpy(targetRowOffset + i * tileRowCopyMemSize,
tileRowOffset,
tileRowCopyMemSize);
}
tileYOffset = (tileYOffset + 1) % aTile.height;
}
}
nsresult
nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
const nsTArray<const Image*>& aSources,
const Image* aTarget,
const nsIntRect& rect)
{
// XXX This code depends on the surface rect containing the filter
// primitive subregion. ComputeTargetBBox, ComputeNeededSourceBBoxes
// and ComputeChangeBBox are all pessimal, so that will normally be OK,
// but nothing clips mFilterPrimitiveSubregion so this should be changed.
nsIntRect tile;
bool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion,
&tile);
NS_ENSURE_TRUE(res, NS_ERROR_FAILURE); // asserts on failure (not
if (tile.IsEmpty())
return NS_OK;
const nsIntRect &surfaceRect = instance->GetSurfaceRect();
if (!tile.Intersects(surfaceRect)) {
// nothing to draw
return NS_OK;
}
// clip tile
tile = tile.Intersect(surfaceRect);
// Get it into surface space
tile -= surfaceRect.TopLeft();
uint8_t* sourceData = aSources[0]->mImage->Data();
uint8_t* targetData = aTarget->mImage->Data();
uint32_t stride = aTarget->mImage->Stride();
/*
* priority: left before right before centre
* and
* top before bottom before centre
*
* eg: If we have a target area which is 1.5 times the width of a tile,
* then, based on alignment, we get:
* 'left and right'
* or
* 'left and centre'
*
*/
int32_t leftPartialTileWidth;
int32_t rightPartialTileWidth;
int32_t centreWidth;
ComputePartialTileExtents(&leftPartialTileWidth,
&rightPartialTileWidth,
&centreWidth,
rect.x,
rect.width,
tile.x,
tile.width);
int32_t topPartialTileHeight;
int32_t bottomPartialTileHeight;
int32_t centreHeight;
ComputePartialTileExtents(&topPartialTileHeight,
&bottomPartialTileHeight,
&centreHeight,
rect.y,
rect.height,
tile.y,
tile.height);
/* We have nine regions of the target area which have to be tiled differetly:
*
* Top Left, Top Middle, Top Right,
* Left Middle, Centre, Right Middle,
* Bottom Left, Bottom Middle, Bottom Right
*
* + Centre is tiled by repeating the tiled image in full.
* + Top Left, Top Middle and Top Right:
* Some of the rows from the top of the tile will be clipped here.
* + Bottom Left, Bottom Middle and Bottom Right:
* Some of the rows from the bottom of the tile will be clipped here.
* + Top Left, Left Middle and Bottom left:
* Some of the columns from the Left of the tile will be clipped here.
* + Top Right, Right Middle and Bottom Right:
* Some of the columns from the right of the tile will be clipped here.
*
* If the sizes and positions of the target and tile are such that the tile
* aligns exactly on any (or all) of the edges, then some (or all) of the
* regions above (except Centre) will be zero sized.
*/
nsIntRect targetRects[] = {
// Top Left
nsIntRect(rect.x, rect.y, leftPartialTileWidth, topPartialTileHeight),
// Top Middle
nsIntRect(rect.x + leftPartialTileWidth,
rect.y,
centreWidth,
topPartialTileHeight),
// Top Right
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.y,
rightPartialTileWidth,
topPartialTileHeight),
// Left Middle
nsIntRect(rect.x,
rect.y + topPartialTileHeight,
leftPartialTileWidth,
centreHeight),
// Centre
nsIntRect(rect.x + leftPartialTileWidth,
rect.y + topPartialTileHeight,
centreWidth,
centreHeight),
// Right Middle
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.y + topPartialTileHeight,
rightPartialTileWidth,
centreHeight),
// Bottom Left
nsIntRect(rect.x,
rect.YMost() - bottomPartialTileHeight,
leftPartialTileWidth,
bottomPartialTileHeight),
// Bottom Middle
nsIntRect(rect.x + leftPartialTileWidth,
rect.YMost() - bottomPartialTileHeight,
centreWidth,
bottomPartialTileHeight),
// Bottom Right
nsIntRect(rect.XMost() - rightPartialTileWidth,
rect.YMost() - bottomPartialTileHeight,
rightPartialTileWidth,
bottomPartialTileHeight)
};
nsIntRect tileRects[] = {
// Top Left
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.YMost() - topPartialTileHeight,
leftPartialTileWidth,
topPartialTileHeight),
// Top Middle
nsIntRect(tile.x,
tile.YMost() - topPartialTileHeight,
tile.width,
topPartialTileHeight),
// Top Right
nsIntRect(tile.x,
tile.YMost() - topPartialTileHeight,
rightPartialTileWidth,
topPartialTileHeight),
// Left Middle
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.y,
leftPartialTileWidth,
tile.height),
// Centre
nsIntRect(tile.x,
tile.y,
tile.width,
tile.height),
// Right Middle
nsIntRect(tile.x,
tile.y,
rightPartialTileWidth,
tile.height),
// Bottom Left
nsIntRect(tile.XMost() - leftPartialTileWidth,
tile.y,
leftPartialTileWidth,
bottomPartialTileHeight),
// Bottom Middle
nsIntRect(tile.x,
tile.y,
tile.width,
bottomPartialTileHeight),
// Bottom Right
nsIntRect(tile.x,
tile.y,
rightPartialTileWidth,
bottomPartialTileHeight)
};
for (uint32_t i = 0; i < ArrayLength(targetRects); ++i) {
TilePixels(targetData,
sourceData,
targetRects[i],
tileRects[i],
stride);
}
return NS_OK;
}
bool
nsSVGFETileElement::AttributeAffectsRendering(int32_t aNameSpaceID,
nsIAtom* aAttribute) const
{
return nsSVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID,
aAttribute) ||
(aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in);
}
//----------------------------------------------------------------------
// nsSVGElement methods
nsSVGElement::StringAttributesInfo
nsSVGFETileElement::GetStringInfo()
{
return StringAttributesInfo(mStringAttributes, sStringInfo,
ArrayLength(sStringInfo));
}
//---------------------Turbulence------------------------
typedef nsSVGFE nsSVGFETurbulenceElementBase;