OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkRTree.h" | 8 #include "SkRTree.h" |
9 #include "SkTSort.h" | |
10 | 9 |
11 static inline uint32_t get_area(const SkIRect& rect); | 10 SkRTree::SkRTree(SkScalar aspectRatio) : fCount(0), fAspectRatio(aspectRatio) {} |
12 static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2); | |
13 static inline uint32_t get_margin(const SkIRect& rect); | |
14 static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2); | |
15 static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out); | |
16 | |
17 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | |
18 | |
19 SkRTree* SkRTree::Create(int minChildren, int maxChildren, SkScalar aspectRatio, | |
20 bool sortWhenBulkLoading) { | |
21 if (minChildren < maxChildren && (maxChildren + 1) / 2 >= minChildren && | |
22 minChildren > 0 && maxChildren < static_cast<int>(SK_MaxU16)) { | |
23 return new SkRTree(minChildren, maxChildren, aspectRatio, sortWhenBulkLo
ading); | |
24 } | |
25 return NULL; | |
26 } | |
27 | |
28 SkRTree::SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio, | |
29 bool sortWhenBulkLoading) | |
30 : fMinChildren(minChildren) | |
31 , fMaxChildren(maxChildren) | |
32 , fNodeSize(sizeof(Node) + sizeof(Branch) * maxChildren) | |
33 , fCount(0) | |
34 , fNodes(fNodeSize * 256) | |
35 , fAspectRatio(aspectRatio) | |
36 , fSortWhenBulkLoading(sortWhenBulkLoading) { | |
37 SkASSERT(minChildren < maxChildren && minChildren > 0 && maxChildren < | |
38 static_cast<int>(SK_MaxU16)); | |
39 SkASSERT((maxChildren + 1) / 2 >= minChildren); | |
40 this->validate(); | |
41 } | |
42 | |
43 SkRTree::~SkRTree() { | |
44 this->clear(); | |
45 } | |
46 | 11 |
47 void SkRTree::insert(SkAutoTMalloc<SkRect>* boundsArray, int N) { | 12 void SkRTree::insert(SkAutoTMalloc<SkRect>* boundsArray, int N) { |
48 SkASSERT(this->isEmpty()); | 13 SkASSERT(0 == fCount); |
49 this->validate(); | |
50 | 14 |
51 SkTDArray<Branch> deferred; | 15 SkTDArray<Branch> branches; |
52 deferred.setReserve(N); | 16 branches.setReserve(N); |
53 | 17 |
54 for (int i = 0; i < N; i++) { | 18 for (int i = 0; i < N; i++) { |
55 SkIRect bounds; | 19 const SkRect& bounds = (*boundsArray)[i]; |
56 (*boundsArray)[i].roundOut(&bounds); | |
57 if (bounds.isEmpty()) { | 20 if (bounds.isEmpty()) { |
58 continue; | 21 continue; |
59 } | 22 } |
60 | 23 |
61 Branch newBranch; | 24 Branch* b = branches.push(); |
62 newBranch.fBounds = bounds; | 25 b->fBounds = bounds; |
63 newBranch.fChild.opIndex = i; | 26 b->fOpIndex = i; |
64 | |
65 deferred.push(newBranch); | |
66 } | 27 } |
67 | 28 |
68 fCount = deferred.count(); | 29 fCount = branches.count(); |
69 if (fCount) { | 30 if (fCount) { |
70 if (1 == fCount) { | 31 if (1 == fCount) { |
71 fRoot.fChild.subtree = this->allocateNode(0); | 32 fNodes.setReserve(1); |
72 fRoot.fChild.subtree->fNumChildren = 0; | 33 Node* n = this->allocateNodeAtLevel(0); |
73 this->insert(fRoot.fChild.subtree, &deferred[0]); | 34 n->fNumChildren = 1; |
74 fRoot.fBounds = deferred[0].fBounds; | 35 n->fChildren[0] = branches[0]; |
| 36 fRoot.fSubtree = n; |
| 37 fRoot.fBounds = branches[0].fBounds; |
75 } else { | 38 } else { |
76 fRoot = this->bulkLoad(&deferred); | 39 fNodes.setReserve(CountNodes(fCount, fAspectRatio)); |
| 40 fRoot = this->bulkLoad(&branches); |
77 } | 41 } |
78 } | 42 } |
79 | |
80 this->validate(); | |
81 } | 43 } |
82 | 44 |
83 void SkRTree::search(const SkRect& fquery, SkTDArray<unsigned>* results) const { | 45 SkRTree::Node* SkRTree::allocateNodeAtLevel(uint16_t level) { |
84 SkIRect query; | 46 SkDEBUGCODE(Node* p = fNodes.begin()); |
85 fquery.roundOut(&query); | 47 Node* out = fNodes.push(); |
86 this->validate(); | 48 SkASSERT(fNodes.begin() == p); // If this fails, we didn't setReserve() eno
ugh. |
87 if (!this->isEmpty() && SkIRect::IntersectsNoEmptyCheck(fRoot.fBounds, query
)) { | |
88 this->search(fRoot.fChild.subtree, query, results); | |
89 } | |
90 this->validate(); | |
91 } | |
92 | |
93 void SkRTree::clear() { | |
94 this->validate(); | |
95 fNodes.reset(); | |
96 fCount = 0; | |
97 this->validate(); | |
98 } | |
99 | |
100 SkRTree::Node* SkRTree::allocateNode(uint16_t level) { | |
101 Node* out = static_cast<Node*>(fNodes.allocThrow(fNodeSize)); | |
102 out->fNumChildren = 0; | 49 out->fNumChildren = 0; |
103 out->fLevel = level; | 50 out->fLevel = level; |
104 return out; | 51 return out; |
105 } | 52 } |
106 | 53 |
107 SkRTree::Branch* SkRTree::insert(Node* root, Branch* branch, uint16_t level) { | 54 // This function parallels bulkLoad, but just counts how many nodes bulkLoad wou
ld allocate. |
108 Branch* toInsert = branch; | 55 int SkRTree::CountNodes(int branches, SkScalar aspectRatio) { |
109 if (root->fLevel != level) { | 56 if (branches == 1) { |
110 int childIndex = this->chooseSubtree(root, branch); | 57 return 1; |
111 toInsert = this->insert(root->child(childIndex)->fChild.subtree, branch,
level); | |
112 root->child(childIndex)->fBounds = this->computeBounds( | |
113 root->child(childIndex)->fChild.subtree); | |
114 } | 58 } |
115 if (toInsert) { | 59 int numBranches = branches / kMaxChildren; |
116 if (root->fNumChildren == fMaxChildren) { | 60 int remainder = branches % kMaxChildren; |
117 // handle overflow by splitting. TODO: opportunistic reinsertion | 61 if (remainder > 0) { |
118 | 62 numBranches++; |
119 // decide on a distribution to divide with | 63 if (remainder >= kMinChildren) { |
120 Node* newSibling = this->allocateNode(root->fLevel); | 64 remainder = 0; |
121 Branch* toDivide = SkNEW_ARRAY(Branch, fMaxChildren + 1); | |
122 for (int i = 0; i < fMaxChildren; ++i) { | |
123 toDivide[i] = *root->child(i); | |
124 } | |
125 toDivide[fMaxChildren] = *toInsert; | |
126 int splitIndex = this->distributeChildren(toDivide); | |
127 | |
128 // divide up the branches | |
129 root->fNumChildren = splitIndex; | |
130 newSibling->fNumChildren = fMaxChildren + 1 - splitIndex; | |
131 for (int i = 0; i < splitIndex; ++i) { | |
132 *root->child(i) = toDivide[i]; | |
133 } | |
134 for (int i = splitIndex; i < fMaxChildren + 1; ++i) { | |
135 *newSibling->child(i - splitIndex) = toDivide[i]; | |
136 } | |
137 SkDELETE_ARRAY(toDivide); | |
138 | |
139 // pass the new sibling branch up to the parent | |
140 branch->fChild.subtree = newSibling; | |
141 branch->fBounds = this->computeBounds(newSibling); | |
142 return branch; | |
143 } else { | 65 } else { |
144 *root->child(root->fNumChildren) = *toInsert; | 66 remainder = kMinChildren - remainder; |
145 ++root->fNumChildren; | |
146 return NULL; | |
147 } | 67 } |
148 } | 68 } |
149 return NULL; | 69 int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) /
aspectRatio)); |
| 70 int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar
(numStrips)); |
| 71 int currentBranch = 0; |
| 72 int nodes = 0; |
| 73 for (int i = 0; i < numStrips; ++i) { |
| 74 for (int j = 0; j < numTiles && currentBranch < branches; ++j) { |
| 75 int incrementBy = kMaxChildren; |
| 76 if (remainder != 0) { |
| 77 if (remainder <= kMaxChildren - kMinChildren) { |
| 78 incrementBy -= remainder; |
| 79 remainder = 0; |
| 80 } else { |
| 81 incrementBy = kMinChildren; |
| 82 remainder -= kMaxChildren - kMinChildren; |
| 83 } |
| 84 } |
| 85 nodes++; |
| 86 currentBranch++; |
| 87 for (int k = 1; k < incrementBy && currentBranch < branches; ++k) { |
| 88 currentBranch++; |
| 89 } |
| 90 } |
| 91 } |
| 92 return nodes + CountNodes(nodes, aspectRatio); |
150 } | 93 } |
151 | 94 |
152 int SkRTree::chooseSubtree(Node* root, Branch* branch) { | 95 SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { |
153 SkASSERT(!root->isLeaf()); | 96 if (branches->count() == 1) { // Only one branch. It will be the root. |
154 if (1 < root->fLevel) { | 97 return (*branches)[0]; |
155 // root's child pointers do not point to leaves, so minimize area increa
se | 98 } |
156 int32_t minAreaIncrease = SK_MaxS32; | 99 |
157 int32_t minArea = SK_MaxS32; | 100 // We might sort our branches here, but we expect Blink gives us a reasonabl
e x,y order. |
158 int32_t bestSubtree = -1; | 101 // Skipping a call to sort (in Y) here resulted in a 17% win for recording w
ith negligible |
159 for (int i = 0; i < root->fNumChildren; ++i) { | 102 // difference in playback speed. |
160 const SkIRect& subtreeBounds = root->child(i)->fBounds; | 103 int numBranches = branches->count() / kMaxChildren; |
161 int32_t areaIncrease = get_area_increase(subtreeBounds, branch->fBou
nds); | 104 int remainder = branches->count() % kMaxChildren; |
162 // break ties in favor of subtree with smallest area | 105 int newBranches = 0; |
163 if (areaIncrease < minAreaIncrease || (areaIncrease == minAreaIncrea
se && | 106 |
164 static_cast<int32_t>(get_area(subtreeBounds)) < minArea)) { | 107 if (remainder > 0) { |
165 minAreaIncrease = areaIncrease; | 108 ++numBranches; |
166 minArea = get_area(subtreeBounds); | 109 // If the remainder isn't enough to fill a node, we'll add fewer nodes t
o other branches. |
167 bestSubtree = i; | 110 if (remainder >= kMinChildren) { |
| 111 remainder = 0; |
| 112 } else { |
| 113 remainder = kMinChildren - remainder; |
| 114 } |
| 115 } |
| 116 |
| 117 int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) /
fAspectRatio)); |
| 118 int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar
(numStrips)); |
| 119 int currentBranch = 0; |
| 120 |
| 121 for (int i = 0; i < numStrips; ++i) { |
| 122 // Might be worth sorting by X here too. |
| 123 for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j)
{ |
| 124 int incrementBy = kMaxChildren; |
| 125 if (remainder != 0) { |
| 126 // if need be, omit some nodes to make up for remainder |
| 127 if (remainder <= kMaxChildren - kMinChildren) { |
| 128 incrementBy -= remainder; |
| 129 remainder = 0; |
| 130 } else { |
| 131 incrementBy = kMinChildren; |
| 132 remainder -= kMaxChildren - kMinChildren; |
| 133 } |
168 } | 134 } |
| 135 Node* n = allocateNodeAtLevel(level); |
| 136 n->fNumChildren = 1; |
| 137 n->fChildren[0] = (*branches)[currentBranch]; |
| 138 Branch b; |
| 139 b.fBounds = (*branches)[currentBranch].fBounds; |
| 140 b.fSubtree = n; |
| 141 ++currentBranch; |
| 142 for (int k = 1; k < incrementBy && currentBranch < branches->count()
; ++k) { |
| 143 b.fBounds.join((*branches)[currentBranch].fBounds); |
| 144 n->fChildren[k] = (*branches)[currentBranch]; |
| 145 ++n->fNumChildren; |
| 146 ++currentBranch; |
| 147 } |
| 148 (*branches)[newBranches] = b; |
| 149 ++newBranches; |
169 } | 150 } |
170 SkASSERT(-1 != bestSubtree); | 151 } |
171 return bestSubtree; | 152 branches->setCount(newBranches); |
172 } else if (1 == root->fLevel) { | 153 return this->bulkLoad(branches, level + 1); |
173 // root's child pointers do point to leaves, so minimize overlap increas
e | 154 } |
174 int32_t minOverlapIncrease = SK_MaxS32; | 155 |
175 int32_t minAreaIncrease = SK_MaxS32; | 156 void SkRTree::search(const SkRect& query, SkTDArray<unsigned>* results) const { |
176 int32_t bestSubtree = -1; | 157 if (fCount > 0 && SkRect::Intersects(fRoot.fBounds, query)) { |
177 for (int32_t i = 0; i < root->fNumChildren; ++i) { | 158 this->search(fRoot.fSubtree, query, results); |
178 const SkIRect& subtreeBounds = root->child(i)->fBounds; | |
179 SkIRect expandedBounds = subtreeBounds; | |
180 join_no_empty_check(branch->fBounds, &expandedBounds); | |
181 int32_t overlap = 0; | |
182 for (int32_t j = 0; j < root->fNumChildren; ++j) { | |
183 if (j == i) continue; | |
184 // Note: this would be more correct if we subtracted the origina
l pre-expanded | |
185 // overlap, but computing overlaps is expensive and omitting it
doesn't seem to | |
186 // hurt query performance. See get_overlap_increase() | |
187 overlap += get_overlap(expandedBounds, root->child(j)->fBounds); | |
188 } | |
189 // break ties with lowest area increase | |
190 if (overlap < minOverlapIncrease || (overlap == minOverlapIncrease &
& | |
191 static_cast<int32_t>(get_area_increase(branch->fBounds, subtreeB
ounds)) < | |
192 minAreaIncrease)) { | |
193 minOverlapIncrease = overlap; | |
194 minAreaIncrease = get_area_increase(branch->fBounds, subtreeBoun
ds); | |
195 bestSubtree = i; | |
196 } | |
197 } | |
198 return bestSubtree; | |
199 } else { | |
200 SkASSERT(false); | |
201 return 0; | |
202 } | 159 } |
203 } | 160 } |
204 | 161 |
205 SkIRect SkRTree::computeBounds(Node* n) { | 162 void SkRTree::search(Node* node, const SkRect& query, SkTDArray<unsigned>* resul
ts) const { |
206 SkIRect r = n->child(0)->fBounds; | 163 for (int i = 0; i < node->fNumChildren; ++i) { |
207 for (int i = 1; i < n->fNumChildren; ++i) { | 164 if (SkRect::Intersects(node->fChildren[i].fBounds, query)) { |
208 join_no_empty_check(n->child(i)->fBounds, &r); | 165 if (0 == node->fLevel) { |
209 } | 166 results->push(node->fChildren[i].fOpIndex); |
210 return r; | |
211 } | |
212 | |
213 int SkRTree::distributeChildren(Branch* children) { | |
214 // We have two sides to sort by on each of two axes: | |
215 const static SortSide sorts[2][2] = { | |
216 {&SkIRect::fLeft, &SkIRect::fRight}, | |
217 {&SkIRect::fTop, &SkIRect::fBottom} | |
218 }; | |
219 | |
220 // We want to choose an axis to split on, then a distribution along that axi
s; we'll need | |
221 // three pieces of info: the split axis, the side to sort by on that axis, a
nd the index | |
222 // to split the sorted array on. | |
223 int32_t sortSide = -1; | |
224 int32_t k = -1; | |
225 int32_t axis = -1; | |
226 int32_t bestS = SK_MaxS32; | |
227 | |
228 // Evaluate each axis, we want the min summed margin-value (s) over all dist
ributions | |
229 for (int i = 0; i < 2; ++i) { | |
230 int32_t minOverlap = SK_MaxS32; | |
231 int32_t minArea = SK_MaxS32; | |
232 int32_t axisBestK = 0; | |
233 int32_t axisBestSide = 0; | |
234 int32_t s = 0; | |
235 | |
236 // Evaluate each sort | |
237 for (int j = 0; j < 2; ++j) { | |
238 SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j]
)); | |
239 | |
240 // Evaluate each split index | |
241 for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) { | |
242 SkIRect r1 = children[0].fBounds; | |
243 SkIRect r2 = children[fMinChildren + k - 1].fBounds; | |
244 for (int32_t l = 1; l < fMinChildren - 1 + k; ++l) { | |
245 join_no_empty_check(children[l].fBounds, &r1); | |
246 } | |
247 for (int32_t l = fMinChildren + k; l < fMaxChildren + 1; ++l) { | |
248 join_no_empty_check(children[l].fBounds, &r2); | |
249 } | |
250 | |
251 int32_t area = get_area(r1) + get_area(r2); | |
252 int32_t overlap = get_overlap(r1, r2); | |
253 s += get_margin(r1) + get_margin(r2); | |
254 | |
255 if (overlap < minOverlap || (overlap == minOverlap && area < min
Area)) { | |
256 minOverlap = overlap; | |
257 minArea = area; | |
258 axisBestSide = j; | |
259 axisBestK = k; | |
260 } | |
261 } | |
262 } | |
263 | |
264 if (s < bestS) { | |
265 bestS = s; | |
266 axis = i; | |
267 sortSide = axisBestSide; | |
268 k = axisBestK; | |
269 } | |
270 } | |
271 | |
272 // replicate the sort of the winning distribution, (we can skip this if the
last | |
273 // sort ended up being best) | |
274 if (!(axis == 1 && sortSide == 1)) { | |
275 SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sor
tSide])); | |
276 } | |
277 | |
278 return fMinChildren - 1 + k; | |
279 } | |
280 | |
281 void SkRTree::search(Node* root, const SkIRect query, SkTDArray<unsigned>* resul
ts) const { | |
282 for (int i = 0; i < root->fNumChildren; ++i) { | |
283 if (SkIRect::IntersectsNoEmptyCheck(root->child(i)->fBounds, query)) { | |
284 if (root->isLeaf()) { | |
285 results->push(root->child(i)->fChild.opIndex); | |
286 } else { | 167 } else { |
287 this->search(root->child(i)->fChild.subtree, query, results); | 168 this->search(node->fChildren[i].fSubtree, query, results); |
288 } | 169 } |
289 } | 170 } |
290 } | 171 } |
291 } | 172 } |
292 | |
293 SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { | |
294 if (branches->count() == 1) { | |
295 // Only one branch: it will be the root | |
296 Branch out = (*branches)[0]; | |
297 branches->rewind(); | |
298 return out; | |
299 } else { | |
300 // We sort the whole list by y coordinates, if we are told to do so. | |
301 // | |
302 // We expect Webkit / Blink to give us a reasonable x,y order. | |
303 // Avoiding this call resulted in a 17% win for recording with | |
304 // negligible difference in playback speed. | |
305 if (fSortWhenBulkLoading) { | |
306 SkTQSort(branches->begin(), branches->end() - 1, RectLessY()); | |
307 } | |
308 | |
309 int numBranches = branches->count() / fMaxChildren; | |
310 int remainder = branches->count() % fMaxChildren; | |
311 int newBranches = 0; | |
312 | |
313 if (0 != remainder) { | |
314 ++numBranches; | |
315 // If the remainder isn't enough to fill a node, we'll need to add f
ewer nodes to | |
316 // some other branches to make up for it | |
317 if (remainder >= fMinChildren) { | |
318 remainder = 0; | |
319 } else { | |
320 remainder = fMinChildren - remainder; | |
321 } | |
322 } | |
323 | |
324 int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches
) * | |
325 SkScalarInvert(fAspectRatio))); | |
326 int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / | |
327 SkIntToScalar(numStrips)); | |
328 int currentBranch = 0; | |
329 | |
330 for (int i = 0; i < numStrips; ++i) { | |
331 // Once again, if we are told to do so, we sort by x. | |
332 if (fSortWhenBulkLoading) { | |
333 int begin = currentBranch; | |
334 int end = currentBranch + numTiles * fMaxChildren - SkMin32(rema
inder, | |
335 (fMaxChildren - fMinChildren) * numTiles); | |
336 if (end > branches->count()) { | |
337 end = branches->count(); | |
338 } | |
339 | |
340 // Now we sort horizontal strips of rectangles by their x coords | |
341 SkTQSort(branches->begin() + begin, branches->begin() + end - 1,
RectLessX()); | |
342 } | |
343 | |
344 for (int j = 0; j < numTiles && currentBranch < branches->count(); +
+j) { | |
345 int incrementBy = fMaxChildren; | |
346 if (remainder != 0) { | |
347 // if need be, omit some nodes to make up for remainder | |
348 if (remainder <= fMaxChildren - fMinChildren) { | |
349 incrementBy -= remainder; | |
350 remainder = 0; | |
351 } else { | |
352 incrementBy = fMinChildren; | |
353 remainder -= fMaxChildren - fMinChildren; | |
354 } | |
355 } | |
356 Node* n = allocateNode(level); | |
357 n->fNumChildren = 1; | |
358 *n->child(0) = (*branches)[currentBranch]; | |
359 Branch b; | |
360 b.fBounds = (*branches)[currentBranch].fBounds; | |
361 b.fChild.subtree = n; | |
362 ++currentBranch; | |
363 for (int k = 1; k < incrementBy && currentBranch < branches->cou
nt(); ++k) { | |
364 b.fBounds.join((*branches)[currentBranch].fBounds); | |
365 *n->child(k) = (*branches)[currentBranch]; | |
366 ++n->fNumChildren; | |
367 ++currentBranch; | |
368 } | |
369 (*branches)[newBranches] = b; | |
370 ++newBranches; | |
371 } | |
372 } | |
373 branches->setCount(newBranches); | |
374 return this->bulkLoad(branches, level + 1); | |
375 } | |
376 } | |
377 | |
378 void SkRTree::validate() const { | |
379 #ifdef SK_DEBUG | |
380 if (this->isEmpty()) { | |
381 return; | |
382 } | |
383 SkASSERT(fCount == this->validateSubtree(fRoot.fChild.subtree, fRoot.fBounds
, true)); | |
384 #endif | |
385 } | |
386 | |
387 int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) const { | |
388 // make sure the pointer is pointing to a valid place | |
389 SkASSERT(fNodes.contains(static_cast<void*>(root))); | |
390 | |
391 if (isRoot) { | |
392 // If the root of this subtree is the overall root, we have looser stand
ards: | |
393 if (root->isLeaf()) { | |
394 SkASSERT(root->fNumChildren >= 1 && root->fNumChildren <= fMaxChildr
en); | |
395 } else { | |
396 SkASSERT(root->fNumChildren >= 2 && root->fNumChildren <= fMaxChildr
en); | |
397 } | |
398 } else { | |
399 SkASSERT(root->fNumChildren >= fMinChildren && root->fNumChildren <= fMa
xChildren); | |
400 } | |
401 | |
402 for (int i = 0; i < root->fNumChildren; ++i) { | |
403 SkASSERT(bounds.contains(root->child(i)->fBounds)); | |
404 } | |
405 | |
406 if (root->isLeaf()) { | |
407 SkASSERT(0 == root->fLevel); | |
408 return root->fNumChildren; | |
409 } else { | |
410 int childCount = 0; | |
411 for (int i = 0; i < root->fNumChildren; ++i) { | |
412 SkASSERT(root->child(i)->fChild.subtree->fLevel == root->fLevel - 1)
; | |
413 childCount += this->validateSubtree(root->child(i)->fChild.subtree, | |
414 root->child(i)->fBounds); | |
415 } | |
416 return childCount; | |
417 } | |
418 } | |
419 | |
420 ////////////////////////////////////////////////////////////////////////////////
/////////////////// | |
421 | |
422 static inline uint32_t get_area(const SkIRect& rect) { | |
423 return rect.width() * rect.height(); | |
424 } | |
425 | |
426 static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2) { | |
427 // I suspect there's a more efficient way of computing this... | |
428 return SkMax32(0, SkMin32(rect1.fRight, rect2.fRight) - SkMax32(rect1.fLeft,
rect2.fLeft)) * | |
429 SkMax32(0, SkMin32(rect1.fBottom, rect2.fBottom) - SkMax32(rect1.fTop
, rect2.fTop)); | |
430 } | |
431 | |
432 // Get the margin (aka perimeter) | |
433 static inline uint32_t get_margin(const SkIRect& rect) { | |
434 return 2 * (rect.width() + rect.height()); | |
435 } | |
436 | |
437 static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2) { | |
438 join_no_empty_check(rect1, &rect2); | |
439 return get_area(rect2) - get_area(rect1); | |
440 } | |
441 | |
442 // Expand 'out' to include 'joinWith' | |
443 static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out) { | |
444 // since we check for empty bounds on insert, we know we'll never have empty
rects | |
445 // and we can save the empty check that SkIRect::join requires | |
446 if (joinWith.fLeft < out->fLeft) { out->fLeft = joinWith.fLeft; } | |
447 if (joinWith.fTop < out->fTop) { out->fTop = joinWith.fTop; } | |
448 if (joinWith.fRight > out->fRight) { out->fRight = joinWith.fRight; } | |
449 if (joinWith.fBottom > out->fBottom) { out->fBottom = joinWith.fBottom; } | |
450 } | |
OLD | NEW |