Flutter Engine
The Flutter Engine
Classes | Public Member Functions | List of all members
flutter::DlRTree Class Reference

#include <dl_rtree.h>

Inheritance diagram for flutter::DlRTree:
SkRefCnt SkRefCntBase

Public Member Functions

 DlRTree (const SkRect rects[], int N, const int ids[]=nullptr, bool predicate(int id)=[](int) { return true;}, int invalid_id=-1)
 
void search (const SkRect &query, std::vector< int > *results) const
 
int id (int result_index) const
 
const SkRectbounds () const
 
const SkRectbounds (int result_index) const
 
size_t bytes_used () const
 Returns the bytes used by the object and all of its node data. More...
 
int leaf_count () const
 
int node_count () const
 
std::list< SkRectsearchAndConsolidateRects (const SkRect &query, bool deband=true) const
 
const DlRegionregion () const
 
DlRegion region (const SkRect &query) const
 
- Public Member Functions inherited from SkRefCntBase
 SkRefCntBase ()
 
virtual ~SkRefCntBase ()
 
bool unique () const
 
void ref () const
 
void unref () const
 

Detailed Description

An R-Tree that stores a list of bounding rectangles with optional associated IDs.

The R-Tree can be searched in one of two ways:

Definition at line 29 of file dl_rtree.h.

Constructor & Destructor Documentation

◆ DlRTree()

flutter::DlRTree::DlRTree ( const SkRect  rects[],
int  N,
const int  ids[] = nullptr,
bool   predicateint id = [](int) { return true; },
int  invalid_id = -1 
)

Construct an R-Tree from the list of rectangles respecting the order in which they appear in the list. An optional array of IDs can be provided to tag each rectangle with information needed by the caller as well as an optional predicate that can filter out rectangles with IDs that should not be stored in the R-Tree.

If an array of IDs is not specified then all leaf nodes will be represented by the |invalid_id| (which defaults to -1).

Invalid rects that are empty or contain a NaN value will not be stored in the R-Tree. And, if a |predicate| function is provided, that function will be called to query if the rectangle associated with the ID should be included.

Duplicate rectangles and IDs are allowed and not processed in any way except to eliminate invalid rectangles and IDs that are rejected by the optional predicate function.

Definition at line 12 of file dl_rtree.cc.

