xref: /aosp_15_r20/external/skia/modules/sksg/src/SkSGGroup.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "modules/sksg/include/SkSGGroup.h"
9 
10 #include "include/core/SkCanvas.h"
11 #include "include/private/base/SkAssert.h"
12 #include "include/private/base/SkDebug.h"
13 #include "modules/sksg/include/SkSGNode.h"
14 
15 #include <algorithm>
16 
17 class SkMatrix;
18 struct SkPoint;
19 
20 namespace sksg {
21 class InvalidationController;
22 
23 Group::Group() = default;
24 
Group(std::vector<sk_sp<RenderNode>> children)25 Group::Group(std::vector<sk_sp<RenderNode>> children)
26     : fChildren(std::move(children)) {
27     for (const auto& child : fChildren) {
28         this->observeInval(child);
29     }
30 }
31 
~Group()32 Group::~Group() {
33     for (const auto& child : fChildren) {
34         this->unobserveInval(child);
35     }
36 }
37 
clear()38 void Group::clear() {
39     for (const auto& child : fChildren) {
40         this->unobserveInval(child);
41     }
42     fChildren.clear();
43 }
44 
addChild(sk_sp<RenderNode> node)45 void Group::addChild(sk_sp<RenderNode> node) {
46     // should we allow duplicates?
47     for (const auto& child : fChildren) {
48         if (child == node) {
49             return;
50         }
51     }
52 
53     this->observeInval(node);
54     fChildren.push_back(std::move(node));
55 
56     this->invalidate();
57 }
58 
removeChild(const sk_sp<RenderNode> & node)59 void Group::removeChild(const sk_sp<RenderNode>& node) {
60     SkDEBUGCODE(const auto origSize = fChildren.size());
61     fChildren.erase(std::remove(fChildren.begin(), fChildren.end(), node), fChildren.end());
62     SkASSERT(fChildren.size() == origSize - 1);
63 
64     this->unobserveInval(node);
65     this->invalidate();
66 }
67 
onRender(SkCanvas * canvas,const RenderContext * ctx) const68 void Group::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
69     const auto local_ctx = ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
70                                                                          canvas->getTotalMatrix(),
71                                                                          fRequiresIsolation);
72 
73     for (const auto& child : fChildren) {
74         child->render(canvas, local_ctx);
75     }
76 }
77 
onNodeAt(const SkPoint & p) const78 const RenderNode* Group::onNodeAt(const SkPoint& p) const {
79     for (auto it = fChildren.crbegin(); it != fChildren.crend(); ++it) {
80         if (const auto* node = (*it)->nodeAt(p)) {
81             return node;
82         }
83     }
84 
85     return nullptr;
86 }
87 
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)88 SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
89     SkASSERT(this->hasInval());
90 
91     SkRect bounds = SkRect::MakeEmpty();
92     fRequiresIsolation = false;
93 
94     for (size_t i = 0; i < fChildren.size(); ++i) {
95         const auto child_bounds = fChildren[i]->revalidate(ic, ctm);
96 
97         // If any of the child nodes overlap, group effects require layer isolation.
98         if (!fRequiresIsolation && i > 0 && child_bounds.intersects(bounds)) {
99 #if 1
100             // Testing conservatively against the union of prev bounds is cheap and good enough.
101             fRequiresIsolation = true;
102 #else
103             // Testing exhaustively doesn't seem to increase the layer elision rate in practice.
104             for (size_t j = 0; j < i; ++ j) {
105                 if (child_bounds.intersects(fChildren[i]->bounds())) {
106                     fRequiresIsolation = true;
107                     break;
108                 }
109             }
110 #endif
111         }
112 
113         bounds.join(child_bounds);
114     }
115 
116     return bounds;
117 }
118 
119 } // namespace sksg
120