• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.graphics;
18 
19 import android.annotation.FloatRange;
20 import android.annotation.IntDef;
21 import android.annotation.NonNull;
22 import android.graphics.drawable.Drawable;
23 
24 import java.lang.annotation.Retention;
25 import java.lang.annotation.RetentionPolicy;
26 
27 /**
28  * Defines a simple shape, used for bounding graphical regions.
29  * <p>
30  * Can be computed for a View, or computed by a Drawable, to drive the shape of
31  * shadows cast by a View, or to clip the contents of the View.
32  *
33  * @see android.view.ViewOutlineProvider
34  * @see android.view.View#setOutlineProvider(android.view.ViewOutlineProvider)
35  * @see Drawable#getOutline(Outline)
36  */
37 public final class Outline {
38     private static final float RADIUS_UNDEFINED = Float.NEGATIVE_INFINITY;
39 
40     /** @hide */
41     public static final int MODE_EMPTY = 0;
42     /** @hide */
43     public static final int MODE_ROUND_RECT = 1;
44     /** @hide */
45     public static final int MODE_CONVEX_PATH = 2;
46 
47     /** @hide */
48     @Retention(RetentionPolicy.SOURCE)
49     @IntDef(flag = false,
50             value = {
51                     MODE_EMPTY,
52                     MODE_ROUND_RECT,
53                     MODE_CONVEX_PATH,
54             })
55     public @interface Mode {}
56 
57     /** @hide */
58     @Mode
59     public int mMode = MODE_EMPTY;
60 
61     /**
62      * Only guaranteed to be non-null when mode == MODE_CONVEX_PATH
63      *
64      * @hide
65      */
66     public Path mPath;
67 
68     /** @hide */
69     public final Rect mRect = new Rect();
70     /** @hide */
71     public float mRadius = RADIUS_UNDEFINED;
72     /** @hide */
73     public float mAlpha;
74 
75     /**
76      * Constructs an empty Outline. Call one of the setter methods to make
77      * the outline valid for use with a View.
78      */
Outline()79     public Outline() {}
80 
81     /**
82      * Constructs an Outline with a copy of the data in src.
83      */
Outline(@onNull Outline src)84     public Outline(@NonNull Outline src) {
85         set(src);
86     }
87 
88     /**
89      * Sets the outline to be empty.
90      *
91      * @see #isEmpty()
92      */
setEmpty()93     public void setEmpty() {
94         if (mPath != null) {
95             // rewind here to avoid thrashing the allocations, but could alternately clear ref
96             mPath.rewind();
97         }
98         mMode = MODE_EMPTY;
99         mRect.setEmpty();
100         mRadius = RADIUS_UNDEFINED;
101     }
102 
103     /**
104      * Returns whether the Outline is empty.
105      * <p>
106      * Outlines are empty when constructed, or if {@link #setEmpty()} is called,
107      * until a setter method is called
108      *
109      * @see #setEmpty()
110      */
isEmpty()111     public boolean isEmpty() {
112         return mMode == MODE_EMPTY;
113     }
114 
115 
116     /**
117      * Returns whether the outline can be used to clip a View.
118      * <p>
119      * Currently, only Outlines that can be represented as a rectangle, circle,
120      * or round rect support clipping.
121      *
122      * @see android.view.View#setClipToOutline(boolean)
123      */
canClip()124     public boolean canClip() {
125         return mMode != MODE_CONVEX_PATH;
126     }
127 
128     /**
129      * Sets the alpha represented by the Outline - the degree to which the
130      * producer is guaranteed to be opaque over the Outline's shape.
131      * <p>
132      * An alpha value of <code>0.0f</code> either represents completely
133      * transparent content, or content that isn't guaranteed to fill the shape
134      * it publishes.
135      * <p>
136      * Content producing a fully opaque (alpha = <code>1.0f</code>) outline is
137      * assumed by the drawing system to fully cover content beneath it,
138      * meaning content beneath may be optimized away.
139      */
setAlpha(@loatRangefrom=0.0, to=1.0) float alpha)140     public void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {
141         mAlpha = alpha;
142     }
143 
144     /**
145      * Returns the alpha represented by the Outline.
146      */
getAlpha()147     public float getAlpha() {
148         return mAlpha;
149     }
150 
151     /**
152      * Replace the contents of this Outline with the contents of src.
153      *
154      * @param src Source outline to copy from.
155      */
set(@onNull Outline src)156     public void set(@NonNull Outline src) {
157         mMode = src.mMode;
158         if (src.mMode == MODE_CONVEX_PATH) {
159             if (mPath == null) {
160                 mPath = new Path();
161             }
162             mPath.set(src.mPath);
163         }
164         mRect.set(src.mRect);
165         mRadius = src.mRadius;
166         mAlpha = src.mAlpha;
167     }
168 
169     /**
170      * Sets the Outline to the rounded rect defined by the input rect, and
171      * corner radius.
172      */
setRect(int left, int top, int right, int bottom)173     public void setRect(int left, int top, int right, int bottom) {
174         setRoundRect(left, top, right, bottom, 0.0f);
175     }
176 
177     /**
178      * Convenience for {@link #setRect(int, int, int, int)}
179      */
setRect(@onNull Rect rect)180     public void setRect(@NonNull Rect rect) {
181         setRect(rect.left, rect.top, rect.right, rect.bottom);
182     }
183 
184     /**
185      * Sets the Outline to the rounded rect defined by the input rect, and corner radius.
186      * <p>
187      * Passing a zero radius is equivalent to calling {@link #setRect(int, int, int, int)}
188      */
setRoundRect(int left, int top, int right, int bottom, float radius)189     public void setRoundRect(int left, int top, int right, int bottom, float radius) {
190         if (left >= right || top >= bottom) {
191             setEmpty();
192             return;
193         }
194 
195         if (mMode == MODE_CONVEX_PATH) {
196             // rewind here to avoid thrashing the allocations, but could alternately clear ref
197             mPath.rewind();
198         }
199         mMode = MODE_ROUND_RECT;
200         mRect.set(left, top, right, bottom);
201         mRadius = radius;
202     }
203 
204     /**
205      * Convenience for {@link #setRoundRect(int, int, int, int, float)}
206      */
setRoundRect(@onNull Rect rect, float radius)207     public void setRoundRect(@NonNull Rect rect, float radius) {
208         setRoundRect(rect.left, rect.top, rect.right, rect.bottom, radius);
209     }
210 
211     /**
212      * Populates {@code outBounds} with the outline bounds, if set, and returns
213      * {@code true}. If no outline bounds are set, or if a path has been set
214      * via {@link #setConvexPath(Path)}, returns {@code false}.
215      *
216      * @param outRect the rect to populate with the outline bounds, if set
217      * @return {@code true} if {@code outBounds} was populated with outline
218      *         bounds, or {@code false} if no outline bounds are set
219      */
getRect(@onNull Rect outRect)220     public boolean getRect(@NonNull Rect outRect) {
221         if (mMode != MODE_ROUND_RECT) {
222             return false;
223         }
224         outRect.set(mRect);
225         return true;
226     }
227 
228     /**
229      * Returns the rounded rect radius, if set, or a value less than 0 if a path has
230      * been set via {@link #setConvexPath(Path)}. A return value of {@code 0}
231      * indicates a non-rounded rect.
232      *
233      * @return the rounded rect radius, or value < 0
234      */
getRadius()235     public float getRadius() {
236         return mRadius;
237     }
238 
239     /**
240      * Sets the outline to the oval defined by input rect.
241      */
setOval(int left, int top, int right, int bottom)242     public void setOval(int left, int top, int right, int bottom) {
243         if (left >= right || top >= bottom) {
244             setEmpty();
245             return;
246         }
247 
248         if ((bottom - top) == (right - left)) {
249             // represent circle as round rect, for efficiency, and to enable clipping
250             setRoundRect(left, top, right, bottom, (bottom - top) / 2.0f);
251             return;
252         }
253 
254         if (mPath == null) {
255             mPath = new Path();
256         } else {
257             mPath.rewind();
258         }
259 
260         mMode = MODE_CONVEX_PATH;
261         mPath.addOval(left, top, right, bottom, Path.Direction.CW);
262         mRect.setEmpty();
263         mRadius = RADIUS_UNDEFINED;
264     }
265 
266     /**
267      * Convenience for {@link #setOval(int, int, int, int)}
268      */
setOval(@onNull Rect rect)269     public void setOval(@NonNull Rect rect) {
270         setOval(rect.left, rect.top, rect.right, rect.bottom);
271     }
272 
273     /**
274      * Sets the Constructs an Outline from a
275      * {@link android.graphics.Path#isConvex() convex path}.
276      */
setConvexPath(@onNull Path convexPath)277     public void setConvexPath(@NonNull Path convexPath) {
278         if (convexPath.isEmpty()) {
279             setEmpty();
280             return;
281         }
282 
283         if (!convexPath.isConvex()) {
284             throw new IllegalArgumentException("path must be convex");
285         }
286 
287         if (mPath == null) {
288             mPath = new Path();
289         }
290 
291         mMode = MODE_CONVEX_PATH;
292         mPath.set(convexPath);
293         mRect.setEmpty();
294         mRadius = RADIUS_UNDEFINED;
295     }
296 
297     /**
298      * Offsets the Outline by (dx,dy)
299      */
offset(int dx, int dy)300     public void offset(int dx, int dy) {
301         if (mMode == MODE_ROUND_RECT) {
302             mRect.offset(dx, dy);
303         } else if (mMode == MODE_CONVEX_PATH) {
304             mPath.offset(dx, dy);
305         }
306     }
307 }
308