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.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