1 /* 2 * Copyright (C) 2006 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.graphics; 18 19 import android.annotation.NonNull; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.os.Build; 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 import android.util.Pools.SynchronizedPool; 25 26 @android.ravenwood.annotation.RavenwoodKeepWholeClass 27 public class Region implements Parcelable { 28 29 private static final int MAX_POOL_SIZE = 10; 30 31 private static final SynchronizedPool<Region> sPool = 32 new SynchronizedPool<Region>(MAX_POOL_SIZE); 33 34 /** 35 * @hide 36 */ 37 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 38 public long mNativeRegion; 39 40 // the native values for these must match up with the enum in SkRegion.h 41 public enum Op { 42 DIFFERENCE(0), 43 INTERSECT(1), 44 UNION(2), 45 XOR(3), 46 REVERSE_DIFFERENCE(4), 47 REPLACE(5); 48 Op(int nativeInt)49 Op(int nativeInt) { 50 this.nativeInt = nativeInt; 51 } 52 53 /** 54 * @hide 55 */ 56 @UnsupportedAppUsage 57 public final int nativeInt; 58 } 59 60 /** Create an empty region 61 */ Region()62 public Region() { 63 this(nativeConstructor()); 64 } 65 66 /** Return a copy of the specified region 67 */ Region(@onNull Region region)68 public Region(@NonNull Region region) { 69 this(nativeConstructor()); 70 nativeSetRegion(mNativeRegion, region.mNativeRegion); 71 } 72 73 /** Return a region set to the specified rectangle 74 */ Region(@onNull Rect r)75 public Region(@NonNull Rect r) { 76 mNativeRegion = nativeConstructor(); 77 nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); 78 } 79 80 /** Return a region set to the specified rectangle 81 */ Region(int left, int top, int right, int bottom)82 public Region(int left, int top, int right, int bottom) { 83 mNativeRegion = nativeConstructor(); 84 nativeSetRect(mNativeRegion, left, top, right, bottom); 85 } 86 87 /** Set the region to the empty region 88 */ setEmpty()89 public void setEmpty() { 90 nativeSetRect(mNativeRegion, 0, 0, 0, 0); 91 } 92 93 /** Set the region to the specified region. 94 */ set(@onNull Region region)95 public boolean set(@NonNull Region region) { 96 nativeSetRegion(mNativeRegion, region.mNativeRegion); 97 return true; 98 } 99 100 /** Set the region to the specified rectangle 101 */ set(@onNull Rect r)102 public boolean set(@NonNull Rect r) { 103 return nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); 104 } 105 106 /** Set the region to the specified rectangle 107 */ set(int left, int top, int right, int bottom)108 public boolean set(int left, int top, int right, int bottom) { 109 return nativeSetRect(mNativeRegion, left, top, right, bottom); 110 } 111 112 /** 113 * Set the region to the area described by the path and clip. 114 * Return true if the resulting region is non-empty. This produces a region 115 * that is identical to the pixels that would be drawn by the path 116 * (with no antialiasing). 117 */ setPath(@onNull Path path, @NonNull Region clip)118 public boolean setPath(@NonNull Path path, @NonNull Region clip) { 119 return nativeSetPath(mNativeRegion, path.readOnlyNI(), clip.mNativeRegion); 120 } 121 122 /** 123 * Return true if this region is empty 124 */ isEmpty()125 public native boolean isEmpty(); 126 127 /** 128 * Return true if the region contains a single rectangle 129 */ isRect()130 public native boolean isRect(); 131 132 /** 133 * Return true if the region contains more than one rectangle 134 */ isComplex()135 public native boolean isComplex(); 136 137 /** 138 * Return a new Rect set to the bounds of the region. If the region is 139 * empty, the Rect will be set to [0, 0, 0, 0] 140 */ 141 @NonNull getBounds()142 public Rect getBounds() { 143 Rect r = new Rect(); 144 nativeGetBounds(mNativeRegion, r); 145 return r; 146 } 147 148 /** 149 * Set the Rect to the bounds of the region. If the region is empty, the 150 * Rect will be set to [0, 0, 0, 0] 151 */ getBounds(@onNull Rect r)152 public boolean getBounds(@NonNull Rect r) { 153 if (r == null) { 154 throw new NullPointerException(); 155 } 156 return nativeGetBounds(mNativeRegion, r); 157 } 158 159 /** 160 * Return the boundary of the region as a new Path. If the region is empty, 161 * the path will also be empty. 162 */ 163 @NonNull getBoundaryPath()164 public Path getBoundaryPath() { 165 Path path = new Path(); 166 nativeGetBoundaryPath(mNativeRegion, path.mutateNI()); 167 return path; 168 } 169 170 /** 171 * Set the path to the boundary of the region. If the region is empty, the 172 * path will also be empty. 173 */ getBoundaryPath(@onNull Path path)174 public boolean getBoundaryPath(@NonNull Path path) { 175 return nativeGetBoundaryPath(mNativeRegion, path.mutateNI()); 176 } 177 178 /** 179 * Return true if the region contains the specified point 180 */ contains(int x, int y)181 public native boolean contains(int x, int y); 182 183 /** 184 * Return true if the region is a single rectangle (not complex) and it 185 * contains the specified rectangle. Returning false is not a guarantee 186 * that the rectangle is not contained by this region, but return true is a 187 * guarantee that the rectangle is contained by this region. 188 */ quickContains(@onNull Rect r)189 public boolean quickContains(@NonNull Rect r) { 190 return quickContains(r.left, r.top, r.right, r.bottom); 191 } 192 193 /** 194 * Return true if the region is a single rectangle (not complex) and it 195 * contains the specified rectangle. Returning false is not a guarantee 196 * that the rectangle is not contained by this region, but return true is a 197 * guarantee that the rectangle is contained by this region. 198 */ quickContains(int left, int top, int right, int bottom)199 public native boolean quickContains(int left, int top, int right, 200 int bottom); 201 202 /** 203 * Return true if the region is empty, or if the specified rectangle does 204 * not intersect the region. Returning false is not a guarantee that they 205 * intersect, but returning true is a guarantee that they do not. 206 */ quickReject(@onNull Rect r)207 public boolean quickReject(@NonNull Rect r) { 208 return quickReject(r.left, r.top, r.right, r.bottom); 209 } 210 211 /** 212 * Return true if the region is empty, or if the specified rectangle does 213 * not intersect the region. Returning false is not a guarantee that they 214 * intersect, but returning true is a guarantee that they do not. 215 */ quickReject(int left, int top, int right, int bottom)216 public native boolean quickReject(int left, int top, int right, int bottom); 217 218 /** 219 * Return true if the region is empty, or if the specified region does not 220 * intersect the region. Returning false is not a guarantee that they 221 * intersect, but returning true is a guarantee that they do not. 222 */ quickReject(Region rgn)223 public native boolean quickReject(Region rgn); 224 225 /** 226 * Translate the region by [dx, dy]. If the region is empty, do nothing. 227 */ translate(int dx, int dy)228 public void translate(int dx, int dy) { 229 translate(dx, dy, null); 230 } 231 232 /** 233 * Set the dst region to the result of translating this region by [dx, dy]. 234 * If this region is empty, then dst will be set to empty. 235 */ translate(int dx, int dy, Region dst)236 public native void translate(int dx, int dy, Region dst); 237 238 /** 239 * Scale the region by the given scale amount. This re-constructs new region by 240 * scaling the rects that this region consists of. New rectis are computed by scaling 241 * coordinates by float, then rounded by roundf() function to integers. This may results 242 * in less internal rects if 0 < scale < 1. Zero and Negative scale result in 243 * an empty region. If this region is empty, do nothing. 244 * 245 * @hide 246 */ 247 @UnsupportedAppUsage scale(float scale)248 public void scale(float scale) { 249 scale(scale, null); 250 } 251 252 /** 253 * Set the dst region to the result of scaling this region by the given scale amount. 254 * If this region is empty, then dst will be set to empty. 255 * @hide 256 */ scale(float scale, Region dst)257 public native void scale(float scale, Region dst); 258 union(@onNull Rect r)259 public final boolean union(@NonNull Rect r) { 260 return op(r, Op.UNION); 261 } 262 263 /** 264 * Perform the specified Op on this region and the specified rect. Return 265 * true if the result of the op is not empty. 266 */ op(@onNull Rect r, @NonNull Op op)267 public boolean op(@NonNull Rect r, @NonNull Op op) { 268 return nativeOp(mNativeRegion, r.left, r.top, r.right, r.bottom, 269 op.nativeInt); 270 } 271 272 /** 273 * Perform the specified Op on this region and the specified rect. Return 274 * true if the result of the op is not empty. 275 */ op(int left, int top, int right, int bottom, @NonNull Op op)276 public boolean op(int left, int top, int right, int bottom, @NonNull Op op) { 277 return nativeOp(mNativeRegion, left, top, right, bottom, 278 op.nativeInt); 279 } 280 281 /** 282 * Perform the specified Op on this region and the specified region. Return 283 * true if the result of the op is not empty. 284 */ op(@onNull Region region, @NonNull Op op)285 public boolean op(@NonNull Region region, @NonNull Op op) { 286 return op(this, region, op); 287 } 288 289 /** 290 * Set this region to the result of performing the Op on the specified rect 291 * and region. Return true if the result is not empty. 292 */ op(@onNull Rect rect, @NonNull Region region, @NonNull Op op)293 public boolean op(@NonNull Rect rect, @NonNull Region region, @NonNull Op op) { 294 return nativeOp(mNativeRegion, rect, region.mNativeRegion, 295 op.nativeInt); 296 } 297 298 /** 299 * Set this region to the result of performing the Op on the specified 300 * regions. Return true if the result is not empty. 301 */ op(@onNull Region region1, @NonNull Region region2, @NonNull Op op)302 public boolean op(@NonNull Region region1, @NonNull Region region2, @NonNull Op op) { 303 return nativeOp(mNativeRegion, region1.mNativeRegion, 304 region2.mNativeRegion, op.nativeInt); 305 } 306 307 @Override toString()308 public String toString() { 309 return nativeToString(mNativeRegion); 310 } 311 312 /** 313 * @return An instance from a pool if such or a new one. 314 * 315 * @hide 316 */ 317 @NonNull obtain()318 public static Region obtain() { 319 Region region = sPool.acquire(); 320 return (region != null) ? region : new Region(); 321 } 322 323 /** 324 * @return An instance from a pool if such or a new one. 325 * 326 * @param other Region to copy values from for initialization. 327 * 328 * @hide 329 */ 330 @NonNull obtain(@onNull Region other)331 public static Region obtain(@NonNull Region other) { 332 Region region = obtain(); 333 region.set(other); 334 return region; 335 } 336 337 /** 338 * Recycles an instance. 339 * 340 * @hide 341 */ 342 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) recycle()343 public void recycle() { 344 setEmpty(); 345 sPool.release(this); 346 } 347 348 ////////////////////////////////////////////////////////////////////////// 349 350 public static final @android.annotation.NonNull Parcelable.Creator<Region> CREATOR 351 = new Parcelable.Creator<Region>() { 352 /** 353 * Rebuild a Region previously stored with writeToParcel(). 354 * @param p Parcel object to read the region from 355 * @return a new region created from the data in the parcel 356 */ 357 @Override 358 public Region createFromParcel(Parcel p) { 359 long ni = nativeCreateFromParcel(p); 360 if (ni == 0) { 361 throw new RuntimeException(); 362 } 363 return new Region(ni); 364 } 365 @Override 366 public Region[] newArray(int size) { 367 return new Region[size]; 368 } 369 }; 370 371 @Override describeContents()372 public int describeContents() { 373 return 0; 374 } 375 376 /** 377 * Write the region and its pixels to the parcel. The region can be 378 * rebuilt from the parcel by calling CREATOR.createFromParcel(). 379 * @param p Parcel object to write the region data into 380 */ 381 @Override writeToParcel(Parcel p, int flags)382 public void writeToParcel(Parcel p, int flags) { 383 if (!nativeWriteToParcel(mNativeRegion, p)) { 384 throw new RuntimeException(); 385 } 386 } 387 388 @Override equals(Object obj)389 public boolean equals(Object obj) { 390 if (obj == null || !(obj instanceof Region)) { 391 return false; 392 } 393 Region peer = (Region) obj; 394 return nativeEquals(mNativeRegion, peer.mNativeRegion); 395 } 396 397 @Override finalize()398 protected void finalize() throws Throwable { 399 try { 400 nativeDestructor(mNativeRegion); 401 mNativeRegion = 0; 402 } finally { 403 super.finalize(); 404 } 405 } 406 Region(long ni)407 Region(long ni) { 408 if (ni == 0) { 409 throw new RuntimeException(); 410 } 411 mNativeRegion = ni; 412 } 413 414 /* Add an unused parameter so constructor can be called from jni without 415 triggering 'not cloneable' exception */ 416 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) Region(long ni, int unused)417 private Region(long ni, int unused) { 418 this(ni); 419 } 420 ni()421 final long ni() { 422 return mNativeRegion; 423 } 424 nativeEquals(long native_r1, long native_r2)425 private static native boolean nativeEquals(long native_r1, long native_r2); 426 nativeConstructor()427 private static native long nativeConstructor(); nativeDestructor(long native_region)428 private static native void nativeDestructor(long native_region); 429 nativeSetRegion(long native_dst, long native_src)430 private static native void nativeSetRegion(long native_dst, long native_src); nativeSetRect(long native_dst, int left, int top, int right, int bottom)431 private static native boolean nativeSetRect(long native_dst, int left, 432 int top, int right, int bottom); nativeSetPath(long native_dst, long native_path, long native_clip)433 private static native boolean nativeSetPath(long native_dst, long native_path, 434 long native_clip); nativeGetBounds(long native_region, Rect rect)435 private static native boolean nativeGetBounds(long native_region, Rect rect); nativeGetBoundaryPath(long native_region, long native_path)436 private static native boolean nativeGetBoundaryPath(long native_region, 437 long native_path); 438 nativeOp(long native_dst, int left, int top, int right, int bottom, int op)439 private static native boolean nativeOp(long native_dst, int left, int top, 440 int right, int bottom, int op); nativeOp(long native_dst, Rect rect, long native_region, int op)441 private static native boolean nativeOp(long native_dst, Rect rect, 442 long native_region, int op); nativeOp(long native_dst, long native_region1, long native_region2, int op)443 private static native boolean nativeOp(long native_dst, long native_region1, 444 long native_region2, int op); 445 nativeCreateFromParcel(Parcel p)446 private static native long nativeCreateFromParcel(Parcel p); nativeWriteToParcel(long native_region, Parcel p)447 private static native boolean nativeWriteToParcel(long native_region, 448 Parcel p); 449 nativeToString(long native_region)450 private static native String nativeToString(long native_region); 451 } 452