LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/gpu - GrShape.h (source / functions) Hit Total Coverage
Test: output.info Lines: 0 108 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 18 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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           0 :     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           0 :     GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
      50           0 :         this->initType(Type::kPath, &path);
      51           0 :         this->attemptToSimplifyPath();
      52           0 :     }
      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           0 :     ~GrShape() { this->changeType(Type::kEmpty); }
     123             : 
     124           0 :     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           0 :     GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
     132           0 :         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           0 :     bool asLine(SkPoint pts[2], bool* inverted) const {
     160           0 :         if (fType != Type::kLine) {
     161           0 :             return false;
     162             :         }
     163           0 :         if (pts) {
     164           0 :             pts[0] = fLineData.fPts[0];
     165           0 :             pts[1] = fLineData.fPts[1];
     166             :         }
     167           0 :         if (inverted) {
     168           0 :             *inverted = fLineData.fInverted;
     169             :         }
     170           0 :         return true;
     171             :     }
     172             : 
     173             :     /** Returns the unstyled geometry as a path. */
     174           0 :     void asPath(SkPath* out) const {
     175           0 :         switch (fType) {
     176             :             case Type::kEmpty:
     177           0 :                 out->reset();
     178           0 :                 break;
     179             :             case Type::kRRect:
     180           0 :                 out->reset();
     181           0 :                 out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
     182             :                 // Below matches the fill type that attemptToSimplifyPath uses.
     183           0 :                 if (fRRectData.fInverted) {
     184           0 :                     out->setFillType(kDefaultPathInverseFillType);
     185             :                 } else {
     186           0 :                     out->setFillType(kDefaultPathFillType);
     187             :                 }
     188           0 :                 break;
     189             :             case Type::kLine:
     190           0 :                 out->reset();
     191           0 :                 out->moveTo(fLineData.fPts[0]);
     192           0 :                 out->lineTo(fLineData.fPts[1]);
     193           0 :                 if (fLineData.fInverted) {
     194           0 :                     out->setFillType(kDefaultPathInverseFillType);
     195             :                 } else {
     196           0 :                     out->setFillType(kDefaultPathFillType);
     197             :                 }
     198           0 :                 break;
     199             :             case Type::kPath:
     200           0 :                 *out = this->path();
     201           0 :                 break;
     202             :         }
     203           0 :     }
     204             : 
     205             :     /**
     206             :      * Returns whether the geometry is empty. Note that applying the style could produce a
     207             :      * non-empty shape.
     208             :      */
     209           0 :     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           0 :     bool knownToBeConvex() const {
     229           0 :         switch (fType) {
     230             :             case Type::kEmpty:
     231           0 :                 return true;
     232             :             case Type::kRRect:
     233           0 :                 return true;
     234             :             case Type::kLine:
     235           0 :                 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           0 :                 return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
     242           0 :                         this->path().isConvex();
     243             :         }
     244           0 :         return false;
     245             :     }
     246             : 
     247             :     /** Is the pre-styled geometry inverse filled? */
     248           0 :     bool inverseFilled() const {
     249           0 :         bool ret = false;
     250           0 :         switch (fType) {
     251             :             case Type::kEmpty:
     252           0 :                 ret = false;
     253           0 :                 break;
     254             :             case Type::kRRect:
     255           0 :                 ret = fRRectData.fInverted;
     256           0 :                 break;
     257             :             case Type::kLine:
     258           0 :                 ret = fLineData.fInverted;
     259           0 :                 break;
     260             :             case Type::kPath:
     261           0 :                 ret = this->path().isInverseFillType();
     262           0 :                 break;
     263             :         }
     264             :         // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
     265           0 :         SkASSERT(!(ret && this->style().isDashed()));
     266           0 :         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           0 :     bool mayBeInverseFilledAfterStyling() const {
     275             :          // An arbitrary path effect can produce an arbitrary output path, which may be inverse
     276             :          // filled.
     277           0 :         if (this->style().hasNonDashPathEffect()) {
     278           0 :             return true;
     279             :         }
     280           0 :         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           0 :     bool knownToBeClosed() const {
     288           0 :         switch (fType) {
     289             :             case Type::kEmpty:
     290           0 :                 return true;
     291             :             case Type::kRRect:
     292           0 :                 return true;
     293             :             case Type::kLine:
     294           0 :                 return false;
     295             :             case Type::kPath:
     296             :                 // SkPath doesn't keep track of the closed status of each contour.
     297           0 :                 return SkPathPriv::IsClosedSingleContour(this->path());
     298             :         }
     299           0 :         return false;
     300             :     }
     301             : 
     302           0 :     uint32_t segmentMask() const {
     303           0 :         switch (fType) {
     304             :             case Type::kEmpty:
     305           0 :                 return 0;
     306             :             case Type::kRRect:
     307           0 :                 if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
     308           0 :                     return SkPath::kConic_SegmentMask;
     309           0 :                 } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
     310           0 :                     return SkPath::kLine_SegmentMask;
     311             :                 }
     312           0 :                 return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
     313             :             case Type::kLine:
     314           0 :                 return SkPath::kLine_SegmentMask;
     315             :             case Type::kPath:
     316           0 :                 return this->path().getSegmentMasks();
     317             :         }
     318           0 :         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           0 :     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           0 :     void initType(Type type, const SkPath* path = nullptr) {
     345           0 :         fType = Type::kEmpty;
     346           0 :         this->changeType(type, path);
     347           0 :     }
     348             : 
     349           0 :     void changeType(Type type, const SkPath* path = nullptr) {
     350           0 :         bool wasPath = Type::kPath == fType;
     351           0 :         fType = type;
     352           0 :         bool isPath = Type::kPath == type;
     353           0 :         SkASSERT(!path || isPath);
     354           0 :         if (wasPath && !isPath) {
     355           0 :             fPathData.fPath.~SkPath();
     356           0 :         } else if (!wasPath && isPath) {
     357           0 :             if (path) {
     358           0 :                 new (&fPathData.fPath) SkPath(*path);
     359             :             } else {
     360           0 :                 new (&fPathData.fPath) SkPath();
     361             :             }
     362           0 :         } else if (isPath && path) {
     363           0 :             fPathData.fPath = *path;
     364             :         }
     365             :         // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
     366           0 :         fPathData.fGenID = 0;
     367           0 :     }
     368             : 
     369           0 :     SkPath& path() {
     370           0 :         SkASSERT(Type::kPath == fType);
     371           0 :         return fPathData.fPath;
     372             :     }
     373             : 
     374           0 :     const SkPath& path() const {
     375           0 :         SkASSERT(Type::kPath == fType);
     376           0 :         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

Generated by: LCOV version 1.13