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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/paint/BoxPainter.h" 5 #include "core/paint/BoxPainter.h"
6 6
7 #include "core/HTMLNames.h" 7 #include "core/HTMLNames.h"
8 #include "core/frame/Settings.h" 8 #include "core/frame/Settings.h"
9 #include "core/html/HTMLFrameOwnerElement.h" 9 #include "core/html/HTMLFrameOwnerElement.h"
10 #include "core/layout/ImageQualityController.h" 10 #include "core/layout/ImageQualityController.h"
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 190
191 for (auto it = reversedPaintList.rbegin(); it != reversedPaintList.rend(); + +it) 191 for (auto it = reversedPaintList.rbegin(); it != reversedPaintList.rend(); + +it)
192 paintFillLayer(m_layoutBox, paintInfo, c, **it, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject); 192 paintFillLayer(m_layoutBox, paintInfo, c, **it, rect, bleedAvoidance, 0, LayoutSize(), op, backgroundObject);
193 193
194 if (shouldDrawBackgroundInSeparateBuffer) 194 if (shouldDrawBackgroundInSeparateBuffer)
195 context.endLayer(); 195 context.endLayer();
196 } 196 }
197 197
198 namespace { 198 namespace {
199 199
200 void applyBoxShadowForBackground(GraphicsContext& context, const LayoutObject& o bj) 200 // RAII shadow helper.
201 { 201 class ShadowContext {
202 const ShadowList* shadowList = obj.style()->boxShadow(); 202 STACK_ALLOCATED();
203 ASSERT(shadowList); 203 public:
204 for (size_t i = shadowList->shadows().size(); i--; ) { 204 ShadowContext(GraphicsContext& context, const LayoutObject& obj, bool applyS hadow)
205 const ShadowData& boxShadow = shadowList->shadows()[i]; 205 : m_saver(context, applyShadow)
206 if (boxShadow.style() != Normal) 206 {
207 continue; 207 if (!applyShadow)
208 FloatSize shadowOffset(boxShadow.x(), boxShadow.y()); 208 return;
209 context.setShadow(shadowOffset, boxShadow.blur(), 209
210 boxShadow.color().resolve(obj.resolveColor(CSSPropertyColor)), 210 const ShadowList* shadowList = obj.style()->boxShadow();
211 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder::Shad owIgnoresAlpha); 211 DCHECK(shadowList);
212 return; 212 for (size_t i = shadowList->shadows().size(); i--; ) {
213 const ShadowData& boxShadow = shadowList->shadows()[i];
214 if (boxShadow.style() != Normal)
215 continue;
216 FloatSize shadowOffset(boxShadow.x(), boxShadow.y());
217 context.setShadow(shadowOffset, boxShadow.blur(),
218 boxShadow.color().resolve(obj.resolveColor(CSSPropertyColor)),
219 DrawLooperBuilder::ShadowRespectsTransforms, DrawLooperBuilder:: ShadowIgnoresAlpha);
220 break;
221 }
213 } 222 }
214 } 223
224 private:
225 GraphicsContextStateSaver m_saver;
226 };
215 227
216 FloatRoundedRect getBackgroundRoundedRect(const LayoutObject& obj, const LayoutR ect& borderRect, 228 FloatRoundedRect getBackgroundRoundedRect(const LayoutObject& obj, const LayoutR ect& borderRect,
217 const InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHei ght, 229 const InlineFlowBox* box, LayoutUnit inlineBoxWidth, LayoutUnit inlineBoxHei ght,
218 bool includeLogicalLeftEdge, bool includeLogicalRightEdge) 230 bool includeLogicalLeftEdge, bool includeLogicalRightEdge)
219 { 231 {
220 FloatRoundedRect border = obj.style()->getRoundedBorderFor(borderRect, inclu deLogicalLeftEdge, includeLogicalRightEdge); 232 FloatRoundedRect border = obj.style()->getRoundedBorderFor(borderRect, inclu deLogicalLeftEdge, includeLogicalRightEdge);
221 if (box && (box->nextLineBox() || box->prevLineBox())) { 233 if (box && (box->nextLineBox() || box->prevLineBox())) {
222 FloatRoundedRect segmentBorder = obj.style()->getRoundedBorderFor(Layout Rect(0, 0, inlineBoxWidth, inlineBoxHeight), 234 FloatRoundedRect segmentBorder = obj.style()->getRoundedBorderFor(Layout Rect(0, 0, inlineBoxWidth, inlineBoxHeight),
223 includeLogicalLeftEdge, includeLogicalRightEdge); 235 includeLogicalLeftEdge, includeLogicalRightEdge);
224 border.setRadii(segmentBorder.getRadii()); 236 border.setRadii(segmentBorder.getRadii());
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
314 bool isBottomLayer; 326 bool isBottomLayer;
315 bool isBorderFill; 327 bool isBorderFill;
316 bool isClippedWithLocalScrolling; 328 bool isClippedWithLocalScrolling;
317 bool isRoundedFill; 329 bool isRoundedFill;
318 330
319 bool shouldPaintImage; 331 bool shouldPaintImage;
320 bool shouldPaintColor; 332 bool shouldPaintColor;
321 bool shouldPaintShadow; 333 bool shouldPaintShadow;
322 }; 334 };
323 335
336 // RAII image paint helper.
337 class ImagePaintContext {
338 STACK_ALLOCATED();
339 public:
340 ImagePaintContext(const LayoutBoxModelObject& obj, GraphicsContext& context,
341 const FillLayer& layer, const StyleImage& styleImage, SkXfermode::Mode o p,
342 const LayoutObject* backgroundObject, const LayoutSize& containerSize)
343 : m_context(context)
344 , m_previousInterpolationQuality(context.imageInterpolationQuality())
345 {
346 SkXfermode::Mode bgOp = WebCoreCompositeToSkiaComposite(layer.composite( ), layer.blendMode());
347 // if op != SkXfermode::kSrcOver_Mode, a mask is being painted.
348 m_compositeOp = (op == SkXfermode::kSrcOver_Mode) ? bgOp : op;
349
350 const LayoutObject& imageClient = backgroundObject ? *backgroundObject : obj;
351 m_image = styleImage.image(imageClient, flooredIntSize(containerSize), o bj.style()->effectiveZoom());
352
353 m_interpolationQuality =
354 BoxPainter::chooseInterpolationQuality(imageClient, m_image.get(), & layer, containerSize);
355 if (m_interpolationQuality != m_previousInterpolationQuality)
356 context.setImageInterpolationQuality(m_interpolationQuality);
357
358 if (layer.maskSourceType() == MaskLuminance)
359 context.setColorFilter(ColorFilterLuminanceToAlpha);
360 }
361
362 ~ImagePaintContext()
363 {
364 if (m_interpolationQuality != m_previousInterpolationQuality)
365 m_context.setImageInterpolationQuality(m_previousInterpolationQualit y);
366 }
367
368 Image* image() const { return m_image.get(); }
369
370 SkXfermode::Mode compositeOp() const { return m_compositeOp; }
371
372 private:
373 RefPtr<Image> m_image;
374 GraphicsContext& m_context;
375 SkXfermode::Mode m_compositeOp;
376 InterpolationQuality m_interpolationQuality;
377 InterpolationQuality m_previousInterpolationQuality;
378 };
379
324 inline bool paintFastBottomLayer(const LayoutBoxModelObject& obj, const PaintInf o& paintInfo, 380 inline bool paintFastBottomLayer(const LayoutBoxModelObject& obj, const PaintInf o& paintInfo,
325 const FillLayerInfo& info, const LayoutRect& rect, BackgroundBleedAvoidance bleedAvoidance, 381 const FillLayerInfo& info, const FillLayer& layer, const LayoutRect& rect,
326 const InlineFlowBox* box, const LayoutSize& boxSize) 382 BackgroundBleedAvoidance bleedAvoidance, const InlineFlowBox* box, const Lay outSize& boxSize,
383 SkXfermode::Mode op, const LayoutObject* backgroundObject,
384 Optional<BackgroundImageGeometry>& geometry)
327 { 385 {
328 // Complex cases not handled on the fast path. 386 // Complex cases not handled on the fast path.
329 if (!info.isBottomLayer || !info.isBorderFill || info.isClippedWithLocalScro lling) 387 if (!info.isBottomLayer || !info.isBorderFill || info.isClippedWithLocalScro lling)
330 return false; 388 return false;
331 389
332 // Not yet.
333 if (info.image)
334 return false;
335
336 // Transparent layer, nothing to paint. 390 // Transparent layer, nothing to paint.
337 if (!info.shouldPaintColor) 391 if (!info.shouldPaintColor && !info.shouldPaintImage)
338 return true; 392 return true;
339 393
394 // When the layer has an image, figure out whether it is covered by a single tile.
395 FloatRect imageTile;
396 if (info.shouldPaintImage) {
397 DCHECK(!geometry);
398 geometry.emplace();
399 geometry->calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobal PaintFlags(), layer, rect);
400
401 if (!geometry->destRect().isEmpty()) {
402 // The tile is too small.
403 if (geometry->tileSize().width() < rect.width() || geometry->tileSiz e().height() < rect.height())
404 return false;
405
406 imageTile = Image::computeTileContaining(
407 FloatPoint(geometry->destRect().location()),
408 FloatSize(geometry->tileSize()),
409 FloatPoint(geometry->phase()),
410 FloatSize(geometry->spaceSize()));
411
412 // The tile is misaligned.
413 if (!imageTile.contains(FloatRect(rect)))
414 return false;
415 }
416 }
417
418 // At this point we're committed to the fast path: the destination (r)rect f its within a single
419 // tile, and we can paint it using direct draw(R)Rect() calls.
340 GraphicsContext& context = paintInfo.context; 420 GraphicsContext& context = paintInfo.context;
341 GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaintShadow); 421 FloatRoundedRect border = info.isRoundedFill
342 if (info.shouldPaintShadow) 422 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvoidan ce, box, boxSize, info.includeLeftEdge, info.includeRightEdge)
343 applyBoxShadowForBackground(context, obj); 423 : FloatRoundedRect(pixelSnappedIntRect(rect));
344 424
345 if (info.isRoundedFill) { 425 Optional<RoundedInnerRectClipper> clipper;
346 FloatRoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance (obj, rect, 426 if (info.isRoundedFill && !border.isRenderable()) {
347 bleedAvoidance, box, boxSize, info.includeLeftEdge, info.includeRigh tEdge); 427 // When the rrect is not renderable, we resort to clipping.
428 // RoundedInnerRectClipper handles this case via discrete, corner-wise c lipping.
429 // TODO(fmalita): looks like the border rrect should always be renderabl e on this path,
430 // thanks to https://www.w3.org/TR/css3-background/#corner-overlap. Inv estigate replacing
431 // the clipper with an assert.
432 clipper.emplace(obj, paintInfo, rect, border, ApplyToContext);
433 border.setRadii(FloatRoundedRect::Radii());
434 }
348 435
349 if (border.isRenderable()) { 436 // Paint the color + shadow if needed.
350 context.fillRoundedRect(border, info.color); 437 if (info.shouldPaintColor) {
351 } else { 438 const ShadowContext shadowContext(context, obj, info.shouldPaintShadow);
352 RoundedInnerRectClipper clipper(obj, paintInfo, rect, border, ApplyT oContext); 439 context.fillRoundedRect(border, info.color);
353 context.fillRect(border.rect(), info.color);
354 }
355 } else {
356 context.fillRect(pixelSnappedIntRect(rect), info.color);
357 } 440 }
358 441
442 // Paint the image + shadow if needed.
443 if (!info.shouldPaintImage || imageTile.isEmpty())
444 return true;
445
446 const ImagePaintContext imageContext(obj, context, layer, *info.image, op, b ackgroundObject,
447 geometry->tileSize());
448 if (!imageContext.image())
449 return true;
450
451 const FloatSize intrinsicTileSize = imageContext.image()->hasRelativeSize()
452 ? imageTile.size()
453 : FloatSize(imageContext.image()->size());
454 const FloatRect srcRect =
455 Image::computeSubsetForTile(imageTile, border.rect(), intrinsicTileSize) ;
456
457 // The shadow may have been applied with the color fill.
458 const ShadowContext shadowContext(context, obj, info.shouldPaintShadow && !i nfo.shouldPaintColor);
459 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", " data",
460 InspectorPaintImageEvent::data(obj, *info.image));
461 context.drawImageRRect(imageContext.image(), border, srcRect, imageContext.c ompositeOp());
462
359 return true; 463 return true;
360 } 464 }
361 465
362 } // anonymous namespace 466 } // anonymous namespace
363 467
364 void BoxPainter::paintFillLayer(const LayoutBoxModelObject& obj, const PaintInfo & paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rec t, BackgroundBleedAvoidance bleedAvoidance, const InlineFlowBox* box, const Layo utSize& boxSize, SkXfermode::Mode op, const LayoutObject* backgroundObject) 468 void BoxPainter::paintFillLayer(const LayoutBoxModelObject& obj, const PaintInfo & paintInfo, const Color& color, const FillLayer& bgLayer, const LayoutRect& rec t, BackgroundBleedAvoidance bleedAvoidance, const InlineFlowBox* box, const Layo utSize& boxSize, SkXfermode::Mode op, const LayoutObject* backgroundObject)
365 { 469 {
366 GraphicsContext& context = paintInfo.context; 470 GraphicsContext& context = paintInfo.context;
367 if (rect.isEmpty()) 471 if (rect.isEmpty())
368 return; 472 return;
369 473
370 const FillLayerInfo info(obj, color, bgLayer, bleedAvoidance, box); 474 const FillLayerInfo info(obj, color, bgLayer, bleedAvoidance, box);
475 Optional<BackgroundImageGeometry> geometry;
371 476
372 // Fast path for drawing simple color backgrounds. 477 // Fast path for drawing simple color backgrounds.
373 if (paintFastBottomLayer(obj, paintInfo, info, rect, bleedAvoidance, box, bo xSize)) 478 if (paintFastBottomLayer(obj, paintInfo, info, bgLayer, rect, bleedAvoidance , box, boxSize, op,
479 backgroundObject, geometry)) {
374 return; 480 return;
481 }
375 482
376 Optional<RoundedInnerRectClipper> clipToBorder; 483 Optional<RoundedInnerRectClipper> clipToBorder;
377 if (info.isRoundedFill) { 484 if (info.isRoundedFill) {
378 FloatRoundedRect border = info.isBorderFill 485 FloatRoundedRect border = info.isBorderFill
379 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvo idance, box, boxSize, info.includeLeftEdge, info.includeRightEdge) 486 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvo idance, box, boxSize, info.includeLeftEdge, info.includeRightEdge)
380 : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxSize. height(), info.includeLeftEdge, info.includeRightEdge); 487 : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxSize. height(), info.includeLeftEdge, info.includeRightEdge);
381 488
382 // Clip to the padding or content boxes as necessary. 489 // Clip to the padding or content boxes as necessary.
383 if (bgLayer.clip() == ContentFillBox) { 490 if (bgLayer.clip() == ContentFillBox) {
384 border = obj.style()->getRoundedInnerBorderFor(LayoutRect(border.rec t()), 491 border = obj.style()->getRoundedInnerBorderFor(LayoutRect(border.rec t()),
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 ASSERT_NOT_REACHED(); 562 ASSERT_NOT_REACHED();
456 break; 563 break;
457 } 564 }
458 565
459 // Paint the color first underneath all images, culled if background image o ccludes it. 566 // Paint the color first underneath all images, culled if background image o ccludes it.
460 // TODO(trchen): In the !bgLayer.hasRepeatXY() case, we could improve the cu lling test 567 // TODO(trchen): In the !bgLayer.hasRepeatXY() case, we could improve the cu lling test
461 // by verifying whether the background image covers the entire painting area . 568 // by verifying whether the background image covers the entire painting area .
462 if (info.isBottomLayer && info.color.alpha()) { 569 if (info.isBottomLayer && info.color.alpha()) {
463 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); 570 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
464 if (info.shouldPaintColor || info.shouldPaintShadow) { 571 if (info.shouldPaintColor || info.shouldPaintShadow) {
465 GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaint Shadow); 572 const ShadowContext shadowContext(context, obj, info.shouldPaintShad ow);
466 if (info.shouldPaintShadow)
467 applyBoxShadowForBackground(context, obj);
468
469 context.fillRect(backgroundRect, info.color); 573 context.fillRect(backgroundRect, info.color);
470 } 574 }
471 } 575 }
472 576
473 // no progressive loading of the background image 577 // no progressive loading of the background image
474 if (info.shouldPaintImage) { 578 if (info.shouldPaintImage) {
475 BackgroundImageGeometry geometry; 579 if (!geometry) {
476 geometry.calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobalP aintFlags(), bgLayer, scrolledPaintRect); 580 geometry.emplace();
581 geometry->calculate(obj, paintInfo.paintContainer(), paintInfo.getGl obalPaintFlags(), bgLayer, scrolledPaintRect);
582 } else {
583 // The geometry was calculated in paintFastBottomLayer().
584 DCHECK(info.isBottomLayer && info.isBorderFill && !info.isClippedWit hLocalScrolling);
585 }
477 586
478 if (!geometry.destRect().isEmpty()) { 587 if (!geometry->destRect().isEmpty()) {
479 SkXfermode::Mode bgOp = WebCoreCompositeToSkiaComposite(bgLayer.comp osite(), bgLayer.blendMode()); 588 const ImagePaintContext imageContext(obj, context, bgLayer, *info.im age, op,
480 // if op != SkXfermode::kSrcOver_Mode, a mask is being painted. 589 backgroundObject, geometry->tileSize());
481 SkXfermode::Mode compositeOp = op == SkXfermode::kSrcOver_Mode ? bgO p : op; 590 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintI mage", "data",
482 const LayoutObject& clientForBackgroundImage = backgroundObject ? *b ackgroundObject : obj; 591 InspectorPaintImageEvent::data(obj, *info.image));
483 RefPtr<Image> image = info.image->image(clientForBackgroundImage, fl ooredIntSize(geometry.tileSize()), obj.style()->effectiveZoom()); 592 context.drawTiledImage(imageContext.image(), FloatRect(geometry->des tRect()),
484 InterpolationQuality interpolationQuality = chooseInterpolationQuali ty(clientForBackgroundImage, image.get(), &bgLayer, LayoutSize(geometry.tileSize ())); 593 FloatPoint(geometry->phase()), FloatSize(geometry->tileSize()),
485 if (bgLayer.maskSourceType() == MaskLuminance) 594 imageContext.compositeOp(), FloatSize(geometry->spaceSize()));
486 context.setColorFilter(ColorFilterLuminanceToAlpha);
487 InterpolationQuality previousInterpolationQuality = context.imageInt erpolationQuality();
488 context.setImageInterpolationQuality(interpolationQuality);
489 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintI mage", "data", InspectorPaintImageEvent::data(obj, *info.image));
490 context.drawTiledImage(image.get(), FloatRect(geometry.destRect()), FloatPoint(geometry.phase()), FloatSize(geometry.tileSize()),
491 compositeOp, FloatSize(geometry.spaceSize()));
492 context.setImageInterpolationQuality(previousInterpolationQuality);
493 } 595 }
494 } 596 }
495 597
496 if (bgLayer.clip() == TextFillBox) { 598 if (bgLayer.clip() == TextFillBox) {
497 // Create the text mask layer. 599 // Create the text mask layer.
498 context.beginLayer(1, SkXfermode::kDstIn_Mode); 600 context.beginLayer(1, SkXfermode::kDstIn_Mode);
499 601
500 // Now draw the text into the mask. We do this by painting using a speci al paint phase that signals to 602 // Now draw the text into the mask. We do this by painting using a speci al paint phase that signals to
501 // InlineTextBoxes that they should just add their contents to the clip. 603 // InlineTextBoxes that they should just add their contents to the clip.
502 PaintInfo info(context, maskRect, PaintPhaseTextClip, GlobalPaintNormalP hase, 0); 604 PaintInfo info(context, maskRect, PaintPhaseTextClip, GlobalPaintNormalP hase, 0);
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 } 823 }
722 } 824 }
723 825
724 bool BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(const ComputedStyle& style, const Document& document) 826 bool BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(const ComputedStyle& style, const Document& document)
725 { 827 {
726 return document.printing() && style.getPrintColorAdjust() == PrintColorAdjus tEconomy 828 return document.printing() && style.getPrintColorAdjust() == PrintColorAdjus tEconomy
727 && (!document.settings() || !document.settings()->shouldPrintBackgrounds ()); 829 && (!document.settings() || !document.settings()->shouldPrintBackgrounds ());
728 } 830 }
729 831
730 } // namespace blink 832 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698