Bug 1012797 - Speed up rendering of linear gradients when only painting the uniform color beyond the gradient edge. r=roc

This commit is contained in:
Markus Stange 2014-05-28 16:14:42 +02:00
parent 1bf339ea5d
commit 917c3cf573

View File

@ -2156,6 +2156,68 @@ FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim)
return NSToCoordRound(multiples*aTileDim + aTilePos);
}
static gfxFloat
LinearGradientStopPositionForPoint(const gfxPoint& aGradientStart,
const gfxPoint& aGradientEnd,
const gfxPoint& aPoint)
{
gfxPoint d = aGradientEnd - aGradientStart;
gfxPoint p = aPoint - aGradientStart;
/**
* Compute a parameter t such that a line perpendicular to the
* d vector, passing through aGradientStart + d*t, also
* passes through aPoint.
*
* t is given by
* (p.x - d.x*t)*d.x + (p.y - d.y*t)*d.y = 0
*
* Solving for t we get
* numerator = d.x*p.x + d.y*p.y
* denominator = d.x^2 + d.y^2
* t = numerator/denominator
*
* In nsCSSRendering::PaintGradient we know the length of d
* is not zero.
*/
double numerator = d.x * p.x + d.y * p.y;
double denominator = d.x * d.x + d.y * d.y;
return numerator / denominator;
}
static bool
RectIsBeyondLinearGradientEdge(const gfxRect& aRect,
const gfxMatrix& aPatternMatrix,
const nsTArray<ColorStop>& aStops,
const gfxPoint& aGradientStart,
const gfxPoint& aGradientEnd,
gfxRGBA* aOutEdgeColor)
{
gfxFloat topLeft = LinearGradientStopPositionForPoint(
aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.TopLeft()));
gfxFloat topRight = LinearGradientStopPositionForPoint(
aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.TopRight()));
gfxFloat bottomLeft = LinearGradientStopPositionForPoint(
aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.BottomLeft()));
gfxFloat bottomRight = LinearGradientStopPositionForPoint(
aGradientStart, aGradientEnd, aPatternMatrix.Transform(aRect.BottomRight()));
const ColorStop& firstStop = aStops[0];
if (topLeft < firstStop.mPosition && topRight < firstStop.mPosition &&
bottomLeft < firstStop.mPosition && bottomRight < firstStop.mPosition) {
*aOutEdgeColor = firstStop.mColor;
return true;
}
const ColorStop& lastStop = aStops.LastElement();
if (topLeft >= lastStop.mPosition && topRight >= lastStop.mPosition &&
bottomLeft >= lastStop.mPosition && bottomRight >= lastStop.mPosition) {
*aOutEdgeColor = lastStop.mColor;
return true;
}
return false;
}
void
nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
nsRenderingContext& aRenderingContext,
@ -2362,11 +2424,13 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
nsRefPtr<gfxPattern> gradientPattern;
bool forceRepeatToCoverTiles = false;
gfxMatrix matrix;
gfxPoint gradientStart;
gfxPoint gradientEnd;
if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR) {
// Compute the actual gradient line ends we need to pass to cairo after
// stops have been normalized.
gfxPoint gradientStart = lineStart + (lineEnd - lineStart)*stopOrigin;
gfxPoint gradientEnd = lineStart + (lineEnd - lineStart)*stopEnd;
gradientStart = lineStart + (lineEnd - lineStart)*stopOrigin;
gradientEnd = lineStart + (lineEnd - lineStart)*stopEnd;
gfxPoint gradientStopStart = lineStart + (lineEnd - lineStart)*firstStop;
gfxPoint gradientStopEnd = lineStart + (lineEnd - lineStart)*lastStop;
@ -2488,6 +2552,9 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
gfxRect areaToFill =
nsLayoutUtils::RectToGfxRect(aFillArea, appUnitsPerDevPixel);
gfxRect dirtyAreaToFill = nsLayoutUtils::RectToGfxRect(dirty, appUnitsPerDevPixel);
dirtyAreaToFill.RoundOut();
gfxMatrix ctm = ctx->CurrentMatrix();
bool isCTMPreservingAxisAlignedRectangles = ctm.PreservesAxisAlignedRectangles();
@ -2535,8 +2602,18 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext,
}
ctx->NewPath();
ctx->Rectangle(fillRect);
ctx->Translate(tileRect.TopLeft());
ctx->SetPattern(gradientPattern);
gfxRect dirtyFillRect = fillRect.Intersect(dirtyAreaToFill);
gfxRect fillRectRelativeToTile = dirtyFillRect - tileRect.TopLeft();
gfxRGBA edgeColor;
if (aGradient->mShape == NS_STYLE_GRADIENT_SHAPE_LINEAR && !isRepeat &&
RectIsBeyondLinearGradientEdge(fillRectRelativeToTile, matrix, stops,
gradientStart, gradientEnd, &edgeColor)) {
ctx->SetColor(edgeColor);
} else {
ctx->Translate(tileRect.TopLeft());
ctx->SetPattern(gradientPattern);
}
ctx->Fill();
ctx->SetMatrix(ctm);
}