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

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

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

Powered by Google App Engine
This is Rietveld 408576698