/* | |

* Copyright 2016 Google Inc. | |

* | |

* Use of this source code is governed by a BSD-style license that can be | |

* found in the LICENSE file. | |

*/ | |

#ifndef GrStyledShape_DEFINED | |

#define GrStyledShape_DEFINED | |

#include "include/core/SkPath.h" | |

#include "include/core/SkRRect.h" | |

#include "include/private/SkTemplates.h" | |

#include "src/core/SkPathPriv.h" | |

#include "src/core/SkTLazy.h" | |

#include "src/gpu/GrStyle.h" | |

#include "src/gpu/geometry/GrShape.h" | |

#include <new> | |

class SkIDChangeListener; | |

/** | |

* Represents a geometric shape (rrect or path) and the GrStyle that it should be rendered with. | |

* It is possible to apply the style to the GrStyledShape to produce a new GrStyledShape where the | |

* geometry reflects the styling information (e.g. is stroked). It is also possible to apply just | |

* the path effect from the style. In this case the resulting shape will include any remaining | |

* stroking information that is to be applied after the path effect. | |

* | |

* Shapes can produce keys that represent only the geometry information, not the style. Note that | |

* when styling information is applied to produce a new shape then the style has been converted | |

* to geometric information and is included in the new shape's key. When the same style is applied | |

* to two shapes that reflect the same underlying geometry the computed keys of the stylized shapes | |

* will be the same. | |

* | |

* Currently this can only be constructed from a path, rect, or rrect though it can become a path | |

* applying style to the geometry. The idea is to expand this to cover most or all of the geometries | |

* that have fast paths in the GPU backend. | |

*/ | |

