Add a fast path for tiled gradients with no pixel alignment or repeat spacing. (bug 1443912, r=mattwoodrow)

MozReview-Commit-ID: 2TKL2ohrex4

--HG--
extra : rebase_source : 5f5f9af22f950fbdc0c695f2e2ad631a33607654
This commit is contained in:
Ryan Hunt 2018-03-19 13:31:41 -05:00
Родитель eb166cd888
Коммит f9ce570d9b
2 изменённых файлов: 114 добавлений и 0 удалений

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

@ -939,6 +939,17 @@ nsCSSGradientRenderer::Paint(gfxContext& aContext,
nscoord xEnd = forceRepeatToCoverTiles ? xStart + aDest.width : dirty.XMost();
nscoord yEnd = forceRepeatToCoverTiles ? yStart + aDest.height : dirty.YMost();
if (TryPaintTilesWithExtendMode(aContext,
gradientPattern,
xStart,
yStart,
dirtyAreaToFill,
aDest,
aRepeatSize,
forceRepeatToCoverTiles)) {
return;
}
// x and y are the top-left corner of the tile to draw
for (nscoord y = yStart; y < yEnd; y += aRepeatSize.height) {
for (nscoord x = xStart; x < xEnd; x += aRepeatSize.width) {
@ -997,6 +1008,92 @@ nsCSSGradientRenderer::Paint(gfxContext& aContext,
}
}
bool
nsCSSGradientRenderer::TryPaintTilesWithExtendMode(gfxContext& aContext,
gfxPattern* aGradientPattern,
nscoord aXStart,
nscoord aYStart,
const gfxRect& aDirtyAreaToFill,
const nsRect& aDest,
const nsSize& aRepeatSize,
bool aForceRepeatToCoverTiles)
{
// If we have forced a non-repeating gradient to repeat to cover tiles,
// then it will be faster to just paint it once using that optimization
if (aForceRepeatToCoverTiles) {
return false;
}
nscoord appUnitsPerDevPixel = mPresContext->AppUnitsPerDevPixel();
// We can only use this fast path if we don't have to worry about pixel
// snapping, and there is no spacing between tiles. We could handle spacing
// by increasing the size of tileSurface and leaving it transparent, but I'm
// not sure it's worth it
bool canUseExtendModeForTiling =
(aXStart % appUnitsPerDevPixel == 0) &&
(aYStart % appUnitsPerDevPixel == 0) &&
(aDest.width % appUnitsPerDevPixel == 0) &&
(aDest.height % appUnitsPerDevPixel == 0) &&
(aRepeatSize.width == aDest.width) &&
(aRepeatSize.height == aDest.height);
if (!canUseExtendModeForTiling) {
return false;
}
IntSize tileSize {
NSAppUnitsToIntPixels(aDest.width, appUnitsPerDevPixel),
NSAppUnitsToIntPixels(aDest.height, appUnitsPerDevPixel),
};
// We only want to do this when there are enough tiles to justify the
// overhead of painting to an offscreen surface. The heuristic here
// is when we will be painting at least 16 tiles or more, this is kind
// of arbitrary
bool shouldUseExtendModeForTiling =
aDirtyAreaToFill.Area() > (tileSize.width * tileSize.height) * 16.0;
if (!shouldUseExtendModeForTiling) {
return false;
}
// Draw the gradient pattern into a surface for our single tile
RefPtr<gfx::SourceSurface> tileSurface;
{
RefPtr<gfx::DrawTarget> tileTarget = aContext.
GetDrawTarget()->
CreateSimilarDrawTarget(tileSize, gfx::SurfaceFormat::B8G8R8A8);
if (!tileTarget || !tileTarget->IsValid()) {
return false;
}
RefPtr<gfxContext> tileContext = gfxContext::CreateOrNull(tileTarget);
tileContext->SetPattern(aGradientPattern);
tileContext->Paint();
tileContext = nullptr;
tileSurface = tileTarget->Snapshot();
tileTarget = nullptr;
}
// Draw the gradient using tileSurface as a repeating pattern masked by
// the dirtyRect
Matrix tileTransform = Matrix::Translation(
NSAppUnitsToFloatPixels(aXStart, appUnitsPerDevPixel),
NSAppUnitsToFloatPixels(aYStart, appUnitsPerDevPixel));
aContext.NewPath();
aContext.Rectangle(aDirtyAreaToFill);
aContext.Fill(SurfacePattern(
tileSurface,
ExtendMode::REPEAT,
tileTransform));
return true;
}
void
nsCSSGradientRenderer::BuildWebRenderParameters(float aOpacity,
wr::ExtendMode& aMode,

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

@ -92,6 +92,23 @@ public:
private:
nsCSSGradientRenderer() {}
/**
* Attempts to paint the tiles for a gradient by painting it once to an
* offscreen surface and then painting that offscreen surface with
* ExtendMode::Repeat to cover all tiles.
*
* Returns false if the optimization wasn't able to be used, in which case
* a fallback should be used.
*/
bool TryPaintTilesWithExtendMode(gfxContext& aContext,
gfxPattern* aGradientPattern,
nscoord aXStart,
nscoord aYStart,
const gfxRect& aDirtyAreaToFill,
const nsRect& aDest,
const nsSize& aRepeatSize,
bool aForceRepeatToCoverTiles);
nsPresContext* mPresContext;
nsStyleGradient* mGradient;
nsTArray<ColorStop> mStops;