mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 389814 - Make tile filter faster. r=longsonr
This commit is contained in:
parent
83c705a616
commit
5776bcd3cd
@ -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,
|
||||
¢reWidth,
|
||||
rect.x,
|
||||
rect.width,
|
||||
tile.x,
|
||||
tile.width);
|
||||
|
||||
int32_t topPartialTileHeight;
|
||||
int32_t bottomPartialTileHeight;
|
||||
int32_t centreHeight;
|
||||
ComputePartialTileExtents(&topPartialTileHeight,
|
||||
&bottomPartialTileHeight,
|
||||
¢reHeight,
|
||||
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)
|
||||
{
|
||||
|
26
layout/reftests/svg/filters/feTile-large-02-ref.svg
Normal file
26
layout/reftests/svg/filters/feTile-large-02-ref.svg
Normal 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 |
19
layout/reftests/svg/filters/feTile-large-02.svg
Normal file
19
layout/reftests/svg/filters/feTile-large-02.svg
Normal 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 |
20
layout/reftests/svg/filters/feTile-outside-01-ref.svg
Normal file
20
layout/reftests/svg/filters/feTile-outside-01-ref.svg
Normal 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 |
19
layout/reftests/svg/filters/feTile-outside-01.svg
Normal file
19
layout/reftests/svg/filters/feTile-outside-01.svg
Normal 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 |
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user