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