17 : invalid_id_(invalid_id) {
18 if (N <= 0) {
19 FML_DCHECK(N >= 0);
20 return;
21 }
22 FML_DCHECK(rects != nullptr);
23
24 // Count the number of rectangles we actually want to track,
25 // which includes only non-empty rectangles whose optional
26 // ID is not filtered by the predicate.
27 int leaf_count = 0;
28 for (int i = 0; i < N; i++) {
29 if (!rects[i].isEmpty()) {
30 if (ids == nullptr || p(ids[i])) {
31 leaf_count++;
32 }
33 }
34 }
35 leaf_count_ = leaf_count;
36
37 // Count the total number of nodes (leaf and internal) up front
38 // so we can resize the vector just once.
39 uint32_t total_node_count = leaf_count;
40 uint32_t gen_count = leaf_count;
41 while (gen_count > 1) {
42 uint32_t family_count = (gen_count + kMaxChildren - 1u) / kMaxChildren;
43 total_node_count += family_count;
44 gen_count = family_count;
45 }
46
47 nodes_.resize(total_node_count);
48
49 // Now place only the tracked rectangles into the nodes array
50 // in the first leaf_count_ entries.
51 int leaf_index = 0;
52 int id = invalid_id;
53 for (int i = 0; i < N; i++) {
54 if (!rects[i].isEmpty()) {
55 if (ids == nullptr || p(id = ids[i])) {
56 Node& node = nodes_[leaf_index++];
57 node.bounds = rects[i];
58 node.id = id;
59 }
60 }
61 }
62 FML_DCHECK(leaf_index == leaf_count);
63
64 // --- Implementation note ---
65 // Many R-Tree algorithms attempt to consolidate nearby rectangles
66 // into branches of the tree in order to maximize the benefit of
67 // bounds testing against whole sub-trees. The Skia code from which
68 // this was based, though, indicated that empirical tests against a
69 // browser client showed little gains in rendering performance while
70 // costing 17% performance in bulk loading the rects into the R-Tree:
71 // https://github.com/google/skia/blob/12b6bd042f7cdffb9012c90c3b4885601fc7be95/src/core/SkRTree.cpp#L96
72 //
73 // Given that this class will most often be used to track rendering
74 // operations that were drawn in an app that performs a type of
75 // "page layout" with rendering proceeding in a linear fashion from
76 // top to bottom (and left to right or right to left), the rectangles
77 // are likely nearly sorted when they are delivered to this constructor
78 // so leaving them in their original order should show similar results
79 // to what Skia found in their empirical browser tests.
80 // ---
81
82 // Continually process the previous level (generation) of nodes,
83 // combining them into a new generation of parent groups each grouping
84 // at most |kMaxChildren| children and joining their bounds into its
85 // parent bounds.
86 // Each generation will end up reduced by a factor of up to kMaxChildren
87 // until there is just one node left, which is the root node of
88 // the R-Tree.
89 uint32_t gen_start = 0;
90 gen_count = leaf_count;
91 while (gen_count > 1) {
92 uint32_t gen_end = gen_start + gen_count;
93
94 uint32_t family_count = (gen_count + kMaxChildren - 1u) / kMaxChildren;
95 FML_DCHECK(gen_end + family_count <= total_node_count);
96
97 // D here is similar to the variable in a Bresenham line algorithm where
98 // we want to slowly move |family_count| steps along the minor axis as
99 // we move |gen_count| steps along the major axis.
100 //
101 // Each inner loop increments D by family_count.
102 // The inner loop executes a total of gen_count times.
103 // Every time D exceeds 0 we subtract gen_count and move to a new parent.
104 // All told we will increment D by family_count a total of gen_count times.
105 // All told we will decrement D by gen_count a total of family_count times.
106 // This leaves D back at its starting value.
107 //
108 // We could bias/balance where the extra children are placed by varying
109 // the initial count of D from 0 to (1 - family_count), but we aren't
110 // looking at this process aesthetically so we just use 0 as an initial
111 // value. Using 0 provides a "greedy" allocation of the extra children.
112 // Bresenham also uses double the size of the steps we use here also to
113 // have better rounding of when the minor axis steps occur, but again we
114 // don't care about the distribution of the extra children.
115 int D = 0;
116
117 uint32_t sibling_index = gen_start;
118 uint32_t parent_index = gen_end;
119 Node* parent = nullptr;
120 while (sibling_index < gen_end) {
121 if ((D += family_count) > 0) {
122 D -= gen_count;
123 FML_DCHECK(parent_index < gen_end + family_count);
124 parent = &nodes_[parent_index++];
125 parent->bounds.setEmpty();
126 parent->child.index = sibling_index;
127 parent->child.count = 0;
128 }
129 FML_DCHECK(parent != nullptr);
130 parent->bounds.join(nodes_[sibling_index++].bounds);
131 parent->child.count++;
132 }
133 FML_DCHECK(D == 0);
134 FML_DCHECK(sibling_index == gen_end);
135 FML_DCHECK(parent_index == gen_end + family_count);
136 gen_start = gen_end;
137 gen_count = family_count;
138 }
139 FML_DCHECK(gen_start + gen_count == total_node_count);
140}
#define N
Definition: beziers.cpp:19
int id(int result_index) const
Definition: dl_rtree.h:85
const SkRect & bounds() const
Definition: dl_rtree.cc:216
int leaf_count() const
Definition: dl_rtree.h:111
#define FML_DCHECK(condition)
Definition: logging.h:103
Definition: dart.idl:29
char id() const

Member Function Documentation

◆ bounds() [1/2]

const SkRect & flutter::DlRTree::bounds ( ) const

Returns maximum and minimum axis values of rectangles in this R-Tree. If R-Tree is empty returns an empty SkRect.

Definition at line 216 of file dl_rtree.cc.

216 {
217 if (!nodes_.empty()) {
218 return nodes_.back().bounds;
219 } else {
220 return kEmpty;
221 }
222}

◆ bounds() [2/2]

const SkRect & flutter::DlRTree::bounds ( int  result_index) const
inline

Return the rectangle bounds for the indicated result of a query or an empty rect if the index is not a valid leaf node index.

Definition at line 97 of file dl_rtree.h.

97 {
98 return (result_index >= 0 && result_index < leaf_count_)
99 ? nodes_[result_index].bounds
100 : kEmpty;
101 }

◆ bytes_used()

size_t flutter::DlRTree::bytes_used ( ) const
inline

Returns the bytes used by the object and all of its node data.

Definition at line 104 of file dl_rtree.h.

104 {
105 return sizeof(DlRTree) + sizeof(Node) * nodes_.size();
106 }
DlRTree(const SkRect rects[], int N, const int ids[]=nullptr, bool predicate(int id)=[](int) { return true;}, int invalid_id=-1)
Definition: dl_rtree.cc:12

◆ id()

int flutter::DlRTree::id ( int  result_index) const
inline

Return the ID for the indicated result of a query or invalid_id if the index is not a valid leaf node index.

