xref: /aosp_15_r20/platform_testing/libraries/flicker/utils/src/android/tools/datatypes/Utils.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
2  * Copyright (C) 2024 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 @file:JvmName("DataTypeUtils")
18 
19 package android.tools.datatypes
20 
21 import android.graphics.Color
22 import android.graphics.Rect
23 import android.graphics.RectF
24 import android.graphics.Region
25 import androidx.core.graphics.toRect
26 import androidx.core.graphics.toRectF
27 import kotlin.math.abs
28 
emptyColornull29 fun emptyColor(): Color = Color.valueOf(/*r */ -1f, /*g */ -1f, /*b */ -1f, /*a */ 0f)
30 
31 fun defaultColor(): Color = Color.valueOf(/*r */ 0f, /* g */ 0f, /* b */ 0f, /* a */ 1f)
32 
33 fun Color.isEmpty(): Boolean =
34     this.red() == -1f && this.green() == -1f && this.blue() == -1f && this.alpha() == 0f
35 
36 fun Color.isNotEmpty(): Boolean = !isEmpty()
37 
38 fun Rect.crop(crop: Rect): Rect = this.toRectF().crop(crop.toRectF()).toRect()
39 
40 fun RectF.crop(crop: RectF): RectF {
41     val newLeft = maxOf(left, crop.left)
42     val newTop = maxOf(top, crop.top)
43     val newRight = minOf(right, crop.right)
44     val newBottom = minOf(bottom, crop.bottom)
45     return RectF(newLeft, newTop, newRight, newBottom)
46 }
47 
RectFnull48 fun RectF.containsWithThreshold(r: RectF, threshold: Float = 0.01f): Boolean {
49     // check for empty first
50     return this.left < this.right &&
51         this.top < this.bottom && // now check for containment
52         (left <= r.left || abs(left - r.left) < threshold) &&
53         (top <= r.top || abs(top - r.top) < threshold) &&
54         (right >= r.right || abs(right - r.right) < threshold) &&
55         (bottom >= r.bottom || abs(bottom - r.bottom) < threshold)
56 }
57 
Rectnull58 fun Rect.intersection(r: Rect): Rect = intersection(r.left, r.top, r.right, r.bottom)
59 
60 /**
61  * If the rectangle specified by left,top,right,bottom intersects this rectangle, return true and
62  * set this rectangle to that intersection, otherwise return false and do not change this rectangle.
63  * No check is performed to see if either rectangle is empty. Note: To just test for intersection,
64  * use intersects()
65  *
66  * @param left The left side of the rectangle being intersected with this rectangle
67  * @param top The top of the rectangle being intersected with this rectangle
68  * @param right The right side of the rectangle being intersected with this rectangle.
69  * @param bottom The bottom of the rectangle being intersected with this rectangle.
70  * @return A rectangle with the intersection coordinates
71  */
72 fun Rect.intersection(left: Int, top: Int, right: Int, bottom: Int): Rect {
73     if (this.left < right && left < this.right && this.top <= bottom && top <= this.bottom) {
74         var intersectionLeft = this.left
75         var intersectionTop = this.top
76         var intersectionRight = this.right
77         var intersectionBottom = this.bottom
78 
79         if (this.left < left) {
80             intersectionLeft = left
81         }
82         if (this.top < top) {
83             intersectionTop = top
84         }
85         if (this.right > right) {
86             intersectionRight = right
87         }
88         if (this.bottom > bottom) {
89             intersectionBottom = bottom
90         }
91         return Rect(intersectionLeft, intersectionTop, intersectionRight, intersectionBottom)
92     }
93     return Rect()
94 }
95 
Regionnull96 fun Region.outOfBoundsRegion(testRegion: Region): Region {
97     val testRect = testRegion.bounds
98     val outOfBoundsRegion = Region(this)
99     outOfBoundsRegion.op(testRect, Region.Op.INTERSECT) && outOfBoundsRegion.op(this, Region.Op.XOR)
100     return outOfBoundsRegion
101 }
102 
uncoveredRegionnull103 fun Region.uncoveredRegion(testRegion: Region): Region {
104     val uncoveredRegion = Region(this)
105     uncoveredRegion.op(testRegion, Region.Op.INTERSECT) &&
106         uncoveredRegion.op(testRegion, Region.Op.XOR)
107     return uncoveredRegion
108 }
109 
coversAtLeastnull110 fun Region.coversAtLeast(testRegion: Region): Boolean {
111     val intersection = Region(this)
112     return intersection.op(testRegion, Region.Op.INTERSECT) &&
113         !intersection.op(testRegion, Region.Op.XOR)
114 }
115 
Regionnull116 fun Region.coversAtMost(testRegion: Region): Boolean {
117     if (this.isEmpty) {
118         return true
119     }
120     val testRect = testRegion.bounds
121     val intersection = Region(this)
122     return intersection.op(testRect, Region.Op.INTERSECT) && !intersection.op(this, Region.Op.XOR)
123 }
124 
coversMoreThannull125 fun Region.coversMoreThan(testRegion: Region): Boolean {
126     return coversAtLeast(testRegion) && !Region(this).minus(testRegion).isEmpty
127 }
128 
minusnull129 fun Region.minus(other: Region): Region {
130     val thisRegion = Region(this)
131     thisRegion.op(other, Region.Op.XOR)
132     return thisRegion
133 }
134