/* * Copyright (C) 2014 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.annotation.NonNull; import android.graphics.drawable.Drawable; /** * Defines a simple shape, used for bounding graphical regions. *
* Can be computed for a View, or computed by a Drawable, to drive the shape of * shadows cast by a View, or to clip the contents of the View. * * @see android.view.ViewOutlineProvider * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider) * @see Drawable#getOutline(Outline) */ public final class Outline { /** @hide */ public Path mPath; /** @hide */ public Rect mRect; /** @hide */ public float mRadius; /** @hide */ public float mAlpha; /** * Constructs an empty Outline. Call one of the setter methods to make * the outline valid for use with a View. */ public Outline() {} /** * Constructs an Outline with a copy of the data in src. */ public Outline(@NonNull Outline src) { set(src); } /** * Sets the outline to be empty. * * @see #isEmpty() */ public void setEmpty() { mPath = null; mRect = null; mRadius = 0; } /** * Returns whether the Outline is empty. *
* Outlines are empty when constructed, or if {@link #setEmpty()} is called, * until a setter method is called * * @see #setEmpty() */ public boolean isEmpty() { return mRect == null && mPath == null; } /** * Returns whether the outline can be used to clip a View. *
* Currently, only Outlines that can be represented as a rectangle, circle, * or round rect support clipping. * * @see {@link android.view.View#setClipToOutline(boolean)} */ public boolean canClip() { return !isEmpty() && mRect != null; } /** * Sets the alpha represented by the Outline - the degree to which the * producer is guaranteed to be opaque over the Outline's shape. *
* An alpha value of 0.0f
either represents completely
* transparent content, or content that isn't guaranteed to fill the shape
* it publishes.
*
* Content producing a fully opaque (alpha = 1.0f
) outline is
* assumed by the drawing system to fully cover content beneath it,
* meaning content beneath may be optimized away.
*/
public void setAlpha(float alpha) {
mAlpha = alpha;
}
/**
* Returns the alpha represented by the Outline.
*/
public float getAlpha() {
return mAlpha;
}
/**
* Replace the contents of this Outline with the contents of src.
*
* @param src Source outline to copy from.
*/
public void set(@NonNull Outline src) {
if (src.mPath != null) {
if (mPath == null) {
mPath = new Path();
}
mPath.set(src.mPath);
mRect = null;
}
if (src.mRect != null) {
if (mRect == null) {
mRect = new Rect();
}
mRect.set(src.mRect);
}
mRadius = src.mRadius;
mAlpha = src.mAlpha;
}
/**
* Sets the Outline to the rounded rect defined by the input rect, and
* corner radius.
*/
public void setRect(int left, int top, int right, int bottom) {
setRoundRect(left, top, right, bottom, 0.0f);
}
/**
* Convenience for {@link #setRect(int, int, int, int)}
*/
public void setRect(@NonNull Rect rect) {
setRect(rect.left, rect.top, rect.right, rect.bottom);
}
/**
* Sets the Outline to the rounded rect defined by the input rect, and corner radius.
*
* Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)} */ public void setRoundRect(int left, int top, int right, int bottom, float radius) { if (left >= right || top >= bottom) { setEmpty(); return; } if (mRect == null) mRect = new Rect(); mRect.set(left, top, right, bottom); mRadius = radius; mPath = null; } /** * Convenience for {@link #setRoundRect(int, int, int, int, float)} */ public void setRoundRect(@NonNull Rect rect, float radius) { setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius); } /** * Sets the outline to the oval defined by input rect. */ public void setOval(int left, int top, int right, int bottom) { if (left >= right || top >= bottom) { setEmpty(); return; } if ((bottom - top) == (right - left)) { // represent circle as round rect, for efficiency, and to enable clipping setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f); return; } if (mPath == null) mPath = new Path(); mPath.reset(); mPath.addOval(left, top, right, bottom, Path.Direction.CW); mRect = null; } /** * Convenience for {@link #setOval(int, int, int, int)} */ public void setOval(@NonNull Rect rect) { setOval(rect.left, rect.top, rect.right, rect.bottom); } /** * Sets the Constructs an Outline from a * {@link android.graphics.Path#isConvex() convex path}. */ public void setConvexPath(@NonNull Path convexPath) { if (convexPath.isEmpty()) { setEmpty(); return; } if (!convexPath.isConvex()) { throw new IllegalArgumentException("path must be convex"); } if (mPath == null) mPath = new Path(); mPath.set(convexPath); mRect = null; mRadius = -1.0f; } /** * Offsets the Outline by (dx,dy) */ public void offset(int dx, int dy) { if (mRect != null) { mRect.offset(dx, dy); } else if (mPath != null) { mPath.offset(dx, dy); } } }