| /* | |
| * Copyright 2012 Google Inc. | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "SkRTree.h" | |
| SkRTree::SkRTree(SkScalar aspectRatio) | |
| : fCount(0), fAspectRatio(isfinite(aspectRatio) ? aspectRatio : 1) {} | |
| SkRect SkRTree::getRootBound() const { | |
| if (fCount) { | |
| return fRoot.fBounds; | |
| } else { | |
| return SkRect::MakeEmpty(); | |
| } | |
| } | |
| void SkRTree::insert(const SkRect boundsArray[], int N) { | |
| SkASSERT(0 == fCount); | |
| SkTDArray<Branch> branches; | |
| branches.setReserve(N); | |
| for (int i = 0; i < N; i++) { | |
| const SkRect& bounds = boundsArray[i]; | |
| if (bounds.isEmpty()) { | |
| continue; | |
| } | |
| Branch* b = branches.push(); | |
| b->fBounds = bounds; | |
| b->fOpIndex = i; | |
| } | |
| fCount = branches.count(); | |
| if (fCount) { | |
| if (1 == fCount) { | |
| fNodes.setReserve(1); | |
| Node* n = this->allocateNodeAtLevel(0); | |
| n->fNumChildren = 1; | |
| n->fChildren[0] = branches[0]; | |
| fRoot.fSubtree = n; | |
| fRoot.fBounds = branches[0].fBounds; | |
| } else { | |
| fNodes.setReserve(CountNodes(fCount, fAspectRatio)); | |
| fRoot = this->bulkLoad(&branches); | |
| } | |
| } | |
| } | |
| SkRTree::Node* SkRTree::allocateNodeAtLevel(uint16_t level) { | |
| SkDEBUGCODE(Node* p = fNodes.begin()); | |
| Node* out = fNodes.push(); | |
| SkASSERT(fNodes.begin() == p); // If this fails, we didn't setReserve() enough. | |
| out->fNumChildren = 0; | |
| out->fLevel = level; | |
| return out; | |
| } | |
| // This function parallels bulkLoad, but just counts how many nodes bulkLoad would allocate. | |
| int SkRTree::CountNodes(int branches, SkScalar aspectRatio) { | |
| if (branches == 1) { | |
| return 1; | |
| } | |
| int numBranches = branches / kMaxChildren; | |
| int remainder = branches % kMaxChildren; | |
| if (remainder > 0) { | |
| numBranches++; | |
| if (remainder >= kMinChildren) { | |
| remainder = 0; | |
| } else { | |
| remainder = kMinChildren - remainder; | |
| } | |
| } | |
| int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / aspectRatio)); | |
| int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); | |
| int currentBranch = 0; | |
| int nodes = 0; | |
| for (int i = 0; i < numStrips; ++i) { | |
| for (int j = 0; j < numTiles && currentBranch < branches; ++j) { | |
| int incrementBy = kMaxChildren; | |
| if (remainder != 0) { | |
| if (remainder <= kMaxChildren - kMinChildren) { | |
| incrementBy -= remainder; | |
| remainder = 0; | |
| } else { | |
| incrementBy = kMinChildren; | |
| remainder -= kMaxChildren - kMinChildren; | |
| } | |
| } | |
| nodes++; | |
| currentBranch++; | |
| for (int k = 1; k < incrementBy && currentBranch < branches; ++k) { | |
| currentBranch++; | |
| } | |
| } | |
| } | |
| return nodes + CountNodes(nodes, aspectRatio); | |
| } | |
| SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) { | |
| if (branches->count() == 1) { // Only one branch. It will be the root. | |
| return (*branches)[0]; | |
| } | |
| // We might sort our branches here, but we expect Blink gives us a reasonable x,y order. | |
| // Skipping a call to sort (in Y) here resulted in a 17% win for recording with negligible | |
| // difference in playback speed. | |
| int numBranches = branches->count() / kMaxChildren; | |
| int remainder = branches->count() % kMaxChildren; | |
| int newBranches = 0; | |
| if (remainder > 0) { | |
| ++numBranches; | |
| // If the remainder isn't enough to fill a node, we'll add fewer nodes to other branches. | |
| if (remainder >= kMinChildren) { | |
| remainder = 0; | |
| } else { | |
| remainder = kMinChildren - remainder; | |
| } | |
| } | |
| int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) / fAspectRatio)); | |
| int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) / SkIntToScalar(numStrips)); | |
| int currentBranch = 0; | |
| for (int i = 0; i < numStrips; ++i) { | |
| // Might be worth sorting by X here too. | |
| for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) { | |
| int incrementBy = kMaxChildren; | |
| if (remainder != 0) { | |
| // if need be, omit some nodes to make up for remainder | |
| if (remainder <= kMaxChildren - kMinChildren) { | |
| incrementBy -= remainder; | |
| remainder = 0; | |
| } else { | |
| incrementBy = kMinChildren; | |
| remainder -= kMaxChildren - kMinChildren; | |
| } | |
| } | |
| Node* n = allocateNodeAtLevel(level); | |
| n->fNumChildren = 1; | |
| n->fChildren[0] = (*branches)[currentBranch]; | |
| Branch b; | |
| b.fBounds = (*branches)[currentBranch].fBounds; | |
| b.fSubtree = n; | |
| ++currentBranch; | |
| for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) { | |
| b.fBounds.join((*branches)[currentBranch].fBounds); | |
| n->fChildren[k] = (*branches)[currentBranch]; | |
| ++n->fNumChildren; | |
| ++currentBranch; | |
| } | |
| (*branches)[newBranches] = b; | |
| ++newBranches; | |
| } | |
| } | |
| branches->setCount(newBranches); | |
| return this->bulkLoad(branches, level + 1); | |
| } | |
| void SkRTree::search(const SkRect& query, SkTDArray<int>* results) const { | |
| if (fCount > 0 && SkRect::Intersects(fRoot.fBounds, query)) { | |
| this->search(fRoot.fSubtree, query, results); | |
| } | |
| } | |
| void SkRTree::search(Node* node, const SkRect& query, SkTDArray<int>* results) const { | |
| for (int i = 0; i < node->fNumChildren; ++i) { | |
| if (SkRect::Intersects(node->fChildren[i].fBounds, query)) { | |
| if (0 == node->fLevel) { | |
| results->push_back(node->fChildren[i].fOpIndex); | |
| } else { | |
| this->search(node->fChildren[i].fSubtree, query, results); | |
| } | |
| } | |
| } | |
| } | |
| size_t SkRTree::bytesUsed() const { | |
| size_t byteCount = sizeof(SkRTree); | |
| byteCount += fNodes.reserved() * sizeof(Node); | |
| return byteCount; | |
| } |