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