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