Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(281)

Unified Diff: third_party/WebKit/Source/core/paint/BoxPainter.cpp

Issue 1949253004: Rounded background image fast path (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: todo Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/paint/BoxPainter.cpp
diff --git a/third_party/WebKit/Source/core/paint/BoxPainter.cpp b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
index 509919544c973584723f02d8cb79912046d45f8f..eef4a43df2b5321067be32c861ac3202a7ff76fc 100644
--- a/third_party/WebKit/Source/core/paint/BoxPainter.cpp
+++ b/third_party/WebKit/Source/core/paint/BoxPainter.cpp
@@ -197,21 +197,33 @@ void BoxPainter::paintFillLayers(const PaintInfo& paintInfo, const Color& c, con
namespace {
-void applyBoxShadowForBackground(GraphicsContext& context, const LayoutObject& obj)
-{
- const ShadowList* shadowList = obj.style()->boxShadow();
- ASSERT(shadowList);
- for (size_t i = shadowList->shadows().size(); i--; ) {
- const ShadowData& boxShadow = shadowList->shadows()[i];
- if (boxShadow.style() != Normal)
- continue;
- FloatSize shadowOffset(boxShadow.x(), boxShadow.y());
- context.setShadow(shadowOffset, boxShadow.blur(),
- boxShadow.color().resolve(obj.resolveColor(CSSPropertyColor)),
- DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
- return;
+// RAII shadow helper.
+class ShadowContext {
+ STACK_ALLOCATED();
+public:
+ ShadowContext(GraphicsContext& context, const LayoutObject& obj, bool applyShadow)
+ : m_saver(context, applyShadow)
+ {
+ if (!applyShadow)
+ return;
+
+ const ShadowList* shadowList = obj.style()->boxShadow();
+ DCHECK(shadowList);
+ for (size_t i = shadowList->shadows().size(); i--; ) {
+ const ShadowData& boxShadow = shadowList->shadows()[i];
+ if (boxShadow.style() != Normal)
+ continue;
+ FloatSize shadowOffset(boxShadow.x(), boxShadow.y());
+ context.setShadow(shadowOffset, boxShadow.blur(),
+ boxShadow.color().resolve(obj.resolveColor(CSSPropertyColor)),
+ DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::ShadowIgnoresAlpha);
+ break;
+ }
}
-}
+
+private:
+ GraphicsContextStateSaver m_saver;
+};
FloatRoundedRect getBackgroundRoundedRect(const LayoutObject& obj, const LayoutRect& borderRect,
const InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHeight,
@@ -321,41 +333,133 @@ struct FillLayerInfo {
bool shouldPaintShadow;
};
+// RAII image paint helper.
+class ImagePaintContext {
+ STACK_ALLOCATED();
+public:
+ ImagePaintContext(const LayoutBoxModelObject& obj, GraphicsContext& context,
+ const FillLayer& layer, const StyleImage& styleImage, SkXfermode::Mode op,
+ const LayoutObject* backgroundObject, const LayoutSize& containerSize)
+ : m_context(context)
+ , m_previousInterpolationQuality(context.imageInterpolationQuality())
+ {
+ SkXfermode::Mode bgOp = WebCoreCompositeToSkiaComposite(layer.composite(), layer.blendMode());
+ // if op != SkXfermode::kSrcOver_Mode, a mask is being painted.
+ m_compositeOp = (op == SkXfermode::kSrcOver_Mode) ? bgOp : op;
+
+ const LayoutObject& imageClient = backgroundObject ? *backgroundObject : obj;
+ m_image = styleImage.image(imageClient, flooredIntSize(containerSize), obj.style()->effectiveZoom());
+
+ m_interpolationQuality =
+ BoxPainter::chooseInterpolationQuality(imageClient, m_image.get(), &layer, containerSize);
+ if (m_interpolationQuality != m_previousInterpolationQuality)
+ context.setImageInterpolationQuality(m_interpolationQuality);
+
+ if (layer.maskSourceType() == MaskLuminance)
+ context.setColorFilter(ColorFilterLuminanceToAlpha);
+ }
+
+ ~ImagePaintContext()
+ {
+ if (m_interpolationQuality != m_previousInterpolationQuality)
+ m_context.setImageInterpolationQuality(m_previousInterpolationQuality);
+ }
+
+ Image* image() const { return m_image.get(); }
+
+ SkXfermode::Mode compositeOp() const { return m_compositeOp; }
+
+private:
+ RefPtr<Image> m_image;
+ GraphicsContext& m_context;
+ SkXfermode::Mode m_compositeOp;
+ InterpolationQuality m_interpolationQuality;
+ InterpolationQuality m_previousInterpolationQuality;
+};
+
inline bool paintFastBottomLayer(const LayoutBoxModelObject& obj, const PaintInfo& paintInfo,
- const FillLayerInfo& info, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance,
- const InlineFlowBox* box, const LayoutSize& boxSize)
+ const FillLayerInfo& info, const FillLayer& layer, const LayoutRect& rect,
+ BackgroundBleedAvoidance bleedAvoidance, const InlineFlowBox* box, const LayoutSize& boxSize,
+ SkXfermode::Mode op, const LayoutObject* backgroundObject,
+ Optional<BackgroundImageGeometry>& geometry)
{
// Complex cases not handled on the fast path.
if (!info.isBottomLayer || !info.isBorderFill || info.isClippedWithLocalScrolling)
return false;
- // Not yet.
- if (info.image)
- return false;
-
// Transparent layer, nothing to paint.
- if (!info.shouldPaintColor)
+ if (!info.shouldPaintColor && !info.shouldPaintImage)
return true;
- GraphicsContext& context = paintInfo.context;
- GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaintShadow);
- if (info.shouldPaintShadow)
- applyBoxShadowForBackground(context, obj);
+ // When the layer has an image, figure out whether it is covered by a single tile.
+ FloatRect imageTile;
+ if (info.shouldPaintImage) {
+ DCHECK(!geometry);
+ geometry.emplace();
+ geometry->calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobalPaintFlags(), layer, rect);
+
+ if (!geometry->destRect().isEmpty()) {
+ // The tile is too small.
+ if (geometry->tileSize().width() < rect.width() || geometry->tileSize().height() < rect.height())
+ return false;
+
+ imageTile = Image::computeTileContaining(
+ FloatPoint(geometry->destRect().location()),
+ FloatSize(geometry->tileSize()),
+ FloatPoint(geometry->phase()),
+ FloatSize(geometry->spaceSize()));
+
+ // The tile is misaligned.
+ if (!imageTile.contains(FloatRect(rect)))
+ return false;
+ }
+ }
- if (info.isRoundedFill) {
- FloatRoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect,
- bleedAvoidance, box, boxSize, info.includeLeftEdge, info.includeRightEdge);
+ // At this point we're committed to the fast path: the destination (r)rect fits within a single
+ // tile, and we can paint it using direct draw(R)Rect() calls.
+ GraphicsContext& context = paintInfo.context;
+ FloatRoundedRect border = info.isRoundedFill
+ ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvoidance, box, boxSize, info.includeLeftEdge, info.includeRightEdge)
+ : FloatRoundedRect(pixelSnappedIntRect(rect));
+
+ Optional<RoundedInnerRectClipper> clipper;
+ if (info.isRoundedFill && !border.isRenderable()) {
+ // When the rrect is not renderable, we resort to clipping.
+ // RoundedInnerRectClipper handles this case via discrete, corner-wise clipping.
+ // TODO(fmalita): looks like the border rrect should always be renderable on this path,
+ // thanks to https://www.w3.org/TR/css3-background/#corner-overlap. Investigate replacing
+ // the clipper with an assert.
+ clipper.emplace(obj, paintInfo, rect, border, ApplyToContext);
+ border.setRadii(FloatRoundedRect::Radii());
+ }
- if (border.isRenderable()) {
- context.fillRoundedRect(border, info.color);
- } else {
- RoundedInnerRectClipper clipper(obj, paintInfo, rect, border, ApplyToContext);
- context.fillRect(border.rect(), info.color);
- }
- } else {
- context.fillRect(pixelSnappedIntRect(rect), info.color);
+ // Paint the color + shadow if needed.
+ if (info.shouldPaintColor) {
+ const ShadowContext shadowContext(context, obj, info.shouldPaintShadow);
+ context.fillRoundedRect(border, info.color);
}
+ // Paint the image + shadow if needed.
+ if (!info.shouldPaintImage || imageTile.isEmpty())
+ return true;
+
+ const ImagePaintContext imageContext(obj, context, layer, *info.image, op, backgroundObject,
+ geometry->tileSize());
+ if (!imageContext.image())
+ return true;
+
+ const FloatSize intrinsicTileSize = imageContext.image()->hasRelativeSize()
+ ? imageTile.size()
+ : FloatSize(imageContext.image()->size());
+ const FloatRect srcRect =
+ Image::computeSubsetForTile(imageTile, border.rect(), intrinsicTileSize);
+
+ // The shadow may have been applied with the color fill.
+ const ShadowContext shadowContext(context, obj, info.shouldPaintShadow && !info.shouldPaintColor);
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data",
+ InspectorPaintImageEvent::data(obj, *info.image));
+ context.drawImageRRect(imageContext.image(), border, srcRect, imageContext.compositeOp());
+
return true;
}
@@ -368,10 +472,13 @@ void BoxPainter::paintFillLayer(const LayoutBoxModelObject& obj, const PaintInfo
return;
const FillLayerInfo info(obj, color, bgLayer, bleedAvoidance, box);
+ Optional<BackgroundImageGeometry> geometry;
// Fast path for drawing simple color backgrounds.
- if (paintFastBottomLayer(obj, paintInfo, info, rect, bleedAvoidance, box, boxSize))
+ if (paintFastBottomLayer(obj, paintInfo, info, bgLayer, rect, bleedAvoidance, box, boxSize, op,
+ backgroundObject, geometry)) {
return;
+ }
Optional<RoundedInnerRectClipper> clipToBorder;
if (info.isRoundedFill) {
@@ -462,34 +569,29 @@ void BoxPainter::paintFillLayer(const LayoutBoxModelObject& obj, const PaintInfo
if (info.isBottomLayer && info.color.alpha()) {
IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
if (info.shouldPaintColor || info.shouldPaintShadow) {
- GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaintShadow);
- if (info.shouldPaintShadow)
- applyBoxShadowForBackground(context, obj);
-
+ const ShadowContext shadowContext(context, obj, info.shouldPaintShadow);
context.fillRect(backgroundRect, info.color);
}
}
// no progressive loading of the background image
if (info.shouldPaintImage) {
- BackgroundImageGeometry geometry;
- geometry.calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobalPaintFlags(), bgLayer, scrolledPaintRect);
-
- if (!geometry.destRect().isEmpty()) {
- SkXfermode::Mode bgOp = WebCoreCompositeToSkiaComposite(bgLayer.composite(), bgLayer.blendMode());
- // if op != SkXfermode::kSrcOver_Mode, a mask is being painted.
- SkXfermode::Mode compositeOp = op == SkXfermode::kSrcOver_Mode ? bgOp : op;
- const LayoutObject& clientForBackgroundImage = backgroundObject ? *backgroundObject : obj;
- RefPtr<Image> image = info.image->image(clientForBackgroundImage, flooredIntSize(geometry.tileSize()), obj.style()->effectiveZoom());
- InterpolationQuality interpolationQuality = chooseInterpolationQuality(clientForBackgroundImage, image.get(), &bgLayer, LayoutSize(geometry.tileSize()));
- if (bgLayer.maskSourceType() == MaskLuminance)
- context.setColorFilter(ColorFilterLuminanceToAlpha);
- InterpolationQuality previousInterpolationQuality = context.imageInterpolationQuality();
- context.setImageInterpolationQuality(interpolationQuality);
- TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data", InspectorPaintImageEvent::data(obj, *info.image));
- context.drawTiledImage(image.get(), FloatRect(geometry.destRect()), FloatPoint(geometry.phase()), FloatSize(geometry.tileSize()),
- compositeOp, FloatSize(geometry.spaceSize()));
- context.setImageInterpolationQuality(previousInterpolationQuality);
+ if (!geometry) {
+ geometry.emplace();
+ geometry->calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobalPaintFlags(), bgLayer, scrolledPaintRect);
+ } else {
+ // The geometry was calculated in paintFastBottomLayer().
+ DCHECK(info.isBottomLayer && info.isBorderFill && !info.isClippedWithLocalScrolling);
+ }
+
+ if (!geometry->destRect().isEmpty()) {
+ const ImagePaintContext imageContext(obj, context, bgLayer, *info.image, op,
+ backgroundObject, geometry->tileSize());
+ TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", "data",
+ InspectorPaintImageEvent::data(obj, *info.image));
+ context.drawTiledImage(imageContext.image(), FloatRect(geometry->destRect()),
+ FloatPoint(geometry->phase()), FloatSize(geometry->tileSize()),
+ imageContext.compositeOp(), FloatSize(geometry->spaceSize()));
}
}

Powered by Google App Engine
This is Rietveld 408576698