1 /* 2 * Copyright (C) 2020 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 com.android.server.wm.traces.common 18 19 /** 20 * Wrapper for FloatRectProto (frameworks/native/services/surfaceflinger/layerproto/layers.proto) 21 * 22 * This class is used by flicker and Winscope 23 */ 24 data class RectF( 25 val left: Float = 0f, 26 val top: Float = 0f, 27 val right: Float = 0f, 28 val bottom: Float = 0f 29 ) { 30 val height: Float get() = bottom - top 31 val width: Float get() = right - left 32 33 /** 34 * Returns true if the rectangle is empty (left >= right or top >= bottom) 35 */ 36 val isEmpty: Boolean 37 get() = width <= 0f || height <= 0f 38 val isNotEmpty: Boolean 39 get() = !isEmpty 40 41 /** 42 * Returns a [Rect] version fo this rectangle. 43 * 44 * All fractional parts are rounded to 0 45 */ toRectnull46 fun toRect(): Rect { 47 return Rect(left.toInt(), top.toInt(), right.toInt(), bottom.toInt()) 48 } 49 50 /** 51 * Returns true iff the specified rectangle r is inside or equal to this 52 * rectangle. An empty rectangle never contains another rectangle. 53 * 54 * @param r The rectangle being tested for containment. 55 * @return true iff the specified rectangle r is inside or equal to this 56 * rectangle 57 */ containsnull58 operator fun contains(r: RectF): Boolean { 59 // check for empty first 60 return this.left < this.right && this.top < this.bottom && // now check for containment 61 left <= r.left && top <= r.top && right >= r.right && bottom >= r.bottom 62 } 63 64 /** 65 * Returns a [RectF] where the dimensions don't exceed those of [crop] 66 * 67 * @param crop The crop that should be applied to this layer 68 */ cropnull69 fun crop(crop: RectF): RectF { 70 val newLeft = maxOf(left, crop.left) 71 val newTop = maxOf(top, crop.top) 72 val newRight = minOf(right, crop.right) 73 val newBottom = minOf(bottom, crop.bottom) 74 return RectF(newLeft, newTop, newRight, newBottom) 75 } 76 77 /** 78 * If the rectangle specified by left,top,right,bottom intersects this 79 * rectangle, return true and set this rectangle to that intersection, 80 * otherwise return false and do not change this rectangle. No check is 81 * performed to see if either rectangle is empty. Note: To just test for 82 * intersection, use intersects() 83 * 84 * @param left The left side of the rectangle being intersected with this 85 * rectangle 86 * @param top The top of the rectangle being intersected with this rectangle 87 * @param right The right side of the rectangle being intersected with this 88 * rectangle. 89 * @param bottom The bottom of the rectangle being intersected with this 90 * rectangle. 91 * @return A rectangle with the intersection coordinates 92 */ intersectionnull93 fun intersection(left: Float, top: Float, right: Float, bottom: Float): RectF { 94 if (this.left < right && left < this.right && this.top <= bottom && top <= this.bottom) { 95 var intersectionLeft = this.left 96 var intersectionTop = this.top 97 var intersectionRight = this.right 98 var intersectionBottom = this.bottom 99 100 if (this.left < left) { 101 intersectionLeft = left 102 } 103 if (this.top < top) { 104 intersectionTop = top 105 } 106 if (this.right > right) { 107 intersectionRight = right 108 } 109 if (this.bottom > bottom) { 110 intersectionBottom = bottom 111 } 112 return RectF(intersectionLeft, intersectionTop, intersectionRight, intersectionBottom) 113 } 114 return EMPTY 115 } 116 117 /** 118 * If the specified rectangle intersects this rectangle, return true and set 119 * this rectangle to that intersection, otherwise return false and do not 120 * change this rectangle. No check is performed to see if either rectangle 121 * is empty. To just test for intersection, use intersects() 122 * 123 * @param r The rectangle being intersected with this rectangle. 124 * @return A rectangle with the intersection coordinates 125 */ intersectionnull126 fun intersection(r: RectF): RectF = intersection(r.left, r.top, r.right, r.bottom) 127 128 fun prettyPrint(): String = 129 if (isEmpty) { 130 "[empty]" 131 } else { 132 val left = FloatFormatter.format(left) 133 val top = FloatFormatter.format(top) 134 val right = FloatFormatter.format(right) 135 val bottom = FloatFormatter.format(bottom) 136 "($left, $top) - ($right, $bottom)" 137 } 138 toStringnull139 override fun toString(): String = prettyPrint() 140 141 override fun equals(other: Any?): Boolean { 142 if (this === other) return true 143 if (other !is RectF) return false 144 145 if (left != other.left) return false 146 if (top != other.top) return false 147 if (right != other.right) return false 148 if (bottom != other.bottom) return false 149 150 return true 151 } 152 hashCodenull153 override fun hashCode(): Int { 154 var result = left.hashCode() 155 result = 31 * result + top.hashCode() 156 result = 31 * result + right.hashCode() 157 result = 31 * result + bottom.hashCode() 158 return result 159 } 160 161 companion object { 162 val EMPTY: RectF = RectF() 163 } 164 } 165