| /* | |
| * 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 GrStyle_DEFINED | |
| #define GrStyle_DEFINED | |
| #include "GrTypes.h" | |
| #include "SkPathEffect.h" | |
| #include "SkStrokeRec.h" | |
| #include "SkTemplates.h" | |
| /** | |
| * Represents the various ways that a GrShape can be styled. It has fill/stroking information | |
| * as well as an optional path effect. If the path effect represents dashing, the dashing | |
| * information is extracted from the path effect and stored explicitly. | |
| * | |
| * This will replace GrStrokeInfo as GrShape is deployed. | |
| */ | |
| class GrStyle { | |
| public: | |
| /** | |
| * A style object that represents a fill with no path effect. | |
| * TODO: constexpr with C++14 | |
| */ | |
| static const GrStyle& SimpleFill() { | |
| static const GrStyle kFill(SkStrokeRec::kFill_InitStyle); | |
| return kFill; | |
| } | |
| /** | |
| * A style object that represents a hairline stroke with no path effect. | |
| * TODO: constexpr with C++14 | |
| */ | |
| static const GrStyle& SimpleHairline() { | |
| static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle); | |
| return kHairline; | |
| } | |
| enum class Apply { | |
| kPathEffectOnly, | |
| kPathEffectAndStrokeRec | |
| }; | |
| /** | |
| * Optional flags for computing keys that may remove unnecessary variation in the key due to | |
| * style settings that don't affect particular classes of geometry. | |
| */ | |
| enum KeyFlags { | |
| // The shape being styled has no open contours. | |
| kClosed_KeyFlag = 0x1, | |
| // The shape being styled doesn't have any joins and so isn't affected by join type. | |
| kNoJoins_KeyFlag = 0x2 | |
| }; | |
| /** | |
| * Computes the key length for a GrStyle. The return will be negative if it cannot be turned | |
| * into a key. This occurs when there is a path effect that is not a dash. The key can | |
| * either reflect just the path effect (if one) or the path effect and the strokerec. Note | |
| * that a simple fill has a zero sized key. | |
| */ | |
| static int KeySize(const GrStyle&, Apply, uint32_t flags = 0); | |
| /** | |
| * Writes a unique key for the style into the provided buffer. This function assumes the buffer | |
| * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative | |
| * value for the combination of GrStyle, Apply and flags params. This is written so that the key | |
| * for just dash application followed by the key for the remaining SkStrokeRec is the same as | |
| * the key for applying dashing and SkStrokeRec all at once. | |
| */ | |
| static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0); | |
| GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {} | |
| explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {} | |
| GrStyle(const SkStrokeRec& strokeRec, SkPathEffect* pe) : fStrokeRec(strokeRec) { | |
| this->initPathEffect(pe); | |
| } | |
| GrStyle(const GrStyle& that) : fStrokeRec(SkStrokeRec::kFill_InitStyle) { *this = that; } | |
| explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) { | |
| this->initPathEffect(paint.getPathEffect()); | |
| } | |
| explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle) | |
| : fStrokeRec(paint, overrideStyle) { | |
| this->initPathEffect(paint.getPathEffect()); | |
| } | |
| GrStyle& operator=(const GrStyle& that) { | |
| fPathEffect = that.fPathEffect; | |
| fDashInfo = that.fDashInfo; | |
| fStrokeRec = that.fStrokeRec; | |
| return *this; | |
| } | |
| void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) { | |
| fDashInfo.reset(); | |
| fPathEffect.reset(nullptr); | |
| if (SkStrokeRec::kFill_InitStyle == fillOrHairline) { | |
| fStrokeRec.setFillStyle(); | |
| } else { | |
| fStrokeRec.setHairlineStyle(); | |
| } | |
| } | |
| /** Is this style a fill with no path effect? */ | |
| bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; } | |
| /** Is this style a hairline with no path effect? */ | |
| bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; } | |
| SkPathEffect* pathEffect() const { return fPathEffect.get(); } | |
| bool hasPathEffect() const { return SkToBool(fPathEffect.get()); } | |
| bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); } | |
| bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; } | |
| SkScalar dashPhase() const { | |
| SkASSERT(this->isDashed()); | |
| return fDashInfo.fPhase; | |
| } | |
| int dashIntervalCnt() const { | |
| SkASSERT(this->isDashed()); | |
| return fDashInfo.fIntervals.count(); | |
| } | |
| const SkScalar* dashIntervals() const { | |
| SkASSERT(this->isDashed()); | |
| return fDashInfo.fIntervals.get(); | |
| } | |
| const SkStrokeRec& strokeRec() const { return fStrokeRec; } | |
| /** Hairline or fill styles without path effects make no alterations to a geometry. */ | |
| bool applies() const { | |
| return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle()); | |
| } | |
| static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) { | |
| // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale | |
| // factor of 1. This isn't necessarily a good choice and in the future we might consider | |
| // taking a bounds here for the perspective case. | |
| return SkScalarAbs(matrix.getMaxScale()); | |
| } | |
| /** | |
| * Applies just the path effect and returns remaining stroke information. This will fail if | |
| * there is no path effect. dst may or may not have been overwritten on failure. Scale controls | |
| * geometric approximations made by the path effect. It is typically computed from the view | |
| * matrix. | |
| */ | |
| bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke, | |
| const SkPath& src, SkScalar scale) const; | |
| /** | |
| * If this succeeds then the result path should be filled or hairlined as indicated by the | |
| * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the | |
| * strokerec doesn't change the geometry. When this fails the outputs may or may not have | |
| * been overwritten. Scale controls geometric approximations made by the path effect and | |
| * stroker. It is typically computed from the view matrix. | |
| */ | |
| bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline, | |
| const SkPath& src, SkScalar scale) const; | |
| /** Given bounds of a path compute the bounds of path with the style applied. */ | |
| void adjustBounds(SkRect* dst, const SkRect& src) const { | |
| if (this->pathEffect()) { | |
| this->pathEffect()->computeFastBounds(dst, src); | |
| // This may not be the correct SkStrokeRec to use. skbug.com/5299 | |
| // It happens to work for dashing. | |
| SkScalar radius = fStrokeRec.getInflationRadius(); | |
| dst->outset(radius, radius); | |
| } else { | |
| SkScalar radius = fStrokeRec.getInflationRadius(); | |
| *dst = src.makeOutset(radius, radius); | |
| } | |
| } | |
| private: | |
| void initPathEffect(SkPathEffect* pe); | |
| struct DashInfo { | |
| DashInfo() : fType(SkPathEffect::kNone_DashType) {} | |
| DashInfo& operator=(const DashInfo& that) { | |
| fType = that.fType; | |
| fPhase = that.fPhase; | |
| fIntervals.reset(that.fIntervals.count()); | |
| sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(), | |
| sizeof(SkScalar) * that.fIntervals.count()); | |
| return *this; | |
| } | |
| void reset() { | |
| fType = SkPathEffect::kNone_DashType; | |
| fIntervals.reset(0); | |
| } | |
| SkPathEffect::DashType fType; | |
| SkScalar fPhase; | |
| SkAutoSTArray<4, SkScalar> fIntervals; | |
| }; | |
| bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const; | |
| SkStrokeRec fStrokeRec; | |
| sk_sp<SkPathEffect> fPathEffect; | |
| DashInfo fDashInfo; | |
| }; | |
| #endif |