| // Copyright (c) 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cc/base/rtree.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <cmath> |
| |
| #include "base/logging.h" |
| |
| namespace cc { |
| |
| RTree::RTree() : num_data_elements_(0u) {} |
| |
| RTree::~RTree() {} |
| |
| RTree::Node* RTree::AllocateNodeAtLevel(int level) { |
| nodes_.push_back(Node()); |
| Node& node = nodes_.back(); |
| node.num_children = 0; |
| node.level = level; |
| return &node; |
| } |
| |
| RTree::Branch RTree::BuildRecursive(std::vector<Branch>* branches, int level) { |
| // Only one branch. It will be the root. |
| if (branches->size() == 1) |
| return (*branches)[0]; |
| |
| // TODO(vmpstr): Investigate if branches should be sorted in y. |
| // The comment from Skia reads: |
| // 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 num_branches = static_cast<int>(branches->size() / MAX_CHILDREN); |
| int remainder = static_cast<int>(branches->size() % MAX_CHILDREN); |
| |
| if (remainder > 0) { |
| ++num_branches; |
| // If the remainder isn't enough to fill a node, we'll add fewer nodes to |
| // other branches. |
| if (remainder >= MIN_CHILDREN) |
| remainder = 0; |
| else |
| remainder = MIN_CHILDREN - remainder; |
| } |
| |
| int num_strips = static_cast<int>(std::ceil(std::sqrt(num_branches))); |
| int num_tiles = static_cast<int>( |
| std::ceil(num_branches / static_cast<float>(num_strips))); |
| size_t current_branch = 0; |
| |
| size_t new_branch_index = 0; |
| for (int i = 0; i < num_strips; ++i) { |
| // Might be worth sorting by X here too. |
| for (int j = 0; j < num_tiles && current_branch < branches->size(); ++j) { |
| int increment_by = MAX_CHILDREN; |
| if (remainder != 0) { |
| // if need be, omit some nodes to make up for remainder |
| if (remainder <= MAX_CHILDREN - MIN_CHILDREN) { |
| increment_by -= remainder; |
| remainder = 0; |
| } else { |
| increment_by = MIN_CHILDREN; |
| remainder -= MAX_CHILDREN - MIN_CHILDREN; |
| } |
| } |
| Node* node = AllocateNodeAtLevel(level); |
| node->num_children = 1; |
| node->children[0] = (*branches)[current_branch]; |
| |
| Branch branch; |
| branch.bounds = (*branches)[current_branch].bounds; |
| branch.subtree = node; |
| ++current_branch; |
| for (int k = 1; k < increment_by && current_branch < branches->size(); |
| ++k) { |
| branch.bounds.Union((*branches)[current_branch].bounds); |
| node->children[k] = (*branches)[current_branch]; |
| ++node->num_children; |
| ++current_branch; |
| } |
| DCHECK_LT(new_branch_index, current_branch); |
| (*branches)[new_branch_index] = branch; |
| ++new_branch_index; |
| } |
| } |
| branches->resize(new_branch_index); |
| return BuildRecursive(branches, level + 1); |
| } |
| |
| void RTree::Search(const gfx::Rect& query, std::vector<size_t>* results) const { |
| if (num_data_elements_ > 0 && query.Intersects(root_.bounds)) |
| SearchRecursive(root_.subtree, query, results); |
| } |
| |
| void RTree::SearchRecursive(Node* node, |
| const gfx::Rect& query, |
| std::vector<size_t>* results) const { |
| for (uint16_t i = 0; i < node->num_children; ++i) { |
| if (query.Intersects(node->children[i].bounds)) { |
| if (node->level == 0) |
| results->push_back(node->children[i].index); |
| else |
| SearchRecursive(node->children[i].subtree, query, results); |
| } |
| } |
| } |
| |
| gfx::Rect RTree::GetBounds() const { |
| return root_.bounds; |
| } |
| |
| } // namespace cc |