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

Side by Side Diff: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp

Issue 2641173008: [SPv2] Add CSS mask support (Closed)
Patch Set: update layout test expectation && add unit tests Created 3 years, 10 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 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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/PaintPropertyTreeBuilder.h" 5 #include "core/paint/PaintPropertyTreeBuilder.h"
6 6
7 #include "core/dom/DOMNodeIds.h" 7 #include "core/dom/DOMNodeIds.h"
8 #include "core/frame/FrameView.h" 8 #include "core/frame/FrameView.h"
9 #include "core/frame/LocalFrame.h" 9 #include "core/frame/LocalFrame.h"
10 #include "core/frame/Settings.h" 10 #include "core/frame/Settings.h"
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 context.current.renderingContextId = 408 context.current.renderingContextId =
409 properties->transform()->renderingContextId(); 409 properties->transform()->renderingContextId();
410 context.current.shouldFlattenInheritedTransform = false; 410 context.current.shouldFlattenInheritedTransform = false;
411 } else { 411 } else {
412 context.current.renderingContextId = 0; 412 context.current.renderingContextId = 0;
413 context.current.shouldFlattenInheritedTransform = true; 413 context.current.shouldFlattenInheritedTransform = true;
414 } 414 }
415 } 415 }
416 } 416 }
417 417
418 static bool computeMaskParameters(IntRect& maskClip,
chrishtr 2017/02/11 23:55:11 The bounds of the mask requires the bounds of chil
trchen 2017/02/13 21:12:02 Only for SVG. For HTML/CSS we only use the current
419 const LayoutObject& object,
420 const LayoutPoint& paintOffset) {
421 DCHECK(object.isBoxModelObject() || object.isSVGChild());
422 const ComputedStyle& style = object.styleRef();
423
424 if (object.isSVGChild()) {
425 // TODO(trchen): Figure out what to do with SVG. SVG may use object
chrishtr 2017/02/13 23:23:49 Remove this comment, per discussion.
trchen 2017/02/14 01:37:30 Alright. Changed it to just "Implement SVG masks."
426 // bounding box for mask sizing, but computing it requires property
427 // tree to be built.
428 return false;
429 }
430 if (!style.hasMask())
431 return false;
432
433 LayoutRect maximumMaskRegion;
434 // For HTML/CSS objects, the extent of the mask is known as "mask
435 // painting area", which is determined by CSS mask-clip property.
436 // We don't implement mask-clip:margin-box or no-clip currently,
437 // so the maximum we can get is border-box.
438 if (object.isBox()) {
439 maximumMaskRegion = toLayoutBox(object).borderBoxRect();
440 } else {
441 // For inline elements, depends on the value of box-decoration-break
442 // there could be one box in multiple fragments or multiple boxes.
443 // Either way here we are only interested in the bounding box of them.
444 DCHECK(object.isLayoutInline());
445 maximumMaskRegion = toLayoutInline(object).linesBoundingBox();
chrishtr 2017/02/13 23:23:49 Can you point me to the legacy code which implemen
trchen 2017/02/14 01:37:30 The legacy code doesn't clip inline overflow corre
chrishtr 2017/02/14 02:11:33 I wonder if this has been filed as a bug before. D
trchen 2017/02/14 02:22:04 I'm not aware of one either.
446 }
447 maximumMaskRegion.moveBy(paintOffset);
448 maskClip = enclosingIntRect(maximumMaskRegion);
449 return true;
450 }
451
418 void PaintPropertyTreeBuilder::updateEffect( 452 void PaintPropertyTreeBuilder::updateEffect(
419 const LayoutObject& object, 453 const LayoutObject& object,
420 PaintPropertyTreeBuilderContext& context) { 454 PaintPropertyTreeBuilderContext& context) {
421 const ComputedStyle& style = object.styleRef(); 455 const ComputedStyle& style = object.styleRef();
422 456
423 const bool isCSSIsolatedGroup = 457 const bool isCSSIsolatedGroup =
424 object.isBoxModelObject() && style.isStackingContext(); 458 object.isBoxModelObject() && style.isStackingContext();
425 if (!isCSSIsolatedGroup && !object.isSVGChild()) { 459 if (!isCSSIsolatedGroup && !object.isSVGChild()) {
426 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { 460 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
427 if (auto* properties = object.getMutableForPainting().paintProperties()) 461 if (auto* properties = object.getMutableForPainting().paintProperties()) {
428 context.forceSubtreeUpdate |= properties->clearEffect(); 462 context.forceSubtreeUpdate |= properties->clearEffect();
463 context.forceSubtreeUpdate |= properties->clearMask();
464 context.forceSubtreeUpdate |= properties->clearMaskClip();
465 }
429 } 466 }
430 return; 467 return;
431 } 468 }
432 469
433 // TODO(trchen): Can't omit effect node if we have 3D children. 470 // TODO(trchen): Can't omit effect node if we have 3D children.
434 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { 471 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
472 const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect;
473
435 bool effectNodeNeeded = false; 474 bool effectNodeNeeded = false;
436 475
437 // Can't omit effect node if we have paint children with exotic blending. 476 // Can't omit effect node if we have paint children with exotic blending.
438 if (object.isSVG()) { 477 if (object.isSVG()) {
439 // Yes, including LayoutSVGRoot, because SVG layout objects don't create 478 // Yes, including LayoutSVGRoot, because SVG layout objects don't create
440 // PaintLayer so PaintLayer::hasNonIsolatedDescendantWithBlendMode() 479 // PaintLayer so PaintLayer::hasNonIsolatedDescendantWithBlendMode()
441 // doesn't catch SVG descendants. 480 // doesn't catch SVG descendants.
442 if (SVGLayoutSupport::isIsolationRequired(&object)) 481 if (SVGLayoutSupport::isIsolationRequired(&object))
443 effectNodeNeeded = true; 482 effectNodeNeeded = true;
444 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { 483 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) {
445 if (layer->hasNonIsolatedDescendantWithBlendMode()) 484 if (layer->hasNonIsolatedDescendantWithBlendMode())
446 effectNodeNeeded = true; 485 effectNodeNeeded = true;
447 } 486 }
448 487
449 SkBlendMode blendMode = object.isBlendingAllowed() 488 SkBlendMode blendMode = object.isBlendingAllowed()
450 ? WebCoreCompositeToSkiaComposite( 489 ? WebCoreCompositeToSkiaComposite(
451 CompositeSourceOver, style.blendMode()) 490 CompositeSourceOver, style.blendMode())
452 : SkBlendMode::kSrcOver; 491 : SkBlendMode::kSrcOver;
453 if (blendMode != SkBlendMode::kSrcOver) 492 if (blendMode != SkBlendMode::kSrcOver)
454 effectNodeNeeded = true; 493 effectNodeNeeded = true;
455 494
456 float opacity = style.opacity(); 495 float opacity = style.opacity();
457 if (opacity != 1.0f) 496 if (opacity != 1.0f)
458 effectNodeNeeded = true; 497 effectNodeNeeded = true;
459 498
499 // We may begin to composite our subtree prior to an animation starts,
500 // but a compositor element ID is only needed when an animation is current.
501 CompositorElementId compositorElementId =
502 style.hasCurrentOpacityAnimation()
503 ? createDomNodeBasedCompositorElementId(object)
504 : CompositorElementId();
505 CompositingReasons compositingReasons = CompositingReasonNone;
506 if (CompositingReasonFinder::requiresCompositingForOpacityAnimation(
507 style)) {
508 compositingReasons = CompositingReasonActiveAnimation;
509 effectNodeNeeded = true;
510 }
511 DCHECK(!style.hasCurrentOpacityAnimation() ||
512 compositingReasons != CompositingReasonNone);
513
514 IntRect maskClip;
515 bool hasMask =
516 computeMaskParameters(maskClip, object, context.current.paintOffset);
517 if (hasMask) {
518 effectNodeNeeded = true;
519
520 auto& properties = object.getMutableForPainting().ensurePaintProperties();
521 context.forceSubtreeUpdate |= properties.updateMaskClip(
522 context.current.clip, context.current.transform,
523 FloatRoundedRect(maskClip));
524 outputClip = properties.maskClip();
525
526 // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
527 // grouping (i.e. descendant-dependent compositing reason) properly yet.
528 // This forces masked subtree always create a layer for now.
529 compositingReasons |= CompositingReasonIsolateCompositedDescendants;
530 } else {
531 if (auto* properties = object.getMutableForPainting().paintProperties())
532 context.forceSubtreeUpdate |= properties->clearMaskClip();
533 }
534
535 if (effectNodeNeeded) {
536 auto& properties = object.getMutableForPainting().ensurePaintProperties();
537 context.forceSubtreeUpdate |= properties.updateEffect(
538 context.currentEffect, context.current.transform, outputClip,
539 CompositorFilterOperations(), opacity, blendMode, compositingReasons,
540 compositorElementId);
541 if (hasMask) {
542 // TODO(crbug.com/683425): PaintArtifactCompositor does not handle
chrishtr 2017/02/13 23:23:49 Please adjust the comment to make it clear that th
trchen 2017/02/14 01:37:30 Done.
543 // grouping (i.e. descendant-dependent compositing reason) properly yet.
544 // This forces mask won't get squashed into a child effect.
545 context.forceSubtreeUpdate |= properties.updateMask(
546 properties.effect(), context.current.transform, outputClip,
547 CompositorFilterOperations(), 1.f, SkBlendMode::kDstIn,
548 CompositingReasonSquashingDisallowed, CompositorElementId());
549 } else {
550 context.forceSubtreeUpdate |= properties.clearMask();
551 }
552 } else {
553 if (auto* properties = object.getMutableForPainting().paintProperties()) {
554 context.forceSubtreeUpdate |= properties->clearEffect();
555 context.forceSubtreeUpdate |= properties->clearMask();
556 }
557 }
558 }
559
560 const auto* properties = object.paintProperties();
561 if (properties && properties->effect()) {
562 context.currentEffect = properties->effect();
563 if (properties->maskClip()) {
564 context.inputClipOfCurrentEffect = context.current.clip =
565 context.absolutePosition.clip = context.fixedPosition.clip =
566 properties->maskClip();
567 }
568 }
569 }
570
571 void PaintPropertyTreeBuilder::updateFilter(
572 const LayoutObject& object,
573 PaintPropertyTreeBuilderContext& context) {
574 const ComputedStyle& style = object.styleRef();
575
576 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
460 CompositorFilterOperations filter; 577 CompositorFilterOperations filter;
461 if (object.isSVGChild()) { 578 if (object.isSVGChild()) {
462 // TODO(trchen): SVG caches filters in SVGResources. Implement it. 579 // TODO(trchen): SVG caches filters in SVGResources. Implement it.
463 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) { 580 } else if (PaintLayer* layer = toLayoutBoxModelObject(object).layer()) {
464 // TODO(trchen): Eliminate PaintLayer dependency. 581 // TODO(trchen): Eliminate PaintLayer dependency.
465 filter = layer->createCompositorFilterOperationsForFilter(style); 582 filter = layer->createCompositorFilterOperationsForFilter(style);
466 } 583 }
467 584
468 const ClipPaintPropertyNode* outputClip = context.inputClipOfCurrentEffect;
469 // The CSS filter spec didn't specify how filters interact with overflow 585 // The CSS filter spec didn't specify how filters interact with overflow
470 // clips. The implementation here mimics the old Blink/WebKit behavior for 586 // clips. The implementation here mimics the old Blink/WebKit behavior for
471 // backward compatibility. 587 // backward compatibility.
472 // Basically the output of the filter will be affected by clips that applies 588 // Basically the output of the filter will be affected by clips that applies
473 // to the current element. The descendants that paints into the input of the 589 // to the current element. The descendants that paints into the input of the
474 // filter ignores any clips collected so far. For example: 590 // filter ignores any clips collected so far. For example:
475 // <div style="overflow:scroll"> 591 // <div style="overflow:scroll">
476 // <div style="filter:blur(1px);"> 592 // <div style="filter:blur(1px);">
477 // <div>A</div> 593 // <div>A</div>
478 // <div style="position:absolute;">B</div> 594 // <div style="position:absolute;">B</div>
479 // </div> 595 // </div>
480 // </div> 596 // </div>
481 // In this example "A" should be clipped if the filter was not present. 597 // In this example "A" should be clipped if the filter was not present.
482 // With the filter, "A" will be rastered without clipping, but instead 598 // With the filter, "A" will be rastered without clipping, but instead
483 // the blurred result will be clipped. 599 // the blurred result will be clipped.
484 // On the other hand, "B" should not be clipped because the overflow clip is 600 // On the other hand, "B" should not be clipped because the overflow clip is
485 // not in its containing block chain, but as the filter output will be 601 // not in its containing block chain, but as the filter output will be
486 // clipped, so a blurred "B" may still be invisible. 602 // clipped, so a blurred "B" may still be invisible.
487 if (!filter.isEmpty()) { 603 const ClipPaintPropertyNode* outputClip = context.current.clip;
488 effectNodeNeeded = true;
489 outputClip = context.current.clip;
490 604
491 // TODO(trchen): A filter may contain spatial operations such that an 605 // TODO(trchen): A filter may contain spatial operations such that an
492 // output pixel may depend on an input pixel outside of the output clip. 606 // output pixel may depend on an input pixel outside of the output clip.
493 // We should generate a special clip node to represent this expansion. 607 // We should generate a special clip node to represent this expansion.
494 }
495 608
496 CompositingReasons compositingReasons = CompositingReasonNone; 609 // We may begin to composite our subtree prior to an animation starts,
497 if (CompositingReasonFinder::requiresCompositingForEffectAnimation(style)) { 610 // but a compositor element ID is only needed when an animation is current.
498 compositingReasons = CompositingReasonActiveAnimation;
499 effectNodeNeeded = true;
500 }
501
502 CompositorElementId compositorElementId = 611 CompositorElementId compositorElementId =
503 (style.hasCurrentOpacityAnimation() || 612 style.hasCurrentFilterAnimation()
504 style.hasCurrentFilterAnimation() ||
505 style.hasCurrentBackdropFilterAnimation())
506 ? createDomNodeBasedCompositorElementId(object) 613 ? createDomNodeBasedCompositorElementId(object)
507 : CompositorElementId(); 614 : CompositorElementId();
615 CompositingReasons compositingReasons =
616 CompositingReasonFinder::requiresCompositingForFilterAnimation(style)
617 ? CompositingReasonActiveAnimation
618 : CompositingReasonNone;
619 DCHECK(!style.hasCurrentFilterAnimation() ||
620 compositingReasons != CompositingReasonNone);
508 621
509 if (effectNodeNeeded) { 622 if (compositingReasons == CompositingReasonNone && filter.isEmpty()) {
623 if (auto* properties = object.getMutableForPainting().paintProperties())
624 context.forceSubtreeUpdate |= properties->clearFilter();
625 } else {
510 auto& properties = object.getMutableForPainting().ensurePaintProperties(); 626 auto& properties = object.getMutableForPainting().ensurePaintProperties();
511 context.forceSubtreeUpdate |= properties.updateEffect( 627 context.forceSubtreeUpdate |= properties.updateFilter(
512 context.currentEffect, context.current.transform, outputClip, 628 context.currentEffect, context.current.transform, outputClip,
513 std::move(filter), opacity, blendMode, compositingReasons, 629 std::move(filter), 1.f, SkBlendMode::kSrcOver, compositingReasons,
514 compositorElementId); 630 compositorElementId);
515 } else {
516 if (auto* properties = object.getMutableForPainting().paintProperties())
517 context.forceSubtreeUpdate |= properties->clearEffect();
518 } 631 }
519 } 632 }
520 633
521 const auto* properties = object.paintProperties(); 634 const auto* properties = object.paintProperties();
522 if (properties && properties->effect()) { 635 if (properties && properties->filter()) {
523 context.currentEffect = properties->effect(); 636 context.currentEffect = properties->filter();
524 if (!properties->effect()->filter().isEmpty()) { 637 // TODO(trchen): Change input clip to expansion hint once implemented.
525 // TODO(trchen): Change input clip to expansion hint once implemented. 638 const ClipPaintPropertyNode* inputClip = properties->filter()->outputClip();
526 const ClipPaintPropertyNode* inputClip = 639 context.inputClipOfCurrentEffect = context.current.clip =
527 properties->effect()->outputClip(); 640 context.absolutePosition.clip = context.fixedPosition.clip = inputClip;
528 context.inputClipOfCurrentEffect = context.current.clip =
529 context.absolutePosition.clip = context.fixedPosition.clip =
530 inputClip;
531 }
532 } 641 }
533 } 642 }
534 643
535 void PaintPropertyTreeBuilder::updateCssClip( 644 void PaintPropertyTreeBuilder::updateCssClip(
536 const LayoutObject& object, 645 const LayoutObject& object,
537 PaintPropertyTreeBuilderContext& context) { 646 PaintPropertyTreeBuilderContext& context) {
538 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { 647 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
539 if (object.hasClip()) { 648 if (object.hasClip()) {
540 // Create clip node for descendants that are not fixed position. 649 // Create clip node for descendants that are not fixed position.
541 // We don't have to setup context.absolutePosition.clip here because this 650 // We don't have to setup context.absolutePosition.clip here because this
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after
939 void PaintPropertyTreeBuilder::updatePropertiesForSelf( 1048 void PaintPropertyTreeBuilder::updatePropertiesForSelf(
940 const LayoutObject& object, 1049 const LayoutObject& object,
941 PaintPropertyTreeBuilderContext& context) { 1050 PaintPropertyTreeBuilderContext& context) {
942 #if DCHECK_IS_ON() 1051 #if DCHECK_IS_ON()
943 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context); 1052 FindObjectPropertiesNeedingUpdateScope checkNeedsUpdateScope(object, context);
944 #endif 1053 #endif
945 1054
946 if (object.isBoxModelObject() || object.isSVG()) { 1055 if (object.isBoxModelObject() || object.isSVG()) {
947 updatePaintOffsetTranslation(object, context); 1056 updatePaintOffsetTranslation(object, context);
948 updateTransform(object, context); 1057 updateTransform(object, context);
949 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) 1058 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
950 updateEffect(object, context); 1059 updateEffect(object, context);
1060 updateFilter(object, context);
1061 }
951 updateCssClip(object, context); 1062 updateCssClip(object, context);
952 updateLocalBorderBoxContext(object, context); 1063 updateLocalBorderBoxContext(object, context);
953 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) 1064 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled())
954 updateScrollbarPaintOffset(object, context); 1065 updateScrollbarPaintOffset(object, context);
955 } 1066 }
956 1067
957 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) { 1068 if (object.needsPaintPropertyUpdate() || context.forceSubtreeUpdate) {
958 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && 1069 if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() &&
959 object.paintOffset() != context.current.paintOffset) { 1070 object.paintOffset() != context.current.paintOffset) {
960 object.getMutableForPainting().setShouldDoFullPaintInvalidation( 1071 object.getMutableForPainting().setShouldDoFullPaintInvalidation(
(...skipping 16 matching lines...) Expand all
977 updateOverflowClip(object, context); 1088 updateOverflowClip(object, context);
978 updatePerspective(object, context); 1089 updatePerspective(object, context);
979 updateSvgLocalToBorderBoxTransform(object, context); 1090 updateSvgLocalToBorderBoxTransform(object, context);
980 updateScrollAndScrollTranslation(object, context); 1091 updateScrollAndScrollTranslation(object, context);
981 updateOutOfFlowContext(object, context); 1092 updateOutOfFlowContext(object, context);
982 1093
983 context.forceSubtreeUpdate |= object.subtreeNeedsPaintPropertyUpdate(); 1094 context.forceSubtreeUpdate |= object.subtreeNeedsPaintPropertyUpdate();
984 } 1095 }
985 1096
986 } // namespace blink 1097 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698