Home | History | Annotate | Download | only in gpu
      1 /*
      2  * Copyright 2016 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #ifndef GrShape_DEFINED
      9 #define GrShape_DEFINED
     10 
     11 #include "GrStyle.h"
     12 #include "SkPath.h"
     13 #include "SkPathPriv.h"
     14 #include "SkRRect.h"
     15 #include "SkTemplates.h"
     16 #include "SkTLazy.h"
     17 #include <new>
     18 
     19 /**
     20  * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
     21  * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
     22  * reflects the styling information (e.g. is stroked). It is also possible to apply just the
     23  * path effect from the style. In this case the resulting shape will include any remaining
     24  * stroking information that is to be applied after the path effect.
     25  *
     26  * Shapes can produce keys that represent only the geometry information, not the style. Note that
     27  * when styling information is applied to produce a new shape then the style has been converted
     28  * to geometric information and is included in the new shape's key. When the same style is applied
     29  * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
     30  * will be the same.
     31  *
     32  * Currently this can only be constructed from a path, rect, or rrect though it can become a path
     33  * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
     34  * that have fast paths in the GPU backend.
     35  */
     36 class GrShape {
     37 public:
     38     // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
     39     // to have to worry about this. This value is exposed for unit tests.
     40     static constexpr int kMaxKeyFromDataVerbCnt = 10;
     41 
     42     GrShape() { this->initType(Type::kEmpty); }
     43 
     44     explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
     45 
     46     explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
     47 
     48     explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
     49 
     50     GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
     51         this->initType(Type::kPath, &path);
     52         this->attemptToSimplifyPath();
     53     }
     54 
     55     GrShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) {
     56         this->initType(Type::kRRect);
     57         fRRectData.fRRect = rrect;
     58         fRRectData.fInverted = false;
     59         fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
     60                                                          &fRRectData.fDir);
     61         this->attemptToSimplifyRRect();
     62     }
     63 
     64     GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
     65             const GrStyle& style)
     66         : fStyle(style) {
     67         this->initType(Type::kRRect);
     68         fRRectData.fRRect = rrect;
     69         fRRectData.fInverted = inverted;
     70         if (style.pathEffect()) {
     71             fRRectData.fDir = dir;
     72             fRRectData.fStart = start;
     73             if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
     74                 fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
     75             } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
     76                 fRRectData.fStart &= 0b110;
     77             }
     78         } else {
     79             fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
     80         }
     81         this->attemptToSimplifyRRect();
     82     }
     83 
     84     GrShape(const SkRect& rect, const GrStyle& style) : fStyle(style) {
     85         this->initType(Type::kRRect);
     86         fRRectData.fRRect = SkRRect::MakeRect(rect);
     87         fRRectData.fInverted = false;
     88         fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
     89                                                         &fRRectData.fDir);
     90         this->attemptToSimplifyRRect();
     91     }
     92 
     93     GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
     94         this->initType(Type::kPath, &path);
     95         this->attemptToSimplifyPath();
     96     }
     97 
     98     GrShape(const SkRRect& rrect, const SkPaint& paint) : fStyle(paint) {
     99         this->initType(Type::kRRect);
    100         fRRectData.fRRect = rrect;
    101         fRRectData.fInverted = false;
    102         fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
    103                                                          &fRRectData.fDir);
    104         this->attemptToSimplifyRRect();
    105     }
    106 
    107     GrShape(const SkRect& rect, const SkPaint& paint) : fStyle(paint) {
    108         this->initType(Type::kRRect);
    109         fRRectData.fRRect = SkRRect::MakeRect(rect);
    110         fRRectData.fInverted = false;
    111         fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
    112                                                         &fRRectData.fDir);
    113         this->attemptToSimplifyRRect();
    114     }
    115 
    116     static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
    117                            SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
    118 
    119     GrShape(const GrShape&);
    120     GrShape& operator=(const GrShape& that);
    121 
    122     ~GrShape() { this->changeType(Type::kEmpty); }
    123 
    124     /**
    125      * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled
    126      * version of the shape.
    127      */
    128     enum class FillInversion {
    129         kPreserve,
    130         kFlip,
    131         kForceNoninverted,
    132         kForceInverted
    133     };
    134     /**
    135      * Makes a filled shape from the pre-styled original shape and optionally modifies whether
    136      * the fill is inverted or not. It's important to note that the original shape's geometry
    137      * may already have been modified if doing so was neutral with respect to its style
    138      * (e.g. filled paths are always closed when stored in a shape and dashed paths are always
    139      * made non-inverted since dashing ignores inverseness).
    140      */
    141     static GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
    142 
    143     const GrStyle& style() const { return fStyle; }
    144 
    145     /**
    146      * Returns a shape that has either applied the path effect or path effect and stroking
    147      * information from this shape's style to its geometry. Scale is used when approximating the
    148      * output geometry and typically is computed from the view matrix
    149      */
    150     GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
    151         return GrShape(*this, apply, scale);
    152     }
    153 
    154     bool isRect() const {
    155         if (Type::kRRect != fType) {
    156             return false;
    157         }
    158 
    159         return fRRectData.fRRect.isRect();
    160     }
    161 
    162     /** Returns the unstyled geometry as a rrect if possible. */
    163     bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
    164         if (Type::kRRect != fType) {
    165             return false;
    166         }
    167         if (rrect) {
    168             *rrect = fRRectData.fRRect;
    169         }
    170         if (dir) {
    171             *dir = fRRectData.fDir;
    172         }
    173         if (start) {
    174             *start = fRRectData.fStart;
    175         }
    176         if (inverted) {
    177             *inverted = fRRectData.fInverted;
    178         }
    179         return true;
    180     }
    181 
    182     /**
    183      * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
    184      * An inverse filled line path is still considered a line.
    185      */
    186     bool asLine(SkPoint pts[2], bool* inverted) const {
    187         if (fType != Type::kLine) {
    188             return false;
    189         }
    190         if (pts) {
    191             pts[0] = fLineData.fPts[0];
    192             pts[1] = fLineData.fPts[1];
    193         }
    194         if (inverted) {
    195             *inverted = fLineData.fInverted;
    196         }
    197         return true;
    198     }
    199 
    200     /** Returns the unstyled geometry as a path. */
    201     void asPath(SkPath* out) const {
    202         switch (fType) {
    203             case Type::kEmpty:
    204                 out->reset();
    205                 break;
    206             case Type::kInvertedEmpty:
    207                 out->reset();
    208                 out->setFillType(kDefaultPathInverseFillType);
    209                 break;
    210             case Type::kRRect:
    211                 out->reset();
    212                 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
    213                 // Below matches the fill type that attemptToSimplifyPath uses.
    214                 if (fRRectData.fInverted) {
    215                     out->setFillType(kDefaultPathInverseFillType);
    216                 } else {
    217                     out->setFillType(kDefaultPathFillType);
    218                 }
    219                 break;
    220             case Type::kArc:
    221                 SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
    222                                               fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
    223                                               fStyle.isSimpleFill());
    224                 if (fArcData.fInverted) {
    225                     out->setFillType(kDefaultPathInverseFillType);
    226                 } else {
    227                     out->setFillType(kDefaultPathFillType);
    228                 }
    229                 break;
    230             case Type::kLine:
    231                 out->reset();
    232                 out->moveTo(fLineData.fPts[0]);
    233                 out->lineTo(fLineData.fPts[1]);
    234                 if (fLineData.fInverted) {
    235                     out->setFillType(kDefaultPathInverseFillType);
    236                 } else {
    237                     out->setFillType(kDefaultPathFillType);
    238                 }
    239                 break;
    240             case Type::kPath:
    241                 *out = this->path();
    242                 break;
    243         }
    244     }
    245 
    246     // Can this shape be drawn as a pair of filled nested rectangles?
    247     bool asNestedRects(SkRect rects[2]) const {
    248         if (Type::kPath != fType) {
    249             return false;
    250         }
    251 
    252         // TODO: it would be better two store DRRects natively in the shape rather than converting
    253         // them to a path and then reextracting the nested rects
    254         if (this->path().isInverseFillType()) {
    255             return false;
    256         }
    257 
    258         SkPath::Direction dirs[2];
    259         if (!this->path().isNestedFillRects(rects, dirs)) {
    260             return false;
    261         }
    262 
    263         if (SkPath::kWinding_FillType == this->path().getFillType() && dirs[0] == dirs[1]) {
    264             // The two rects need to be wound opposite to each other
    265             return false;
    266         }
    267 
    268         // Right now, nested rects where the margin is not the same width
    269         // all around do not render correctly
    270         const SkScalar* outer = rects[0].asScalars();
    271         const SkScalar* inner = rects[1].asScalars();
    272 
    273         bool allEq = true;
    274 
    275         SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
    276         bool allGoE1 = margin >= SK_Scalar1;
    277 
    278         for (int i = 1; i < 4; ++i) {
    279             SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
    280             if (temp < SK_Scalar1) {
    281                 allGoE1 = false;
    282             }
    283             if (!SkScalarNearlyEqual(margin, temp)) {
    284                 allEq = false;
    285             }
    286         }
    287 
    288         return allEq || allGoE1;
    289     }
    290 
    291     /**
    292      * Returns whether the geometry is empty. Note that applying the style could produce a
    293      * non-empty shape. It also may have an inverse fill.
    294      */
    295     bool isEmpty() const { return Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
    296 
    297     /**
    298      * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
    299      * the inverse fill nature of the geometry.
    300      */
    301     SkRect bounds() const;
    302 
    303     /**
    304      * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
    305      * status).
    306      */
    307     SkRect styledBounds() const;
    308 
    309     /**
    310      * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
    311      * convex path is considered to be closed if they styling reflects a fill and not otherwise.
    312      * This is because filling closes all contours in the path.
    313      */
    314     bool knownToBeConvex() const {
    315         switch (fType) {
    316             case Type::kEmpty:
    317                 return true;
    318             case Type::kInvertedEmpty:
    319                 return true;
    320             case Type::kRRect:
    321                 return true;
    322             case Type::kArc:
    323                 return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
    324                                                    SkToBool(fArcData.fUseCenter),
    325                                                    fStyle.isSimpleFill());
    326             case Type::kLine:
    327                 return true;
    328             case Type::kPath:
    329                 // SkPath.isConvex() really means "is this path convex were it to be closed" and
    330                 // thus doesn't give the correct answer for stroked paths, hence we also check
    331                 // whether the path is either filled or closed. Convex paths may only have one
    332                 // contour hence isLastContourClosed() is a sufficient for a convex path.
    333                 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
    334                         this->path().isConvex();
    335         }
    336         return false;
    337     }
    338 
    339     /** Is the pre-styled geometry inverse filled? */
    340     bool inverseFilled() const {
    341         bool ret = false;
    342         switch (fType) {
    343             case Type::kEmpty:
    344                 ret = false;
    345                 break;
    346             case Type::kInvertedEmpty:
    347                 ret = true;
    348                 break;
    349             case Type::kRRect:
    350                 ret = fRRectData.fInverted;
    351                 break;
    352             case Type::kArc:
    353                 ret = fArcData.fInverted;
    354                 break;
    355             case Type::kLine:
    356                 ret = fLineData.fInverted;
    357                 break;
    358             case Type::kPath:
    359                 ret = this->path().isInverseFillType();
    360                 break;
    361         }
    362         // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
    363         SkASSERT(!(ret && this->style().isDashed()));
    364         return ret;
    365     }
    366 
    367     /**
    368      * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
    369      * because an arbitrary path effect could produce an inverse filled path. In other cases this
    370      * can be thought of as "inverseFilledAfterStyling()".
    371      */
    372     bool mayBeInverseFilledAfterStyling() const {
    373          // An arbitrary path effect can produce an arbitrary output path, which may be inverse
    374          // filled.
    375         if (this->style().hasNonDashPathEffect()) {
    376             return true;
    377         }
    378         return this->inverseFilled();
    379     }
    380 
    381     /**
    382      * Is it known that the unstyled geometry has no unclosed contours. This means that it will
    383      * not have any caps if stroked (modulo the effect of any path effect).
    384      */
    385     bool knownToBeClosed() const {
    386         switch (fType) {
    387             case Type::kEmpty:
    388                 return true;
    389             case Type::kInvertedEmpty:
    390                 return true;
    391             case Type::kRRect:
    392                 return true;
    393             case Type::kArc:
    394                 return fArcData.fUseCenter;
    395             case Type::kLine:
    396                 return false;
    397             case Type::kPath:
    398                 // SkPath doesn't keep track of the closed status of each contour.
    399                 return SkPathPriv::IsClosedSingleContour(this->path());
    400         }
    401         return false;
    402     }
    403 
    404     uint32_t segmentMask() const {
    405         switch (fType) {
    406             case Type::kEmpty:
    407                 return 0;
    408             case Type::kInvertedEmpty:
    409                 return 0;
    410             case Type::kRRect:
    411                 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
    412                     return SkPath::kConic_SegmentMask;
    413                 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
    414                            fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
    415                     return SkPath::kLine_SegmentMask;
    416                 }
    417                 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
    418             case Type::kArc:
    419                 if (fArcData.fUseCenter) {
    420                     return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
    421                 }
    422                 return SkPath::kConic_SegmentMask;
    423             case Type::kLine:
    424                 return SkPath::kLine_SegmentMask;
    425             case Type::kPath:
    426                 return this->path().getSegmentMasks();
    427         }
    428         return 0;
    429     }
    430 
    431     /**
    432      * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
    433      * A negative value is returned if the shape has no key (shouldn't be cached).
    434      */
    435     int unstyledKeySize() const;
    436 
    437     bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
    438 
    439     /**
    440      * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
    441      * space allocated for the key and that unstyledKeySize() does not return a negative value
    442      * for this shape.
    443      */
    444     void writeUnstyledKey(uint32_t* key) const;
    445 
    446     /**
    447      * Adds a listener to the *original* path. Typically used to invalidate cached resources when
    448      * a path is no longer in-use. If the shape started out as something other than a path, this
    449      * does nothing.
    450      */
    451     void addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener>) const;
    452 
    453     /**
    454      * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get
    455      * the generation ID of the *original* path. This is the path that will receive
    456      * GenIDChangeListeners added to this shape.
    457      */
    458     uint32_t testingOnly_getOriginalGenerationID() const;
    459     bool testingOnly_isPath() const;
    460     bool testingOnly_isNonVolatilePath() const;
    461 
    462 private:
    463     enum class Type {
    464         kEmpty,
    465         kInvertedEmpty,
    466         kRRect,
    467         kArc,
    468         kLine,
    469         kPath,
    470     };
    471 
    472     void initType(Type type, const SkPath* path = nullptr) {
    473         fType = Type::kEmpty;
    474         this->changeType(type, path);
    475     }
    476 
    477     void changeType(Type type, const SkPath* path = nullptr) {
    478         bool wasPath = Type::kPath == fType;
    479         fType = type;
    480         bool isPath = Type::kPath == type;
    481         SkASSERT(!path || isPath);
    482         if (wasPath && !isPath) {
    483             fPathData.fPath.~SkPath();
    484         } else if (!wasPath && isPath) {
    485             if (path) {
    486                 new (&fPathData.fPath) SkPath(*path);
    487             } else {
    488                 new (&fPathData.fPath) SkPath();
    489             }
    490         } else if (isPath && path) {
    491             fPathData.fPath = *path;
    492         }
    493         // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
    494         fPathData.fGenID = 0;
    495     }
    496 
    497     SkPath& path() {
    498         SkASSERT(Type::kPath == fType);
    499         return fPathData.fPath;
    500     }
    501 
    502     const SkPath& path() const {
    503         SkASSERT(Type::kPath == fType);
    504         return fPathData.fPath;
    505     }
    506 
    507     /** Constructor used by the applyStyle() function */
    508     GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
    509 
    510     /**
    511      * Determines the key we should inherit from the input shape's geometry and style when
    512      * we are applying the style to create a new shape.
    513      */
    514     void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
    515 
    516     void attemptToSimplifyPath();
    517     void attemptToSimplifyRRect();
    518     void attemptToSimplifyLine();
    519     void attemptToSimplifyArc();
    520 
    521     bool attemptToSimplifyStrokedLineToRRect();
    522 
    523     /** Gets the path that gen id listeners should be added to. */
    524     const SkPath* originalPathForListeners() const;
    525 
    526     // Defaults to use when there is no distinction between even/odd and winding fills.
    527     static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
    528     static constexpr SkPath::FillType kDefaultPathInverseFillType =
    529             SkPath::kInverseEvenOdd_FillType;
    530 
    531     static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
    532     static constexpr unsigned kDefaultRRectStart = 0;
    533 
    534     static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
    535                                                 SkPath::Direction* dir) {
    536         *dir = kDefaultRRectDir;
    537         // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
    538         // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
    539         if (!hasPathEffect) {
    540             // It doesn't matter what start we use, just be consistent to avoid redundant keys.
    541             return kDefaultRRectStart;
    542         }
    543         // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
    544         // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
    545         // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
    546         bool swapX = rect.fLeft > rect.fRight;
    547         bool swapY = rect.fTop > rect.fBottom;
    548         if (swapX && swapY) {
    549             // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
    550             return 2 * 2;
    551         } else if (swapX) {
    552             *dir = SkPath::kCCW_Direction;
    553             // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
    554             return 2 * 1;
    555         } else if (swapY) {
    556             *dir = SkPath::kCCW_Direction;
    557             // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
    558             return 2 * 3;
    559         }
    560         return 0;
    561     }
    562 
    563     static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
    564                                                  SkPath::Direction* dir) {
    565         // This comes from SkPath's interface. The default for adding a SkRRect to a path is
    566         // clockwise beginning at starting index 6.
    567         static constexpr unsigned kPathRRectStartIdx = 6;
    568         *dir = kDefaultRRectDir;
    569         if (!hasPathEffect) {
    570             // It doesn't matter what start we use, just be consistent to avoid redundant keys.
    571             return kDefaultRRectStart;
    572         }
    573         return kPathRRectStartIdx;
    574     }
    575 
    576     union {
    577         struct {
    578             SkRRect fRRect;
    579             SkPath::Direction fDir;
    580             unsigned fStart;
    581             bool fInverted;
    582         } fRRectData;
    583         struct {
    584             SkRect fOval;
    585             SkScalar fStartAngleDegrees;
    586             SkScalar fSweepAngleDegrees;
    587             int16_t fUseCenter;
    588             int16_t fInverted;
    589         } fArcData;
    590         struct {
    591             SkPath fPath;
    592             // Gen ID of the original path (fPath may be modified)
    593             int32_t fGenID;
    594         } fPathData;
    595         struct {
    596             SkPoint fPts[2];
    597             bool fInverted;
    598         } fLineData;
    599     };
    600     GrStyle fStyle;
    601     SkTLazy<SkPath> fInheritedPathForListeners;
    602     SkAutoSTArray<8, uint32_t>  fInheritedKey;
    603     Type fType;
    604 };
    605 #endif
    606