123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605 |
- /*
- * 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 GrShape_DEFINED
- #define GrShape_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 <new>
- /**
- * 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 GrShape to produce a new GrShape 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 GrShape {
- 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;
- GrShape() { this->initType(Type::kEmpty); }
- explicit GrShape(const SkPath& path) : GrShape(path, GrStyle::SimpleFill()) {}
- explicit GrShape(const SkRRect& rrect) : GrShape(rrect, GrStyle::SimpleFill()) {}
- explicit GrShape(const SkRect& rect) : GrShape(rect, GrStyle::SimpleFill()) {}
- GrShape(const SkPath& path, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kPath, &path);
- this->attemptToSimplifyPath();
- }
- GrShape(const SkRRect& rrect, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, style.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
- GrShape(const SkRRect& rrect, SkPath::Direction dir, unsigned start, bool inverted,
- const GrStyle& style)
- : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = inverted;
- if (style.pathEffect()) {
- fRRectData.fDir = dir;
- fRRectData.fStart = start;
- if (fRRectData.fRRect.getType() == SkRRect::kRect_Type) {
- fRRectData.fStart = (fRRectData.fStart + 1) & 0b110;
- } else if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
- fRRectData.fStart &= 0b110;
- }
- } else {
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, false, &fRRectData.fDir);
- }
- this->attemptToSimplifyRRect();
- }
- GrShape(const SkRect& rect, const GrStyle& style) : fStyle(style) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = SkRRect::MakeRect(rect);
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRectDirAndStartIndex(rect, style.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
- GrShape(const SkPath& path, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kPath, &path);
- this->attemptToSimplifyPath();
- }
- GrShape(const SkRRect& rrect, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRRectDirAndStartIndex(rrect, fStyle.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
- GrShape(const SkRect& rect, const SkPaint& paint) : fStyle(paint) {
- this->initType(Type::kRRect);
- fRRectData.fRRect = SkRRect::MakeRect(rect);
- fRRectData.fInverted = false;
- fRRectData.fStart = DefaultRectDirAndStartIndex(rect, fStyle.hasPathEffect(),
- &fRRectData.fDir);
- this->attemptToSimplifyRRect();
- }
- static GrShape MakeArc(const SkRect& oval, SkScalar startAngleDegrees,
- SkScalar sweepAngleDegrees, bool useCenter, const GrStyle& style);
- GrShape(const GrShape&);
- GrShape& operator=(const GrShape& that);
- ~GrShape() { this->changeType(Type::kEmpty); }
- /**
- * 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 GrShape MakeFilled(const GrShape& original, FillInversion = FillInversion::kPreserve);
- const GrStyle& style() const { return fStyle; }
- /**
- * 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
- */
- GrShape applyStyle(GrStyle::Apply apply, SkScalar scale) const {
- return GrShape(*this, apply, scale);
- }
- bool isRect() const {
- if (Type::kRRect != fType) {
- return false;
- }
- return fRRectData.fRRect.isRect();
- }
- /** Returns the unstyled geometry as a rrect if possible. */
- bool asRRect(SkRRect* rrect, SkPath::Direction* dir, unsigned* start, bool* inverted) const {
- if (Type::kRRect != fType) {
- return false;
- }
- if (rrect) {
- *rrect = fRRectData.fRRect;
- }
- if (dir) {
- *dir = fRRectData.fDir;
- }
- if (start) {
- *start = fRRectData.fStart;
- }
- if (inverted) {
- *inverted = fRRectData.fInverted;
- }
- return true;
- }
- /**
- * 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 {
- if (fType != Type::kLine) {
- return false;
- }
- if (pts) {
- pts[0] = fLineData.fPts[0];
- pts[1] = fLineData.fPts[1];
- }
- if (inverted) {
- *inverted = fLineData.fInverted;
- }
- return true;
- }
- /** Returns the unstyled geometry as a path. */
- void asPath(SkPath* out) const {
- switch (fType) {
- case Type::kEmpty:
- out->reset();
- break;
- case Type::kInvertedEmpty:
- out->reset();
- out->setFillType(kDefaultPathInverseFillType);
- break;
- case Type::kRRect:
- out->reset();
- out->addRRect(fRRectData.fRRect, fRRectData.fDir, fRRectData.fStart);
- // Below matches the fill type that attemptToSimplifyPath uses.
- if (fRRectData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kArc:
- SkPathPriv::CreateDrawArcPath(out, fArcData.fOval, fArcData.fStartAngleDegrees,
- fArcData.fSweepAngleDegrees, fArcData.fUseCenter,
- fStyle.isSimpleFill());
- if (fArcData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kLine:
- out->reset();
- out->moveTo(fLineData.fPts[0]);
- out->lineTo(fLineData.fPts[1]);
- if (fLineData.fInverted) {
- out->setFillType(kDefaultPathInverseFillType);
- } else {
- out->setFillType(kDefaultPathFillType);
- }
- break;
- case Type::kPath:
- *out = this->path();
- break;
- }
- }
- // Can this shape be drawn as a pair of filled nested rectangles?
- bool asNestedRects(SkRect rects[2]) const {
- if (Type::kPath != fType) {
- return false;
- }
- // TODO: it would be better two store DRRects natively in the shape rather than converting
- // them to a path and then reextracting the nested rects
- if (this->path().isInverseFillType()) {
- return false;
- }
- SkPath::Direction dirs[2];
- if (!this->path().isNestedFillRects(rects, dirs)) {
- return false;
- }
- if (SkPath::kWinding_FillType == this->path().getFillType() && dirs[0] == dirs[1]) {
- // The two rects need to be wound opposite to each other
- return false;
- }
- // Right now, nested rects where the margin is not the same width
- // all around do not render correctly
- const SkScalar* outer = rects[0].asScalars();
- const SkScalar* inner = rects[1].asScalars();
- bool allEq = true;
- SkScalar margin = SkScalarAbs(outer[0] - inner[0]);
- bool allGoE1 = margin >= SK_Scalar1;
- for (int i = 1; i < 4; ++i) {
- SkScalar temp = SkScalarAbs(outer[i] - inner[i]);
- if (temp < SK_Scalar1) {
- allGoE1 = false;
- }
- if (!SkScalarNearlyEqual(margin, temp)) {
- allEq = false;
- }
- }
- return allEq || allGoE1;
- }
- /**
- * 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 Type::kEmpty == fType || Type::kInvertedEmpty == fType; }
- /**
- * Gets the bounds of the geometry without reflecting the shape's styling. This ignores
- * the inverse fill nature of the geometry.
- */
- SkRect bounds() const;
- /**
- * 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 {
- switch (fType) {
- case Type::kEmpty:
- return true;
- case Type::kInvertedEmpty:
- return true;
- case Type::kRRect:
- return true;
- case Type::kArc:
- return SkPathPriv::DrawArcIsConvex(fArcData.fSweepAngleDegrees,
- SkToBool(fArcData.fUseCenter),
- fStyle.isSimpleFill());
- case Type::kLine:
- return true;
- case Type::kPath:
- // SkPath.isConvex() really means "is this path convex were it to be closed" and
- // thus doesn't give the correct answer for stroked paths, hence we also check
- // whether the path is either filled or closed. Convex paths may only have one
- // contour hence isLastContourClosed() is a sufficient for a convex path.
- return (this->style().isSimpleFill() || this->path().isLastContourClosed()) &&
- this->path().isConvex();
- }
- return false;
- }
- /** Is the pre-styled geometry inverse filled? */
- bool inverseFilled() const {
- bool ret = false;
- switch (fType) {
- case Type::kEmpty:
- ret = false;
- break;
- case Type::kInvertedEmpty:
- ret = true;
- break;
- case Type::kRRect:
- ret = fRRectData.fInverted;
- break;
- case Type::kArc:
- ret = fArcData.fInverted;
- break;
- case Type::kLine:
- ret = fLineData.fInverted;
- break;
- case Type::kPath:
- ret = this->path().isInverseFillType();
- break;
- }
- // Dashing ignores inverseness. We should have caught this earlier. skbug.com/5421
- SkASSERT(!(ret && this->style().isDashed()));
- return ret;
- }
- /**
- * 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 {
- switch (fType) {
- case Type::kEmpty:
- return true;
- case Type::kInvertedEmpty:
- return true;
- case Type::kRRect:
- return true;
- case Type::kArc:
- return fArcData.fUseCenter;
- case Type::kLine:
- return false;
- case Type::kPath:
- // SkPath doesn't keep track of the closed status of each contour.
- return SkPathPriv::IsClosedSingleContour(this->path());
- }
- return false;
- }
- uint32_t segmentMask() const {
- switch (fType) {
- case Type::kEmpty:
- return 0;
- case Type::kInvertedEmpty:
- return 0;
- case Type::kRRect:
- if (fRRectData.fRRect.getType() == SkRRect::kOval_Type) {
- return SkPath::kConic_SegmentMask;
- } else if (fRRectData.fRRect.getType() == SkRRect::kRect_Type ||
- fRRectData.fRRect.getType() == SkRRect::kEmpty_Type) {
- return SkPath::kLine_SegmentMask;
- }
- return SkPath::kLine_SegmentMask | SkPath::kConic_SegmentMask;
- case Type::kArc:
- if (fArcData.fUseCenter) {
- return SkPath::kConic_SegmentMask | SkPath::kLine_SegmentMask;
- }
- return SkPath::kConic_SegmentMask;
- case Type::kLine:
- return SkPath::kLine_SegmentMask;
- case Type::kPath:
- return this->path().getSegmentMasks();
- }
- return 0;
- }
- /**
- * Gets the size of the key for the shape represented by this GrShape (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<SkPathRef::GenIDChangeListener>) 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;
- private:
- enum class Type {
- kEmpty,
- kInvertedEmpty,
- kRRect,
- kArc,
- kLine,
- kPath,
- };
- void initType(Type type, const SkPath* path = nullptr) {
- fType = Type::kEmpty;
- this->changeType(type, path);
- }
- void changeType(Type type, const SkPath* path = nullptr) {
- bool wasPath = Type::kPath == fType;
- fType = type;
- bool isPath = Type::kPath == type;
- SkASSERT(!path || isPath);
- if (wasPath && !isPath) {
- fPathData.fPath.~SkPath();
- } else if (!wasPath && isPath) {
- if (path) {
- new (&fPathData.fPath) SkPath(*path);
- } else {
- new (&fPathData.fPath) SkPath();
- }
- } else if (isPath && path) {
- fPathData.fPath = *path;
- }
- // Whether or not we use the path's gen ID is decided in attemptToSimplifyPath.
- fPathData.fGenID = 0;
- }
- SkPath& path() {
- SkASSERT(Type::kPath == fType);
- return fPathData.fPath;
- }
- const SkPath& path() const {
- SkASSERT(Type::kPath == fType);
- return fPathData.fPath;
- }
- /** Constructor used by the applyStyle() function */
- GrShape(const GrShape& 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 GrShape& parentShape, GrStyle::Apply, SkScalar scale);
- void attemptToSimplifyPath();
- void attemptToSimplifyRRect();
- void attemptToSimplifyLine();
- void attemptToSimplifyArc();
- bool attemptToSimplifyStrokedLineToRRect();
- /** Gets the path that gen id listeners should be added to. */
- const SkPath* originalPathForListeners() const;
- // Defaults to use when there is no distinction between even/odd and winding fills.
- static constexpr SkPath::FillType kDefaultPathFillType = SkPath::kEvenOdd_FillType;
- static constexpr SkPath::FillType kDefaultPathInverseFillType =
- SkPath::kInverseEvenOdd_FillType;
- static constexpr SkPath::Direction kDefaultRRectDir = SkPath::kCW_Direction;
- static constexpr unsigned kDefaultRRectStart = 0;
- static unsigned DefaultRectDirAndStartIndex(const SkRect& rect, bool hasPathEffect,
- SkPath::Direction* dir) {
- *dir = kDefaultRRectDir;
- // This comes from SkPath's interface. The default for adding a SkRect is counter clockwise
- // beginning at index 0 (which happens to correspond to rrect index 0 or 7).
- if (!hasPathEffect) {
- // It doesn't matter what start we use, just be consistent to avoid redundant keys.
- return kDefaultRRectStart;
- }
- // In SkPath a rect starts at index 0 by default. This is the top left corner. However,
- // we store rects as rrects. RRects don't preserve the invertedness, but rather sort the
- // rect edges. Thus, we may need to modify the rrect's start index to account for the sort.
- bool swapX = rect.fLeft > rect.fRight;
- bool swapY = rect.fTop > rect.fBottom;
- if (swapX && swapY) {
- // 0 becomes start index 2 and times 2 to convert from rect the rrect indices.
- return 2 * 2;
- } else if (swapX) {
- *dir = SkPath::kCCW_Direction;
- // 0 becomes start index 1 and times 2 to convert from rect the rrect indices.
- return 2 * 1;
- } else if (swapY) {
- *dir = SkPath::kCCW_Direction;
- // 0 becomes start index 3 and times 2 to convert from rect the rrect indices.
- return 2 * 3;
- }
- return 0;
- }
- static unsigned DefaultRRectDirAndStartIndex(const SkRRect& rrect, bool hasPathEffect,
- SkPath::Direction* dir) {
- // This comes from SkPath's interface. The default for adding a SkRRect to a path is
- // clockwise beginning at starting index 6.
- static constexpr unsigned kPathRRectStartIdx = 6;
- *dir = kDefaultRRectDir;
- if (!hasPathEffect) {
- // It doesn't matter what start we use, just be consistent to avoid redundant keys.
- return kDefaultRRectStart;
- }
- return kPathRRectStartIdx;
- }
- union {
- struct {
- SkRRect fRRect;
- SkPath::Direction fDir;
- unsigned fStart;
- bool fInverted;
- } fRRectData;
- struct {
- SkRect fOval;
- SkScalar fStartAngleDegrees;
- SkScalar fSweepAngleDegrees;
- int16_t fUseCenter;
- int16_t fInverted;
- } fArcData;
- struct {
- SkPath fPath;
- // Gen ID of the original path (fPath may be modified)
- int32_t fGenID;
- } fPathData;
- struct {
- SkPoint fPts[2];
- bool fInverted;
- } fLineData;
- };
- GrStyle fStyle;
- SkTLazy<SkPath> fInheritedPathForListeners;
- SkAutoSTArray<8, uint32_t> fInheritedKey;
- Type fType;
- };
- #endif
|