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 
     18 /**
     19  * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with.
     20  * It is possible to apply the style to the GrShape to produce a new GrShape where the geometry
     21  * reflects the styling information (e.g. is stroked). It is also possible to apply just the
     22  * path effect from the style. In this case the resulting shape will include any remaining
     23  * stroking information that is to be applied after the path effect.
     24  *
     25  * Shapes can produce keys that represent only the geometry information, not the style. Note that
     26  * when styling information is applied to produce a new shape then the style has been converted
     27  * to geometric information and is included in the new shape's key. When the same style is applied
     28  * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes
     29  * will be the same.
     30  *
     31  * Currently this can only be constructed from a path, rect, or rrect though it can become a path
     32  * applying style to the geometry. The idea is to expand this to cover most or all of the geometries
     33  * that have SkCanvas::draw APIs.
     34  */
     35 class GrShape {
     36 public:
     37     // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed
     38     // to have to worry about this. This value is exposed for unit tests.
     39     static constexpr int kMaxKeyFromDataVerbCnt = 10;
     40 
     41     GrShape() { this->initType(Type::kEmpty); }
     42 
     43     explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
     44 
     45     explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
     46 
     47     explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
     48 
     49     GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
     50         this->initType(Type::kPath, &path);
     51         this->attemptToSimplifyPath();
     52     }
     53 
     54     GrShape(const SkRRect& rrect, const GrStyle& style)
     55         : 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)
     85         : fStyle(style) {
     86         this->initType(Type::kRRect);
     87         fRRectData.fRRect = SkRRect::MakeRect(rect);
     88         fRRectData.fInverted = false;
     89         fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
     90                                                         &fRRectData.fDir);
     91         this->attemptToSimplifyRRect();
     92     }
     93 
     94     GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
     95         this->initType(Type::kPath, &path);
     96         this->attemptToSimplifyPath();
     97     }
     98 
     99     GrShape(const SkRRect& rrect, const SkPaint& paint)
    100         : fStyle(paint) {
    101         this->initType(Type::kRRect);
    102         fRRectData.fRRect = rrect;
    103         fRRectData.fInverted = false;
    104         fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
    105                                                          &fRRectData.fDir);
    106         this->attemptToSimplifyRRect();
    107     }
    108 
    109     GrShape(const SkRect& rect, const SkPaint& paint)
    110         : fStyle(paint) {
    111         this->initType(Type::kRRect);
    112         fRRectData.fRRect = SkRRect::MakeRect(rect);
    113         fRRectData.fInverted = false;
    114         fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
    115                                                         &fRRectData.fDir);
    116         this->attemptToSimplifyRRect();
    117     }
    118 
    119     GrShape(const GrShape&);
    120     GrShape& operator=(const GrShape& that);
    121 
    122     ~GrShape() { this->changeType(Type::kEmpty); }
    123 
    124     const GrStyle& style() const { return fStyle; }
    125 
    126     /**
    127      * Returns a shape that has either applied the path effect or path effect and stroking
    128      * information from this shape's style to its geometry. Scale is used when approximating the
    129      * output geometry and typically is computed from the view matrix
    130      */
    131     GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
    132         return GrShape(*this, apply, scale);
    133     }
    134 
    135     /** Returns the unstyled geometry as a rrect if possible. */
    136     bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
    137         if (Type::kRRect != fType) {
    138             return false;
    139         }
    140         if (rrect) {
    141             *rrect = fRRectData.fRRect;
    142         }
    143         if (dir) {
    144             *dir = fRRectData.fDir;
    145         }
    146         if (start) {
    147             *start = fRRectData.fStart;
    148         }
    149         if (inverted) {
    150             *inverted = fRRectData.fInverted;
    151         }
    152         return true;
    153     }
    154 
    155     /**
    156      * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints.
    157      * An inverse filled line path is still considered a line.
    158      */
    159     bool asLine(SkPoint pts[2], bool* inverted) const {
    160         if (fType != Type::kLine) {
    161             return false;
    162         }
    163         if (pts) {
    164             pts[0] = fLineData.fPts[0];
    165             pts[1] = fLineData.fPts[1];
    166         }
    167         if (inverted) {
    168             *inverted = fLineData.fInverted;
    169         }
    170         return true;
    171     }
    172 
    173     /** Returns the unstyled geometry as a path. */
    174     void asPath(SkPath* out) const {
    175         switch (fType) {
    176             case Type::kEmpty:
    177                 out->reset();
    178                 break;
    179             case Type::kRRect:
    180                 out->reset();
    181                 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
    182                 // Below matches the fill type that attemptToSimplifyPath uses.
    183                 if (fRRectData.fInverted) {
    184                     out->setFillType(kDefaultPathInverseFillType);
    185                 } else {
    186                     out->setFillType(kDefaultPathFillType);
    187                 }
    188                 break;
    189             case Type::kLine:
    190                 out->reset();
    191                 out->moveTo(fLineData.fPts[0]);
    192                 out->lineTo(fLineData.fPts[1]);
    193                 if (fLineData.fInverted) {
    194                     out->setFillType(kDefaultPathInverseFillType);
    195                 } else {
    196                     out->setFillType(kDefaultPathFillType);
    197                 }
    198                 break;
    199             case Type::kPath:
    200                 *out = this->path();
    201                 break;
    202         }
    203     }
    204 
    205     /**
    206      * Returns whether the geometry is empty. Note that applying the style could produce a
    207      * non-empty shape.
    208      */
    209     bool isEmpty() const { return Type::kEmpty == fType; }
    210 
    211     /**
    212      * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
    213      * the inverse fill nature of the geometry.
    214      */
    215     SkRect bounds() const;
    216 
    217     /**
    218      * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill
    219      * status).
    220      */
    221     SkRect styledBounds() const;
    222 
    223     /**
    224      * Is this shape known to be convex, before styling is applied. An unclosed but otherwise
    225      * convex path is considered to be closed if they styling reflects a fill and not otherwise.
    226      * This is because filling closes all contours in the path.
    227      */
    228     bool knownToBeConvex() const {
    229         switch (fType) {
    230             case Type::kEmpty:
    231                 return true;
    232             case Type::kRRect:
    233                 return true;
    234             case Type::kLine:
    235                 return true;
    236             case Type::kPath:
    237                 // SkPath.isConvex() really means "is this path convex were it to be closed" and
    238                 // thus doesn't give the correct answer for stroked paths, hence we also check
    239                 // whether the path is either filled or closed. Convex paths may only have one
    240                 // contour hence isLastContourClosed() is a sufficient for a convex path.
    241                 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
    242                         this->path().isConvex();
    243         }
    244         return false;
    245     }
    246 
    247     /** Is the pre-styled geometry inverse filled? */
    248     bool inverseFilled() const {
    249         bool ret = false;
    250         switch (fType) {
    251             case Type::kEmpty:
    252                 ret = false;
    253                 break;
    254             case Type::kRRect:
    255                 ret = fRRectData.fInverted;
    256                 break;
    257             case Type::kLine:
    258                 ret = fLineData.fInverted;
    259                 break;
    260             case Type::kPath:
    261                 ret = this->path().isInverseFillType();
    262                 break;
    263         }
    264         // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
    265         SkASSERT(!(ret && this->style().isDashed()));
    266         return ret;
    267     }
    268 
    269     /**
    270      * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in
    271      * because an arbitrary path effect could produce an inverse filled path. In other cases this
    272      * can be thought of as "inverseFilledAfterStyling()".
    273      */
    274     bool mayBeInverseFilledAfterStyling() const {
    275          // An arbitrary path effect can produce an arbitrary output path, which may be inverse
    276          // filled.
    277         if (this->style().hasNonDashPathEffect()) {
    278             return true;
    279         }
    280         return this->inverseFilled();
    281     }
    282 
    283     /**
    284      * Is it known that the unstyled geometry has no unclosed contours. This means that it will
    285      * not have any caps if stroked (modulo the effect of any path effect).
    286      */
    287     bool knownToBeClosed() const {
    288         switch (fType) {
    289             case Type::kEmpty:
    290                 return true;
    291             case Type::kRRect:
    292                 return true;
    293             case Type::kLine:
    294                 return false;
    295             case Type::kPath:
    296                 // SkPath doesn't keep track of the closed status of each contour.
    297                 return SkPathPriv::IsClosedSingleContour(this->path());
    298         }
    299         return false;
    300     }
    301 
    302     uint32_t segmentMask() const {
    303         switch (fType) {
    304             case Type::kEmpty:
    305                 return 0;
    306             case Type::kRRect:
    307                 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
    308                     return SkPath::kConic_SegmentMask;
    309                 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
    310                     return SkPath::kLine_SegmentMask;
    311                 }
    312                 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
    313             case Type::kLine:
    314                 return SkPath::kLine_SegmentMask;
    315             case Type::kPath:
    316                 return this->path().getSegmentMasks();
    317         }
    318         return 0;
    319     }
    320 
    321     /**
    322      * Gets the size of the key for the shape represented by this GrShape (ignoring its styling).
    323      * A negative value is returned if the shape has no key (shouldn't be cached).
    324      */
    325     int unstyledKeySize() const;
    326 
    327     bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; }
    328 
    329     /**
    330      * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough
    331      * space allocated for the key and that unstyledKeySize() does not return a negative value
    332      * for this shape.
    333      */
    334     void writeUnstyledKey(uint32_t* key) const;
    335 
    336 private:
    337     enum class Type {
    338         kEmpty,
    339         kRRect,
    340         kLine,
    341         kPath,
    342     };
    343 
    344     void initType(Type type, const SkPath* path = nullptr) {
    345         fType = Type::kEmpty;
    346         this->changeType(type, path);
    347     }
    348 
    349     void changeType(Type type, const SkPath* path = nullptr) {
    350         bool wasPath = Type::kPath == fType;
    351         fType = type;
    352         bool isPath = Type::kPath == type;
    353         SkASSERT(!path || isPath);
    354         if (wasPath && !isPath) {
    355             fPathData.fPath.~SkPath();
    356         } else if (!wasPath && isPath) {
    357             if (path) {
    358                 new (&fPathData.fPath) SkPath(*path);
    359             } else {
    360                 new (&fPathData.fPath) SkPath();
    361             }
    362         } else if (isPath && path) {
    363             fPathData.fPath = *path;
    364         }
    365         // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
    366         fPathData.fGenID = 0;
    367     }
    368 
    369     SkPath& path() {
    370         SkASSERT(Type::kPath == fType);
    371         return fPathData.fPath;
    372     }
    373 
    374     const SkPath& path() const {
    375         SkASSERT(Type::kPath == fType);
    376         return fPathData.fPath;
    377     }
    378 
    379     /** Constructor used by the applyStyle() function */
    380     GrShape(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
    381 
    382     /**
    383      * Determines the key we should inherit from the input shape's geometry and style when
    384      * we are applying the style to create a new shape.
    385      */
    386     void setInheritedKey(const GrShape& parentShape, GrStyle::Apply, SkScalar scale);
    387 
    388     void attemptToSimplifyPath();
    389     void attemptToSimplifyRRect();
    390     void attemptToSimplifyLine();
    391 
    392     // Defaults to use when there is no distinction between even/odd and winding fills.
    393     static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
    394     static constexpr SkPath::FillType kDefaultPathInverseFillType =
    395             SkPath::kInverseEvenOdd_FillType;
    396 
    397     static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
    398     static constexpr unsigned kDefaultRRectStart = 0;
    399 
    400     static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
    401                                                 SkPath::Direction* dir) {
    402         *dir = kDefaultRRectDir;
    403         // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
    404         // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
    405         if (!hasPathEffect) {
    406             // It doesn't matter what start we use, just be consistent to avoid redundant keys.
    407             return kDefaultRRectStart;
    408         }
    409         // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
    410         // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
    411         // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
    412         bool swapX = rect.fLeft > rect.fRight;
    413         bool swapY = rect.fTop > rect.fBottom;
    414         if (swapX && swapY) {
    415             // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
    416             return 2 * 2;
    417         } else if (swapX) {
    418             *dir = SkPath::kCCW_Direction;
    419             // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
    420             return 2 * 1;
    421         } else if (swapY) {
    422             *dir = SkPath::kCCW_Direction;
    423             // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
    424             return 2 * 3;
    425         }
    426         return 0;
    427     }
    428 
    429     static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
    430                                                  SkPath::Direction* dir) {
    431         // This comes from SkPath's interface. The default for adding a SkRRect to a path is
    432         // clockwise beginning at starting index 6.
    433         static constexpr unsigned kPathRRectStartIdx = 6;
    434         *dir = kDefaultRRectDir;
    435         if (!hasPathEffect) {
    436             // It doesn't matter what start we use, just be consistent to avoid redundant keys.
    437             return kDefaultRRectStart;
    438         }
    439         return kPathRRectStartIdx;
    440     }
    441 
    442     Type                        fType;
    443     union {
    444         struct {
    445             SkRRect                     fRRect;
    446             SkPath::Direction           fDir;
    447             unsigned                    fStart;
    448             bool                        fInverted;
    449         } fRRectData;
    450         struct {
    451             SkPath                      fPath;
    452             // Gen ID of the original path (fPath may be modified)
    453             int32_t                     fGenID;
    454         } fPathData;
    455         struct {
    456             SkPoint                     fPts[2];
    457             bool                        fInverted;
    458         } fLineData;
    459     };
    460     GrStyle                     fStyle;
    461     SkAutoSTArray<8, uint32_t>  fInheritedKey;
    462 };
    463 #endif
    464