Flutter Engine
The Flutter Engine
dl_rtree_unittests.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/display_list/geometry/dl_rtree.h"
6#include "gtest/gtest.h"
7
9
10namespace flutter {
11namespace testing {
12
13#ifndef NDEBUG
14TEST(DisplayListRTree, NullRectListNonZeroCount) {
15 EXPECT_DEATH_IF_SUPPORTED(new DlRTree(nullptr, 1), "rects != nullptr");
16}
17
18TEST(DisplayListRTree, NegativeCount) {
19 EXPECT_DEATH_IF_SUPPORTED(new DlRTree(nullptr, -1), "N >= 0");
20}
21
22TEST(DisplayListRTree, NullSearchResultVector) {
23 DlRTree tree(nullptr, 0);
24 EXPECT_DEATH_IF_SUPPORTED(tree.search(SkRect::MakeLTRB(0, 0, 1, 1), nullptr),
25 "results != nullptr");
26}
27#endif
28
29TEST(DisplayListRTree, NullRectListZeroCount) {
30 DlRTree tree(nullptr, 0);
31 EXPECT_EQ(tree.leaf_count(), 0);
32 EXPECT_EQ(tree.node_count(), 0);
33 std::vector<int> results;
34 auto huge = SkRect::MakeLTRB(-1e6, -1e6, 1e6, 1e6);
35 tree.search(huge, &results);
36 EXPECT_EQ(results.size(), 0u);
37 auto list = tree.searchAndConsolidateRects(huge);
38 EXPECT_EQ(list.size(), 0u);
39}
40
41TEST(DisplayListRTree, ManySizes) {
42 // A diagonal of non-overlapping 10x10 rectangles spaced 20
43 // pixels apart.
44 // Rect 1 goes from 0 to 10
45 // Rect 2 goes from 20 to 30
46 // etc. in both dimensions
47 const int kMaxN = 250;
48 SkRect rects[kMaxN + 1];
49 int ids[kMaxN + 1];
50 for (int i = 0; i <= kMaxN; i++) {
51 rects[i].setXYWH(i * 20, i * 20, 10, 10);
52 ids[i] = i + 42;
53 }
54 std::vector<int> results;
55 for (int N = 0; N <= kMaxN; N++) {
56 DlRTree tree(rects, N, ids);
57 auto desc = "node count = " + std::to_string(N);
58 EXPECT_EQ(tree.leaf_count(), N) << desc;
59 EXPECT_GE(tree.node_count(), N) << desc;
60 EXPECT_EQ(tree.id(-1), -1) << desc;
61 EXPECT_EQ(tree.bounds(-1), SkRect::MakeEmpty()) << desc;
62 EXPECT_EQ(tree.id(N), -1) << desc;
63 EXPECT_EQ(tree.bounds(N), SkRect::MakeEmpty()) << desc;
64 results.clear();
65 tree.search(SkRect::MakeEmpty(), &results);
66 EXPECT_EQ(results.size(), 0u) << desc;
67 results.clear();
68 tree.search(SkRect::MakeLTRB(2, 2, 8, 8), &results);
69 if (N == 0) {
70 EXPECT_EQ(results.size(), 0u) << desc;
71 } else {
72 EXPECT_EQ(results.size(), 1u) << desc;
73 EXPECT_EQ(results[0], 0) << desc;
74 EXPECT_EQ(tree.id(results[0]), ids[0]) << desc;
75 EXPECT_EQ(tree.bounds(results[0]), rects[0]) << desc;
76 for (int i = 1; i < N; i++) {
77 results.clear();
78 auto query = SkRect::MakeXYWH(i * 20 + 2, i * 20 + 2, 6, 6);
79 tree.search(query, &results);
80 EXPECT_EQ(results.size(), 1u) << desc;
81 EXPECT_EQ(results[0], i) << desc;
82 EXPECT_EQ(tree.id(results[0]), ids[i]) << desc;
83 EXPECT_EQ(tree.bounds(results[0]), rects[i]) << desc;
84 auto list = tree.searchAndConsolidateRects(query);
85 EXPECT_EQ(list.size(), 1u);
86 EXPECT_EQ(list.front(), rects[i]);
87 }
88 }
89 }
90}
91
92TEST(DisplayListRTree, HugeSize) {
93 // A diagonal of non-overlapping 10x10 rectangles spaced 20
94 // pixels apart.
95 // Rect 1 goes from 0 to 10
96 // Rect 2 goes from 20 to 30
97 // etc. in both dimensions
98 const int N = 10000;
99 SkRect rects[N];
100 int ids[N];
101 for (int i = 0; i < N; i++) {
102 rects[i].setXYWH(i * 20, i * 20, 10, 10);
103 ids[i] = i + 42;
104 }
105 DlRTree tree(rects, N, ids);
106 EXPECT_EQ(tree.leaf_count(), N);
107 EXPECT_GE(tree.node_count(), N);
108 EXPECT_EQ(tree.id(-1), -1);
109 EXPECT_EQ(tree.bounds(-1), SkRect::MakeEmpty());
110 EXPECT_EQ(tree.id(N), -1);
111 EXPECT_EQ(tree.bounds(N), SkRect::MakeEmpty());
112 std::vector<int> results;
113 tree.search(SkRect::MakeEmpty(), &results);
114 EXPECT_EQ(results.size(), 0u);
115 for (int i = 0; i < N; i++) {
116 results.clear();
117 tree.search(SkRect::MakeXYWH(i * 20 + 2, i * 20 + 2, 6, 6), &results);
118 EXPECT_EQ(results.size(), 1u);
119 EXPECT_EQ(results[0], i);
120 EXPECT_EQ(tree.id(results[0]), ids[i]);
121 EXPECT_EQ(tree.bounds(results[0]), rects[i]);
122 }
123}
124
125TEST(DisplayListRTree, Grid) {
126 // Non-overlapping 10 x 10 rectangles starting at 5, 5 with
127 // 10 pixels between them.
128 // Rect 1 goes from 5 to 15
129 // Rect 2 goes from 25 to 35
130 // etc. in both dimensions
131 const int ROWS = 10;
132 const int COLS = 10;
133 const int N = ROWS * COLS;
134 SkRect rects[N];
135 int ids[N];
136 for (int r = 0; r < ROWS; r++) {
137 int y = r * 20 + 5;
138 for (int c = 0; c < COLS; c++) {
139 int x = c * 20 + 5;
140 int i = r * COLS + c;
141 rects[i] = SkRect::MakeXYWH(x, y, 10, 10);
142 ids[i] = i + 42;
143 }
144 }
145 DlRTree tree(rects, N, ids);
146 EXPECT_EQ(tree.leaf_count(), N);
147 EXPECT_GE(tree.node_count(), N);
148 EXPECT_EQ(tree.id(-1), -1);
149 EXPECT_EQ(tree.bounds(-1), SkRect::MakeEmpty());
150 EXPECT_EQ(tree.id(N), -1);
151 EXPECT_EQ(tree.bounds(N), SkRect::MakeEmpty());
152 std::vector<int> results;
153 tree.search(SkRect::MakeEmpty(), &results);
154 EXPECT_EQ(results.size(), 0u);
155 // Testing eqch rect for a single hit
156 for (int r = 0; r < ROWS; r++) {
157 int y = r * 20 + 5;
158 for (int c = 0; c < COLS; c++) {
159 int x = c * 20 + 5;
160 int i = r * COLS + c;
161 auto desc =
162 "row " + std::to_string(r + 1) + ", col " + std::to_string(c + 1);
163 results.clear();
164 auto query = SkRect::MakeXYWH(x + 2, y + 2, 6, 6);
165 tree.search(query, &results);
166 EXPECT_EQ(results.size(), 1u) << desc;
167 EXPECT_EQ(results[0], i) << desc;
168 EXPECT_EQ(tree.id(results[0]), ids[i]) << desc;
169 EXPECT_EQ(tree.bounds(results[0]), rects[i]) << desc;
170 auto list = tree.searchAndConsolidateRects(query);
171 EXPECT_EQ(list.size(), 1u);
172 EXPECT_EQ(list.front(), rects[i]);
173 }
174 }
175 // Testing inside each gap for no hits
176 for (int r = 1; r < ROWS; r++) {
177 int y = r * 20 + 5;
178 for (int c = 1; c < COLS; c++) {
179 int x = c * 20 + 5;
180 auto desc =
181 "row " + std::to_string(r + 1) + ", col " + std::to_string(c + 1);
182 results.clear();
183 auto query = SkRect::MakeXYWH(x - 8, y - 8, 6, 6);
184 tree.search(query, &results);
185 EXPECT_EQ(results.size(), 0u) << desc;
186 auto list = tree.searchAndConsolidateRects(query);
187 EXPECT_EQ(list.size(), 0u) << desc;
188 }
189 }
190 // Spanning each gap for a quad of hits
191 for (int r = 1; r < ROWS; r++) {
192 int y = r * 20 + 5;
193 for (int c = 1; c < COLS; c++) {
194 int x = c * 20 + 5;
195 // We will hit this rect and the ones above/left of us
196 int i = r * COLS + c;
197 auto desc =
198 "row " + std::to_string(r + 1) + ", col " + std::to_string(c + 1);
199 results.clear();
200 auto query = SkRect::MakeXYWH(x - 11, y - 11, 12, 12);
201 tree.search(query, &results);
202 EXPECT_EQ(results.size(), 4u) << desc;
203
204 // First rect is above and to the left
205 EXPECT_EQ(results[0], i - COLS - 1) << desc;
206 EXPECT_EQ(tree.id(results[0]), ids[i - COLS - 1]) << desc;
207 EXPECT_EQ(tree.bounds(results[0]), rects[i - COLS - 1]) << desc;
208
209 // Second rect is above
210 EXPECT_EQ(results[1], i - COLS) << desc;
211 EXPECT_EQ(tree.id(results[1]), ids[i - COLS]) << desc;
212 EXPECT_EQ(tree.bounds(results[1]), rects[i - COLS]) << desc;
213
214 // Third rect is left
215 EXPECT_EQ(results[2], i - 1) << desc;
216 EXPECT_EQ(tree.id(results[2]), ids[i - 1]) << desc;
217 EXPECT_EQ(tree.bounds(results[2]), rects[i - 1]) << desc;
218
219 // Fourth rect is us
220 EXPECT_EQ(results[3], i) << desc;
221 EXPECT_EQ(tree.id(results[3]), ids[i]) << desc;
222 EXPECT_EQ(tree.bounds(results[3]), rects[i]) << desc;
223
224 auto list = tree.searchAndConsolidateRects(query);
225 EXPECT_EQ(list.size(), 4u);
226 list.remove(rects[i - COLS - 1]);
227 list.remove(rects[i - COLS]);
228 list.remove(rects[i - 1]);
229 list.remove(rects[i]);
230 EXPECT_EQ(list.size(), 0u);
231 }
232 }
233}
234
235TEST(DisplayListRTree, OverlappingRects) {
236 // Rectangles are centered at coordinates 15, 35, and 55 and are 15 wide
237 // This gives them 10 pixels of overlap with the rectangles on either
238 // side of them and the 10 pixels around their center coordinate are
239 // exclusive to themselves.
240 // So, horizontally and vertically, they cover the following ranges:
241 // First row/col: 0 to 30
242 // Second row/col: 20 to 50
243 // Third row/col: 40 to 70
244 // Coords 0 to 20 are only the first row/col
245 // Coords 20 to 30 are both first and second row/col
246 // Coords 30 to 40 are only the second row/col
247 // Coords 40 to 50 are both second and third row/col
248 // Coords 50 to 70 are only the third row/col
249 //
250 // In either dimension:
251 // 0------------------20--------30--------40--------50------------------70
252 // | rect1 |
253 // | 1 & 2 |
254 // | rect2 |
255 // | 2 & 3 |
256 // | rect3 |
257 SkRect rects[9];
258 for (int r = 0; r < 3; r++) {
259 int y = 15 + 20 * r;
260 for (int c = 0; c < 3; c++) {
261 int x = 15 + 20 * c;
262 rects[r * 3 + c].setLTRB(x - 15, y - 15, x + 15, y + 15);
263 }
264 }
265 DlRTree tree(rects, 9);
266 // Tiny rects only intersecting a single source rect
267 for (int r = 0; r < 3; r++) {
268 int y = 15 + 20 * r;
269 for (int c = 0; c < 3; c++) {
270 int x = 15 + 20 * c;
271 auto query = SkRect::MakeLTRB(x - 1, y - 1, x + 1, y + 1);
272 auto list = tree.searchAndConsolidateRects(query);
273 EXPECT_EQ(list.size(), 1u);
274 EXPECT_EQ(list.front(), rects[r * 3 + c]);
275 }
276 }
277 // Wide rects intersecting 3 source rects horizontally
278 for (int r = 0; r < 3; r++) {
279 int c = 1;
280 int y = 15 + 20 * r;
281 int x = 15 + 20 * c;
282 auto query = SkRect::MakeLTRB(x - 6, y - 1, x + 6, y + 1);
283 auto list = tree.searchAndConsolidateRects(query);
284 EXPECT_EQ(list.size(), 1u);
285 EXPECT_EQ(list.front(), SkRect::MakeLTRB(x - 35, y - 15, x + 35, y + 15));
286 }
287 // Tall rects intersecting 3 source rects vertically
288 for (int c = 0; c < 3; c++) {
289 int r = 1;
290 int x = 15 + 20 * c;
291 int y = 15 + 20 * r;
292 auto query = SkRect::MakeLTRB(x - 1, y - 6, x + 1, y + 6);
293 auto list = tree.searchAndConsolidateRects(query);
294 EXPECT_EQ(list.size(), 1u);
295 EXPECT_EQ(list.front(), SkRect::MakeLTRB(x - 15, y - 35, x + 15, y + 35));
296 }
297 // Finally intersecting all 9 rects
298 auto query = SkRect::MakeLTRB(35 - 6, 35 - 6, 35 + 6, 35 + 6);
299 auto list = tree.searchAndConsolidateRects(query);
300 EXPECT_EQ(list.size(), 1u);
301 EXPECT_EQ(list.front(), SkRect::MakeLTRB(0, 0, 70, 70));
302}
303
304TEST(DisplayListRTree, Region) {
305 SkRect rect[9];
306 for (int i = 0; i < 9; i++) {
307 rect[i] = SkRect::MakeXYWH(i * 10, i * 10, 20, 20);
308 }
309 DlRTree rtree(rect, 9);
310 const auto& region = rtree.region();
311 auto rects = region.getRects(true);
312 std::vector<SkIRect> expected_rects{
313 SkIRect::MakeLTRB(0, 0, 20, 10), SkIRect::MakeLTRB(0, 10, 30, 20),
314 SkIRect::MakeLTRB(10, 20, 40, 30), SkIRect::MakeLTRB(20, 30, 50, 40),
315 SkIRect::MakeLTRB(30, 40, 60, 50), SkIRect::MakeLTRB(40, 50, 70, 60),
316 SkIRect::MakeLTRB(50, 60, 80, 70), SkIRect::MakeLTRB(60, 70, 90, 80),
317 SkIRect::MakeLTRB(70, 80, 100, 90), SkIRect::MakeLTRB(80, 90, 100, 100),
318 };
319 EXPECT_EQ(rects.size(), expected_rects.size());
320}
321
322} // namespace testing
323} // namespace flutter
#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 node_count() const
Definition: dl_rtree.h:115
void search(const SkRect &query, std::vector< int > *results) const
Definition: dl_rtree.cc:142
const DlRegion & region() const
Definition: dl_rtree.cc:204
int leaf_count() const
Definition: dl_rtree.h:111
std::list< SkRect > searchAndConsolidateRects(const SkRect &query, bool deband=true) const
Definition: dl_rtree.cc:163
double y
double x
ClipOpAndAA opAA SkRegion region
Definition: SkRecords.h:238
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
TEST(DisplayListComplexity, EmptyDisplayList)
static SkString to_string(int n)
Definition: nanobench.cpp:119
static constexpr SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b)
Definition: SkRect.h:91
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595
void setXYWH(float x, float y, float width, float height)
Definition: SkRect.h:931
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
void setLTRB(float left, float top, float right, float bottom)
Definition: SkRect.h:865
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition: SkRect.h:646