/* * Copyright (C) 2006 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.graphics; import android.os.Parcel; import android.os.Parcelable; import android.util.Pools.SynchronizedPool; public class Region implements Parcelable { private static final int MAX_POOL_SIZE = 10; private static final SynchronizedPool sPool = new SynchronizedPool(MAX_POOL_SIZE); /** * @hide */ public final long mNativeRegion; // the native values for these must match up with the enum in SkRegion.h public enum Op { DIFFERENCE(0), INTERSECT(1), UNION(2), XOR(3), REVERSE_DIFFERENCE(4), REPLACE(5); Op(int nativeInt) { this.nativeInt = nativeInt; } /** * @hide */ public final int nativeInt; } /** Create an empty region */ public Region() { this(nativeConstructor()); } /** Return a copy of the specified region */ public Region(Region region) { this(nativeConstructor()); nativeSetRegion(mNativeRegion, region.mNativeRegion); } /** Return a region set to the specified rectangle */ public Region(Rect r) { mNativeRegion = nativeConstructor(); nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); } /** Return a region set to the specified rectangle */ public Region(int left, int top, int right, int bottom) { mNativeRegion = nativeConstructor(); nativeSetRect(mNativeRegion, left, top, right, bottom); } /** Set the region to the empty region */ public void setEmpty() { nativeSetRect(mNativeRegion, 0, 0, 0, 0); } /** Set the region to the specified region. */ public boolean set(Region region) { nativeSetRegion(mNativeRegion, region.mNativeRegion); return true; } /** Set the region to the specified rectangle */ public boolean set(Rect r) { return nativeSetRect(mNativeRegion, r.left, r.top, r.right, r.bottom); } /** Set the region to the specified rectangle */ public boolean set(int left, int top, int right, int bottom) { return nativeSetRect(mNativeRegion, left, top, right, bottom); } /** * Set the region to the area described by the path and clip. * Return true if the resulting region is non-empty. This produces a region * that is identical to the pixels that would be drawn by the path * (with no antialiasing). */ public boolean setPath(Path path, Region clip) { return nativeSetPath(mNativeRegion, path.ni(), clip.mNativeRegion); } /** * Return true if this region is empty */ public native boolean isEmpty(); /** * Return true if the region contains a single rectangle */ public native boolean isRect(); /** * Return true if the region contains more than one rectangle */ public native boolean isComplex(); /** * Return a new Rect set to the bounds of the region. If the region is * empty, the Rect will be set to [0, 0, 0, 0] */ public Rect getBounds() { Rect r = new Rect(); nativeGetBounds(mNativeRegion, r); return r; } /** * Set the Rect to the bounds of the region. If the region is empty, the * Rect will be set to [0, 0, 0, 0] */ public boolean getBounds(Rect r) { if (r == null) { throw new NullPointerException(); } return nativeGetBounds(mNativeRegion, r); } /** * Return the boundary of the region as a new Path. If the region is empty, * the path will also be empty. */ public Path getBoundaryPath() { Path path = new Path(); nativeGetBoundaryPath(mNativeRegion, path.ni()); return path; } /** * Set the path to the boundary of the region. If the region is empty, the * path will also be empty. */ public boolean getBoundaryPath(Path path) { return nativeGetBoundaryPath(mNativeRegion, path.ni()); } /** * Return true if the region contains the specified point */ public native boolean contains(int x, int y); /** * Return true if the region is a single rectangle (not complex) and it * contains the specified rectangle. Returning false is not a guarantee * that the rectangle is not contained by this region, but return true is a * guarantee that the rectangle is contained by this region. */ public boolean quickContains(Rect r) { return quickContains(r.left, r.top, r.right, r.bottom); } /** * Return true if the region is a single rectangle (not complex) and it * contains the specified rectangle. Returning false is not a guarantee * that the rectangle is not contained by this region, but return true is a * guarantee that the rectangle is contained by this region. */ public native boolean quickContains(int left, int top, int right, int bottom); /** * Return true if the region is empty, or if the specified rectangle does * not intersect the region. Returning false is not a guarantee that they * intersect, but returning true is a guarantee that they do not. */ public boolean quickReject(Rect r) { return quickReject(r.left, r.top, r.right, r.bottom); } /** * Return true if the region is empty, or if the specified rectangle does * not intersect the region. Returning false is not a guarantee that they * intersect, but returning true is a guarantee that they do not. */ public native boolean quickReject(int left, int top, int right, int bottom); /** * Return true if the region is empty, or if the specified region does not * intersect the region. Returning false is not a guarantee that they * intersect, but returning true is a guarantee that they do not. */ public native boolean quickReject(Region rgn); /** * Translate the region by [dx, dy]. If the region is empty, do nothing. */ public void translate(int dx, int dy) { translate(dx, dy, null); } /** * Set the dst region to the result of translating this region by [dx, dy]. * If this region is empty, then dst will be set to empty. */ public native void translate(int dx, int dy, Region dst); /** * Scale the region by the given scale amount. This re-constructs new region by * scaling the rects that this region consists of. New rectis are computed by scaling * coordinates by float, then rounded by roundf() function to integers. This may results * in less internal rects if 0 < scale < 1. Zero and Negative scale result in * an empty region. If this region is empty, do nothing. * * @hide */ public void scale(float scale) { scale(scale, null); } /** * Set the dst region to the result of scaling this region by the given scale amount. * If this region is empty, then dst will be set to empty. * @hide */ public native void scale(float scale, Region dst); public final boolean union(Rect r) { return op(r, Op.UNION); } /** * Perform the specified Op on this region and the specified rect. Return * true if the result of the op is not empty. */ public boolean op(Rect r, Op op) { return nativeOp(mNativeRegion, r.left, r.top, r.right, r.bottom, op.nativeInt); } /** * Perform the specified Op on this region and the specified rect. Return * true if the result of the op is not empty. */ public boolean op(int left, int top, int right, int bottom, Op op) { return nativeOp(mNativeRegion, left, top, right, bottom, op.nativeInt); } /** * Perform the specified Op on this region and the specified region. Return * true if the result of the op is not empty. */ public boolean op(Region region, Op op) { return op(this, region, op); } /** * Set this region to the result of performing the Op on the specified rect * and region. Return true if the result is not empty. */ public boolean op(Rect rect, Region region, Op op) { return nativeOp(mNativeRegion, rect, region.mNativeRegion, op.nativeInt); } /** * Set this region to the result of performing the Op on the specified * regions. Return true if the result is not empty. */ public boolean op(Region region1, Region region2, Op op) { return nativeOp(mNativeRegion, region1.mNativeRegion, region2.mNativeRegion, op.nativeInt); } public String toString() { return nativeToString(mNativeRegion); } /** * @return An instance from a pool if such or a new one. * * @hide */ public static Region obtain() { Region region = sPool.acquire(); return (region != null) ? region : new Region(); } /** * @return An instance from a pool if such or a new one. * * @param other Region to copy values from for initialization. * * @hide */ public static Region obtain(Region other) { Region region = obtain(); region.set(other); return region; } /** * Recycles an instance. * * @hide */ public void recycle() { setEmpty(); sPool.release(this); } ////////////////////////////////////////////////////////////////////////// public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { /** * Rebuild a Region previously stored with writeToParcel(). * @param p Parcel object to read the region from * @return a new region created from the data in the parcel */ public Region createFromParcel(Parcel p) { long ni = nativeCreateFromParcel(p); if (ni == 0) { throw new RuntimeException(); } return new Region(ni); } public Region[] newArray(int size) { return new Region[size]; } }; public int describeContents() { return 0; } /** * Write the region and its pixels to the parcel. The region can be * rebuilt from the parcel by calling CREATOR.createFromParcel(). * @param p Parcel object to write the region data into */ public void writeToParcel(Parcel p, int flags) { if (!nativeWriteToParcel(mNativeRegion, p)) { throw new RuntimeException(); } } @Override public boolean equals(Object obj) { if (obj == null || !(obj instanceof Region)) { return false; } Region peer = (Region) obj; return nativeEquals(mNativeRegion, peer.mNativeRegion); } protected void finalize() throws Throwable { try { nativeDestructor(mNativeRegion); } finally { super.finalize(); } } Region(long ni) { if (ni == 0) { throw new RuntimeException(); } mNativeRegion = ni; } /* add dummy parameter so constructor can be called from jni without triggering 'not cloneable' exception */ private Region(long ni, int dummy) { this(ni); } final long ni() { return mNativeRegion; } private static native boolean nativeEquals(long native_r1, long native_r2); private static native long nativeConstructor(); private static native void nativeDestructor(long native_region); private static native void nativeSetRegion(long native_dst, long native_src); private static native boolean nativeSetRect(long native_dst, int left, int top, int right, int bottom); private static native boolean nativeSetPath(long native_dst, long native_path, long native_clip); private static native boolean nativeGetBounds(long native_region, Rect rect); private static native boolean nativeGetBoundaryPath(long native_region, long native_path); private static native boolean nativeOp(long native_dst, int left, int top, int right, int bottom, int op); private static native boolean nativeOp(long native_dst, Rect rect, long native_region, int op); private static native boolean nativeOp(long native_dst, long native_region1, long native_region2, int op); private static native long nativeCreateFromParcel(Parcel p); private static native boolean nativeWriteToParcel(long native_region, Parcel p); private static native String nativeToString(long native_region); }