Outline.java revision 77b5cad3efedd20f2b7cc14d87ccce1b0261960a
1/*
2 * Copyright (C) 2014 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
17package android.graphics;
18
19import android.annotation.NonNull;
20import android.graphics.drawable.Drawable;
21import android.view.View;
22
23/**
24 * Defines a simple shape, used for bounding graphical regions.
25 * <p>
26 * Can be used with a View, or computed by a Drawable, to drive the shape of shadows cast by a
27 * View, or to clip the contents of the View.
28 *
29 * @see View#setOutline(Outline)
30 * @see Drawable#getOutline(Outline)
31 */
32public final class Outline {
33    /** @hide */
34    public Path mPath;
35
36    /** @hide */
37    public Rect mRect;
38    /** @hide */
39    public float mRadius;
40    /** @hide */
41    public float mAlpha;
42
43    /**
44     * Constructs an empty Outline. Call one of the setter methods to make
45     * the outline valid for use with a View.
46     */
47    public Outline() {}
48
49    /**
50     * Constructs an Outline with a copy of the data in src.
51     */
52    public Outline(@NonNull Outline src) {
53        set(src);
54    }
55
56    /**
57     * Sets the outline to be empty.
58     *
59     * @see #isEmpty()
60     */
61    public void setEmpty() {
62        mPath = null;
63        mRect = null;
64        mRadius = 0;
65    }
66
67    /**
68     * Returns whether the Outline is empty.
69     * <p>
70     * Outlines are empty when constructed, or if {@link #setEmpty()} is called,
71     * until a setter method is called
72     *
73     * @see #setEmpty()
74     */
75    public boolean isEmpty() {
76        return mRect == null && mPath == null;
77    }
78
79
80    /**
81     * Returns whether the outline can be used to clip a View.
82     *
83     * Currently, only outlines that can be represented as a rectangle, circle, or round rect
84     * support clipping.
85     *
86     * @see {@link View#setClipToOutline(boolean)}
87     */
88    public boolean canClip() {
89        return !isEmpty() && mRect != null;
90    }
91
92    /**
93     * Sets the alpha represented by the Outline.
94     *
95     * Content producing a fully opaque (alpha = 1.0f) outline is assumed, by the drawing system,
96     * to fully cover content beneath it, meaning content beneath may be optimized away.
97     *
98     * @hide
99     */
100    public void setAlpha(float alpha) {
101        mAlpha = alpha;
102    }
103
104    /**
105     * Sets the alpha represented by the Outline.
106     *
107     * @hide
108     */
109    public float getAlpha() {
110        return mAlpha;
111    }
112
113    /**
114     * Replace the contents of this Outline with the contents of src.
115     *
116     * @param src Source outline to copy from.
117     */
118    public void set(@NonNull Outline src) {
119        if (src.mPath != null) {
120            if (mPath == null) {
121                mPath = new Path();
122            }
123            mPath.set(src.mPath);
124            mRect = null;
125        }
126        if (src.mRect != null) {
127            if (mRect == null) {
128                mRect = new Rect();
129            }
130            mRect.set(src.mRect);
131        }
132        mRadius = src.mRadius;
133        mAlpha = src.mAlpha;
134    }
135
136    /**
137     * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
138     */
139    public void setRect(int left, int top, int right, int bottom) {
140        setRoundRect(left, top, right, bottom, 0.0f);
141    }
142
143    /**
144     * Convenience for {@link #setRect(int, int, int, int)}
145     */
146    public void setRect(@NonNull Rect rect) {
147        setRect(rect.left, rect.top, rect.right, rect.bottom);
148    }
149
150    /**
151     * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
152     *
153     * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
154     */
155    public void setRoundRect(int left, int top, int right, int bottom, float radius) {
156        if (left >= right || top >= bottom) {
157            setEmpty();
158            return;
159        }
160
161        if (mRect == null) mRect = new Rect();
162        mRect.set(left, top, right, bottom);
163        mRadius = radius;
164        mPath = null;
165    }
166
167    /**
168     * Convenience for {@link #setRoundRect(int, int, int, int, float)}
169     */
170    public void setRoundRect(@NonNull Rect rect, float radius) {
171        setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
172    }
173
174    /**
175     * Sets the outline to the oval defined by input rect.
176     */
177    public void setOval(int left, int top, int right, int bottom) {
178        if (left >= right || top >= bottom) {
179            setEmpty();
180            return;
181        }
182
183        if ((bottom - top) == (right - left)) {
184            // represent circle as round rect, for efficiency, and to enable clipping
185            setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f);
186            return;
187        }
188
189        if (mPath == null) mPath = new Path();
190        mPath.reset();
191        mPath.addOval(left, top, right, bottom, Path.Direction.CW);
192        mRect = null;
193    }
194
195    /**
196     * Convenience for {@link #setOval(int, int, int, int)}
197     */
198    public void setOval(@NonNull Rect rect) {
199        setOval(rect.left, rect.top, rect.right, rect.bottom);
200    }
201
202    /**
203     * Sets the Constructs an Outline from a {@link android.graphics.Path#isConvex() convex path}.
204     */
205    public void setConvexPath(@NonNull Path convexPath) {
206        if (convexPath.isEmpty()) {
207            setEmpty();
208            return;
209        }
210
211        if (!convexPath.isConvex()) {
212            throw new IllegalArgumentException("path must be convex");
213        }
214        if (mPath == null) mPath = new Path();
215
216        mPath.set(convexPath);
217        mRect = null;
218        mRadius = -1.0f;
219    }
220}
221