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