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