123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767 |
- /*
- * Copyright 2016 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "src/gpu/geometry/GrShape.h"
- #include <utility>
- GrShape& GrShape::operator=(const GrShape& that) {
- fStyle = that.fStyle;
- this->changeType(that.fType, Type::kPath == that.fType ? &that.path() : nullptr);
- switch (fType) {
- case Type::kEmpty:
- break;
- case Type::kInvertedEmpty:
- break;
- case Type::kRRect:
- fRRectData = that.fRRectData;
- break;
- case Type::kArc:
- fArcData = that.fArcData;
- break;
- case Type::kLine:
- fLineData = that.fLineData;
- break;
- case Type::kPath:
- fPathData.fGenID = that.fPathData.fGenID;
- break;
- }
- fInheritedKey.reset(that.fInheritedKey.count());
- sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
- sizeof(uint32_t) * fInheritedKey.count());
- if (that.fInheritedPathForListeners.isValid()) {
- fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
- } else {
- fInheritedPathForListeners.reset();
- }
- return *this;
- }
- static bool flip_inversion(bool originalIsInverted, GrShape::FillInversion inversion) {
- switch (inversion) {
- case GrShape::FillInversion::kPreserve:
- return false;
- case GrShape::FillInversion::kFlip:
- return true;
- case GrShape::FillInversion::kForceInverted:
- return !originalIsInverted;
- case GrShape::FillInversion::kForceNoninverted:
- return originalIsInverted;
- }
- return false;
- }
- static bool is_inverted(bool originalIsInverted, GrShape::FillInversion inversion) {
- switch (inversion) {
- case GrShape::FillInversion::kPreserve:
- return originalIsInverted;
- case GrShape::FillInversion::kFlip:
- return !originalIsInverted;
- case GrShape::FillInversion::kForceInverted:
- return true;
- case GrShape::FillInversion::kForceNoninverted:
- return false;
- }
- return false;
- }
- GrShape GrShape::MakeFilled(const GrShape& original, FillInversion inversion) {
- if (original.style().isSimpleFill() && !flip_inversion(original.inverseFilled(), inversion)) {
- // By returning the original rather than falling through we can preserve any inherited style
- // key. Otherwise, we wipe it out below since the style change invalidates it.
- return original;
- }
- GrShape result;
- if (original.fInheritedPathForListeners.isValid()) {
- result.fInheritedPathForListeners.set(*original.fInheritedPathForListeners.get());
- }
- switch (original.fType) {
- case Type::kRRect:
- result.fType = original.fType;
- result.fRRectData.fRRect = original.fRRectData.fRRect;
- result.fRRectData.fDir = kDefaultRRectDir;
- result.fRRectData.fStart = kDefaultRRectStart;
- result.fRRectData.fInverted = is_inverted(original.fRRectData.fInverted, inversion);
- break;
- case Type::kArc:
- result.fType = original.fType;
- result.fArcData.fOval = original.fArcData.fOval;
- result.fArcData.fStartAngleDegrees = original.fArcData.fStartAngleDegrees;
- result.fArcData.fSweepAngleDegrees = original.fArcData.fSweepAngleDegrees;
- result.fArcData.fUseCenter = original.fArcData.fUseCenter;
- result.fArcData.fInverted = is_inverted(original.fArcData.fInverted, inversion);
- break;
- case Type::kLine:
- // Lines don't fill.
- if (is_inverted(original.fLineData.fInverted, inversion)) {
- result.fType = Type::kInvertedEmpty;
- } else {
- result.fType = Type::kEmpty;
- }
- break;
- case Type::kEmpty:
- result.fType = is_inverted(false, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
- break;
- case Type::kInvertedEmpty:
- result.fType = is_inverted(true, inversion) ? Type::kInvertedEmpty : Type::kEmpty;
- break;
- case Type::kPath:
- result.initType(Type::kPath, &original.fPathData.fPath);
- result.fPathData.fGenID = original.fPathData.fGenID;
- if (flip_inversion(original.fPathData.fPath.isInverseFillType(), inversion)) {
- result.fPathData.fPath.toggleInverseFillType();
- }
- if (!original.style().isSimpleFill()) {
- // Going from a non-filled style to fill may allow additional simplifications (e.g.
- // closing an open rect that wasn't closed in the original shape because it had
- // stroke style).
- result.attemptToSimplifyPath();
- }
- break;
- }
- // We don't copy the inherited key since it can contain path effect information that we just
- // stripped.
- return result;
- }
- SkRect GrShape::bounds() const {
- // Bounds where left == bottom or top == right can indicate a line or point shape. We return
- // inverted bounds for a truly empty shape.
- static constexpr SkRect kInverted = SkRect::MakeLTRB(1, 1, -1, -1);
- switch (fType) {
- case Type::kEmpty:
- return kInverted;
- case Type::kInvertedEmpty:
- return kInverted;
- case Type::kLine: {
- SkRect bounds;
- if (fLineData.fPts[0].fX < fLineData.fPts[1].fX) {
- bounds.fLeft = fLineData.fPts[0].fX;
- bounds.fRight = fLineData.fPts[1].fX;
- } else {
- bounds.fLeft = fLineData.fPts[1].fX;
- bounds.fRight = fLineData.fPts[0].fX;
- }
- if (fLineData.fPts[0].fY < fLineData.fPts[1].fY) {
- bounds.fTop = fLineData.fPts[0].fY;
- bounds.fBottom = fLineData.fPts[1].fY;
- } else {
- bounds.fTop = fLineData.fPts[1].fY;
- bounds.fBottom = fLineData.fPts[0].fY;
- }
- return bounds;
- }
- case Type::kRRect:
- return fRRectData.fRRect.getBounds();
- case Type::kArc:
- // Could make this less conservative by looking at angles.
- return fArcData.fOval;
- case Type::kPath:
- return this->path().getBounds();
- }
- SK_ABORT("Unknown shape type");
- return kInverted;
- }
- SkRect GrShape::styledBounds() const {
- if (this->isEmpty() && !fStyle.hasNonDashPathEffect()) {
- return SkRect::MakeEmpty();
- }
- SkRect bounds;
- fStyle.adjustBounds(&bounds, this->bounds());
- return bounds;
- }
- // If the path is small enough to be keyed from its data this returns key length, otherwise -1.
- static int path_key_from_data_size(const SkPath& path) {
- const int verbCnt = path.countVerbs();
- if (verbCnt > GrShape::kMaxKeyFromDataVerbCnt) {
- return -1;
- }
- const int pointCnt = path.countPoints();
- const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
- GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
- GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
- // 2 is for the verb cnt and a fill type. Each verb is a byte but we'll pad the verb data out to
- // a uint32_t length.
- return 2 + (SkAlign4(verbCnt) >> 2) + 2 * pointCnt + conicWeightCnt;
- }
- // Writes the path data key into the passed pointer.
- static void write_path_key_from_data(const SkPath& path, uint32_t* origKey) {
- uint32_t* key = origKey;
- // The check below should take care of negative values casted positive.
- const int verbCnt = path.countVerbs();
- const int pointCnt = path.countPoints();
- const int conicWeightCnt = SkPathPriv::ConicWeightCnt(path);
- SkASSERT(verbCnt <= GrShape::kMaxKeyFromDataVerbCnt);
- SkASSERT(pointCnt && verbCnt);
- *key++ = path.getFillType();
- *key++ = verbCnt;
- memcpy(key, SkPathPriv::VerbData(path), verbCnt * sizeof(uint8_t));
- int verbKeySize = SkAlign4(verbCnt);
- // pad out to uint32_t alignment using value that will stand out when debugging.
- uint8_t* pad = reinterpret_cast<uint8_t*>(key)+ verbCnt;
- memset(pad, 0xDE, verbKeySize - verbCnt);
- key += verbKeySize >> 2;
- memcpy(key, SkPathPriv::PointData(path), sizeof(SkPoint) * pointCnt);
- GR_STATIC_ASSERT(sizeof(SkPoint) == 2 * sizeof(uint32_t));
- key += 2 * pointCnt;
- sk_careful_memcpy(key, SkPathPriv::ConicWeightData(path), sizeof(SkScalar) * conicWeightCnt);
- GR_STATIC_ASSERT(sizeof(SkScalar) == sizeof(uint32_t));
- SkDEBUGCODE(key += conicWeightCnt);
- SkASSERT(key - origKey == path_key_from_data_size(path));
- }
- int GrShape::unstyledKeySize() const {
- if (fInheritedKey.count()) {
- return fInheritedKey.count();
- }
- switch (fType) {
- case Type::kEmpty:
- return 1;
- case Type::kInvertedEmpty:
- return 1;
- case Type::kRRect:
- SkASSERT(!fInheritedKey.count());
- GR_STATIC_ASSERT(0 == SkRRect::kSizeInMemory % sizeof(uint32_t));
- // + 1 for the direction, start index, and inverseness.
- return SkRRect::kSizeInMemory / sizeof(uint32_t) + 1;
- case Type::kArc:
- SkASSERT(!fInheritedKey.count());
- GR_STATIC_ASSERT(0 == sizeof(fArcData) % sizeof(uint32_t));
- return sizeof(fArcData) / sizeof(uint32_t);
- case Type::kLine:
- GR_STATIC_ASSERT(2 * sizeof(uint32_t) == sizeof(SkPoint));
- // 4 for the end points and 1 for the inverseness
- return 5;
- case Type::kPath: {
- if (0 == fPathData.fGenID) {
- return -1;
- }
- int dataKeySize = path_key_from_data_size(fPathData.fPath);
- if (dataKeySize >= 0) {
- return dataKeySize;
- }
- // The key is the path ID and fill type.
- return 2;
- }
- }
- SK_ABORT("Should never get here.");
- return 0;
- }
- void GrShape::writeUnstyledKey(uint32_t* key) const {
- SkASSERT(this->unstyledKeySize());
- SkDEBUGCODE(uint32_t* origKey = key;)
- if (fInheritedKey.count()) {
- memcpy(key, fInheritedKey.get(), sizeof(uint32_t) * fInheritedKey.count());
- SkDEBUGCODE(key += fInheritedKey.count();)
- } else {
- switch (fType) {
- case Type::kEmpty:
- *key++ = 1;
- break;
- case Type::kInvertedEmpty:
- *key++ = 2;
- break;
- case Type::kRRect:
- fRRectData.fRRect.writeToMemory(key);
- key += SkRRect::kSizeInMemory / sizeof(uint32_t);
- *key = (fRRectData.fDir == SkPath::kCCW_Direction) ? (1 << 31) : 0;
- *key |= fRRectData.fInverted ? (1 << 30) : 0;
- *key++ |= fRRectData.fStart;
- SkASSERT(fRRectData.fStart < 8);
- break;
- case Type::kArc:
- memcpy(key, &fArcData, sizeof(fArcData));
- key += sizeof(fArcData) / sizeof(uint32_t);
- break;
- case Type::kLine:
- memcpy(key, fLineData.fPts, 2 * sizeof(SkPoint));
- key += 4;
- *key++ = fLineData.fInverted ? 1 : 0;
- break;
- case Type::kPath: {
- SkASSERT(fPathData.fGenID);
- int dataKeySize = path_key_from_data_size(fPathData.fPath);
- if (dataKeySize >= 0) {
- write_path_key_from_data(fPathData.fPath, key);
- return;
- }
- *key++ = fPathData.fGenID;
- // We could canonicalize the fill rule for paths that don't differentiate between
- // even/odd or winding fill (e.g. convex).
- *key++ = this->path().getFillType();
- break;
- }
- }
- }
- SkASSERT(key - origKey == this->unstyledKeySize());
- }
- void GrShape::setInheritedKey(const GrShape &parent, GrStyle::Apply apply, SkScalar scale) {
- SkASSERT(!fInheritedKey.count());
- // If the output shape turns out to be simple, then we will just use its geometric key
- if (Type::kPath == fType) {
- // We want ApplyFullStyle(ApplyPathEffect(shape)) to have the same key as
- // ApplyFullStyle(shape).
- // The full key is structured as (geo,path_effect,stroke).
- // If we do ApplyPathEffect we get geo,path_effect as the inherited key. If we then
- // do ApplyFullStyle we'll memcpy geo,path_effect into the new inherited key
- // and then append the style key (which should now be stroke only) at the end.
- int parentCnt = parent.fInheritedKey.count();
- bool useParentGeoKey = !parentCnt;
- if (useParentGeoKey) {
- parentCnt = parent.unstyledKeySize();
- if (parentCnt < 0) {
- // The parent's geometry has no key so we will have no key.
- fPathData.fGenID = 0;
- return;
- }
- }
- uint32_t styleKeyFlags = 0;
- if (parent.knownToBeClosed()) {
- styleKeyFlags |= GrStyle::kClosed_KeyFlag;
- }
- if (parent.asLine(nullptr, nullptr)) {
- styleKeyFlags |= GrStyle::kNoJoins_KeyFlag;
- }
- int styleCnt = GrStyle::KeySize(parent.fStyle, apply, styleKeyFlags);
- if (styleCnt < 0) {
- // The style doesn't allow a key, set the path gen ID to 0 so that we fail when
- // we try to get a key for the shape.
- fPathData.fGenID = 0;
- return;
- }
- fInheritedKey.reset(parentCnt + styleCnt);
- if (useParentGeoKey) {
- // This will be the geo key.
- parent.writeUnstyledKey(fInheritedKey.get());
- } else {
- // This should be (geo,path_effect).
- memcpy(fInheritedKey.get(), parent.fInheritedKey.get(),
- parentCnt * sizeof(uint32_t));
- }
- // Now turn (geo,path_effect) or (geo) into (geo,path_effect,stroke)
- GrStyle::WriteKey(fInheritedKey.get() + parentCnt, parent.fStyle, apply, scale,
- styleKeyFlags);
- }
- }
- const SkPath* GrShape::originalPathForListeners() const {
- if (fInheritedPathForListeners.isValid()) {
- return fInheritedPathForListeners.get();
- } else if (Type::kPath == fType && !fPathData.fPath.isVolatile()) {
- return &fPathData.fPath;
- }
- return nullptr;
- }
- void GrShape::addGenIDChangeListener(sk_sp<SkPathRef::GenIDChangeListener> listener) const {
- if (const auto* lp = this->originalPathForListeners()) {
- SkPathPriv::AddGenIDChangeListener(*lp, std::move(listener));
- }
- }
- GrShape GrShape::MakeArc(const SkRect& oval, SkScalar startAngleDegrees, SkScalar sweepAngleDegrees,
- bool useCenter, const GrStyle& style) {
- GrShape result;
- result.changeType(Type::kArc);
- result.fArcData.fOval = oval;
- result.fArcData.fStartAngleDegrees = startAngleDegrees;
- result.fArcData.fSweepAngleDegrees = sweepAngleDegrees;
- result.fArcData.fUseCenter = useCenter;
- result.fArcData.fInverted = false;
- result.fStyle = style;
- result.attemptToSimplifyArc();
- return result;
- }
- GrShape::GrShape(const GrShape& that) : fStyle(that.fStyle) {
- const SkPath* thatPath = Type::kPath == that.fType ? &that.fPathData.fPath : nullptr;
- this->initType(that.fType, thatPath);
- switch (fType) {
- case Type::kEmpty:
- break;
- case Type::kInvertedEmpty:
- break;
- case Type::kRRect:
- fRRectData = that.fRRectData;
- break;
- case Type::kArc:
- fArcData = that.fArcData;
- break;
- case Type::kLine:
- fLineData = that.fLineData;
- break;
- case Type::kPath:
- fPathData.fGenID = that.fPathData.fGenID;
- break;
- }
- fInheritedKey.reset(that.fInheritedKey.count());
- sk_careful_memcpy(fInheritedKey.get(), that.fInheritedKey.get(),
- sizeof(uint32_t) * fInheritedKey.count());
- if (that.fInheritedPathForListeners.isValid()) {
- fInheritedPathForListeners.set(*that.fInheritedPathForListeners.get());
- }
- }
- GrShape::GrShape(const GrShape& parent, GrStyle::Apply apply, SkScalar scale) {
- // TODO: Add some quantization of scale for better cache performance here or leave that up
- // to caller?
- // TODO: For certain shapes and stroke params we could ignore the scale. (e.g. miter or bevel
- // stroke of a rect).
- if (!parent.style().applies() ||
- (GrStyle::Apply::kPathEffectOnly == apply && !parent.style().pathEffect())) {
- this->initType(Type::kEmpty);
- *this = parent;
- return;
- }
- SkPathEffect* pe = parent.fStyle.pathEffect();
- SkTLazy<SkPath> tmpPath;
- const GrShape* parentForKey = &parent;
- SkTLazy<GrShape> tmpParent;
- this->initType(Type::kPath);
- fPathData.fGenID = 0;
- if (pe) {
- const SkPath* srcForPathEffect;
- if (parent.fType == Type::kPath) {
- srcForPathEffect = &parent.path();
- } else {
- srcForPathEffect = tmpPath.init();
- parent.asPath(tmpPath.get());
- }
- // Should we consider bounds? Would have to include in key, but it'd be nice to know
- // if the bounds actually modified anything before including in key.
- SkStrokeRec strokeRec = parent.fStyle.strokeRec();
- if (!parent.fStyle.applyPathEffectToPath(&this->path(), &strokeRec, *srcForPathEffect,
- scale)) {
- tmpParent.init(*srcForPathEffect, GrStyle(strokeRec, nullptr));
- *this = tmpParent.get()->applyStyle(apply, scale);
- return;
- }
- // A path effect has access to change the res scale but we aren't expecting it to and it
- // would mess up our key computation.
- SkASSERT(scale == strokeRec.getResScale());
- if (GrStyle::Apply::kPathEffectAndStrokeRec == apply && strokeRec.needToApply()) {
- // The intermediate shape may not be a general path. If we we're just applying
- // the path effect then attemptToReduceFromPath would catch it. This means that
- // when we subsequently applied the remaining strokeRec we would have a non-path
- // parent shape that would be used to determine the the stroked path's key.
- // We detect that case here and change parentForKey to a temporary that represents
- // the simpler shape so that applying both path effect and the strokerec all at
- // once produces the same key.
- tmpParent.init(this->path(), GrStyle(strokeRec, nullptr));
- tmpParent.get()->setInheritedKey(parent, GrStyle::Apply::kPathEffectOnly, scale);
- if (!tmpPath.isValid()) {
- tmpPath.init();
- }
- tmpParent.get()->asPath(tmpPath.get());
- SkStrokeRec::InitStyle fillOrHairline;
- // The parent shape may have simplified away the strokeRec, check for that here.
- if (tmpParent.get()->style().applies()) {
- SkAssertResult(tmpParent.get()->style().applyToPath(&this->path(), &fillOrHairline,
- *tmpPath.get(), scale));
- } else if (tmpParent.get()->style().isSimpleFill()) {
- fillOrHairline = SkStrokeRec::kFill_InitStyle;
- } else {
- SkASSERT(tmpParent.get()->style().isSimpleHairline());
- fillOrHairline = SkStrokeRec::kHairline_InitStyle;
- }
- fStyle.resetToInitStyle(fillOrHairline);
- parentForKey = tmpParent.get();
- } else {
- fStyle = GrStyle(strokeRec, nullptr);
- }
- } else {
- const SkPath* srcForParentStyle;
- if (parent.fType == Type::kPath) {
- srcForParentStyle = &parent.path();
- } else {
- srcForParentStyle = tmpPath.init();
- parent.asPath(tmpPath.get());
- }
- SkStrokeRec::InitStyle fillOrHairline;
- SkASSERT(parent.fStyle.applies());
- SkASSERT(!parent.fStyle.pathEffect());
- SkAssertResult(parent.fStyle.applyToPath(&this->path(), &fillOrHairline, *srcForParentStyle,
- scale));
- fStyle.resetToInitStyle(fillOrHairline);
- }
- if (parent.fInheritedPathForListeners.isValid()) {
- fInheritedPathForListeners.set(*parent.fInheritedPathForListeners.get());
- } else if (Type::kPath == parent.fType && !parent.fPathData.fPath.isVolatile()) {
- fInheritedPathForListeners.set(parent.fPathData.fPath);
- }
- this->attemptToSimplifyPath();
- this->setInheritedKey(*parentForKey, apply, scale);
- }
- void GrShape::attemptToSimplifyPath() {
- SkRect rect;
- SkRRect rrect;
- SkPath::Direction rrectDir;
- unsigned rrectStart;
- bool inverted = this->path().isInverseFillType();
- SkPoint pts[2];
- if (this->path().isEmpty()) {
- // Dashing ignores inverseness skbug.com/5421.
- this->changeType(inverted && !this->style().isDashed() ? Type::kInvertedEmpty
- : Type::kEmpty);
- } else if (this->path().isLine(pts)) {
- this->changeType(Type::kLine);
- fLineData.fPts[0] = pts[0];
- fLineData.fPts[1] = pts[1];
- fLineData.fInverted = inverted;
- } else if (SkPathPriv::IsRRect(this->path(), &rrect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fDir = rrectDir;
- fRRectData.fStart = rrectStart;
- fRRectData.fInverted = inverted;
- SkASSERT(!fRRectData.fRRect.isEmpty());
- } else if (SkPathPriv::IsOval(this->path(), &rect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect.setOval(rect);
- fRRectData.fDir = rrectDir;
- fRRectData.fInverted = inverted;
- // convert from oval indexing to rrect indexiing.
- fRRectData.fStart = 2 * rrectStart;
- } else if (SkPathPriv::IsSimpleClosedRect(this->path(), &rect, &rrectDir, &rrectStart)) {
- this->changeType(Type::kRRect);
- // When there is a path effect we restrict rect detection to the narrower API that
- // gives us the starting position. Otherwise, we will retry with the more aggressive
- // isRect().
- fRRectData.fRRect.setRect(rect);
- fRRectData.fInverted = inverted;
- fRRectData.fDir = rrectDir;
- // convert from rect indexing to rrect indexiing.
- fRRectData.fStart = 2 * rrectStart;
- } else if (!this->style().hasPathEffect()) {
- bool closed;
- if (this->path().isRect(&rect, &closed, nullptr)) {
- if (closed || this->style().isSimpleFill()) {
- this->changeType(Type::kRRect);
- fRRectData.fRRect.setRect(rect);
- // Since there is no path effect the dir and start index is immaterial.
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
- // There isn't dashing so we will have to preserver inverseness.
- fRRectData.fInverted = inverted;
- }
- }
- }
- if (Type::kPath != fType) {
- fInheritedKey.reset(0);
- // Whenever we simplify to a non-path, break the chain so we no longer refer to the
- // original path. This prevents attaching genID listeners to temporary paths created when
- // drawing simple shapes.
- fInheritedPathForListeners.reset();
- if (Type::kRRect == fType) {
- this->attemptToSimplifyRRect();
- } else if (Type::kLine == fType) {
- this->attemptToSimplifyLine();
- }
- } else {
- if (fInheritedKey.count() || this->path().isVolatile()) {
- fPathData.fGenID = 0;
- } else {
- fPathData.fGenID = this->path().getGenerationID();
- }
- if (!this->style().hasNonDashPathEffect()) {
- if (this->style().strokeRec().getStyle() == SkStrokeRec::kStroke_Style ||
- this->style().strokeRec().getStyle() == SkStrokeRec::kHairline_Style) {
- // Stroke styles don't differentiate between winding and even/odd.
- // Moreover, dashing ignores inverseness (skbug.com/5421)
- bool inverse = !this->style().isDashed() && this->path().isInverseFillType();
- if (inverse) {
- this->path().setFillType(kDefaultPathInverseFillType);
- } else {
- this->path().setFillType(kDefaultPathFillType);
- }
- } else if (this->path().isConvex()) {
- // There is no distinction between even/odd and non-zero winding count for convex
- // paths.
- if (this->path().isInverseFillType()) {
- this->path().setFillType(kDefaultPathInverseFillType);
- } else {
- this->path().setFillType(kDefaultPathFillType);
- }
- }
- }
- }
- }
- void GrShape::attemptToSimplifyRRect() {
- SkASSERT(Type::kRRect == fType);
- SkASSERT(!fInheritedKey.count());
- if (fRRectData.fRRect.isEmpty()) {
- // An empty filled rrect is equivalent to a filled empty path with inversion preserved.
- if (fStyle.isSimpleFill()) {
- fType = fRRectData.fInverted ? Type::kInvertedEmpty : Type::kEmpty;
- fStyle = GrStyle::SimpleFill();
- return;
- }
- // Dashing a rrect with no width or height is equivalent to filling an emtpy path.
- // When skbug.com/7387 is fixed this should be modified or removed as a dashed zero length
- // line will produce cap geometry if the effect begins in an "on" interval.
- if (fStyle.isDashed() && !fRRectData.fRRect.width() && !fRRectData.fRRect.height()) {
- // Dashing ignores the inverseness (currently). skbug.com/5421.
- fType = Type::kEmpty;
- fStyle = GrStyle::SimpleFill();
- return;
- }
- }
- if (!this->style().hasPathEffect()) {
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
- } else if (fStyle.isDashed()) {
- // Dashing ignores the inverseness (currently). skbug.com/5421
- fRRectData.fInverted = false;
- // Possible TODO here: Check whether the dash results in a single arc or line.
- }
- // Turn a stroke-and-filled miter rect into a filled rect. TODO: more rrect stroke shortcuts.
- if (!fStyle.hasPathEffect() &&
- fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style &&
- fStyle.strokeRec().getJoin() == SkPaint::kMiter_Join &&
- fStyle.strokeRec().getMiter() >= SK_ScalarSqrt2 &&
- fRRectData.fRRect.isRect()) {
- SkScalar r = fStyle.strokeRec().getWidth() / 2;
- fRRectData.fRRect = SkRRect::MakeRect(fRRectData.fRRect.rect().makeOutset(r, r));
- fStyle = GrStyle::SimpleFill();
- }
- }
- void GrShape::attemptToSimplifyLine() {
- SkASSERT(Type::kLine == fType);
- SkASSERT(!fInheritedKey.count());
- if (fStyle.isDashed()) {
- bool allOffsZero = true;
- for (int i = 1; i < fStyle.dashIntervalCnt() && allOffsZero; i += 2) {
- allOffsZero = !fStyle.dashIntervals()[i];
- }
- if (allOffsZero && this->attemptToSimplifyStrokedLineToRRect()) {
- return;
- }
- // Dashing ignores inverseness.
- fLineData.fInverted = false;
- return;
- } else if (fStyle.hasPathEffect()) {
- return;
- }
- if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style) {
- // Make stroke + fill be stroke since the fill is empty.
- SkStrokeRec rec = fStyle.strokeRec();
- rec.setStrokeStyle(fStyle.strokeRec().getWidth(), false);
- fStyle = GrStyle(rec, nullptr);
- }
- if (fStyle.isSimpleFill()) {
- this->changeType(fLineData.fInverted ? Type::kInvertedEmpty : Type::kEmpty);
- return;
- }
- if (fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style &&
- this->attemptToSimplifyStrokedLineToRRect()) {
- return;
- }
- // Only path effects could care about the order of the points. Otherwise canonicalize
- // the point order.
- SkPoint* pts = fLineData.fPts;
- if (pts[1].fY < pts[0].fY || (pts[1].fY == pts[0].fY && pts[1].fX < pts[0].fX)) {
- using std::swap;
- swap(pts[0], pts[1]);
- }
- }
- void GrShape::attemptToSimplifyArc() {
- SkASSERT(fType == Type::kArc);
- SkASSERT(!fArcData.fInverted);
- if (fArcData.fOval.isEmpty() || !fArcData.fSweepAngleDegrees) {
- this->changeType(Type::kEmpty);
- return;
- }
- // Assuming no path effect, a filled, stroked, hairline, or stroke-and-filled arc that traverses
- // the full circle and doesn't use the center point is an oval. Unless it has square or round
- // caps. They may protrude out of the oval. Round caps can't protrude out of a circle but we're
- // ignoring that for now.
- if (fStyle.isSimpleFill() || (!fStyle.pathEffect() && !fArcData.fUseCenter &&
- fStyle.strokeRec().getCap() == SkPaint::kButt_Cap)) {
- if (fArcData.fSweepAngleDegrees >= 360.f || fArcData.fSweepAngleDegrees <= -360.f) {
- auto oval = fArcData.fOval;
- this->changeType(Type::kRRect);
- this->fRRectData.fRRect.setOval(oval);
- this->fRRectData.fDir = kDefaultRRectDir;
- this->fRRectData.fStart = kDefaultRRectStart;
- this->fRRectData.fInverted = false;
- return;
- }
- }
- if (!fStyle.pathEffect()) {
- // Canonicalize the arc such that the start is always in [0, 360) and the sweep is always
- // positive.
- if (fArcData.fSweepAngleDegrees < 0) {
- fArcData.fStartAngleDegrees = fArcData.fStartAngleDegrees + fArcData.fSweepAngleDegrees;
- fArcData.fSweepAngleDegrees = -fArcData.fSweepAngleDegrees;
- }
- }
- if (this->fArcData.fStartAngleDegrees < 0 || this->fArcData.fStartAngleDegrees >= 360.f) {
- this->fArcData.fStartAngleDegrees = SkScalarMod(this->fArcData.fStartAngleDegrees, 360.f);
- }
- // Possible TODOs here: Look at whether dash pattern results in a single dash and convert to
- // non-dashed stroke. Stroke and fill can be fill if circular and no path effect. Just stroke
- // could as well if the stroke fills the center.
- }
- bool GrShape::attemptToSimplifyStrokedLineToRRect() {
- SkASSERT(Type::kLine == fType);
- SkASSERT(fStyle.strokeRec().getStyle() == SkStrokeRec::kStroke_Style);
- SkRect rect;
- SkVector outset;
- // If we allowed a rotation angle for rrects we could capture all cases here.
- if (fLineData.fPts[0].fY == fLineData.fPts[1].fY) {
- rect.fLeft = SkTMin(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
- rect.fRight = SkTMax(fLineData.fPts[0].fX, fLineData.fPts[1].fX);
- rect.fTop = rect.fBottom = fLineData.fPts[0].fY;
- outset.fY = fStyle.strokeRec().getWidth() / 2.f;
- outset.fX = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fY;
- } else if (fLineData.fPts[0].fX == fLineData.fPts[1].fX) {
- rect.fTop = SkTMin(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
- rect.fBottom = SkTMax(fLineData.fPts[0].fY, fLineData.fPts[1].fY);
- rect.fLeft = rect.fRight = fLineData.fPts[0].fX;
- outset.fX = fStyle.strokeRec().getWidth() / 2.f;
- outset.fY = SkPaint::kButt_Cap == fStyle.strokeRec().getCap() ? 0.f : outset.fX;
- } else {
- return false;
- }
- rect.outset(outset.fX, outset.fY);
- if (rect.isEmpty()) {
- this->changeType(Type::kEmpty);
- fStyle = GrStyle::SimpleFill();
- return true;
- }
- SkRRect rrect;
- if (fStyle.strokeRec().getCap() == SkPaint::kRound_Cap) {
- SkASSERT(outset.fX == outset.fY);
- rrect = SkRRect::MakeRectXY(rect, outset.fX, outset.fY);
- } else {
- rrect = SkRRect::MakeRect(rect);
- }
- bool inverted = fLineData.fInverted && !fStyle.hasPathEffect();
- this->changeType(Type::kRRect);
- fRRectData.fRRect = rrect;
- fRRectData.fInverted = inverted;
- fRRectData.fDir = kDefaultRRectDir;
- fRRectData.fStart = kDefaultRRectStart;
- fStyle = GrStyle::SimpleFill();
- return true;
- }
|