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 "SkRectPriv.h"
9#include "SkSGNode.h"
10#include "SkSGInvalidationController.h"
11
12namespace sksg {
13
14class Node::ScopedFlag {
15public:
16    ScopedFlag(Node* node, uint32_t flag)
17        : fNode(node)
18        , fFlag(flag)
19        , fWasSet(node->fFlags & flag) {
20        node->fFlags |= flag;
21    }
22    ~ScopedFlag() {
23        if (!fWasSet) {
24            fNode->fFlags &= ~fFlag;;
25        }
26    }
27
28    bool wasSet() const { return fWasSet; }
29
30private:
31    Node*    fNode;
32    uint32_t fFlag;
33    bool     fWasSet;
34};
35
36#define TRAVERSAL_GUARD                                  \
37    ScopedFlag traversal_guard(this, kInTraversal_Flag); \
38    if (traversal_guard.wasSet())                        \
39        return
40
41Node::Node(uint32_t invalTraits)
42    : fInvalObserver(nullptr)
43    , fBounds(SkRectPriv::MakeLargeS32())
44    , fInvalTraits(invalTraits)
45    , fFlags(kInvalidated_Flag) {}
46
47Node::~Node() {
48    if (fFlags & kObserverArray_Flag) {
49        SkASSERT(fInvalObserverArray->isEmpty());
50        delete fInvalObserverArray;
51    } else {
52        SkASSERT(!fInvalObserver);
53    }
54}
55
56void Node::observeInval(const sk_sp<Node>& node) {
57    SkASSERT(node);
58    if (!(node->fFlags & kObserverArray_Flag)) {
59        if (!node->fInvalObserver) {
60            node->fInvalObserver = this;
61            return;
62        }
63
64        auto observers = new SkTDArray<Node*>();
65        observers->setReserve(2);
66        observers->push(node->fInvalObserver);
67
68        node->fInvalObserverArray = observers;
69        node->fFlags |= kObserverArray_Flag;
70    }
71
72    // No duplicate observers.
73    SkASSERT(node->fInvalObserverArray->find(this) < 0);
74
75    node->fInvalObserverArray->push(this);
76}
77
78void Node::unobserveInval(const sk_sp<Node>& node) {
79    SkASSERT(node);
80    if (!(node->fFlags & kObserverArray_Flag)) {
81        SkASSERT(node->fInvalObserver == this);
82        node->fInvalObserver = nullptr;
83        return;
84    }
85
86    const auto idx = node->fInvalObserverArray->find(this);
87    SkASSERT(idx >= 0);
88    node->fInvalObserverArray->remove(idx);
89}
90
91template <typename Func>
92void Node::forEachInvalObserver(Func&& func) const {
93    if (fFlags & kObserverArray_Flag) {
94        for (const auto& parent : *fInvalObserverArray) {
95            func(parent);
96        }
97        return;
98    }
99
100    if (fInvalObserver) {
101        func(fInvalObserver);
102    }
103}
104
105void Node::invalidate(bool damageBubbling) {
106    TRAVERSAL_GUARD;
107
108    if (this->hasInval() && (!damageBubbling || (fFlags & kDamage_Flag))) {
109        // All done.
110        return;
111    }
112
113    if (damageBubbling && !(fInvalTraits & kBubbleDamage_Trait)) {
114        // Found a damage observer.
115        fFlags |= kDamage_Flag;
116        damageBubbling = false;
117    }
118
119    fFlags |= kInvalidated_Flag;
120
121    forEachInvalObserver([&](Node* observer) {
122        observer->invalidate(damageBubbling);
123    });
124}
125
126const SkRect& Node::revalidate(InvalidationController* ic, const SkMatrix& ctm) {
127    TRAVERSAL_GUARD fBounds;
128
129    if (!this->hasInval()) {
130        return fBounds;
131    }
132
133    SkRect prevBounds;
134    if (fFlags & kDamage_Flag) {
135        prevBounds = fBounds;
136    }
137
138    fBounds = this->onRevalidate(ic, ctm);
139
140    if (fFlags & kDamage_Flag) {
141        ic->inval(prevBounds, ctm);
142        if (fBounds != prevBounds) {
143            ic->inval(fBounds, ctm);
144        }
145    }
146
147    fFlags &= ~(kInvalidated_Flag | kDamage_Flag);
148
149    return fBounds;
150}
151
152} // namespace sksg
153