Bug 389814 - Make tile filter faster. r=longsonr

This commit is contained in:
O S K Chaitanya 2013-03-04 12:13:48 +00:00
parent 4ed0f42e95
commit 44208759b9
6 changed files with 396 additions and 21 deletions

View File

@ -2670,7 +2670,7 @@ protected:
nsSVGString mStringAttributes[1];
static StringInfo sStringInfo[1];
};
nsSVGElement::StringInfo nsSVGFEFloodElement::sStringInfo[1] =
{
{ &nsGkAtoms::result, kNameSpaceID_None, true }
@ -2891,6 +2891,151 @@ static int32_t WrapInterval(int32_t aVal, int32_t aMax)
//----------------------------------------------------------------------
// 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,
@ -2903,7 +3048,8 @@ nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
// but nothing clips mFilterPrimitiveSubregion so this should be changed.
nsIntRect tile;
bool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion, &tile);
bool res = gfxUtils::GfxRectToIntRect(aSources[0]->mFilterPrimitiveSubregion,
&tile);
NS_ENSURE_TRUE(res, NS_ERROR_FAILURE); // asserts on failure (not
if (tile.IsEmpty())
@ -2915,6 +3061,9 @@ nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
return NS_OK;
}
// clip tile
tile = tile.Intersect(surfaceRect);
// Get it into surface space
tile -= surfaceRect.TopLeft();
@ -2922,21 +3071,161 @@ nsSVGFETileElement::Filter(nsSVGFilterInstance *instance,
uint8_t* targetData = aTarget->mImage->Data();
uint32_t stride = aTarget->mImage->Stride();
// the offset to add to our x/y coordinates (which are relative to the
// temporary surface data) to get coordinates relative to the origin
// of the tile
nsIntPoint offset(-tile.x + tile.width, -tile.y + tile.height);
for (int32_t y = rect.y; y < rect.YMost(); y++) {
uint32_t tileY = tile.y + WrapInterval(y + offset.y, tile.height);
if (tileY < (uint32_t)surfaceRect.height) {
for (int32_t x = rect.x; x < rect.XMost(); x++) {
uint32_t tileX = tile.x + WrapInterval(x + offset.x, tile.width);
if (tileX < (uint32_t)surfaceRect.width) {
*(uint32_t*)(targetData + y * stride + 4 * x) =
*(uint32_t*)(sourceData + tileY * stride + 4 * tileX);
}
}
}
/*
* 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;
@ -2946,9 +3235,9 @@ bool
nsSVGFETileElement::AttributeAffectsRendering(int32_t aNameSpaceID,
nsIAtom* aAttribute) const
{
return nsSVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID, aAttribute) ||
(aNameSpaceID == kNameSpaceID_None &&
aAttribute == nsGkAtoms::in);
return nsSVGFETileElementBase::AttributeAffectsRendering(aNameSpaceID,
aAttribute) ||
(aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::in);
}
//----------------------------------------------------------------------
@ -5576,7 +5865,7 @@ nsSVGFEImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return rv;
}
void
nsSVGFEImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
{

View File

@ -0,0 +1,26 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Reference for feTile filter with tile exceeding the bounds of the target area on all sides</title>
<rect width="20" height="20" fill="#00ff00"/>
<rect x="40" width="20" height="20" fill="#00ff00"/>
<rect x="80" width="20" height="20" fill="#00ff00"/>
<rect x="20" y="20" width="20" height="20" fill="#0000ff"/>
<rect x="60" y="20" width="20" height="20" fill="#0000ff"/>
<rect y="40" width="20" height="20" fill="#00ff00"/>
<rect x="40" y="40" width="20" height="20" fill="#00ff00"/>
<rect x="80" y="40" width="20" height="20" fill="#00ff00"/>
<rect x="20" y="60" width="20" height="20" fill="#0000ff"/>
<rect x="60" y="60" width="20" height="20" fill="#0000ff"/>
<rect y="80" width="20" height="20" fill="#00ff00"/>
<rect x="40" y="80" width="20" height="20" fill="#00ff00"/>
<rect x="80" y="80" width="20" height="20" fill="#00ff00"/>
</svg>

After

Width:  |  Height:  |  Size: 1016 B

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Test case for feTile filter with tile exceeding the bounds of the target area on all sides</title>
<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
x="0" y="0" width="1" height="1">
<feFlood flood-color="#00ff00" x="0%" y="0%" width="20%" height="20%" result="flood1"/>
<feFlood flood-color="#0000ff" x="20%" y="20%" width="20%" height="20%" result="flood2"/>
<feComposite in="flood1" in2="flood2" x="0%" y="0%" width="40%" height="40%" result="pair"/>
<feTile x="-10%" y="-10%" width="120%" height="120%" in="pair"/>
</filter>
<g filter="url(#f1)">
<rect width="100" height="100" fill="#00ff00"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 832 B

View File

@ -0,0 +1,20 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Reference for feTile filter with x and y for feTile set to values outside the target area.</title>
<rect width="20" height="20" fill="#00ff00"/>
<rect x="40" width="20" height="20" fill="#00ff00"/>
<rect x="20" y="20" width="20" height="20" fill="#0000ff"/>
<rect x="60" y="20" width="10" height="20" fill="#0000ff"/>
<rect x="00" y="40" width="20" height="20" fill="#00ff00"/>
<rect x="40" y="40" width="20" height="20" fill="#00ff00"/>
<rect x="20" y="60" width="20" height="10" fill="#0000ff"/>
<rect x="60" y="60" width="10" height="10" fill="#0000ff"/>
</svg>

After

Width:  |  Height:  |  Size: 736 B

View File

@ -0,0 +1,19 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Test case for feTile filter with x and y for feTile set to values outside the target area.</title>
<filter id="f1" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox"
x="0" y="0" width="1" height="1">
<feFlood flood-color="#00ff00" x="0%" y="0%" width="20%" height="20%" result="flood1"/>
<feFlood flood-color="#0000ff" x="20%" y="20%" width="20%" height="20%" result="flood2"/>
<feComposite in="flood1" in2="flood2" x="0%" y="0%" width="40%" height="40%" result="pair"/>
<feTile x="-10%" y="-10%" width="80%" height="80%" in="pair"/>
</filter>
<g filter="url(#f1)">
<rect width="100" height="100" fill="#00ff00"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 830 B

View File

@ -89,6 +89,8 @@ skip-if(B2G) fuzzy-if(cocoaWidget&&layersGPUAccelerated,4,93) == feDistantLight-
== feMorphology-radius-zero-02.svg pass.svg
== feTile-large-01.svg pass.svg
== feTile-large-02.svg feTile-large-02-ref.svg
== feTile-outside-01.svg feTile-outside-01-ref.svg
== feDiffuseLighting-1.svg feDiffuseLighting-1-ref.svg