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