| /* | 
 |  * 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(aspectRatio) {} | 
 |  | 
 | void SkRTree::insert(SkAutoTMalloc<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<unsigned>* 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<unsigned>* 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; | 
 | } |