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

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

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

Powered by Google App Engine
This is Rietveld 408576698