Definition at line 85 of file dl_rtree.h.

85 {
86 return (result_index >= 0 && result_index < leaf_count_)
87 ? nodes_[result_index].id
88 : invalid_id_;
89 }

◆ leaf_count()

int flutter::DlRTree::leaf_count ( ) const
inline

Returns the number of leaf nodes corresponding to non-empty rectangles that were passed in the constructor and not filtered out by the predicate.

Definition at line 111 of file dl_rtree.h.

111{ return leaf_count_; }

◆ node_count()

int flutter::DlRTree::node_count ( ) const
inline

Return the total number of nodes used in the R-Tree, both leaf and internal consolidation nodes.

Definition at line 115 of file dl_rtree.h.

115{ return nodes_.size(); }

◆ region() [1/2]

const DlRegion & flutter::DlRTree::region ( ) const

Returns DlRegion that represents the union of all rectangles in the R-Tree.

Definition at line 204 of file dl_rtree.cc.

204 {
205 if (!region_) {
206 std::vector<SkIRect> rects;
207 rects.resize(leaf_count_);
208 for (int i = 0; i < leaf_count_; i++) {
209 nodes_[i].bounds.roundOut(&rects[i]);
210 }
211 region_.emplace(rects);
212 }
213 return *region_;
214}

◆ region() [2/2]

DlRegion flutter::DlRTree::region ( const SkRect query) const
inline

Returns DlRegion that represents the union of all rectangles in the R-Tree intersected with the query rect.

Definition at line 135 of file dl_rtree.h.

135 {
136 return DlRegion::MakeIntersection(region(), DlRegion(query.roundOut()));
137 }
const DlRegion & region() const
Definition: dl_rtree.cc:204
static DlRegion MakeIntersection(const DlRegion &a, const DlRegion &b)
Definition: dl_region.cc:497
void roundOut(SkIRect *dst) const
Definition: SkRect.h:1241

◆ search()

void flutter::DlRTree::search ( const SkRect query,
std::vector< int > *  results 
) const

Search the rectangles and return a vector of leaf node indices for rectangles that intersect the query.

Note that the indices are internal indices of the stored data and not the index of the rectangles or ids in the constructor. The returned indices may not themselves be in numerical order, but they will represent the rectangles and IDs in the order in which they were passed into the constructor. The actual rectangle and ID associated with each index can be retrieved using the |DlRTreeid| and |DlRTree::bounds| methods.

Definition at line 142 of file dl_rtree.cc.

142 {
143 FML_DCHECK(results != nullptr);
144 if (query.isEmpty()) {
145 return;
146 }
147 if (nodes_.size() <= 0) {
148 FML_DCHECK(leaf_count_ == 0);
149 return;
150 }
151 const Node& root = nodes_.back();
152 if (root.bounds.intersects(query)) {
153 if (nodes_.size() == 1) {
154 FML_DCHECK(leaf_count_ == 1);
155 // The root node is the only node and it is a leaf node
156 results->push_back(0);
157 } else {
158 search(root, query, results);
159 }
160 }
161}
void search(const SkRect &query, std::vector< int > *results) const
Definition: dl_rtree.cc:142
string root
Definition: scale_cpu.py:20
bool isEmpty() const
Definition: SkRect.h:693

◆ searchAndConsolidateRects()

std::list< SkRect > flutter::DlRTree::searchAndConsolidateRects ( const SkRect query,
bool  deband = true 
) const

Finds the rects in the tree that intersect with the query rect.

The returned list of rectangles will be non-overlapping. In other words, the bounds of each rect in the result list are mutually exclusive.

If |deband| is true, then matching rectangles from adjacent DlRegion spanlines will be joined together. This reduces the number of rectangles returned, but requires some extra computation.

Definition at line 163 of file dl_rtree.cc.

164 {
165 // Get the indexes for the operations that intersect with the query rect.
166 std::vector<int> intermediary_results;
167 search(query, &intermediary_results);
168
169 std::vector<SkIRect> rects;
170 rects.reserve(intermediary_results.size());
171 for (int index : intermediary_results) {
172 SkIRect current_record_rect;
173 bounds(index).roundOut(&current_record_rect);
174 rects.push_back(current_record_rect);
175 }
176 DlRegion region(rects);
177
178 auto non_overlapping_rects = region.getRects(deband);
179 std::list<SkRect> final_results;
180 for (const auto& rect : non_overlapping_rects) {
181 final_results.push_back(SkRect::Make(rect));
182 }
183 return final_results;
184}
std::vector< SkIRect > getRects(bool deband=true) const
Definition: dl_region.cc:563
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
Definition: SkRect.h:32
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669

The documentation for this class was generated from the following files: