• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
12 #include <algorithm>
13 
14 namespace sksg {
15 
16 Group::Group() = default;
17 
Group(std::vector<sk_sp<RenderNode>> children)18 Group::Group(std::vector<sk_sp<RenderNode>> children)
19     : fChildren(std::move(children)) {
20     for (const auto& child : fChildren) {
21         this->observeInval(child);
22     }
23 }
24 
~Group()25 Group::~Group() {
26     for (const auto& child : fChildren) {
27         this->unobserveInval(child);
28     }
29 }
30 
clear()31 void Group::clear() {
32     for (const auto& child : fChildren) {
33         this->unobserveInval(child);
34     }
35     fChildren.clear();
36 }
37 
addChild(sk_sp<RenderNode> node)38 void Group::addChild(sk_sp<RenderNode> node) {
39     // should we allow duplicates?
40     for (const auto& child : fChildren) {
41         if (child == node) {
42             return;
43         }
44     }
45 
46     this->observeInval(node);
47     fChildren.push_back(std::move(node));
48 
49     this->invalidate();
50 }
51 
removeChild(const sk_sp<RenderNode> & node)52 void Group::removeChild(const sk_sp<RenderNode>& node) {
53     SkDEBUGCODE(const auto origSize = fChildren.size());
54     fChildren.erase(std::remove(fChildren.begin(), fChildren.end(), node), fChildren.end());
55     SkASSERT(fChildren.size() == origSize - 1);
56 
57     this->unobserveInval(node);
58     this->invalidate();
59 }
60 
onRender(SkCanvas * canvas,const RenderContext * ctx) const61 void Group::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
62     const auto local_ctx = ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(),
63                                                                          canvas->getTotalMatrix(),
64                                                                          fRequiresIsolation);
65 
66     for (const auto& child : fChildren) {
67         child->render(canvas, local_ctx);
68     }
69 }
70 
onNodeAt(const SkPoint & p) const71 const RenderNode* Group::onNodeAt(const SkPoint& p) const {
72     for (auto it = fChildren.crbegin(); it != fChildren.crend(); ++it) {
73         if (const auto* node = (*it)->nodeAt(p)) {
74             return node;
75         }
76     }
77 
78     return nullptr;
79 }
80 
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)81 SkRect Group::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
82     SkASSERT(this->hasInval());
83 
84     SkRect bounds = SkRect::MakeEmpty();
85     fRequiresIsolation = false;
86 
87     for (size_t i = 0; i < fChildren.size(); ++i) {
88         const auto child_bounds = fChildren[i]->revalidate(ic, ctm);
89 
90         // If any of the child nodes overlap, group effects require layer isolation.
91         if (!fRequiresIsolation && i > 0 && child_bounds.intersects(bounds)) {
92 #if 1
93             // Testing conservatively against the union of prev bounds is cheap and good enough.
94             fRequiresIsolation = true;
95 #else
96             // Testing exhaustively doesn't seem to increase the layer elision rate in practice.
97             for (size_t j = 0; j < i; ++ j) {
98                 if (child_bounds.intersects(fChildren[i]->bounds())) {
99                     fRequiresIsolation = true;
100                     break;
101                 }
102             }
103 #endif
104         }
105 
106         bounds.join(child_bounds);
107     }
108 
109     return bounds;
110 }
111 
112 } // namespace sksg
113