Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit dd54c47

Browse files
author
Emmanuel Garcia
committed
Implement searchRects method
1 parent 7800778 commit dd54c47

File tree

3 files changed

+184
-12
lines changed

3 files changed

+184
-12
lines changed

third_party/skia/platform_view_rtree.cc

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
#include "skia/platform_view_rtree.h"
99

10-
SkRTree::SkRTree() : fCount(0) {}
10+
PlatformViewRTree::PlatformViewRTree() : fCount(0) {}
1111

12-
void SkRTree::insert(const SkRect boundsArray[],
12+
void PlatformViewRTree::insert(const SkRect boundsArray[],
1313
const SkBBoxHierarchy::Metadata metadata[],
1414
int N) {
1515
SkASSERT(0 == fCount);
@@ -30,6 +30,7 @@ void SkRTree::insert(const SkRect boundsArray[],
3030
Branch b;
3131
b.fBounds = bounds;
3232
b.fOpIndex = i;
33+
b.isDraw = (metadata == nullptr) ? false : metadata[i].isDraw;
3334
branches.push_back(b);
3435
}
3536

@@ -48,11 +49,11 @@ if (fCount) {
4849
}
4950
}
5051

51-
void SkRTree::insert(const SkRect boundsArray[], int N) {
52+
void PlatformViewRTree::insert(const SkRect boundsArray[], int N) {
5253
insert(boundsArray, nullptr, N);
5354
}
5455

55-
SkRTree::Node* SkRTree::allocateNodeAtLevel(uint16_t level) {
56+
PlatformViewRTree::Node* PlatformViewRTree::allocateNodeAtLevel(uint16_t level) {
5657
SkDEBUGCODE(Node* p = fNodes.data());
5758
fNodes.push_back(Node{});
5859
Node& out = fNodes.back();
@@ -64,7 +65,7 @@ SkRTree::Node* SkRTree::allocateNodeAtLevel(uint16_t level) {
6465

6566
// This function parallels bulkLoad, but just counts how many nodes bulkLoad
6667
// would allocate.
67-
int SkRTree::CountNodes(int branches) {
68+
int PlatformViewRTree::CountNodes(int branches) {
6869
if (branches == 1) {
6970
return 1;
7071
}
@@ -100,7 +101,7 @@ int SkRTree::CountNodes(int branches) {
100101
return nodes + CountNodes(nodes);
101102
}
102103

103-
SkRTree::Branch SkRTree::bulkLoad(std::vector<Branch>* branches, int level) {
104+
PlatformViewRTree::Branch PlatformViewRTree::bulkLoad(std::vector<Branch>* branches, int level) {
104105
if (branches->size() == 1) { // Only one branch. It will be the root.
105106
return (*branches)[0];
106107
}
@@ -156,13 +157,13 @@ SkRTree::Branch SkRTree::bulkLoad(std::vector<Branch>* branches, int level) {
156157
return this->bulkLoad(branches, level + 1);
157158
}
158159

159-
void SkRTree::search(const SkRect& query, std::vector<int>* results) const {
160+
void PlatformViewRTree::search(const SkRect& query, std::vector<int>* results) const {
160161
if (fCount > 0 && SkRect::Intersects(fRoot.fBounds, query)) {
161162
this->search(fRoot.fSubtree, query, results);
162163
}
163164
}
164165

165-
void SkRTree::search(Node* node, const SkRect& query, std::vector<int>* results) const {
166+
void PlatformViewRTree::search(Node* node, const SkRect& query, std::vector<int>* results) const {
166167
for (int i = 0; i < node->fNumChildren; ++i) {
167168
if (!SkRect::Intersects(node->fChildren[i].fBounds, query)) {
168169
continue;
@@ -175,8 +176,74 @@ void SkRTree::search(Node* node, const SkRect& query, std::vector<int>* results)
175176
}
176177
}
177178

178-
size_t SkRTree::bytesUsed() const {
179-
size_t byteCount = sizeof(SkRTree);
179+
void PlatformViewRTree::searchRects(const SkRect& query, std::vector<SkRect*>* results) const {
180+
if (fCount > 0 && SkRect::Intersects(fRoot.fBounds, query)) {
181+
this->searchRects(fRoot.fSubtree, query, results);
182+
}
183+
}
184+
185+
void PlatformViewRTree::searchRects(Node* node,
186+
const SkRect& query,
187+
std::vector<SkRect*>* results) const {
188+
if (!SkRect::Intersects(fRoot.fBounds, query)) {
189+
return;
190+
}
191+
for (int i = 0; i < node->fNumChildren; ++i) {
192+
if (!SkRect::Intersects(node->fChildren[i].fBounds, query)) {
193+
continue;
194+
}
195+
// Non-leaf node
196+
if (0 != node->fLevel) {
197+
this->searchRects(node->fChildren[i].fSubtree, query, results);
198+
continue;
199+
}
200+
// Ignore records that don't draw anything.
201+
if (!node->fChildren[i].isDraw) {
202+
continue;
203+
}
204+
SkRect* currentRecordRect = &node->fChildren[i].fBounds;
205+
std::vector<SkRect*> currentResults = *results;
206+
bool replacedExistingRect = false;
207+
// If the current record rect intersects with any of the rects in the
208+
// result vector, then join them, and update the rect in results.
209+
size_t joiningRectIdx = currentResults.size();
210+
size_t resultIdx = 0;
211+
while (resultIdx < results->size()) {
212+
if (SkRect::Intersects(*currentResults[resultIdx], *currentRecordRect)) {
213+
joiningRectIdx = resultIdx;
214+
replacedExistingRect = true;
215+
currentResults[joiningRectIdx]->join(*currentRecordRect);
216+
break;
217+
}
218+
resultIdx++;
219+
}
220+
resultIdx = joiningRectIdx + 1;
221+
// It's possible that after joining rects will result in duplicated
222+
// rects in the results vector. For example, consider a result vector
223+
// that contains rects A, B. If a new rect C is a superset of A and B,
224+
// then A and B are the same set after the merge. As a result, find such
225+
// cases and remove them from the result vector.
226+
while (replacedExistingRect && resultIdx < results->size()) {
227+
if (SkRect::Intersects(*currentResults[resultIdx], *currentResults[joiningRectIdx])) {
228+
currentResults[joiningRectIdx]->join(*currentResults[resultIdx]);
229+
results->erase(results->begin() + resultIdx);
230+
} else {
231+
resultIdx++;
232+
}
233+
}
234+
if (!replacedExistingRect) {
235+
results->push_back(currentRecordRect);
236+
}
237+
}
238+
}
239+
240+
size_t PlatformViewRTree::bytesUsed() const {
241+
size_t byteCount = sizeof(PlatformViewRTree);
180242
byteCount += fNodes.capacity() * sizeof(Node);
181243
return byteCount;
182244
}
245+
246+
PlatformViewRTreeFactory::PlatformViewRTreeFactory(sk_sp<PlatformViewRTree>& r_tree)
247+
: r_tree_(r_tree) {}
248+
249+
sk_sp<SkBBoxHierarchy> PlatformViewRTreeFactory::operator()() const { return r_tree_; }

third_party/skia/platform_view_rtree.h

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,19 @@
2727
* Beckmann, N.; Kriegel, H. P.; Schneider, R.; Seeger, B. (1990). "The R*-tree:
2828
* an efficient and robust access method for points and rectangles"
2929
*/
30-
class SkRTree : public SkBBoxHierarchy {
30+
class PlatformViewRTree : public SkBBoxHierarchy {
3131
public:
32-
SkRTree();
32+
PlatformViewRTree();
3333

3434
void insert(const SkRect[], const SkBBoxHierarchy::Metadata[], int N) override;
3535
void insert(const SkRect[], int N) override;
3636
void search(const SkRect& query, std::vector<int>* results) const override;
3737

38+
// Finds the rects in the tree that intersect with the query rect. Each of the
39+
// entries in the result vector don't intersect with each other. In other words,
40+
// the entries are mutually exclusive.
41+
void searchRects(const SkRect& query, std::vector<SkRect*>* results) const;
42+
3843
size_t bytesUsed() const override;
3944

4045
// Methods and constants below here are only public for tests.
@@ -56,6 +61,7 @@ class SkRTree : public SkBBoxHierarchy {
5661
Node* fSubtree;
5762
int fOpIndex;
5863
};
64+
bool isDraw;
5965
SkRect fBounds;
6066
};
6167

@@ -66,6 +72,7 @@ class SkRTree : public SkBBoxHierarchy {
6672
};
6773

6874
void search(Node* root, const SkRect& query, std::vector<int>* results) const;
75+
void searchRects(Node* root, const SkRect& query, std::vector<SkRect*>* results) const;
6976

7077
// Consumes the input array.
7178
Branch bulkLoad(std::vector<Branch>* branches, int level = 0);
@@ -81,4 +88,13 @@ class SkRTree : public SkBBoxHierarchy {
8188
std::vector<Node> fNodes;
8289
};
8390

91+
class PlatformViewRTreeFactory : public SkBBHFactory {
92+
public:
93+
PlatformViewRTreeFactory(sk_sp<PlatformViewRTree>& r_tree);
94+
sk_sp<SkBBoxHierarchy> operator()() const override;
95+
96+
private:
97+
sk_sp<PlatformViewRTree> r_tree_;
98+
};
99+
84100
#endif // SKIA_PLATFORM_VIEW_RTREE_H_
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2012 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+
#include "flutter/testing/testing.h"
8+
#include "flutter/testing/thread_test.h"
9+
10+
#include "skia/platform_view_rtree.h"
11+
#include "third_party/skia/include/core/SkCanvas.h"
12+
#include "third_party/skia/include/core/SkPictureRecorder.h"
13+
14+
namespace flutter {
15+
namespace testing {
16+
17+
using PlatformViewRTreeTest = ThreadTest;
18+
19+
TEST_F(PlatformViewRTreeTest, NoIntersection) {
20+
auto r_tree = sk_make_sp<PlatformViewRTree>();
21+
auto rtree_factory = PlatformViewRTreeFactory(r_tree);
22+
auto recorder = std::make_unique<SkPictureRecorder>();
23+
auto recording_canvas = recorder->beginRecording(SkRect::MakeIWH(1000, 1000), &rtree_factory);
24+
25+
auto rect_paint = SkPaint();
26+
rect_paint.setColor(SkColors::kCyan);
27+
rect_paint.setStyle(SkPaint::Style::kFill_Style);
28+
29+
recording_canvas->drawRect(SkRect::MakeXYWH(20, 20, 20, 20), rect_paint);
30+
recorder->finishRecordingAsPicture();
31+
32+
auto hits = std::vector<SkRect*>();
33+
auto unobstructed = SkRect::MakeXYWH(40, 40, 20, 20);
34+
35+
r_tree->searchRects(unobstructed, &hits);
36+
ASSERT_TRUE(hits.empty());
37+
}
38+
39+
TEST_F(PlatformViewRTreeTest, Intersection) {
40+
auto r_tree = sk_make_sp<PlatformViewRTree>();
41+
auto rtree_factory = PlatformViewRTreeFactory(r_tree);
42+
auto recorder = std::make_unique<SkPictureRecorder>();
43+
auto recording_canvas = recorder->beginRecording(SkRect::MakeIWH(1000, 1000), &rtree_factory);
44+
45+
auto rect_paint = SkPaint();
46+
rect_paint.setColor(SkColors::kCyan);
47+
rect_paint.setStyle(SkPaint::Style::kFill_Style);
48+
49+
recording_canvas->drawRect(SkRect::MakeXYWH(120, 120, 40, 40), rect_paint);
50+
51+
recorder->finishRecordingAsPicture();
52+
53+
// Hits that partially overlap with a drawn area return bboxes describing the
54+
// intersection of the query and the drawn area.
55+
auto one_hit = SkRect::MakeXYWH(140, 140, 40, 40);
56+
auto hits = std::vector<SkRect*>();
57+
58+
r_tree->searchRects(one_hit, &hits);
59+
ASSERT_EQ(1UL, hits.size());
60+
ASSERT_EQ(*hits[0], SkRect::MakeXYWH(120, 120, 40, 40));
61+
}
62+
63+
TEST_F(PlatformViewRTreeTest, JoinRectsWhenIntersected) {
64+
auto r_tree = sk_make_sp<PlatformViewRTree>();
65+
auto rtree_factory = PlatformViewRTreeFactory(r_tree);
66+
auto recorder = std::make_unique<SkPictureRecorder>();
67+
auto recording_canvas = recorder->beginRecording(SkRect::MakeIWH(1000, 1000), &rtree_factory);
68+
69+
auto rect_paint = SkPaint();
70+
rect_paint.setColor(SkColors::kCyan);
71+
rect_paint.setStyle(SkPaint::Style::kFill_Style);
72+
73+
recording_canvas->drawRect(SkRect::MakeXYWH(120, 120, 40, 40), rect_paint);
74+
recording_canvas->drawRect(SkRect::MakeXYWH(140, 140, 40, 40), rect_paint);
75+
76+
recorder->finishRecordingAsPicture();
77+
78+
// Hits that partially overlap with a drawn area return bboxes describing the
79+
// intersection of the query and the drawn area.
80+
auto one_hit = SkRect::MakeXYWH(142, 142, 10, 10);
81+
auto hits = std::vector<SkRect*>();
82+
83+
r_tree->searchRects(one_hit, &hits);
84+
ASSERT_EQ(1UL, hits.size());
85+
ASSERT_EQ(*hits[0], SkRect::MakeXYWH(120, 120, 60, 60));
86+
}
87+
88+
} // namespace testing
89+
} // namespace flutter

0 commit comments

Comments
 (0)