1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.tools.common.datatypes 18 19 import android.tools.common.datatypes.Region.Companion.from 20 import kotlin.js.JsExport 21 import kotlin.js.JsName 22 import kotlin.math.min 23 24 /** 25 * Wrapper for RegionProto (frameworks/native/services/surfaceflinger/layerproto/common.proto) 26 * 27 * Implementation based android.graphics.Region's native implementation found in SkRegion.cpp 28 * 29 * This class is used by flicker and Winscope 30 * 31 * It has a single constructor and different [from] functions on its companion because JS doesn't 32 * support constructor overload 33 */ 34 @JsExport 35 class Region(rects: Array<Rect> = arrayOf()) : DataType() { 36 private var fBounds = Rect.EMPTY 37 private var fRunHead: RunHead? = RunHead(isEmptyHead = true) 38 39 init { 40 if (rects.isEmpty()) { 41 setEmpty() 42 } else { 43 for (rect in rects) { 44 union(rect) 45 } 46 } 47 } 48 49 @JsName("rects") 50 val rects 51 get() = getRectsFromString(toString()) 52 53 @JsName("width") 54 val width: Int 55 get() = bounds.width 56 @JsName("height") 57 val height: Int 58 get() = bounds.height 59 60 // if null we are a rect not empty 61 override val isEmpty 62 get() = fRunHead?.isEmptyHead ?: false 63 @JsName("bounds") 64 val bounds 65 get() = fBounds 66 67 /** Set the region to the empty region */ 68 @JsName("setEmpty") 69 fun setEmpty(): Boolean { 70 fBounds = Rect.EMPTY 71 fRunHead = RunHead(isEmptyHead = true) 72 73 return false 74 } 75 76 /** Set the region to the specified region. */ 77 @JsName("setRegion") 78 fun set(region: Region): Boolean { 79 fBounds = region.fBounds.clone() 80 fRunHead = region.fRunHead?.clone() 81 return !(fRunHead?.isEmptyHead ?: false) 82 } 83 84 /** Set the region to the specified rectangle */ 85 @JsName("setRect") 86 fun set(r: Rect): Boolean { 87 return if (r.isEmpty || RUN_TYPE_SENTINEL == r.right || RUN_TYPE_SENTINEL == r.bottom) { 88 this.setEmpty() 89 } else { 90 fBounds = r 91 fRunHead = null 92 true 93 } 94 } 95 96 /** Set the region to the specified rectangle */ 97 @JsName("set") 98 operator fun set(left: Int, top: Int, right: Int, bottom: Int): Boolean { 99 return set(Rect.withoutCache(left, top, right, bottom)) 100 } 101 102 @JsName("isRect") 103 fun isRect(): Boolean { 104 return fRunHead == null 105 } 106 107 @JsName("isComplex") 108 fun isComplex(): Boolean { 109 return !this.isEmpty && !this.isRect() 110 } 111 112 @JsName("contains") 113 fun contains(x: Int, y: Int): Boolean { 114 if (!fBounds.contains(x, y)) { 115 return false 116 } 117 if (this.isRect()) { 118 return true 119 } 120 require(this.isComplex()) 121 122 val runs = fRunHead!!.findScanline(y) 123 124 // Skip the Bottom and IntervalCount 125 var runsIndex = 2 126 127 // Just walk this scanline, checking each interval. The X-sentinel will 128 // appear as a left-interval (runs[0]) and should abort the search. 129 // 130 // We could do a bsearch, using interval-count (runs[1]), but need to time 131 // when that would be worthwhile. 132 // 133 while (true) { 134 if (x < runs[runsIndex]) { 135 break 136 } 137 if (x < runs[runsIndex + 1]) { 138 return true 139 } 140 runsIndex += 2 141 } 142 return false 143 } 144 145 class Iterator(private val rgn: Region) { 146 private var done: Boolean 147 private var rect: Rect 148 private var fRuns: Array<Int>? = null 149 private var fRunsIndex = 0 150 151 init { 152 fRunsIndex = 0 153 if (rgn.isEmpty) { 154 rect = Rect.EMPTY 155 done = true 156 } else { 157 done = false 158 if (rgn.isRect()) { 159 rect = rgn.fBounds 160 fRuns = null 161 } else { 162 fRuns = rgn.fRunHead!!.readonlyRuns 163 rect = Rect.withoutCache(fRuns!![3], fRuns!![0], fRuns!![4], fRuns!![1]) 164 fRunsIndex = 5 165 } 166 } 167 } 168 169 fun next() { 170 if (done) { 171 return 172 } 173 174 if (fRuns == null) { // rect case 175 done = true 176 return 177 } 178 179 val runs = fRuns!! 180 var runsIndex = fRunsIndex 181 182 if (runs[runsIndex] < RUN_TYPE_SENTINEL) { // valid X value 183 rect = 184 Rect.withoutCache(runs[runsIndex], rect.top, runs[runsIndex + 1], rect.bottom) 185 runsIndex += 2 186 } else { // we're at the end of a line 187 runsIndex += 1 188 if (runs[runsIndex] < RUN_TYPE_SENTINEL) { // valid Y value 189 val intervals = runs[runsIndex + 1] 190 if (0 == intervals) { // empty line 191 rect = 192 Rect.withoutCache(rect.left, runs[runsIndex], rect.right, rect.bottom) 193 runsIndex += 3 194 } else { 195 rect = Rect.withoutCache(rect.left, rect.bottom, rect.right, rect.bottom) 196 } 197 198 assertSentinel(runs[runsIndex + 2], false) 199 assertSentinel(runs[runsIndex + 3], false) 200 rect = 201 Rect.withoutCache( 202 runs[runsIndex + 2], 203 rect.top, 204 runs[runsIndex + 3], 205 runs[runsIndex] 206 ) 207 runsIndex += 4 208 } else { // end of rgn 209 done = true 210 } 211 } 212 fRunsIndex = runsIndex 213 } 214 215 @JsName("done") 216 fun done(): Boolean { 217 return done 218 } 219 220 fun rect(): Rect { 221 return rect 222 } 223 } 224 225 override fun doPrintValue(): String { 226 val iter = Iterator(this) 227 val result = StringBuilder("SkRegion(") 228 while (!iter.done()) { 229 val r = iter.rect() 230 result.append("(${r.left},${r.top},${r.right},${r.bottom})") 231 iter.next() 232 } 233 result.append(")") 234 return result.toString() 235 } 236 237 // the native values for these must match up with the enum in SkRegion.h 238 enum class Op(val nativeInt: Int) { 239 DIFFERENCE(0), 240 INTERSECT(1), 241 UNION(2), 242 XOR(3), 243 REVERSE_DIFFERENCE(4), 244 REPLACE(5) 245 } 246 247 fun union(r: Rect): Boolean { 248 return op(r, Op.UNION) 249 } 250 251 fun toRectF(): RectF { 252 return bounds.toRectF() 253 } 254 255 private fun oper(rgnA: Region, rgnB: Region, op: Op): Boolean { 256 // simple cases 257 when (op) { 258 Op.REPLACE -> { 259 this.set(rgnB) 260 return !rgnB.isEmpty 261 } 262 Op.REVERSE_DIFFERENCE -> { 263 // collapse difference and reverse-difference into just difference 264 return this.oper(rgnB, rgnA, Op.DIFFERENCE) 265 } 266 Op.DIFFERENCE -> { 267 if (rgnA.isEmpty) { 268 this.setEmpty() 269 return false 270 } 271 if (rgnB.isEmpty || rgnA.bounds.intersection(rgnB.bounds).isEmpty) { 272 this.set(rgnA) 273 return !rgnA.isEmpty 274 } 275 if (rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds)) { 276 this.setEmpty() 277 return false 278 } 279 } 280 Op.INTERSECT -> { 281 when { 282 rgnA.isEmpty || 283 rgnB.isEmpty || 284 rgnA.bounds.intersection(rgnB.bounds).isEmpty -> { 285 this.setEmpty() 286 return false 287 } 288 rgnA.isRect() && rgnB.isRect() -> { 289 val rectIntersection = rgnA.bounds.intersection(rgnB.bounds) 290 this.set(rgnA.bounds.intersection(rgnB.bounds)) 291 return !rectIntersection.isEmpty 292 } 293 rgnA.isRect() && rgnA.bounds.contains(rgnB.bounds) -> { 294 this.set(rgnB) 295 return !rgnB.isEmpty 296 } 297 rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds) -> { 298 this.set(rgnA) 299 return !rgnA.isEmpty 300 } 301 } 302 } 303 Op.UNION -> { 304 when { 305 rgnA.isEmpty -> { 306 this.set(rgnB) 307 return !rgnB.isEmpty 308 } 309 rgnB.isEmpty -> { 310 this.set(rgnA) 311 return !rgnA.isEmpty 312 } 313 rgnA.isRect() && rgnA.bounds.contains(rgnB.bounds) -> { 314 this.set(rgnA) 315 return !rgnA.isEmpty 316 } 317 rgnB.isRect() && rgnB.bounds.contains(rgnA.bounds) -> { 318 this.set(rgnB) 319 return !rgnB.isEmpty 320 } 321 } 322 } 323 Op.XOR -> { 324 when { 325 rgnA.isEmpty -> { 326 this.set(rgnB) 327 return !rgnB.isEmpty 328 } 329 rgnB.isEmpty -> { 330 this.set(rgnA) 331 return !rgnA.isEmpty 332 } 333 } 334 } 335 } 336 337 val array = RunArray() 338 val count = operate(rgnA.getRuns(), rgnB.getRuns(), array, op) 339 require(count <= array.count) 340 return this.setRuns(array, count) 341 } 342 343 class RunArray { 344 private val kRunArrayStackCount = 256 345 var runs: Array<Int> = Array(kRunArrayStackCount) { 0 } 346 private var fCount: Int = kRunArrayStackCount 347 348 val count: Int 349 get() = fCount 350 351 operator fun get(i: Int): Int { 352 return runs[i] 353 } 354 355 fun resizeToAtLeast(_count: Int) { 356 var count = _count 357 if (count > fCount) { 358 // leave at least 50% extra space for future growth. 359 count += count shr 1 360 val newRuns = Array(count) { 0 } 361 runs.forEachIndexed { index, value -> newRuns[index] = value } 362 runs = newRuns 363 fCount = count 364 } 365 } 366 367 operator fun set(i: Int, value: Int) { 368 runs[i] = value 369 } 370 371 fun subList(startIndex: Int, stopIndex: Int): RunArray { 372 val subRuns = RunArray() 373 subRuns.resizeToAtLeast(this.fCount) 374 for (i in startIndex until stopIndex) { 375 subRuns.runs[i - startIndex] = this.runs[i] 376 } 377 return subRuns 378 } 379 380 fun clone(): RunArray { 381 val clone = RunArray() 382 clone.runs = runs.copyOf() 383 clone.fCount = fCount 384 return clone 385 } 386 } 387 388 /** 389 * Set this region to the result of performing the Op on the specified regions. Return true if 390 * the result is not empty. 391 */ 392 @JsName("opRegions") 393 fun op(rgnA: Region, rgnB: Region, op: Op): Boolean { 394 return this.oper(rgnA, rgnB, op) 395 } 396 397 private fun getRuns(): Array<Int> { 398 val runs: Array<Int> 399 if (this.isEmpty) { 400 runs = Array(RECT_REGION_RUNS) { 0 } 401 runs[0] = RUN_TYPE_SENTINEL 402 } else if (this.isRect()) { 403 runs = buildRectRuns(fBounds) 404 } else { 405 runs = fRunHead!!.readonlyRuns 406 } 407 408 return runs 409 } 410 411 private fun buildRectRuns(bounds: Rect): Array<Int> { 412 val runs = Array(RECT_REGION_RUNS) { 0 } 413 runs[0] = bounds.top 414 runs[1] = bounds.bottom 415 runs[2] = 1 // 1 interval for this scanline 416 runs[3] = bounds.left 417 runs[4] = bounds.right 418 runs[5] = RUN_TYPE_SENTINEL 419 runs[6] = RUN_TYPE_SENTINEL 420 return runs 421 } 422 423 class RunHead(val isEmptyHead: Boolean = false) { 424 fun setRuns(runs: RunArray, count: Int) { 425 this.runs = runs 426 this.fRunCount = count 427 } 428 429 fun computeRunBounds(): Rect { 430 var runsIndex = 0 431 val top = runs[runsIndex] 432 runsIndex++ 433 434 var bot: Int 435 var ySpanCount = 0 436 var intervalCount = 0 437 var left = Int.MAX_VALUE 438 var right = Int.MIN_VALUE 439 440 do { 441 bot = runs[runsIndex] 442 runsIndex++ 443 require(bot < RUN_TYPE_SENTINEL) 444 ySpanCount += 1 445 446 val intervals = runs[runsIndex] 447 runsIndex++ 448 require(intervals >= 0) 449 require(intervals < RUN_TYPE_SENTINEL) 450 451 if (intervals > 0) { 452 val L = runs[runsIndex] 453 require(L < RUN_TYPE_SENTINEL) 454 if (left > L) { 455 left = L 456 } 457 458 runsIndex += intervals * 2 459 val R = runs[runsIndex - 1] 460 require(R < RUN_TYPE_SENTINEL) 461 if (right < R) { 462 right = R 463 } 464 465 intervalCount += intervals 466 } 467 require(RUN_TYPE_SENTINEL == runs[runsIndex]) 468 runsIndex += 1 // skip x-sentinel 469 470 // test Y-sentinel 471 } while (RUN_TYPE_SENTINEL > runs[runsIndex]) 472 473 fYSpanCount = ySpanCount 474 fIntervalCount = intervalCount 475 476 return Rect.from(left, top, right, bot) 477 } 478 479 fun clone(): RunHead { 480 val clone = RunHead(isEmptyHead) 481 clone.fIntervalCount = fIntervalCount 482 clone.fYSpanCount = fYSpanCount 483 clone.runs = runs.clone() 484 clone.fRunCount = fRunCount 485 return clone 486 } 487 488 /** 489 * Return the scanline that contains the Y value. This requires that the Y value is already 490 * known to be contained within the bounds of the region, and so this routine never returns 491 * nullptr. 492 * 493 * It returns the beginning of the scanline, starting with its Bottom value. 494 */ 495 fun findScanline(y: Int): Array<Int> { 496 val runs = readonlyRuns 497 498 // if the top-check fails, we didn't do a quick check on the bounds 499 require(y >= runs[0]) 500 501 var runsIndex = 1 // skip top-Y 502 while (true) { 503 val bottom = runs[runsIndex] 504 // If we hit this, we've walked off the region, and our bounds check 505 // failed. 506 require(bottom < RUN_TYPE_SENTINEL) 507 if (y < bottom) { 508 break 509 } 510 runsIndex = skipEntireScanline(runsIndex) 511 } 512 513 val newArray = Array(runs.size - runsIndex) { 0 } 514 runs.copyInto( 515 newArray, 516 destinationOffset = 0, 517 startIndex = runsIndex, 518 endIndex = runs.size - runsIndex 519 ) 520 return newArray 521 } 522 523 /** 524 * Given a scanline (including its Bottom value at runs[0]), return the next scanline. 525 * Asserts that there is one (i.e. runs[0] < Sentinel) 526 */ 527 fun skipEntireScanline(_runsIndex: Int): Int { 528 var runsIndex = _runsIndex 529 // we are not the Y Sentinel 530 require(runs[runsIndex] < RUN_TYPE_SENTINEL) 531 532 val intervals = runs[runsIndex + 1] 533 require(runs[runsIndex + 2 + intervals * 2] == RUN_TYPE_SENTINEL) 534 535 // skip the entire line [B N [L R] S] 536 runsIndex += 1 + 1 + intervals * 2 + 1 537 return runsIndex 538 } 539 540 private var fIntervalCount: Int = 0 541 private var fYSpanCount: Int = 0 542 var runs = RunArray() 543 var fRunCount: Int = 0 544 545 val readonlyRuns: Array<Int> 546 get() = runs.runs 547 } 548 549 private fun setRuns(runs: RunArray, _count: Int): Boolean { 550 require(_count > 0) 551 552 var count = _count 553 554 if (isRunCountEmpty(count)) { 555 assertSentinel(runs[count - 1], true) 556 return this.setEmpty() 557 } 558 559 // trim off any empty spans from the top and bottom 560 // weird I should need this, perhaps op() could be smarter... 561 var trimmedRuns = runs 562 if (count > RECT_REGION_RUNS) { 563 var stopIndex = count 564 assertSentinel(runs[0], false) // top 565 assertSentinel(runs[1], false) // bottom 566 // runs[2] is uncomputed intervalCount 567 568 var trimLeft = false 569 if (runs[3] == RUN_TYPE_SENTINEL) { // should be first left... 570 trimLeft = true 571 assertSentinel(runs[1], false) // bot: a sentinel would mean two in a row 572 assertSentinel(runs[2], false) // interval count 573 assertSentinel(runs[3], false) // left 574 assertSentinel(runs[4], false) // right 575 } 576 577 assertSentinel(runs[stopIndex - 1], true) 578 assertSentinel(runs[stopIndex - 2], true) 579 580 var trimRight = false 581 // now check for a trailing empty span 582 if (runs[stopIndex - 5] == RUN_TYPE_SENTINEL) { 583 // eek, stop[-4] was a bottom with no x-runs 584 trimRight = true 585 } 586 587 var startIndex = 0 588 if (trimLeft) { 589 startIndex += 3 590 trimmedRuns = trimmedRuns.subList(startIndex, count) // skip empty initial span 591 trimmedRuns[0] = runs[1] // set new top to prev bottom 592 } 593 if (trimRight) { 594 // kill empty last span 595 trimmedRuns[stopIndex - 4] = RUN_TYPE_SENTINEL 596 stopIndex -= 3 597 assertSentinel(runs[stopIndex - 1], true) // last y-sentinel 598 assertSentinel(runs[stopIndex - 2], true) // last x-sentinel 599 assertSentinel(runs[stopIndex - 3], false) // last right 600 assertSentinel(runs[stopIndex - 4], false) // last left 601 assertSentinel(runs[stopIndex - 5], false) // last interval-count 602 assertSentinel(runs[stopIndex - 6], false) // last bottom 603 trimmedRuns = trimmedRuns.subList(startIndex, stopIndex) 604 } 605 606 count = stopIndex - startIndex 607 } 608 609 require(count >= RECT_REGION_RUNS) 610 611 if (runsAreARect(trimmedRuns, count)) { 612 fBounds = 613 Rect.withoutCache(trimmedRuns[3], trimmedRuns[0], trimmedRuns[4], trimmedRuns[1]) 614 return this.setRect(fBounds) 615 } 616 617 // if we get here, we need to become a complex region 618 if (!this.isComplex() || fRunHead!!.fRunCount != count) { 619 fRunHead = RunHead() 620 fRunHead!!.fRunCount = count 621 require(this.isComplex()) 622 } 623 624 // must call this before we can write directly into runs() 625 // in case we are sharing the buffer with another region (copy on write) 626 // fRunHead = fRunHead->ensureWritable(); 627 // memcpy(fRunHead, runs, count * sizeof(RunType)) 628 fRunHead!!.setRuns(trimmedRuns, count) 629 fBounds = fRunHead!!.computeRunBounds() 630 631 // Our computed bounds might be too large, so we have to check here. 632 if (fBounds.isEmpty) { 633 return this.setEmpty() 634 } 635 636 return true 637 } 638 639 private fun setRect(r: Rect): Boolean { 640 if (r.isEmpty || RUN_TYPE_SENTINEL == r.right || RUN_TYPE_SENTINEL == r.bottom) { 641 return this.setEmpty() 642 } 643 fBounds = r 644 fRunHead = null 645 return true 646 } 647 648 private fun isRunCountEmpty(count: Int): Boolean { 649 return count <= 2 650 } 651 652 private fun runsAreARect(runs: RunArray, count: Int): Boolean { 653 require(count >= RECT_REGION_RUNS) 654 655 if (count == RECT_REGION_RUNS) { 656 assertSentinel(runs[1], false) // bottom 657 require(1 == runs[2]) 658 assertSentinel(runs[3], false) // left 659 assertSentinel(runs[4], false) // right 660 assertSentinel(runs[5], true) 661 assertSentinel(runs[6], true) 662 663 require(runs[0] < runs[1]) // valid height 664 require(runs[3] < runs[4]) // valid width 665 666 return true 667 } 668 return false 669 } 670 671 class RgnOper(var top: Int, private val runArray: RunArray, op: Op) { 672 private val fMin = gOpMinMax[op]!!.min 673 private val fMax = gOpMinMax[op]!!.max 674 675 private var fStartDst = 0 676 private var fPrevDst = 1 677 private var fPrevLen = 0 678 679 fun addSpan( 680 bottom: Int, 681 aRuns: Array<Int>, 682 bRuns: Array<Int>, 683 aRunsIndex: Int, 684 bRunsIndex: Int 685 ) { 686 // skip X values and slots for the next Y+intervalCount 687 val start = fPrevDst + fPrevLen + 2 688 // start points to beginning of dst interval 689 val stop = 690 operateOnSpan(aRuns, bRuns, aRunsIndex, bRunsIndex, runArray, start, fMin, fMax) 691 val len = stop - start 692 require(len >= 1 && (len and 1) == 1) 693 require(RUN_TYPE_SENTINEL == runArray[stop - 1]) 694 695 // Assert memcmp won't exceed fArray->count(). 696 require(runArray.count >= start + len - 1) 697 if ( 698 fPrevLen == len && 699 (1 == len || 700 runArray 701 .subList(fPrevDst, fPrevDst + len) 702 .runs 703 .contentEquals(runArray.subList(start, start + len).runs)) 704 ) { 705 // update Y value 706 runArray[fPrevDst - 2] = bottom 707 } else { // accept the new span 708 if (len == 1 && fPrevLen == 0) { 709 top = bottom // just update our bottom 710 } else { 711 runArray[start - 2] = bottom 712 runArray[start - 1] = len / 2 // len shr 1 713 fPrevDst = start 714 fPrevLen = len 715 } 716 } 717 } 718 719 fun flush(): Int { 720 runArray[fStartDst] = top 721 // Previously reserved enough for TWO sentinals. 722 // SkASSERT(fArray->count() > SkToInt(fPrevDst + fPrevLen)); 723 runArray[fPrevDst + fPrevLen] = RUN_TYPE_SENTINEL 724 return fPrevDst - fStartDst + fPrevLen + 1 725 } 726 727 class SpanRect( 728 private val aRuns: Array<Int>, 729 private val bRuns: Array<Int>, 730 aIndex: Int, 731 bIndex: Int 732 ) { 733 var fLeft: Int = 0 734 var fRight: Int = 0 735 var fInside: Int = 0 736 737 var fALeft: Int 738 var fARight: Int 739 var fBLeft: Int 740 var fBRight: Int 741 var fARuns: Int 742 var fBRuns: Int 743 744 init { 745 fALeft = aRuns[aIndex] 746 fARight = aRuns[aIndex + 1] 747 fBLeft = bRuns[bIndex] 748 fBRight = bRuns[bIndex + 1] 749 fARuns = aIndex + 2 750 fBRuns = bIndex + 2 751 } 752 753 fun done(): Boolean { 754 require(fALeft <= RUN_TYPE_SENTINEL) 755 require(fBLeft <= RUN_TYPE_SENTINEL) 756 return fALeft == RUN_TYPE_SENTINEL && fBLeft == RUN_TYPE_SENTINEL 757 } 758 759 fun next() { 760 val inside: Int 761 val left: Int 762 var right = 0 763 var aFlush = false 764 var bFlush = false 765 766 var aLeft = fALeft 767 var aRight = fARight 768 var bLeft = fBLeft 769 var bRight = fBRight 770 771 if (aLeft < bLeft) { 772 inside = 1 773 left = aLeft 774 if (aRight <= bLeft) { // [...] <...> 775 right = aRight 776 aFlush = true 777 } else { // [...<..]...> or [...<...>...] 778 aLeft = bLeft 779 right = bLeft 780 } 781 } else if (bLeft < aLeft) { 782 inside = 2 783 left = bLeft 784 if (bRight <= aLeft) { // [...] <...> 785 right = bRight 786 bFlush = true 787 } else { // [...<..]...> or [...<...>...] 788 bLeft = aLeft 789 right = aLeft 790 } 791 } else { // a_left == b_left 792 inside = 3 793 left = aLeft // or b_left 794 if (aRight <= bRight) { 795 bLeft = aRight 796 right = aRight 797 aFlush = true 798 } 799 if (bRight <= aRight) { 800 aLeft = bRight 801 right = bRight 802 bFlush = true 803 } 804 } 805 806 if (aFlush) { 807 aLeft = aRuns[fARuns] 808 fARuns++ 809 aRight = aRuns[fARuns] 810 fARuns++ 811 } 812 if (bFlush) { 813 bLeft = bRuns[fBRuns] 814 fBRuns++ 815 bRight = bRuns[fBRuns] 816 fBRuns++ 817 } 818 819 require(left <= right) 820 821 // now update our state 822 fALeft = aLeft 823 fARight = aRight 824 fBLeft = bLeft 825 fBRight = bRight 826 827 fLeft = left 828 fRight = right 829 fInside = inside 830 } 831 } 832 833 private fun operateOnSpan( 834 a_runs: Array<Int>, 835 b_runs: Array<Int>, 836 a_run_index: Int, 837 b_run_index: Int, 838 array: RunArray, 839 dstOffset: Int, 840 min: Int, 841 max: Int 842 ): Int { 843 // This is a worst-case for this span plus two for TWO terminating sentinels. 844 array.resizeToAtLeast( 845 dstOffset + 846 distanceToSentinel(a_runs, a_run_index) + 847 distanceToSentinel(b_runs, b_run_index) + 848 2 849 ) 850 var dstIndex = dstOffset 851 852 val rec = SpanRect(a_runs, b_runs, a_run_index, b_run_index) 853 var firstInterval = true 854 855 while (!rec.done()) { 856 rec.next() 857 858 val left = rec.fLeft 859 val right = rec.fRight 860 861 // add left,right to our dst buffer (checking for coincidence 862 if ( 863 (rec.fInside - min).toUInt() <= (max - min).toUInt() && left < right 864 ) { // skip if equal 865 if (firstInterval || array[dstIndex - 1] < left) { 866 array[dstIndex] = left 867 dstIndex++ 868 array[dstIndex] = right 869 dstIndex++ 870 firstInterval = false 871 } else { 872 // update the right edge 873 array[dstIndex - 1] = right 874 } 875 } 876 } 877 878 array[dstIndex] = RUN_TYPE_SENTINEL 879 dstIndex++ 880 return dstIndex // dst - &(*array)[0] 881 } 882 883 private fun distanceToSentinel(runs: Array<Int>, startIndex: Int): Int { 884 var index = startIndex 885 if (runs.size <= index) { 886 println("We fucked up...") 887 } 888 while (runs[index] != RUN_TYPE_SENTINEL) { 889 if (runs.size <= index + 2) { 890 println("We fucked up...") 891 return 256 892 } 893 index += 2 894 } 895 return index - startIndex 896 } 897 } 898 899 private fun operate( 900 aRuns: Array<Int>, 901 bRuns: Array<Int>, 902 dst: RunArray, 903 op: Op, 904 _aRunsIndex: Int = 0, 905 _bRunsIndex: Int = 0 906 ): Int { 907 var aRunsIndex = _aRunsIndex 908 var bRunsIndex = _bRunsIndex 909 910 var aTop = aRuns[aRunsIndex] 911 aRunsIndex++ 912 var aBot = aRuns[aRunsIndex] 913 aRunsIndex++ 914 var bTop = bRuns[bRunsIndex] 915 bRunsIndex++ 916 var bBot = bRuns[bRunsIndex] 917 bRunsIndex++ 918 919 aRunsIndex++ // skip the intervalCount 920 bRunsIndex++ // skip the intervalCount 921 922 val gEmptyScanline: Array<Int> = 923 arrayOf( 924 0, // fake bottom value 925 0, // zero intervals 926 RUN_TYPE_SENTINEL, 927 // just need a 2nd value, since spanRec.init() reads 2 values, even 928 // though if the first value is the sentinel, it ignores the 2nd value. 929 // w/o the 2nd value here, we might read uninitialized memory. 930 // This happens when we are using gSentinel, which is pointing at 931 // our sentinel value. 932 0 933 ) 934 val gSentinel = 2 935 936 // Now aRuns and bRuns to their intervals (or sentinel) 937 938 assertSentinel(aTop, false) 939 assertSentinel(aBot, false) 940 assertSentinel(bTop, false) 941 assertSentinel(bBot, false) 942 943 val oper = RgnOper(min(aTop, bTop), dst, op) 944 945 var prevBot = RUN_TYPE_SENTINEL // so we fail the first test 946 947 while (aBot < RUN_TYPE_SENTINEL || bBot < RUN_TYPE_SENTINEL) { 948 var top: Int 949 var bot = 0 950 951 var run0 = gEmptyScanline 952 var run0Index = gSentinel 953 var run1 = gEmptyScanline 954 var run1Index = gSentinel 955 var aFlush = false 956 var bFlush = false 957 958 if (aTop < bTop) { 959 top = aTop 960 run0 = aRuns 961 run0Index = aRunsIndex 962 if (aBot <= bTop) { // [...] <...> 963 bot = aBot 964 aFlush = true 965 } else { // [...<..]...> or [...<...>...] 966 aTop = bTop 967 bot = bTop 968 } 969 } else if (bTop < aTop) { 970 top = bTop 971 run1 = bRuns 972 run1Index = bRunsIndex 973 if (bBot <= aTop) { // [...] <...> 974 bot = bBot 975 bFlush = true 976 } else { // [...<..]...> or [...<...>...] 977 bTop = aTop 978 bot = aTop 979 } 980 } else { // aTop == bTop 981 top = aTop // or bTop 982 run0 = aRuns 983 run0Index = aRunsIndex 984 run1 = bRuns 985 run1Index = bRunsIndex 986 if (aBot <= bBot) { 987 bTop = aBot 988 bot = aBot 989 aFlush = true 990 } 991 if (bBot <= aBot) { 992 aTop = bBot 993 bot = bBot 994 bFlush = true 995 } 996 } 997 998 if (top > prevBot) { 999 oper.addSpan(top, gEmptyScanline, gEmptyScanline, gSentinel, gSentinel) 1000 } 1001 oper.addSpan(bot, run0, run1, run0Index, run1Index) 1002 1003 if (aFlush) { 1004 aRunsIndex = skipIntervals(aRuns, aRunsIndex) 1005 aTop = aBot 1006 aBot = aRuns[aRunsIndex] 1007 aRunsIndex++ // skip to next index 1008 aRunsIndex++ // skip uninitialized intervalCount 1009 if (aBot == RUN_TYPE_SENTINEL) { 1010 aTop = aBot 1011 } 1012 } 1013 if (bFlush) { 1014 bRunsIndex = skipIntervals(bRuns, bRunsIndex) 1015 bTop = bBot 1016 bBot = bRuns[bRunsIndex] 1017 bRunsIndex++ // skip to next index 1018 bRunsIndex++ // skip uninitialized intervalCount 1019 if (bBot == RUN_TYPE_SENTINEL) { 1020 bTop = bBot 1021 } 1022 } 1023 1024 prevBot = bot 1025 } 1026 1027 return oper.flush() 1028 } 1029 1030 private fun skipIntervals(runs: Array<Int>, index: Int): Int { 1031 val intervals = runs[index - 1] 1032 return index + intervals * 2 + 1 1033 } 1034 1035 /** 1036 * Perform the specified Op on this region and the specified region. Return true if the result 1037 * of the op is not empty. 1038 */ 1039 @JsName("opRegion") 1040 fun op(region: Region, op: Op): Boolean { 1041 return op(this, region, op) 1042 } 1043 1044 /** 1045 * Perform the specified Op on this region and the specified rect. Return true if the result of 1046 * the op is not empty. 1047 */ 1048 @JsName("op") 1049 fun op(left: Int, top: Int, right: Int, bottom: Int, op: Op): Boolean { 1050 return op(Rect.withoutCache(left, top, right, bottom), op) 1051 } 1052 1053 /** 1054 * Perform the specified Op on this region and the specified rect. Return true if the result of 1055 * the op is not empty. 1056 */ 1057 @JsName("opRect") 1058 fun op(r: Rect, op: Op): Boolean { 1059 return op(from(r), op) 1060 } 1061 1062 /** 1063 * Set this region to the result of performing the Op on the specified rect and region. Return 1064 * true if the result is not empty. 1065 */ 1066 @JsName("opAndSetRegion") 1067 fun op(rect: Rect, region: Region, op: Op): Boolean { 1068 return op(from(rect), region, op) 1069 } 1070 1071 @JsName("minus") 1072 fun minus(other: Region): Region { 1073 val thisRegion = from(this) 1074 thisRegion.op(other, Op.XOR) 1075 return thisRegion 1076 } 1077 1078 @JsName("coversAtMost") 1079 fun coversAtMost(testRegion: Region): Boolean { 1080 val testRect = testRegion.bounds 1081 val intersection = from(this) 1082 return intersection.op(testRect, Op.INTERSECT) && !intersection.op(this, Op.XOR) 1083 } 1084 1085 @JsName("coversAtLeast") 1086 fun coversAtLeast(testRegion: Region): Boolean { 1087 val intersection = from(this) 1088 return intersection.op(testRegion, Op.INTERSECT) && !intersection.op(testRegion, Op.XOR) 1089 } 1090 1091 @JsName("coversMoreThan") 1092 fun coversMoreThan(testRegion: Region): Boolean { 1093 return coversAtLeast(testRegion) && from(this).minus(testRegion).isNotEmpty 1094 } 1095 1096 @JsName("outOfBoundsRegion") 1097 fun outOfBoundsRegion(testRegion: Region): Region { 1098 val testRect = testRegion.bounds 1099 val outOfBoundsRegion = from(this) 1100 outOfBoundsRegion.op(testRect, Op.INTERSECT) && outOfBoundsRegion.op(this, Op.XOR) 1101 return outOfBoundsRegion 1102 } 1103 1104 @JsName("uncoveredRegion") 1105 fun uncoveredRegion(testRegion: Region): Region { 1106 val uncoveredRegion = from(this) 1107 uncoveredRegion.op(testRegion, Op.INTERSECT) && uncoveredRegion.op(testRegion, Op.XOR) 1108 return uncoveredRegion 1109 } 1110 1111 companion object { 1112 @JsName("EMPTY") 1113 val EMPTY: Region 1114 get() = Region() 1115 1116 private const val RUN_TYPE_SENTINEL = 0x7FFFFFFF 1117 1118 private const val RECT_REGION_RUNS = 7 1119 1120 private class MinMax(val min: Int, val max: Int) 1121 1122 private val gOpMinMax = 1123 mapOf( 1124 Op.DIFFERENCE to MinMax(1, 1), 1125 Op.INTERSECT to MinMax(3, 3), 1126 Op.UNION to MinMax(1, 3), 1127 Op.XOR to MinMax(1, 2) 1128 ) 1129 1130 @JsName("from") 1131 fun from(left: Int, top: Int, right: Int, bottom: Int): Region = 1132 from(Rect.withoutCache(left, top, right, bottom)) 1133 1134 @JsName("fromRegion") fun from(region: Region): Region = Region().also { it.set(region) } 1135 1136 @JsName("fromRect") 1137 fun from(rect: Rect? = null): Region = 1138 Region().also { 1139 it.fRunHead = null 1140 it.setRect(rect ?: Rect.EMPTY) 1141 } 1142 1143 @JsName("fromRectF") fun from(rect: RectF?): Region = from(rect?.toRect()) 1144 1145 @JsName("fromEmpty") fun from(): Region = from(Rect.EMPTY) 1146 1147 private fun skRegionValueIsSentinel(value: Int): Boolean { 1148 return value == RUN_TYPE_SENTINEL 1149 } 1150 1151 private fun assertSentinel(value: Int, isSentinel: Boolean) { 1152 require(skRegionValueIsSentinel(value) == isSentinel) 1153 } 1154 1155 private fun getRectsFromString(regionString: String): Array<Rect> { 1156 val rects = mutableListOf<Rect>() 1157 1158 if (regionString == "SkRegion()") { 1159 return rects.toTypedArray() 1160 } 1161 1162 var nativeRegionString = regionString.replace("SkRegion", "") 1163 nativeRegionString = nativeRegionString.substring(2, nativeRegionString.length - 2) 1164 nativeRegionString = nativeRegionString.replace(")(", ",") 1165 1166 var rect = Rect.EMPTY 1167 for ((i, coord) in nativeRegionString.split(",").withIndex()) { 1168 when (i % 4) { 1169 0 -> rect = Rect.withoutCache(coord.toInt(), 0, 0, 0) 1170 1 -> rect = Rect.withoutCache(rect.left, coord.toInt(), 0, 0) 1171 2 -> rect = Rect.withoutCache(rect.left, rect.top, coord.toInt(), 0) 1172 3 -> { 1173 rect = Rect.withoutCache(rect.left, rect.top, rect.right, coord.toInt()) 1174 rects.add(rect) 1175 } 1176 } 1177 } 1178 1179 return rects.toTypedArray() 1180 } 1181 } 1182 } 1183