gfx/skia/trunk/src/core/SkRTree.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

michael@0 1 /*
michael@0 2 * Copyright 2012 Google Inc.
michael@0 3 *
michael@0 4 * Use of this source code is governed by a BSD-style license that can be
michael@0 5 * found in the LICENSE file.
michael@0 6 */
michael@0 7
michael@0 8 #include "SkRTree.h"
michael@0 9 #include "SkTSort.h"
michael@0 10
michael@0 11 static inline uint32_t get_area(const SkIRect& rect);
michael@0 12 static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2);
michael@0 13 static inline uint32_t get_margin(const SkIRect& rect);
michael@0 14 static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2);
michael@0 15 static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out);
michael@0 16
michael@0 17 ///////////////////////////////////////////////////////////////////////////////////////////////////
michael@0 18
michael@0 19 SkRTree* SkRTree::Create(int minChildren, int maxChildren, SkScalar aspectRatio,
michael@0 20 bool sortWhenBulkLoading) {
michael@0 21 if (minChildren < maxChildren && (maxChildren + 1) / 2 >= minChildren &&
michael@0 22 minChildren > 0 && maxChildren < static_cast<int>(SK_MaxU16)) {
michael@0 23 return new SkRTree(minChildren, maxChildren, aspectRatio, sortWhenBulkLoading);
michael@0 24 }
michael@0 25 return NULL;
michael@0 26 }
michael@0 27
michael@0 28 SkRTree::SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio,
michael@0 29 bool sortWhenBulkLoading)
michael@0 30 : fMinChildren(minChildren)
michael@0 31 , fMaxChildren(maxChildren)
michael@0 32 , fNodeSize(sizeof(Node) + sizeof(Branch) * maxChildren)
michael@0 33 , fCount(0)
michael@0 34 , fNodes(fNodeSize * 256)
michael@0 35 , fAspectRatio(aspectRatio)
michael@0 36 , fSortWhenBulkLoading(sortWhenBulkLoading) {
michael@0 37 SkASSERT(minChildren < maxChildren && minChildren > 0 && maxChildren <
michael@0 38 static_cast<int>(SK_MaxU16));
michael@0 39 SkASSERT((maxChildren + 1) / 2 >= minChildren);
michael@0 40 this->validate();
michael@0 41 }
michael@0 42
michael@0 43 SkRTree::~SkRTree() {
michael@0 44 this->clear();
michael@0 45 }
michael@0 46
michael@0 47 void SkRTree::insert(void* data, const SkIRect& bounds, bool defer) {
michael@0 48 this->validate();
michael@0 49 if (bounds.isEmpty()) {
michael@0 50 SkASSERT(false);
michael@0 51 return;
michael@0 52 }
michael@0 53 Branch newBranch;
michael@0 54 newBranch.fBounds = bounds;
michael@0 55 newBranch.fChild.data = data;
michael@0 56 if (this->isEmpty()) {
michael@0 57 // since a bulk-load into an existing tree is as of yet unimplemented (and arguably not
michael@0 58 // of vital importance right now), we only batch up inserts if the tree is empty.
michael@0 59 if (defer) {
michael@0 60 fDeferredInserts.push(newBranch);
michael@0 61 return;
michael@0 62 } else {
michael@0 63 fRoot.fChild.subtree = allocateNode(0);
michael@0 64 fRoot.fChild.subtree->fNumChildren = 0;
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 Branch* newSibling = insert(fRoot.fChild.subtree, &newBranch);
michael@0 69 fRoot.fBounds = this->computeBounds(fRoot.fChild.subtree);
michael@0 70
michael@0 71 if (NULL != newSibling) {
michael@0 72 Node* oldRoot = fRoot.fChild.subtree;
michael@0 73 Node* newRoot = this->allocateNode(oldRoot->fLevel + 1);
michael@0 74 newRoot->fNumChildren = 2;
michael@0 75 *newRoot->child(0) = fRoot;
michael@0 76 *newRoot->child(1) = *newSibling;
michael@0 77 fRoot.fChild.subtree = newRoot;
michael@0 78 fRoot.fBounds = this->computeBounds(fRoot.fChild.subtree);
michael@0 79 }
michael@0 80
michael@0 81 ++fCount;
michael@0 82 this->validate();
michael@0 83 }
michael@0 84
michael@0 85 void SkRTree::flushDeferredInserts() {
michael@0 86 this->validate();
michael@0 87 if (this->isEmpty() && fDeferredInserts.count() > 0) {
michael@0 88 fCount = fDeferredInserts.count();
michael@0 89 if (1 == fCount) {
michael@0 90 fRoot.fChild.subtree = allocateNode(0);
michael@0 91 fRoot.fChild.subtree->fNumChildren = 0;
michael@0 92 this->insert(fRoot.fChild.subtree, &fDeferredInserts[0]);
michael@0 93 fRoot.fBounds = fDeferredInserts[0].fBounds;
michael@0 94 } else {
michael@0 95 fRoot = this->bulkLoad(&fDeferredInserts);
michael@0 96 }
michael@0 97 } else {
michael@0 98 // TODO: some algorithm for bulk loading into an already populated tree
michael@0 99 SkASSERT(0 == fDeferredInserts.count());
michael@0 100 }
michael@0 101 fDeferredInserts.rewind();
michael@0 102 this->validate();
michael@0 103 }
michael@0 104
michael@0 105 void SkRTree::search(const SkIRect& query, SkTDArray<void*>* results) {
michael@0 106 this->validate();
michael@0 107 if (0 != fDeferredInserts.count()) {
michael@0 108 this->flushDeferredInserts();
michael@0 109 }
michael@0 110 if (!this->isEmpty() && SkIRect::IntersectsNoEmptyCheck(fRoot.fBounds, query)) {
michael@0 111 this->search(fRoot.fChild.subtree, query, results);
michael@0 112 }
michael@0 113 this->validate();
michael@0 114 }
michael@0 115
michael@0 116 void SkRTree::clear() {
michael@0 117 this->validate();
michael@0 118 fNodes.reset();
michael@0 119 fDeferredInserts.rewind();
michael@0 120 fCount = 0;
michael@0 121 this->validate();
michael@0 122 }
michael@0 123
michael@0 124 SkRTree::Node* SkRTree::allocateNode(uint16_t level) {
michael@0 125 Node* out = static_cast<Node*>(fNodes.allocThrow(fNodeSize));
michael@0 126 out->fNumChildren = 0;
michael@0 127 out->fLevel = level;
michael@0 128 return out;
michael@0 129 }
michael@0 130
michael@0 131 SkRTree::Branch* SkRTree::insert(Node* root, Branch* branch, uint16_t level) {
michael@0 132 Branch* toInsert = branch;
michael@0 133 if (root->fLevel != level) {
michael@0 134 int childIndex = this->chooseSubtree(root, branch);
michael@0 135 toInsert = this->insert(root->child(childIndex)->fChild.subtree, branch, level);
michael@0 136 root->child(childIndex)->fBounds = this->computeBounds(
michael@0 137 root->child(childIndex)->fChild.subtree);
michael@0 138 }
michael@0 139 if (NULL != toInsert) {
michael@0 140 if (root->fNumChildren == fMaxChildren) {
michael@0 141 // handle overflow by splitting. TODO: opportunistic reinsertion
michael@0 142
michael@0 143 // decide on a distribution to divide with
michael@0 144 Node* newSibling = this->allocateNode(root->fLevel);
michael@0 145 Branch* toDivide = SkNEW_ARRAY(Branch, fMaxChildren + 1);
michael@0 146 for (int i = 0; i < fMaxChildren; ++i) {
michael@0 147 toDivide[i] = *root->child(i);
michael@0 148 }
michael@0 149 toDivide[fMaxChildren] = *toInsert;
michael@0 150 int splitIndex = this->distributeChildren(toDivide);
michael@0 151
michael@0 152 // divide up the branches
michael@0 153 root->fNumChildren = splitIndex;
michael@0 154 newSibling->fNumChildren = fMaxChildren + 1 - splitIndex;
michael@0 155 for (int i = 0; i < splitIndex; ++i) {
michael@0 156 *root->child(i) = toDivide[i];
michael@0 157 }
michael@0 158 for (int i = splitIndex; i < fMaxChildren + 1; ++i) {
michael@0 159 *newSibling->child(i - splitIndex) = toDivide[i];
michael@0 160 }
michael@0 161 SkDELETE_ARRAY(toDivide);
michael@0 162
michael@0 163 // pass the new sibling branch up to the parent
michael@0 164 branch->fChild.subtree = newSibling;
michael@0 165 branch->fBounds = this->computeBounds(newSibling);
michael@0 166 return branch;
michael@0 167 } else {
michael@0 168 *root->child(root->fNumChildren) = *toInsert;
michael@0 169 ++root->fNumChildren;
michael@0 170 return NULL;
michael@0 171 }
michael@0 172 }
michael@0 173 return NULL;
michael@0 174 }
michael@0 175
michael@0 176 int SkRTree::chooseSubtree(Node* root, Branch* branch) {
michael@0 177 SkASSERT(!root->isLeaf());
michael@0 178 if (1 < root->fLevel) {
michael@0 179 // root's child pointers do not point to leaves, so minimize area increase
michael@0 180 int32_t minAreaIncrease = SK_MaxS32;
michael@0 181 int32_t minArea = SK_MaxS32;
michael@0 182 int32_t bestSubtree = -1;
michael@0 183 for (int i = 0; i < root->fNumChildren; ++i) {
michael@0 184 const SkIRect& subtreeBounds = root->child(i)->fBounds;
michael@0 185 int32_t areaIncrease = get_area_increase(subtreeBounds, branch->fBounds);
michael@0 186 // break ties in favor of subtree with smallest area
michael@0 187 if (areaIncrease < minAreaIncrease || (areaIncrease == minAreaIncrease &&
michael@0 188 static_cast<int32_t>(get_area(subtreeBounds)) < minArea)) {
michael@0 189 minAreaIncrease = areaIncrease;
michael@0 190 minArea = get_area(subtreeBounds);
michael@0 191 bestSubtree = i;
michael@0 192 }
michael@0 193 }
michael@0 194 SkASSERT(-1 != bestSubtree);
michael@0 195 return bestSubtree;
michael@0 196 } else if (1 == root->fLevel) {
michael@0 197 // root's child pointers do point to leaves, so minimize overlap increase
michael@0 198 int32_t minOverlapIncrease = SK_MaxS32;
michael@0 199 int32_t minAreaIncrease = SK_MaxS32;
michael@0 200 int32_t bestSubtree = -1;
michael@0 201 for (int32_t i = 0; i < root->fNumChildren; ++i) {
michael@0 202 const SkIRect& subtreeBounds = root->child(i)->fBounds;
michael@0 203 SkIRect expandedBounds = subtreeBounds;
michael@0 204 join_no_empty_check(branch->fBounds, &expandedBounds);
michael@0 205 int32_t overlap = 0;
michael@0 206 for (int32_t j = 0; j < root->fNumChildren; ++j) {
michael@0 207 if (j == i) continue;
michael@0 208 // Note: this would be more correct if we subtracted the original pre-expanded
michael@0 209 // overlap, but computing overlaps is expensive and omitting it doesn't seem to
michael@0 210 // hurt query performance. See get_overlap_increase()
michael@0 211 overlap += get_overlap(expandedBounds, root->child(j)->fBounds);
michael@0 212 }
michael@0 213 // break ties with lowest area increase
michael@0 214 if (overlap < minOverlapIncrease || (overlap == minOverlapIncrease &&
michael@0 215 static_cast<int32_t>(get_area_increase(branch->fBounds, subtreeBounds)) <
michael@0 216 minAreaIncrease)) {
michael@0 217 minOverlapIncrease = overlap;
michael@0 218 minAreaIncrease = get_area_increase(branch->fBounds, subtreeBounds);
michael@0 219 bestSubtree = i;
michael@0 220 }
michael@0 221 }
michael@0 222 return bestSubtree;
michael@0 223 } else {
michael@0 224 SkASSERT(false);
michael@0 225 return 0;
michael@0 226 }
michael@0 227 }
michael@0 228
michael@0 229 SkIRect SkRTree::computeBounds(Node* n) {
michael@0 230 SkIRect r = n->child(0)->fBounds;
michael@0 231 for (int i = 1; i < n->fNumChildren; ++i) {
michael@0 232 join_no_empty_check(n->child(i)->fBounds, &r);
michael@0 233 }
michael@0 234 return r;
michael@0 235 }
michael@0 236
michael@0 237 int SkRTree::distributeChildren(Branch* children) {
michael@0 238 // We have two sides to sort by on each of two axes:
michael@0 239 const static SortSide sorts[2][2] = {
michael@0 240 {&SkIRect::fLeft, &SkIRect::fRight},
michael@0 241 {&SkIRect::fTop, &SkIRect::fBottom}
michael@0 242 };
michael@0 243
michael@0 244 // We want to choose an axis to split on, then a distribution along that axis; we'll need
michael@0 245 // three pieces of info: the split axis, the side to sort by on that axis, and the index
michael@0 246 // to split the sorted array on.
michael@0 247 int32_t sortSide = -1;
michael@0 248 int32_t k = -1;
michael@0 249 int32_t axis = -1;
michael@0 250 int32_t bestS = SK_MaxS32;
michael@0 251
michael@0 252 // Evaluate each axis, we want the min summed margin-value (s) over all distributions
michael@0 253 for (int i = 0; i < 2; ++i) {
michael@0 254 int32_t minOverlap = SK_MaxS32;
michael@0 255 int32_t minArea = SK_MaxS32;
michael@0 256 int32_t axisBestK = 0;
michael@0 257 int32_t axisBestSide = 0;
michael@0 258 int32_t s = 0;
michael@0 259
michael@0 260 // Evaluate each sort
michael@0 261 for (int j = 0; j < 2; ++j) {
michael@0 262 SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j]));
michael@0 263
michael@0 264 // Evaluate each split index
michael@0 265 for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) {
michael@0 266 SkIRect r1 = children[0].fBounds;
michael@0 267 SkIRect r2 = children[fMinChildren + k - 1].fBounds;
michael@0 268 for (int32_t l = 1; l < fMinChildren - 1 + k; ++l) {
michael@0 269 join_no_empty_check(children[l].fBounds, &r1);
michael@0 270 }
michael@0 271 for (int32_t l = fMinChildren + k; l < fMaxChildren + 1; ++l) {
michael@0 272 join_no_empty_check(children[l].fBounds, &r2);
michael@0 273 }
michael@0 274
michael@0 275 int32_t area = get_area(r1) + get_area(r2);
michael@0 276 int32_t overlap = get_overlap(r1, r2);
michael@0 277 s += get_margin(r1) + get_margin(r2);
michael@0 278
michael@0 279 if (overlap < minOverlap || (overlap == minOverlap && area < minArea)) {
michael@0 280 minOverlap = overlap;
michael@0 281 minArea = area;
michael@0 282 axisBestSide = j;
michael@0 283 axisBestK = k;
michael@0 284 }
michael@0 285 }
michael@0 286 }
michael@0 287
michael@0 288 if (s < bestS) {
michael@0 289 bestS = s;
michael@0 290 axis = i;
michael@0 291 sortSide = axisBestSide;
michael@0 292 k = axisBestK;
michael@0 293 }
michael@0 294 }
michael@0 295
michael@0 296 // replicate the sort of the winning distribution, (we can skip this if the last
michael@0 297 // sort ended up being best)
michael@0 298 if (!(axis == 1 && sortSide == 1)) {
michael@0 299 SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sortSide]));
michael@0 300 }
michael@0 301
michael@0 302 return fMinChildren - 1 + k;
michael@0 303 }
michael@0 304
michael@0 305 void SkRTree::search(Node* root, const SkIRect query, SkTDArray<void*>* results) const {
michael@0 306 for (int i = 0; i < root->fNumChildren; ++i) {
michael@0 307 if (SkIRect::IntersectsNoEmptyCheck(root->child(i)->fBounds, query)) {
michael@0 308 if (root->isLeaf()) {
michael@0 309 results->push(root->child(i)->fChild.data);
michael@0 310 } else {
michael@0 311 this->search(root->child(i)->fChild.subtree, query, results);
michael@0 312 }
michael@0 313 }
michael@0 314 }
michael@0 315 }
michael@0 316
michael@0 317 SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) {
michael@0 318 if (branches->count() == 1) {
michael@0 319 // Only one branch: it will be the root
michael@0 320 Branch out = (*branches)[0];
michael@0 321 branches->rewind();
michael@0 322 return out;
michael@0 323 } else {
michael@0 324 // We sort the whole list by y coordinates, if we are told to do so.
michael@0 325 //
michael@0 326 // We expect Webkit / Blink to give us a reasonable x,y order.
michael@0 327 // Avoiding this call resulted in a 17% win for recording with
michael@0 328 // negligible difference in playback speed.
michael@0 329 if (fSortWhenBulkLoading) {
michael@0 330 SkTQSort(branches->begin(), branches->end() - 1, RectLessY());
michael@0 331 }
michael@0 332
michael@0 333 int numBranches = branches->count() / fMaxChildren;
michael@0 334 int remainder = branches->count() % fMaxChildren;
michael@0 335 int newBranches = 0;
michael@0 336
michael@0 337 if (0 != remainder) {
michael@0 338 ++numBranches;
michael@0 339 // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to
michael@0 340 // some other branches to make up for it
michael@0 341 if (remainder >= fMinChildren) {
michael@0 342 remainder = 0;
michael@0 343 } else {
michael@0 344 remainder = fMinChildren - remainder;
michael@0 345 }
michael@0 346 }
michael@0 347
michael@0 348 int numStrips = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(numBranches) *
michael@0 349 SkScalarInvert(fAspectRatio)));
michael@0 350 int numTiles = SkScalarCeilToInt(SkIntToScalar(numBranches) /
michael@0 351 SkIntToScalar(numStrips));
michael@0 352 int currentBranch = 0;
michael@0 353
michael@0 354 for (int i = 0; i < numStrips; ++i) {
michael@0 355 // Once again, if we are told to do so, we sort by x.
michael@0 356 if (fSortWhenBulkLoading) {
michael@0 357 int begin = currentBranch;
michael@0 358 int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder,
michael@0 359 (fMaxChildren - fMinChildren) * numTiles);
michael@0 360 if (end > branches->count()) {
michael@0 361 end = branches->count();
michael@0 362 }
michael@0 363
michael@0 364 // Now we sort horizontal strips of rectangles by their x coords
michael@0 365 SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX());
michael@0 366 }
michael@0 367
michael@0 368 for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) {
michael@0 369 int incrementBy = fMaxChildren;
michael@0 370 if (remainder != 0) {
michael@0 371 // if need be, omit some nodes to make up for remainder
michael@0 372 if (remainder <= fMaxChildren - fMinChildren) {
michael@0 373 incrementBy -= remainder;
michael@0 374 remainder = 0;
michael@0 375 } else {
michael@0 376 incrementBy = fMinChildren;
michael@0 377 remainder -= fMaxChildren - fMinChildren;
michael@0 378 }
michael@0 379 }
michael@0 380 Node* n = allocateNode(level);
michael@0 381 n->fNumChildren = 1;
michael@0 382 *n->child(0) = (*branches)[currentBranch];
michael@0 383 Branch b;
michael@0 384 b.fBounds = (*branches)[currentBranch].fBounds;
michael@0 385 b.fChild.subtree = n;
michael@0 386 ++currentBranch;
michael@0 387 for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) {
michael@0 388 b.fBounds.join((*branches)[currentBranch].fBounds);
michael@0 389 *n->child(k) = (*branches)[currentBranch];
michael@0 390 ++n->fNumChildren;
michael@0 391 ++currentBranch;
michael@0 392 }
michael@0 393 (*branches)[newBranches] = b;
michael@0 394 ++newBranches;
michael@0 395 }
michael@0 396 }
michael@0 397 branches->setCount(newBranches);
michael@0 398 return this->bulkLoad(branches, level + 1);
michael@0 399 }
michael@0 400 }
michael@0 401
michael@0 402 void SkRTree::validate() {
michael@0 403 #ifdef SK_DEBUG
michael@0 404 if (this->isEmpty()) {
michael@0 405 return;
michael@0 406 }
michael@0 407 SkASSERT(fCount == this->validateSubtree(fRoot.fChild.subtree, fRoot.fBounds, true));
michael@0 408 #endif
michael@0 409 }
michael@0 410
michael@0 411 int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) {
michael@0 412 // make sure the pointer is pointing to a valid place
michael@0 413 SkASSERT(fNodes.contains(static_cast<void*>(root)));
michael@0 414
michael@0 415 if (isRoot) {
michael@0 416 // If the root of this subtree is the overall root, we have looser standards:
michael@0 417 if (root->isLeaf()) {
michael@0 418 SkASSERT(root->fNumChildren >= 1 && root->fNumChildren <= fMaxChildren);
michael@0 419 } else {
michael@0 420 SkASSERT(root->fNumChildren >= 2 && root->fNumChildren <= fMaxChildren);
michael@0 421 }
michael@0 422 } else {
michael@0 423 SkASSERT(root->fNumChildren >= fMinChildren && root->fNumChildren <= fMaxChildren);
michael@0 424 }
michael@0 425
michael@0 426 for (int i = 0; i < root->fNumChildren; ++i) {
michael@0 427 SkASSERT(bounds.contains(root->child(i)->fBounds));
michael@0 428 }
michael@0 429
michael@0 430 if (root->isLeaf()) {
michael@0 431 SkASSERT(0 == root->fLevel);
michael@0 432 return root->fNumChildren;
michael@0 433 } else {
michael@0 434 int childCount = 0;
michael@0 435 for (int i = 0; i < root->fNumChildren; ++i) {
michael@0 436 SkASSERT(root->child(i)->fChild.subtree->fLevel == root->fLevel - 1);
michael@0 437 childCount += this->validateSubtree(root->child(i)->fChild.subtree,
michael@0 438 root->child(i)->fBounds);
michael@0 439 }
michael@0 440 return childCount;
michael@0 441 }
michael@0 442 }
michael@0 443
michael@0 444 void SkRTree::rewindInserts() {
michael@0 445 SkASSERT(this->isEmpty()); // Currently only supports deferred inserts
michael@0 446 while (!fDeferredInserts.isEmpty() &&
michael@0 447 fClient->shouldRewind(fDeferredInserts.top().fChild.data)) {
michael@0 448 fDeferredInserts.pop();
michael@0 449 }
michael@0 450 }
michael@0 451
michael@0 452 ///////////////////////////////////////////////////////////////////////////////////////////////////
michael@0 453
michael@0 454 static inline uint32_t get_area(const SkIRect& rect) {
michael@0 455 return rect.width() * rect.height();
michael@0 456 }
michael@0 457
michael@0 458 static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2) {
michael@0 459 // I suspect there's a more efficient way of computing this...
michael@0 460 return SkMax32(0, SkMin32(rect1.fRight, rect2.fRight) - SkMax32(rect1.fLeft, rect2.fLeft)) *
michael@0 461 SkMax32(0, SkMin32(rect1.fBottom, rect2.fBottom) - SkMax32(rect1.fTop, rect2.fTop));
michael@0 462 }
michael@0 463
michael@0 464 // Get the margin (aka perimeter)
michael@0 465 static inline uint32_t get_margin(const SkIRect& rect) {
michael@0 466 return 2 * (rect.width() + rect.height());
michael@0 467 }
michael@0 468
michael@0 469 static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2) {
michael@0 470 join_no_empty_check(rect1, &rect2);
michael@0 471 return get_area(rect2) - get_area(rect1);
michael@0 472 }
michael@0 473
michael@0 474 // Expand 'out' to include 'joinWith'
michael@0 475 static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out) {
michael@0 476 // since we check for empty bounds on insert, we know we'll never have empty rects
michael@0 477 // and we can save the empty check that SkIRect::join requires
michael@0 478 if (joinWith.fLeft < out->fLeft) { out->fLeft = joinWith.fLeft; }
michael@0 479 if (joinWith.fTop < out->fTop) { out->fTop = joinWith.fTop; }
michael@0 480 if (joinWith.fRight > out->fRight) { out->fRight = joinWith.fRight; }
michael@0 481 if (joinWith.fBottom > out->fBottom) { out->fBottom = joinWith.fBottom; }
michael@0 482 }

mercurial