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 GrStyledShape_DEFINED 9 #define GrStyledShape_DEFINED 10 11 #include "include/core/SkPath.h" 12 #include "include/core/SkRRect.h" 13 #include "include/private/SkTemplates.h" 14 #include "src/core/SkPathPriv.h" 15 #include "src/core/SkTLazy.h" 16 #include "src/gpu/GrStyle.h" 17 #include "src/gpu/geometry/GrShape.h" 18 #include <new> 19 20 class SkIDChangeListener; 21 22 /** 23 * Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. 24 * It is possible to apply the style to the GrStyledShape to produce a new GrStyledShape where the 25 * geometry reflects the styling information (e.g. is stroked). It is also possible to apply just 26 * the path effect from the style. In this case the resulting shape will include any remaining 27 * stroking information that is to be applied after the path effect. 28 * 29 * Shapes can produce keys that represent only the geometry information, not the style. Note that 30 * when styling information is applied to produce a new shape then the style has been converted 31 * to geometric information and is included in the new shape's key. When the same style is applied 32 * to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes 33 * will be the same. 34 * 35 * Currently this can only be constructed from a path, rect, or rrect though it can become a path 36 * applying style to the geometry. The idea is to expand this to cover most or all of the geometries 37 * that have fast paths in the GPU backend. 38 */ 39 class GrStyledShape { 40 public: 41 // Keys for paths may be extracted from the path data for small paths. Clients aren't supposed 42 // to have to worry about this. This value is exposed for unit tests. 43 static constexpr int kMaxKeyFromDataVerbCnt = 10; 44 GrStyledShape()45 GrStyledShape() {} 46 47 enum class DoSimplify : bool { kNo = false, kYes }; 48 49 explicit GrStyledShape(const SkPath& path, DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(path,GrStyle::SimpleFill (),doSimplify)50 : GrStyledShape(path, GrStyle::SimpleFill(), doSimplify) {} 51 52 explicit GrStyledShape(const SkRRect& rrect, DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(rrect,GrStyle::SimpleFill (),doSimplify)53 : GrStyledShape(rrect, GrStyle::SimpleFill(), doSimplify) {} 54 55 explicit GrStyledShape(const SkRect& rect, DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(rect,GrStyle::SimpleFill (),doSimplify)56 : GrStyledShape(rect, GrStyle::SimpleFill(), doSimplify) {} 57 58 GrStyledShape(const SkPath& path, const SkPaint& paint, 59 DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(path,GrStyle (paint),doSimplify)60 : GrStyledShape(path, GrStyle(paint), doSimplify) {} 61 62 GrStyledShape(const SkRRect& rrect, const SkPaint& paint, 63 DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(rrect,GrStyle (paint),doSimplify)64 : GrStyledShape(rrect, GrStyle(paint), doSimplify) {} 65 66 GrStyledShape(const SkRect& rect, const SkPaint& paint, 67 DoSimplify doSimplify = DoSimplify::kYes) GrStyledShape(rect,GrStyle (paint),doSimplify)68 : GrStyledShape(rect, GrStyle(paint), doSimplify) {} 69 70 GrStyledShape(const SkPath& path, const GrStyle& style, 71 DoSimplify doSimplify = DoSimplify::kYes) fShape(path)72 : fShape(path), fStyle(style) { 73 if (doSimplify == DoSimplify::kYes) { 74 this->simplify(); 75 } 76 } 77 78 GrStyledShape(const SkRRect& rrect, const GrStyle& style, 79 DoSimplify doSimplify = DoSimplify::kYes) fShape(rrect)80 : fShape(rrect), fStyle(style) { 81 if (doSimplify == DoSimplify::kYes) { 82 this->simplify(); 83 } 84 } 85 86 GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, 87 const GrStyle& style, DoSimplify doSimplify = DoSimplify::kYes) fShape(rrect)88 : fShape(rrect) 89 , fStyle(style) { 90 fShape.setPathWindingParams(dir, start); 91 fShape.setInverted(inverted); 92 if (doSimplify == DoSimplify::kYes) { 93 this->simplify(); 94 } 95 } 96 97 GrStyledShape(const SkRect& rect, const GrStyle& style, 98 DoSimplify doSimplify = DoSimplify::kYes) fShape(rect)99 : fShape(rect), fStyle(style) { 100 if (doSimplify == DoSimplify::kYes) { 101 this->simplify(); 102 } 103 } 104 105 GrStyledShape(const GrStyledShape&); 106 107 static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, 108 SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style, 109 DoSimplify = DoSimplify::kYes); 110 111 GrStyledShape& operator=(const GrStyledShape& that); 112 113 /** 114 * Informs MakeFilled on how to modify that shape's fill rule when making a simple filled 115 * version of the shape. 116 */ 117 enum class FillInversion { 118 kPreserve, 119 kFlip, 120 kForceNoninverted, 121 kForceInverted 122 }; 123 /** 124 * Makes a filled shape from the pre-styled original shape and optionally modifies whether 125 * the fill is inverted or not. It's important to note that the original shape's geometry 126 * may already have been modified if doing so was neutral with respect to its style 127 * (e.g. filled paths are always closed when stored in a shape and dashed paths are always 128 * made non-inverted since dashing ignores inverseness). 129 */ 130 static GrStyledShape MakeFilled(const GrStyledShape& original, 131 FillInversion = FillInversion::kPreserve); 132 style()133 const GrStyle& style() const { return fStyle; } 134 135 // True if the shape and/or style were modified into a simpler, equivalent pairing simplified()136 bool simplified() const { return fSimplified; } 137 138 /** 139 * Returns a shape that has either applied the path effect or path effect and stroking 140 * information from this shape's style to its geometry. Scale is used when approximating the 141 * output geometry and typically is computed from the view matrix 142 */ applyStyle(GrStyle::Apply apply,SkScalar scale)143 GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { 144 return GrStyledShape(*this, apply, scale); 145 } 146 isRect()147 bool isRect() const { 148 // Should have simplified a rrect to a rect if possible already. 149 SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect()); 150 return fShape.isRect(); 151 } 152 153 /** Returns the unstyled geometry as a rrect if possible. */ 154 bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const; 155 156 /** 157 * If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. 158 * An inverse filled line path is still considered a line. 159 */ 160 bool asLine(SkPoint pts[2], bool* inverted) const; 161 162 // Can this shape be drawn as a pair of filled nested rectangles? 163 bool asNestedRects(SkRect rects[2]) const; 164 165 /** Returns the unstyled geometry as a path. */ asPath(SkPath * out)166 void asPath(SkPath* out) const { 167 fShape.asPath(out, fStyle.isSimpleFill()); 168 } 169 170 /** 171 * Returns whether the geometry is empty. Note that applying the style could produce a 172 * non-empty shape. It also may have an inverse fill. 173 */ isEmpty()174 bool isEmpty() const { return fShape.isEmpty(); } 175 176 /** 177 * Gets the bounds of the geometry without reflecting the shape's styling. This ignores 178 * the inverse fill nature of the geometry. 179 */ bounds()180 SkRect bounds() const { return fShape.bounds(); } 181 182 /** 183 * Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill 184 * status). 185 */ 186 SkRect styledBounds() const; 187 188 /** 189 * Is this shape known to be convex, before styling is applied. An unclosed but otherwise 190 * convex path is considered to be closed if they styling reflects a fill and not otherwise. 191 * This is because filling closes all contours in the path. 192 */ knownToBeConvex()193 bool knownToBeConvex() const { 194 return fShape.convex(fStyle.isSimpleFill()); 195 } 196 197 /** 198 * Does the shape have a known winding direction. Some degenerate convex shapes may not have 199 * a computable direction, but this is not always a requirement for path renderers so it is 200 * kept separate from knownToBeConvex(). 201 */ knownDirection()202 bool knownDirection() const { 203 // Assuming this is called after knownToBeConvex(), this should just be relying on 204 // cached convexity and direction and will be cheap. 205 return !fShape.isPath() || 206 SkPathPriv::ComputeFirstDirection(fShape.path()) != SkPathFirstDirection::kUnknown; 207 } 208 209 /** Is the pre-styled geometry inverse filled? */ inverseFilled()210 bool inverseFilled() const { 211 // Since the path tracks inverted-fillness itself, it should match what was recorded. 212 SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType()); 213 // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 214 SkASSERT(!(fShape.inverted() && this->style().isDashed())); 215 return fShape.inverted(); 216 } 217 218 /** 219 * Might applying the styling to the geometry produce an inverse fill. The "may" part comes in 220 * because an arbitrary path effect could produce an inverse filled path. In other cases this 221 * can be thought of as "inverseFilledAfterStyling()". 222 */ mayBeInverseFilledAfterStyling()223 bool mayBeInverseFilledAfterStyling() const { 224 // An arbitrary path effect can produce an arbitrary output path, which may be inverse 225 // filled. 226 if (this->style().hasNonDashPathEffect()) { 227 return true; 228 } 229 return this->inverseFilled(); 230 } 231 232 /** 233 * Is it known that the unstyled geometry has no unclosed contours. This means that it will 234 * not have any caps if stroked (modulo the effect of any path effect). 235 */ knownToBeClosed()236 bool knownToBeClosed() const { 237 // This refers to the base shape and does not depend on invertedness. 238 return fShape.closed(); 239 } 240 segmentMask()241 uint32_t segmentMask() const { 242 // This refers to the base shape and does not depend on invertedness. 243 return fShape.segmentMask(); 244 } 245 246 /** 247 * Gets the size of the key for the shape represented by this GrStyledShape (ignoring its 248 * styling). A negative value is returned if the shape has no key (shouldn't be cached). 249 */ 250 int unstyledKeySize() const; 251 hasUnstyledKey()252 bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } 253 254 /** 255 * Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough 256 * space allocated for the key and that unstyledKeySize() does not return a negative value 257 * for this shape. 258 */ 259 void writeUnstyledKey(uint32_t* key) const; 260 261 /** 262 * Adds a listener to the *original* path. Typically used to invalidate cached resources when 263 * a path is no longer in-use. If the shape started out as something other than a path, this 264 * does nothing. 265 */ 266 void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const; 267 268 /** 269 * Helpers that are only exposed for unit tests, to determine if the shape is a path, and get 270 * the generation ID of the *original* path. This is the path that will receive 271 * GenIDChangeListeners added to this shape. 272 */ 273 uint32_t testingOnly_getOriginalGenerationID() const; 274 bool testingOnly_isPath() const; 275 bool testingOnly_isNonVolatilePath() const; 276 277 /** 278 * Similar to GrShape::simplify but also takes into account style and stroking, possibly 279 * applying the style explicitly to produce a new analytic shape with a simpler style. 280 * Unless "doSimplify" is kNo, this method gets called automatically during construction. 281 */ 282 void simplify(); 283 284 private: 285 /** Constructor used by the applyStyle() function */ 286 GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); 287 288 /** 289 * Determines the key we should inherit from the input shape's geometry and style when 290 * we are applying the style to create a new shape. 291 */ 292 void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); 293 294 /** 295 * As part of the simplification process, some shapes can have stroking trivially evaluated 296 * and form a new geometry with just a fill. 297 */ 298 void simplifyStroke(); 299 300 /** Gets the path that gen id listeners should be added to. */ 301 const SkPath* originalPathForListeners() const; 302 303 GrShape fShape; 304 GrStyle fStyle; 305 // Gen ID of the original path (path may be modified or simplified away). 306 int32_t fGenID = 0; 307 bool fClosed = false; 308 bool fSimplified = false; 309 310 SkTLazy<SkPath> fInheritedPathForListeners; 311 SkAutoSTArray<8, uint32_t> fInheritedKey; 312 }; 313 #endif 314