• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, tolerance: Int = 2): Boolean {
111     // Expand [this] by [tolerance] pixels in all directions
112     val expandedRegion =
113         Region(this).apply {
114             op(
115                 Region(
116                     bounds.left - tolerance,
117                     bounds.top - tolerance,
118                     bounds.right + tolerance,
119                     bounds.bottom + tolerance,
120                 ),
121                 Region.Op.UNION,
122             )
123         }
124 
125     val intersection = Region(expandedRegion)
126     return intersection.op(testRegion, Region.Op.INTERSECT) &&
127         !intersection.op(testRegion, Region.Op.XOR)
128 }
129 
Regionnull130 fun Region.coversAtMost(testRegion: Region): Boolean {
131     if (this.isEmpty) {
132         return true
133     }
134     val testRect = testRegion.bounds
135     val intersection = Region(this)
136     return intersection.op(testRect, Region.Op.INTERSECT) && !intersection.op(this, Region.Op.XOR)
137 }
138 
coversMoreThannull139 fun Region.coversMoreThan(testRegion: Region): Boolean {
140     return coversAtLeast(testRegion) && !Region(this).minus(testRegion).isEmpty
141 }
142 
minusnull143 fun Region.minus(other: Region): Region {
144     val thisRegion = Region(this)
145     thisRegion.op(other, Region.Op.XOR)
146     return thisRegion
147 }
148