class GrStyledShape { | |

public: | |

// Keys for paths may be extracted from the path data for small paths. Clients aren't supposed | |

// to have to worry about this. This value is exposed for unit tests. | |

static constexpr int kMaxKeyFromDataVerbCnt = 10; | |

GrStyledShape() {} | |

enum class DoSimplify : bool { kNo = false, kYes }; | |

explicit GrStyledShape(const SkPath& path, DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(path, GrStyle::SimpleFill(), doSimplify) {} | |

explicit GrStyledShape(const SkRRect& rrect, DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(rrect, GrStyle::SimpleFill(), doSimplify) {} | |

explicit GrStyledShape(const SkRect& rect, DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(rect, GrStyle::SimpleFill(), doSimplify) {} | |

GrStyledShape(const SkPath& path, const SkPaint& paint, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(path, GrStyle(paint), doSimplify) {} | |

GrStyledShape(const SkRRect& rrect, const SkPaint& paint, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(rrect, GrStyle(paint), doSimplify) {} | |

GrStyledShape(const SkRect& rect, const SkPaint& paint, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: GrStyledShape(rect, GrStyle(paint), doSimplify) {} | |

GrStyledShape(const SkPath& path, const GrStyle& style, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: fShape(path), fStyle(style) { | |

if (doSimplify == DoSimplify::kYes) { | |

this->simplify(); | |

} | |

} | |

GrStyledShape(const SkRRect& rrect, const GrStyle& style, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: fShape(rrect), fStyle(style) { | |

if (doSimplify == DoSimplify::kYes) { | |

this->simplify(); | |

} | |

} | |

GrStyledShape(const SkRRect& rrect, SkPathDirection dir, unsigned start, bool inverted, | |

const GrStyle& style, DoSimplify doSimplify = DoSimplify::kYes) | |

: fShape(rrect) | |

, fStyle(style) { | |

fShape.setPathWindingParams(dir, start); | |

fShape.setInverted(inverted); | |

if (doSimplify == DoSimplify::kYes) { | |

this->simplify(); | |

} | |

} | |

GrStyledShape(const SkRect& rect, const GrStyle& style, | |

DoSimplify doSimplify = DoSimplify::kYes) | |

: fShape(rect), fStyle(style) { | |

if (doSimplify == DoSimplify::kYes) { | |

this->simplify(); | |

} | |

} | |

GrStyledShape(const GrStyledShape&); | |

static GrStyledShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees, | |

SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style, | |

DoSimplify = DoSimplify::kYes); | |

GrStyledShape& operator=(const GrStyledShape& that); | |

/** | |

* Informs MakeFilled on how to modify that shape's fill rule when making a simple filled | |

* version of the shape. | |

*/ | |

enum class FillInversion { | |

kPreserve, | |

kFlip, | |

kForceNoninverted, | |

kForceInverted | |

}; | |

/** | |

* Makes a filled shape from the pre-styled original shape and optionally modifies whether | |

* the fill is inverted or not. It's important to note that the original shape's geometry | |

* may already have been modified if doing so was neutral with respect to its style | |

* (e.g. filled paths are always closed when stored in a shape and dashed paths are always | |

* made non-inverted since dashing ignores inverseness). | |

*/ | |

static GrStyledShape MakeFilled(const GrStyledShape& original, | |

FillInversion = FillInversion::kPreserve); | |

const GrStyle& style() const { return fStyle; } | |

// True if the shape and/or style were modified into a simpler, equivalent pairing | |

bool simplified() const { return fSimplified; } | |

/** | |

* Returns a shape that has either applied the path effect or path effect and stroking | |

* information from this shape's style to its geometry. Scale is used when approximating the | |

* output geometry and typically is computed from the view matrix | |

*/ | |

GrStyledShape applyStyle(GrStyle::Apply apply, SkScalar scale) const { | |

return GrStyledShape(*this, apply, scale); | |

} | |

bool isRect() const { | |

// Should have simplified a rrect to a rect if possible already. | |

SkASSERT(!fShape.isRRect() || !fShape.rrect().isRect()); | |

return fShape.isRect(); | |

} | |

/** Returns the unstyled geometry as a rrect if possible. */ | |

bool asRRect(SkRRect* rrect, SkPathDirection* dir, unsigned* start, bool* inverted) const; | |

/** | |

* If the unstyled shape is a straight line segment, returns true and sets pts to the endpoints. | |

* An inverse filled line path is still considered a line. | |

*/ | |

bool asLine(SkPoint pts[2], bool* inverted) const; | |

// Can this shape be drawn as a pair of filled nested rectangles? | |

bool asNestedRects(SkRect rects[2]) const; | |

/** Returns the unstyled geometry as a path. */ | |

void asPath(SkPath* out) const { | |

fShape.asPath(out, fStyle.isSimpleFill()); | |

} | |

/** | |

* Returns whether the geometry is empty. Note that applying the style could produce a | |

* non-empty shape. It also may have an inverse fill. | |

*/ | |

bool isEmpty() const { return fShape.isEmpty(); } | |

/** | |

* Gets the bounds of the geometry without reflecting the shape's styling. This ignores | |

* the inverse fill nature of the geometry. | |

*/ | |

SkRect bounds() const { return fShape.bounds(); } | |

/** | |

* Gets the bounds of the geometry reflecting the shape's styling (ignoring inverse fill | |

* status). | |

*/ | |

SkRect styledBounds() const; | |

/** | |

* Is this shape known to be convex, before styling is applied. An unclosed but otherwise | |

* convex path is considered to be closed if they styling reflects a fill and not otherwise. | |

* This is because filling closes all contours in the path. | |

*/ | |

bool knownToBeConvex() const { | |

return fShape.convex(fStyle.isSimpleFill()); | |

} | |

/** | |

* Does the shape have a known winding direction. Some degenerate convex shapes may not have | |

* a computable direction, but this is not always a requirement for path renderers so it is | |

* kept separate from knownToBeConvex(). | |

*/ | |

bool knownDirection() const { | |

// Assuming this is called after knownToBeConvex(), this should just be relying on | |

// cached convexity and direction and will be cheap. | |

return !fShape.isPath() || | |

SkPathPriv::ComputeFirstDirection(fShape.path()) != SkPathFirstDirection::kUnknown; | |

} | |

/** Is the pre-styled geometry inverse filled? */ | |

bool inverseFilled() const { | |

// Since the path tracks inverted-fillness itself, it should match what was recorded. | |

SkASSERT(!fShape.isPath() || fShape.inverted() == fShape.path().isInverseFillType()); | |

// Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421 | |

SkASSERT(!(fShape.inverted() && this->style().isDashed())); | |

return fShape.inverted(); | |

} | |

/** | |

* Might applying the styling to the geometry produce an inverse fill. The "may" part comes in | |

* because an arbitrary path effect could produce an inverse filled path. In other cases this | |

* can be thought of as "inverseFilledAfterStyling()". | |

*/ | |

bool mayBeInverseFilledAfterStyling() const { | |

// An arbitrary path effect can produce an arbitrary output path, which may be inverse | |

// filled. | |

if (this->style().hasNonDashPathEffect()) { | |

return true; | |

} | |

return this->inverseFilled(); | |

} | |

/** | |

* Is it known that the unstyled geometry has no unclosed contours. This means that it will | |

* not have any caps if stroked (modulo the effect of any path effect). | |

*/ | |

bool knownToBeClosed() const { | |

// This refers to the base shape and does not depend on invertedness. | |

return fShape.closed(); | |

} | |

uint32_t segmentMask() const { | |

// This refers to the base shape and does not depend on invertedness. | |

return fShape.segmentMask(); | |

} | |

/** | |

* Gets the size of the key for the shape represented by this GrStyledShape (ignoring its | |

* styling). A negative value is returned if the shape has no key (shouldn't be cached). | |

*/ | |

int unstyledKeySize() const; | |

bool hasUnstyledKey() const { return this->unstyledKeySize() >= 0; } | |

/** | |

* Writes unstyledKeySize() bytes into the provided pointer. Assumes that there is enough | |

* space allocated for the key and that unstyledKeySize() does not return a negative value | |

* for this shape. | |

*/ | |

void writeUnstyledKey(uint32_t* key) const; | |

/** | |

* Adds a listener to the *original* path. Typically used to invalidate cached resources when | |

* a path is no longer in-use. If the shape started out as something other than a path, this | |

* does nothing. | |

*/ | |

void addGenIDChangeListener(sk_sp<SkIDChangeListener>) const; | |

/** | |

* Helpers that are only exposed for unit tests, to determine if the shape is a path, and get | |

* the generation ID of the *original* path. This is the path that will receive | |

* GenIDChangeListeners added to this shape. | |

*/ | |

uint32_t testingOnly_getOriginalGenerationID() const; | |

bool testingOnly_isPath() const; | |

bool testingOnly_isNonVolatilePath() const; | |

/** | |

* Similar to GrShape::simplify but also takes into account style and stroking, possibly | |

* applying the style explicitly to produce a new analytic shape with a simpler style. | |

* Unless "doSimplify" is kNo, this method gets called automatically during construction. | |

*/ | |

void simplify(); | |

private: | |

/** Constructor used by the applyStyle() function */ | |

GrStyledShape(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); | |

/** | |

* Determines the key we should inherit from the input shape's geometry and style when | |

* we are applying the style to create a new shape. | |

*/ | |

void setInheritedKey(const GrStyledShape& parentShape, GrStyle::Apply, SkScalar scale); | |

/** | |

* As part of the simplification process, some shapes can have stroking trivially evaluated | |

* and form a new geometry with just a fill. | |

*/ | |

void simplifyStroke(); | |

/** Gets the path that gen id listeners should be added to. */ | |

const SkPath* originalPathForListeners() const; | |

GrShape fShape; | |

GrStyle fStyle; | |

// Gen ID of the original path (path may be modified or simplified away). | |

int32_t fGenID = 0; | |

bool fClosed = false; | |

bool fSimplified = false; | |

SkTLazy<SkPath> fInheritedPathForListeners; | |

SkAutoSTArray<8, uint32_t> fInheritedKey; | |

}; | |

#endif |