Bug 1274624 - Speed up tiled axis-aligned linear gradient painting - r=mstange
This commit is contained in:
@@ -2532,6 +2532,79 @@ ResolvePremultipliedAlpha(nsTArray<ColorStop>& aStops)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ColorStop
|
||||||
|
InterpolateColorStop(const ColorStop& aFirst, const ColorStop& aSecond,
|
||||||
|
double aPosition, const Color& aDefault)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aFirst.mPosition <= aPosition);
|
||||||
|
MOZ_ASSERT(aPosition <= aSecond.mPosition);
|
||||||
|
|
||||||
|
double delta = aSecond.mPosition - aFirst.mPosition;
|
||||||
|
|
||||||
|
if (delta < 1e-6) {
|
||||||
|
return ColorStop(aPosition, false, aDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ColorStop(aPosition, false,
|
||||||
|
Unpremultiply(InterpolateColor(Premultiply(aFirst.mColor),
|
||||||
|
Premultiply(aSecond.mColor),
|
||||||
|
(aPosition - aFirst.mPosition) / delta)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp and extend the given ColorStop array in-place to fit exactly into the
|
||||||
|
// range [0, 1].
|
||||||
|
static void
|
||||||
|
ClampColorStops(nsTArray<ColorStop>& aStops)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aStops.Length() > 0);
|
||||||
|
|
||||||
|
// If all stops are outside the range, then get rid of everything and replace
|
||||||
|
// with a single colour.
|
||||||
|
if (aStops.Length() < 2 || aStops[0].mPosition > 1 ||
|
||||||
|
aStops.LastElement().mPosition < 0) {
|
||||||
|
Color c = aStops[0].mPosition > 1 ? aStops[0].mColor : aStops.LastElement().mColor;
|
||||||
|
aStops.Clear();
|
||||||
|
aStops.AppendElement(ColorStop(0, false, c));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the 0 and 1 points if they fall in the range of |aStops|, and discard
|
||||||
|
// all stops outside the range [0, 1].
|
||||||
|
// XXX: If we have stops positioned at 0 or 1, we only keep the innermost of
|
||||||
|
// those stops. This should be fine for the current user(s) of this function.
|
||||||
|
for (size_t i = aStops.Length() - 1; i > 0; i--) {
|
||||||
|
if (aStops[i - 1].mPosition < 1 && aStops[i].mPosition >= 1) {
|
||||||
|
// Add a point to position 1.
|
||||||
|
aStops[i] = InterpolateColorStop(aStops[i - 1], aStops[i],
|
||||||
|
/* aPosition = */ 1,
|
||||||
|
aStops[i - 1].mColor);
|
||||||
|
// Remove all the elements whose position is greater than 1.
|
||||||
|
aStops.RemoveElementsAt(i + 1, aStops.Length() - (i + 1));
|
||||||
|
}
|
||||||
|
if (aStops[i - 1].mPosition <= 0 && aStops[i].mPosition > 0) {
|
||||||
|
// Add a point to position 0.
|
||||||
|
aStops[i - 1] = InterpolateColorStop(aStops[i - 1], aStops[i],
|
||||||
|
/* aPosition = */ 0,
|
||||||
|
aStops[i].mColor);
|
||||||
|
// Remove all of the preceding stops -- they are all negative.
|
||||||
|
aStops.RemoveElementsAt(0, i - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(aStops[0].mPosition >= 0);
|
||||||
|
MOZ_ASSERT(aStops.LastElement().mPosition <= 1);
|
||||||
|
|
||||||
|
// The end points won't exist yet if they don't fall in the original range of
|
||||||
|
// |aStops|. Create them if needed.
|
||||||
|
if (aStops[0].mPosition > 0) {
|
||||||
|
aStops.InsertElementAt(0, ColorStop(0, false, aStops[0].mColor));
|
||||||
|
}
|
||||||
|
if (aStops.LastElement().mPosition < 1) {
|
||||||
|
aStops.AppendElement(ColorStop(1, false, aStops.LastElement().mColor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
||||||
nsRenderingContext& aRenderingContext,
|
nsRenderingContext& aRenderingContext,
|
||||||
@@ -2646,6 +2719,55 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a non-repeating linear gradient is axis-aligned and there are no gaps
|
||||||
|
// between tiles, we can optimise away most of the work by converting to a
|
||||||
|
// repeating linear gradient and filling the whole destination rect at once.
|
||||||
|
bool forceRepeatToCoverTiles =
|
||||||
|
aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR &&
|
||||||
|
(lineStart.x == lineEnd.x) != (lineStart.y == lineEnd.y) &&
|
||||||
|
aRepeatSize.width == aDest.width && aRepeatSize.height == aDest.height &&
|
||||||
|
!aGradient->mRepeating && !cellContainsFill;
|
||||||
|
|
||||||
|
gfxMatrix matrix;
|
||||||
|
if (forceRepeatToCoverTiles) {
|
||||||
|
// Length of the source rectangle along the gradient axis.
|
||||||
|
double rectLen;
|
||||||
|
// The position of the start of the rectangle along the gradient.
|
||||||
|
double offset;
|
||||||
|
|
||||||
|
// The gradient line is "backwards". Flip the line upside down to make
|
||||||
|
// things easier, and then rotate the matrix to turn everything back the
|
||||||
|
// right way up.
|
||||||
|
if (lineStart.x > lineEnd.x || lineStart.y > lineEnd.y) {
|
||||||
|
std::swap(lineStart, lineEnd);
|
||||||
|
matrix.Scale(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fit the gradient line exactly into the source rect.
|
||||||
|
if (lineStart.x != lineEnd.x) {
|
||||||
|
rectLen = srcSize.width;
|
||||||
|
offset = ((double)aSrc.x - lineStart.x) / lineLength;
|
||||||
|
lineStart.x = aSrc.x;
|
||||||
|
lineEnd.x = aSrc.x + srcSize.width;
|
||||||
|
} else {
|
||||||
|
rectLen = srcSize.height;
|
||||||
|
offset = ((double)aSrc.y - lineStart.y) / lineLength;
|
||||||
|
lineStart.y = aSrc.y;
|
||||||
|
lineEnd.y = aSrc.y + srcSize.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust gradient stop positions for the new gradient line.
|
||||||
|
double scale = lineLength / rectLen;
|
||||||
|
for (size_t i = 0; i < stops.Length(); i++) {
|
||||||
|
stops[i].mPosition = (stops[i].mPosition - offset) * fabs(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp or extrapolate gradient stops to exactly [0, 1].
|
||||||
|
ClampColorStops(stops);
|
||||||
|
|
||||||
|
lineLength = rectLen;
|
||||||
|
}
|
||||||
|
|
||||||
// Eliminate negative-position stops if the gradient is radial.
|
// Eliminate negative-position stops if the gradient is radial.
|
||||||
double firstStop = stops[0].mPosition;
|
double firstStop = stops[0].mPosition;
|
||||||
if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && firstStop < 0.0) {
|
if (aGradient->mShape != NS_STYLE_GRADIENT_SHAPE_LINEAR && firstStop < 0.0) {
|
||||||
@@ -2745,8 +2867,6 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
|||||||
|
|
||||||
// Create the gradient pattern.
|
// Create the gradient pattern.
|
||||||
RefPtr<gfxPattern> gradientPattern;
|
RefPtr<gfxPattern> gradientPattern;
|
||||||
bool forceRepeatToCoverTiles = false;
|
|
||||||
gfxMatrix matrix;
|
|
||||||
gfxPoint gradientStart;
|
gfxPoint gradientStart;
|
||||||
gfxPoint gradientEnd;
|
gfxPoint gradientEnd;
|
||||||
if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
|
if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
|
||||||
@@ -2770,18 +2890,6 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
|
|||||||
|
|
||||||
gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
|
gradientPattern = new gfxPattern(gradientStart.x, gradientStart.y,
|
||||||
gradientEnd.x, gradientEnd.y);
|
gradientEnd.x, gradientEnd.y);
|
||||||
|
|
||||||
// When the gradient line is parallel to the x axis from the left edge
|
|
||||||
// to the right edge of a tile, then we can repeat by just repeating the
|
|
||||||
// gradient.
|
|
||||||
if (!cellContainsFill &&
|
|
||||||
stopDelta != 0.0 && // if 0.0, gradientStopEnd is bogus (see above)
|
|
||||||
((gradientStopStart.y == gradientStopEnd.y && gradientStopStart.x == 0 &&
|
|
||||||
gradientStopEnd.x == srcSize.width) ||
|
|
||||||
(gradientStopStart.x == gradientStopEnd.x && gradientStopStart.y == 0 &&
|
|
||||||
gradientStopEnd.y == srcSize.height))) {
|
|
||||||
forceRepeatToCoverTiles = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
NS_ASSERTION(firstStop >= 0.0,
|
NS_ASSERTION(firstStop >= 0.0,
|
||||||
"Negative stops not allowed for radial gradients");
|
"Negative stops not allowed for radial gradients");
|
||||||
|
|||||||
Reference in New Issue
Block a user