Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | |
michael@0 | 2 | /* |
michael@0 | 3 | * Copyright 2012 Google Inc. |
michael@0 | 4 | * |
michael@0 | 5 | * Use of this source code is governed by a BSD-style license that can be |
michael@0 | 6 | * found in the LICENSE file. |
michael@0 | 7 | */ |
michael@0 | 8 | |
michael@0 | 9 | #ifndef SkRTree_DEFINED |
michael@0 | 10 | #define SkRTree_DEFINED |
michael@0 | 11 | |
michael@0 | 12 | #include "SkRect.h" |
michael@0 | 13 | #include "SkTDArray.h" |
michael@0 | 14 | #include "SkChunkAlloc.h" |
michael@0 | 15 | #include "SkBBoxHierarchy.h" |
michael@0 | 16 | |
michael@0 | 17 | /** |
michael@0 | 18 | * An R-Tree implementation. In short, it is a balanced n-ary tree containing a hierarchy of |
michael@0 | 19 | * bounding rectangles. |
michael@0 | 20 | * |
michael@0 | 21 | * Much like a B-Tree it maintains balance by enforcing minimum and maximum child counts, and |
michael@0 | 22 | * splitting nodes when they become overfull. Unlike B-trees, however, we're using spatial data; so |
michael@0 | 23 | * there isn't a canonical ordering to use when choosing insertion locations and splitting |
michael@0 | 24 | * distributions. A variety of heuristics have been proposed for these problems; here, we're using |
michael@0 | 25 | * something resembling an R*-tree, which attempts to minimize area and overlap during insertion, |
michael@0 | 26 | * and aims to minimize a combination of margin, overlap, and area when splitting. |
michael@0 | 27 | * |
michael@0 | 28 | * One detail that is thus far unimplemented that may improve tree quality is attempting to remove |
michael@0 | 29 | * and reinsert nodes when they become full, instead of immediately splitting (nodes that may have |
michael@0 | 30 | * been placed well early on may hurt the tree later when more nodes have been added; removing |
michael@0 | 31 | * and reinserting nodes generally helps reduce overlap and make a better tree). Deletion of nodes |
michael@0 | 32 | * is also unimplemented. |
michael@0 | 33 | * |
michael@0 | 34 | * For more details see: |
michael@0 | 35 | * |
michael@0 | 36 | * Beckmann, N.; Kriegel, H. P.; Schneider, R.; Seeger, B. (1990). "The R*-tree: |
michael@0 | 37 | * an efficient and robust access method for points and rectangles" |
michael@0 | 38 | * |
michael@0 | 39 | * It also supports bulk-loading from a batch of bounds and values; if you don't require the tree |
michael@0 | 40 | * to be usable in its intermediate states while it is being constructed, this is significantly |
michael@0 | 41 | * quicker than individual insertions and produces more consistent trees. |
michael@0 | 42 | */ |
michael@0 | 43 | class SkRTree : public SkBBoxHierarchy { |
michael@0 | 44 | public: |
michael@0 | 45 | SK_DECLARE_INST_COUNT(SkRTree) |
michael@0 | 46 | |
michael@0 | 47 | /** |
michael@0 | 48 | * Create a new R-Tree with specified min/max child counts. |
michael@0 | 49 | * The child counts are valid iff: |
michael@0 | 50 | * - (max + 1) / 2 >= min (splitting an overfull node must be enough to populate 2 nodes) |
michael@0 | 51 | * - min < max |
michael@0 | 52 | * - min > 0 |
michael@0 | 53 | * - max < SK_MaxU16 |
michael@0 | 54 | * If you have some prior information about the distribution of bounds you're expecting, you |
michael@0 | 55 | * can provide an optional aspect ratio parameter. This allows the bulk-load algorithm to create |
michael@0 | 56 | * better proportioned tiles of rectangles. |
michael@0 | 57 | */ |
michael@0 | 58 | static SkRTree* Create(int minChildren, int maxChildren, SkScalar aspectRatio = 1, |
michael@0 | 59 | bool orderWhenBulkLoading = true); |
michael@0 | 60 | virtual ~SkRTree(); |
michael@0 | 61 | |
michael@0 | 62 | /** |
michael@0 | 63 | * Insert a node, consisting of bounds and a data value into the tree, if we don't immediately |
michael@0 | 64 | * need to use the tree; we may allow the insert to be deferred (this can allow us to bulk-load |
michael@0 | 65 | * a large batch of nodes at once, which tends to be faster and produce a better tree). |
michael@0 | 66 | * @param data The data value |
michael@0 | 67 | * @param bounds The corresponding bounding box |
michael@0 | 68 | * @param defer Can this insert be deferred? (this may be ignored) |
michael@0 | 69 | */ |
michael@0 | 70 | virtual void insert(void* data, const SkIRect& bounds, bool defer = false) SK_OVERRIDE; |
michael@0 | 71 | |
michael@0 | 72 | /** |
michael@0 | 73 | * If any inserts have been deferred, this will add them into the tree |
michael@0 | 74 | */ |
michael@0 | 75 | virtual void flushDeferredInserts() SK_OVERRIDE; |
michael@0 | 76 | |
michael@0 | 77 | /** |
michael@0 | 78 | * Given a query rectangle, populates the passed-in array with the elements it intersects |
michael@0 | 79 | */ |
michael@0 | 80 | virtual void search(const SkIRect& query, SkTDArray<void*>* results) SK_OVERRIDE; |
michael@0 | 81 | |
michael@0 | 82 | virtual void clear() SK_OVERRIDE; |
michael@0 | 83 | bool isEmpty() const { return 0 == fCount; } |
michael@0 | 84 | |
michael@0 | 85 | /** |
michael@0 | 86 | * Gets the depth of the tree structure |
michael@0 | 87 | */ |
michael@0 | 88 | virtual int getDepth() const SK_OVERRIDE { |
michael@0 | 89 | return this->isEmpty() ? 0 : fRoot.fChild.subtree->fLevel + 1; |
michael@0 | 90 | } |
michael@0 | 91 | |
michael@0 | 92 | /** |
michael@0 | 93 | * This gets the insertion count (rather than the node count) |
michael@0 | 94 | */ |
michael@0 | 95 | virtual int getCount() const SK_OVERRIDE { return fCount; } |
michael@0 | 96 | |
michael@0 | 97 | virtual void rewindInserts() SK_OVERRIDE; |
michael@0 | 98 | |
michael@0 | 99 | private: |
michael@0 | 100 | |
michael@0 | 101 | struct Node; |
michael@0 | 102 | |
michael@0 | 103 | /** |
michael@0 | 104 | * A branch of the tree, this may contain a pointer to another interior node, or a data value |
michael@0 | 105 | */ |
michael@0 | 106 | struct Branch { |
michael@0 | 107 | union { |
michael@0 | 108 | Node* subtree; |
michael@0 | 109 | void* data; |
michael@0 | 110 | } fChild; |
michael@0 | 111 | SkIRect fBounds; |
michael@0 | 112 | }; |
michael@0 | 113 | |
michael@0 | 114 | /** |
michael@0 | 115 | * A node in the tree, has between fMinChildren and fMaxChildren (the root is a special case) |
michael@0 | 116 | */ |
michael@0 | 117 | struct Node { |
michael@0 | 118 | uint16_t fNumChildren; |
michael@0 | 119 | uint16_t fLevel; |
michael@0 | 120 | bool isLeaf() { return 0 == fLevel; } |
michael@0 | 121 | // Since we want to be able to pick min/max child counts at runtime, we assume the creator |
michael@0 | 122 | // has allocated sufficient space directly after us in memory, and index into that space |
michael@0 | 123 | Branch* child(size_t index) { |
michael@0 | 124 | return reinterpret_cast<Branch*>(this + 1) + index; |
michael@0 | 125 | } |
michael@0 | 126 | }; |
michael@0 | 127 | |
michael@0 | 128 | typedef int32_t SkIRect::*SortSide; |
michael@0 | 129 | |
michael@0 | 130 | // Helper for sorting our children arrays by sides of their rects |
michael@0 | 131 | struct RectLessThan { |
michael@0 | 132 | RectLessThan(SkRTree::SortSide side) : fSide(side) { } |
michael@0 | 133 | bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) const { |
michael@0 | 134 | return lhs.fBounds.*fSide < rhs.fBounds.*fSide; |
michael@0 | 135 | } |
michael@0 | 136 | private: |
michael@0 | 137 | const SkRTree::SortSide fSide; |
michael@0 | 138 | }; |
michael@0 | 139 | |
michael@0 | 140 | struct RectLessX { |
michael@0 | 141 | bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) { |
michael@0 | 142 | return ((lhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1) < |
michael@0 | 143 | ((rhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1); |
michael@0 | 144 | } |
michael@0 | 145 | }; |
michael@0 | 146 | |
michael@0 | 147 | struct RectLessY { |
michael@0 | 148 | bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) { |
michael@0 | 149 | return ((lhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1) < |
michael@0 | 150 | ((rhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1); |
michael@0 | 151 | } |
michael@0 | 152 | }; |
michael@0 | 153 | |
michael@0 | 154 | SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio, bool orderWhenBulkLoading); |
michael@0 | 155 | |
michael@0 | 156 | /** |
michael@0 | 157 | * Recursively descend the tree to find an insertion position for 'branch', updates |
michael@0 | 158 | * bounding boxes on the way up. |
michael@0 | 159 | */ |
michael@0 | 160 | Branch* insert(Node* root, Branch* branch, uint16_t level = 0); |
michael@0 | 161 | |
michael@0 | 162 | int chooseSubtree(Node* root, Branch* branch); |
michael@0 | 163 | SkIRect computeBounds(Node* n); |
michael@0 | 164 | int distributeChildren(Branch* children); |
michael@0 | 165 | void search(Node* root, const SkIRect query, SkTDArray<void*>* results) const; |
michael@0 | 166 | |
michael@0 | 167 | /** |
michael@0 | 168 | * This performs a bottom-up bulk load using the STR (sort-tile-recursive) algorithm, this |
michael@0 | 169 | * seems to generally produce better, more consistent trees at significantly lower cost than |
michael@0 | 170 | * repeated insertions. |
michael@0 | 171 | * |
michael@0 | 172 | * This consumes the input array. |
michael@0 | 173 | * |
michael@0 | 174 | * TODO: Experiment with other bulk-load algorithms (in particular the Hilbert pack variant, |
michael@0 | 175 | * which groups rects by position on the Hilbert curve, is probably worth a look). There also |
michael@0 | 176 | * exist top-down bulk load variants (VAMSplit, TopDownGreedy, etc). |
michael@0 | 177 | */ |
michael@0 | 178 | Branch bulkLoad(SkTDArray<Branch>* branches, int level = 0); |
michael@0 | 179 | |
michael@0 | 180 | void validate(); |
michael@0 | 181 | int validateSubtree(Node* root, SkIRect bounds, bool isRoot = false); |
michael@0 | 182 | |
michael@0 | 183 | const int fMinChildren; |
michael@0 | 184 | const int fMaxChildren; |
michael@0 | 185 | const size_t fNodeSize; |
michael@0 | 186 | |
michael@0 | 187 | // This is the count of data elements (rather than total nodes in the tree) |
michael@0 | 188 | int fCount; |
michael@0 | 189 | |
michael@0 | 190 | Branch fRoot; |
michael@0 | 191 | SkChunkAlloc fNodes; |
michael@0 | 192 | SkTDArray<Branch> fDeferredInserts; |
michael@0 | 193 | SkScalar fAspectRatio; |
michael@0 | 194 | bool fSortWhenBulkLoading; |
michael@0 | 195 | |
michael@0 | 196 | Node* allocateNode(uint16_t level); |
michael@0 | 197 | |
michael@0 | 198 | typedef SkBBoxHierarchy INHERITED; |
michael@0 | 199 | }; |
michael@0 | 200 | |
michael@0 | 201 | #endif |