/*
* 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(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;
}