Home | History | Annotate | Download | only in graphics
      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.NonNull;
     21 import 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  */
     33 public 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