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