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