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