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

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: review 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()) {
chrishtr 2016/05/20 15:44:13 It's not so good that this code duplicates informa
f(malita) 2016/05/20 19:17:11 Agreed. The current API is convoluted and hard to
chrishtr 2016/05/20 19:52:35 Sounds like a lot of work. Have you sketched it ou
f(malita) 2016/05/22 14:04:06 I do expect it to be quite a bit of work, but a) I
chrishtr 2016/05/23 19:59:22 Talking about it at BlinkOn sounds good.
402 // The tile is too small.
403 if (geometry->tileSize().width() < rect.width() || geometry->tileSiz e().height() < rect.height())
404 return false;
405
406 const FloatRect destRect(geometry->destRect());
407 const FloatPoint srcPoint(geometry->phase());
408
409 // This duplicates Image::drawTiled() logic.
410 // TODO(fmalita): find a way to share?
411 const FloatSize actualTileSize(geometry->tileSize() + geometry->spac eSize());
412 imageTile.setLocation(FloatPoint(
413 destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width() ) - actualTileSize.width(), actualTileSize.width()),
414 destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height( )) - actualTileSize.height(), actualTileSize.height())
415 ));
416 imageTile.setSize(FloatSize(geometry->tileSize()));
417
418 // The tile is misaligned.
419 if (!imageTile.contains(FloatRect(rect)))
420 return false;
421 }
422 }
423
424 // At this point we're committed to the fast path: the destination (r)rect f its within a single
425 // tile, and we can paint it using direct draw(R)Rect() calls.
340 GraphicsContext& context = paintInfo.context; 426 GraphicsContext& context = paintInfo.context;
341 GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaintShadow); 427 FloatRoundedRect border = info.isRoundedFill
342 if (info.shouldPaintShadow) 428 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvoidan ce, box, boxSize, info.includeLeftEdge, info.includeRightEdge)
343 applyBoxShadowForBackground(context, obj); 429 : FloatRoundedRect(pixelSnappedIntRect(rect));
344 430
345 if (info.isRoundedFill) { 431 Optional<RoundedInnerRectClipper> clipper;
346 FloatRoundedRect border = backgroundRoundedRectAdjustedForBleedAvoidance (obj, rect, 432 if (info.isRoundedFill && !border.isRenderable()) {
347 bleedAvoidance, box, boxSize, info.includeLeftEdge, info.includeRigh tEdge); 433 // When the rrect is not renderable, we resort to clipping.
434 // RoundedInnerRectClipper handles this case via discrete, corner-wise c lipping.
435 clipper.emplace(obj, paintInfo, rect, border, ApplyToContext);
436 border.setRadii(FloatRoundedRect::Radii());
437 }
348 438
349 if (border.isRenderable()) { 439 // Paint the color + shadow if needed.
350 context.fillRoundedRect(border, info.color); 440 if (info.shouldPaintColor) {
351 } else { 441 const ShadowContext shadowContext(context, obj, info.shouldPaintShadow);
352 RoundedInnerRectClipper clipper(obj, paintInfo, rect, border, ApplyT oContext); 442 context.fillRoundedRect(border, info.color);
353 context.fillRect(border.rect(), info.color);
354 }
355 } else {
356 context.fillRect(pixelSnappedIntRect(rect), info.color);
357 } 443 }
358 444
445 // Paint the image + shadow if needed.
446 if (!info.shouldPaintImage || imageTile.isEmpty())
447 return true;
448
449 const ImagePaintContext imageContext(obj, context, layer, *info.image, op, b ackgroundObject,
450 geometry->tileSize());
451 if (!imageContext.image())
452 return true;
453
454 // Compute the image src rect (tile subset) which gets mapped onto the dest/ border rect.
455 // TODO(fmalita): share with Image::drawTiled()?
456 const FloatSize intrinsicTileSize = imageContext.image()->hasRelativeSize()
457 ? imageTile.size()
458 : FloatSize(imageContext.image()->size());
459 const FloatSize tileScale(
460 intrinsicTileSize.width() / imageTile.width(),
461 intrinsicTileSize.height() / imageTile.height());
462 FloatRect srcRect = border.rect();
463 srcRect.moveBy(-imageTile.location());
464 srcRect.scale(tileScale.width(), tileScale.height());
465
466 // The shadow may have been applied with the color fill.
467 const ShadowContext shadowContext(context, obj, info.shouldPaintShadow && !i nfo.shouldPaintColor);
468 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintImage", " data",
469 InspectorPaintImageEvent::data(obj, *info.image));
470 context.drawImageRRect(imageContext.image(), border, &srcRect, imageContext. compositeOp());
471
359 return true; 472 return true;
360 } 473 }
361 474
362 } // anonymous namespace 475 } // anonymous namespace
363 476
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) 477 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 { 478 {
366 GraphicsContext& context = paintInfo.context; 479 GraphicsContext& context = paintInfo.context;
367 if (rect.isEmpty()) 480 if (rect.isEmpty())
368 return; 481 return;
369 482
370 const FillLayerInfo info(obj, color, bgLayer, bleedAvoidance, box); 483 const FillLayerInfo info(obj, color, bgLayer, bleedAvoidance, box);
484 Optional<BackgroundImageGeometry> geometry;
371 485
372 // Fast path for drawing simple color backgrounds. 486 // Fast path for drawing simple color backgrounds.
373 if (paintFastBottomLayer(obj, paintInfo, info, rect, bleedAvoidance, box, bo xSize)) 487 if (paintFastBottomLayer(obj, paintInfo, info, bgLayer, rect, bleedAvoidance , box, boxSize, op,
488 backgroundObject, geometry)) {
374 return; 489 return;
490 }
375 491
376 Optional<RoundedInnerRectClipper> clipToBorder; 492 Optional<RoundedInnerRectClipper> clipToBorder;
377 if (info.isRoundedFill) { 493 if (info.isRoundedFill) {
378 FloatRoundedRect border = info.isBorderFill 494 FloatRoundedRect border = info.isBorderFill
379 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvo idance, box, boxSize, info.includeLeftEdge, info.includeRightEdge) 495 ? backgroundRoundedRectAdjustedForBleedAvoidance(obj, rect, bleedAvo idance, box, boxSize, info.includeLeftEdge, info.includeRightEdge)
380 : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxSize. height(), info.includeLeftEdge, info.includeRightEdge); 496 : getBackgroundRoundedRect(obj, rect, box, boxSize.width(), boxSize. height(), info.includeLeftEdge, info.includeRightEdge);
381 497
382 // Clip to the padding or content boxes as necessary. 498 // Clip to the padding or content boxes as necessary.
383 if (bgLayer.clip() == ContentFillBox) { 499 if (bgLayer.clip() == ContentFillBox) {
384 border = obj.style()->getRoundedInnerBorderFor(LayoutRect(border.rec t()), 500 border = obj.style()->getRoundedInnerBorderFor(LayoutRect(border.rec t()),
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 ASSERT_NOT_REACHED(); 571 ASSERT_NOT_REACHED();
456 break; 572 break;
457 } 573 }
458 574
459 // Paint the color first underneath all images, culled if background image o ccludes it. 575 // 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 576 // 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 . 577 // by verifying whether the background image covers the entire painting area .
462 if (info.isBottomLayer && info.color.alpha()) { 578 if (info.isBottomLayer && info.color.alpha()) {
463 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect)); 579 IntRect backgroundRect(pixelSnappedIntRect(scrolledPaintRect));
464 if (info.shouldPaintColor || info.shouldPaintShadow) { 580 if (info.shouldPaintColor || info.shouldPaintShadow) {
465 GraphicsContextStateSaver shadowStateSaver(context, info.shouldPaint Shadow); 581 const ShadowContext shadowContext(context, obj, info.shouldPaintShad ow);
466 if (info.shouldPaintShadow)
467 applyBoxShadowForBackground(context, obj);
468
469 context.fillRect(backgroundRect, info.color); 582 context.fillRect(backgroundRect, info.color);
470 } 583 }
471 } 584 }
472 585
473 // no progressive loading of the background image 586 // no progressive loading of the background image
474 if (info.shouldPaintImage) { 587 if (info.shouldPaintImage) {
475 BackgroundImageGeometry geometry; 588 if (!geometry) {
476 geometry.calculate(obj, paintInfo.paintContainer(), paintInfo.getGlobalP aintFlags(), bgLayer, scrolledPaintRect); 589 geometry.emplace();
590 geometry->calculate(obj, paintInfo.paintContainer(), paintInfo.getGl obalPaintFlags(), bgLayer, scrolledPaintRect);
591 } else {
592 // The geometry was calculated in paintFastBottomLayer().
593 DCHECK(info.isBottomLayer && info.isBorderFill && !info.isClippedWit hLocalScrolling);
594 }
477 595
478 if (!geometry.destRect().isEmpty()) { 596 if (!geometry->destRect().isEmpty()) {
479 SkXfermode::Mode bgOp = WebCoreCompositeToSkiaComposite(bgLayer.comp osite(), bgLayer.blendMode()); 597 const ImagePaintContext imageContext(obj, context, bgLayer, *info.im age, op,
480 // if op != SkXfermode::kSrcOver_Mode, a mask is being painted. 598 backgroundObject, geometry->tileSize());
481 SkXfermode::Mode compositeOp = op == SkXfermode::kSrcOver_Mode ? bgO p : op; 599 TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "PaintI mage", "data",
482 const LayoutObject& clientForBackgroundImage = backgroundObject ? *b ackgroundObject : obj; 600 InspectorPaintImageEvent::data(obj, *info.image));
483 RefPtr<Image> image = info.image->image(clientForBackgroundImage, fl ooredIntSize(geometry.tileSize()), obj.style()->effectiveZoom()); 601 context.drawTiledImage(imageContext.image(), FloatRect(geometry->des tRect()),
484 InterpolationQuality interpolationQuality = chooseInterpolationQuali ty(clientForBackgroundImage, image.get(), &bgLayer, LayoutSize(geometry.tileSize ())); 602 FloatPoint(geometry->phase()), FloatSize(geometry->tileSize()),
485 if (bgLayer.maskSourceType() == MaskLuminance) 603 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 } 604 }
494 } 605 }
495 606
496 if (bgLayer.clip() == TextFillBox) { 607 if (bgLayer.clip() == TextFillBox) {
497 // Create the text mask layer. 608 // Create the text mask layer.
498 context.beginLayer(1, SkXfermode::kDstIn_Mode); 609 context.beginLayer(1, SkXfermode::kDstIn_Mode);
499 610
500 // Now draw the text into the mask. We do this by painting using a speci al paint phase that signals to 611 // 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. 612 // InlineTextBoxes that they should just add their contents to the clip.
502 PaintInfo info(context, maskRect, PaintPhaseTextClip, GlobalPaintNormalP hase, 0); 613 PaintInfo info(context, maskRect, PaintPhaseTextClip, GlobalPaintNormalP hase, 0);
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
721 } 832 }
722 } 833 }
723 834
724 bool BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(const ComputedStyle& style, const Document& document) 835 bool BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(const ComputedStyle& style, const Document& document)
725 { 836 {
726 return document.printing() && style.getPrintColorAdjust() == PrintColorAdjus tEconomy 837 return document.printing() && style.getPrintColorAdjust() == PrintColorAdjus tEconomy
727 && (!document.settings() || !document.settings()->shouldPrintBackgrounds ()); 838 && (!document.settings() || !document.settings()->shouldPrintBackgrounds ());
728 } 839 }
729 840
730 } // namespace blink 841 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698