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 GrStyle_DEFINED 9 #define GrStyle_DEFINED 10 11 #include "GrTypes.h" 12 #include "SkPathEffect.h" 13 #include "SkStrokeRec.h" 14 #include "SkTemplates.h" 15 16 /** 17 * Represents the various ways that a GrShape can be styled. It has fill/stroking information 18 * as well as an optional path effect. If the path effect represents dashing, the dashing 19 * information is extracted from the path effect and stored explicitly. 20 * 21 * This will replace GrStrokeInfo as GrShape is deployed. 22 */ 23 class GrStyle { 24 public: 25 /** 26 * A style object that represents a fill with no path effect. 27 * TODO: constexpr with C++14 28 */ SimpleFill()29 static const GrStyle& SimpleFill() { 30 static const GrStyle kFill(SkStrokeRec::kFill_InitStyle); 31 return kFill; 32 } 33 34 /** 35 * A style object that represents a hairline stroke with no path effect. 36 * TODO: constexpr with C++14 37 */ SimpleHairline()38 static const GrStyle& SimpleHairline() { 39 static const GrStyle kHairline(SkStrokeRec::kHairline_InitStyle); 40 return kHairline; 41 } 42 43 enum class Apply { 44 kPathEffectOnly, 45 kPathEffectAndStrokeRec 46 }; 47 48 /** 49 * Optional flags for computing keys that may remove unnecessary variation in the key due to 50 * style settings that don't affect particular classes of geometry. 51 */ 52 enum KeyFlags { 53 // The shape being styled has no open contours. 54 kClosed_KeyFlag = 0x1, 55 // The shape being styled doesn't have any joins and so isn't affected by join type. 56 kNoJoins_KeyFlag = 0x2 57 }; 58 59 /** 60 * Computes the key length for a GrStyle. The return will be negative if it cannot be turned 61 * into a key. This occurs when there is a path effect that is not a dash. The key can 62 * either reflect just the path effect (if one) or the path effect and the strokerec. Note 63 * that a simple fill has a zero sized key. 64 */ 65 static int KeySize(const GrStyle&, Apply, uint32_t flags = 0); 66 67 /** 68 * Writes a unique key for the style into the provided buffer. This function assumes the buffer 69 * has room for at least KeySize() values. It assumes that KeySize() returns a non-negative 70 * value for the combination of GrStyle, Apply and flags params. This is written so that the key 71 * for just dash application followed by the key for the remaining SkStrokeRec is the same as 72 * the key for applying dashing and SkStrokeRec all at once. 73 */ 74 static void WriteKey(uint32_t*, const GrStyle&, Apply, SkScalar scale, uint32_t flags = 0); 75 GrStyle()76 GrStyle() : GrStyle(SkStrokeRec::kFill_InitStyle) {} 77 GrStyle(SkStrokeRec::InitStyle initStyle)78 explicit GrStyle(SkStrokeRec::InitStyle initStyle) : fStrokeRec(initStyle) {} 79 GrStyle(const SkStrokeRec & strokeRec,sk_sp<SkPathEffect> pe)80 GrStyle(const SkStrokeRec& strokeRec, sk_sp<SkPathEffect> pe) : fStrokeRec(strokeRec) { 81 this->initPathEffect(std::move(pe)); 82 } 83 84 GrStyle(const GrStyle& that) = default; 85 GrStyle(const SkPaint & paint)86 explicit GrStyle(const SkPaint& paint) : fStrokeRec(paint) { 87 this->initPathEffect(paint.refPathEffect()); 88 } 89 GrStyle(const SkPaint & paint,SkPaint::Style overrideStyle)90 explicit GrStyle(const SkPaint& paint, SkPaint::Style overrideStyle) 91 : fStrokeRec(paint, overrideStyle) { 92 this->initPathEffect(paint.refPathEffect()); 93 } 94 95 GrStyle& operator=(const GrStyle& that) { 96 fPathEffect = that.fPathEffect; 97 fDashInfo = that.fDashInfo; 98 fStrokeRec = that.fStrokeRec; 99 return *this; 100 } 101 resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline)102 void resetToInitStyle(SkStrokeRec::InitStyle fillOrHairline) { 103 fDashInfo.reset(); 104 fPathEffect.reset(nullptr); 105 if (SkStrokeRec::kFill_InitStyle == fillOrHairline) { 106 fStrokeRec.setFillStyle(); 107 } else { 108 fStrokeRec.setHairlineStyle(); 109 } 110 } 111 112 /** Is this style a fill with no path effect? */ isSimpleFill()113 bool isSimpleFill() const { return fStrokeRec.isFillStyle() && !fPathEffect; } 114 115 /** Is this style a hairline with no path effect? */ isSimpleHairline()116 bool isSimpleHairline() const { return fStrokeRec.isHairlineStyle() && !fPathEffect; } 117 pathEffect()118 SkPathEffect* pathEffect() const { return fPathEffect.get(); } refPathEffect()119 sk_sp<SkPathEffect> refPathEffect() const { return fPathEffect; } 120 hasPathEffect()121 bool hasPathEffect() const { return SkToBool(fPathEffect.get()); } 122 hasNonDashPathEffect()123 bool hasNonDashPathEffect() const { return fPathEffect.get() && !this->isDashed(); } 124 isDashed()125 bool isDashed() const { return SkPathEffect::kDash_DashType == fDashInfo.fType; } dashPhase()126 SkScalar dashPhase() const { 127 SkASSERT(this->isDashed()); 128 return fDashInfo.fPhase; 129 } dashIntervalCnt()130 int dashIntervalCnt() const { 131 SkASSERT(this->isDashed()); 132 return fDashInfo.fIntervals.count(); 133 } dashIntervals()134 const SkScalar* dashIntervals() const { 135 SkASSERT(this->isDashed()); 136 return fDashInfo.fIntervals.get(); 137 } 138 strokeRec()139 const SkStrokeRec& strokeRec() const { return fStrokeRec; } 140 141 /** Hairline or fill styles without path effects make no alterations to a geometry. */ applies()142 bool applies() const { 143 return this->pathEffect() || (!fStrokeRec.isFillStyle() && !fStrokeRec.isHairlineStyle()); 144 } 145 MatrixToScaleFactor(const SkMatrix & matrix)146 static SkScalar MatrixToScaleFactor(const SkMatrix& matrix) { 147 // getMaxScale will return -1 if the matrix has perspective. In that case we can use a scale 148 // factor of 1. This isn't necessarily a good choice and in the future we might consider 149 // taking a bounds here for the perspective case. 150 return SkScalarAbs(matrix.getMaxScale()); 151 } 152 /** 153 * Applies just the path effect and returns remaining stroke information. This will fail if 154 * there is no path effect. dst may or may not have been overwritten on failure. Scale controls 155 * geometric approximations made by the path effect. It is typically computed from the view 156 * matrix. 157 */ 158 bool SK_WARN_UNUSED_RESULT applyPathEffectToPath(SkPath* dst, SkStrokeRec* remainingStoke, 159 const SkPath& src, SkScalar scale) const; 160 161 /** 162 * If this succeeds then the result path should be filled or hairlined as indicated by the 163 * returned SkStrokeRec::InitStyle value. Will fail if there is no path effect and the 164 * strokerec doesn't change the geometry. When this fails the outputs may or may not have 165 * been overwritten. Scale controls geometric approximations made by the path effect and 166 * stroker. It is typically computed from the view matrix. 167 */ 168 bool SK_WARN_UNUSED_RESULT applyToPath(SkPath* dst, SkStrokeRec::InitStyle* fillOrHairline, 169 const SkPath& src, SkScalar scale) const; 170 171 /** Given bounds of a path compute the bounds of path with the style applied. */ adjustBounds(SkRect * dst,const SkRect & src)172 void adjustBounds(SkRect* dst, const SkRect& src) const { 173 if (this->pathEffect()) { 174 this->pathEffect()->computeFastBounds(dst, src); 175 // This may not be the correct SkStrokeRec to use. skbug.com/5299 176 // It happens to work for dashing. 177 SkScalar radius = fStrokeRec.getInflationRadius(); 178 dst->outset(radius, radius); 179 } else { 180 SkScalar radius = fStrokeRec.getInflationRadius(); 181 *dst = src.makeOutset(radius, radius); 182 } 183 } 184 185 private: 186 void initPathEffect(sk_sp<SkPathEffect> pe); 187 188 struct DashInfo { DashInfoDashInfo189 DashInfo() : fType(SkPathEffect::kNone_DashType) {} DashInfoDashInfo190 DashInfo(const DashInfo& that) { *this = that; } 191 DashInfo& operator=(const DashInfo& that) { 192 fType = that.fType; 193 fPhase = that.fPhase; 194 fIntervals.reset(that.fIntervals.count()); 195 sk_careful_memcpy(fIntervals.get(), that.fIntervals.get(), 196 sizeof(SkScalar) * that.fIntervals.count()); 197 return *this; 198 } resetDashInfo199 void reset() { 200 fType = SkPathEffect::kNone_DashType; 201 fIntervals.reset(0); 202 } 203 SkPathEffect::DashType fType; 204 SkScalar fPhase; 205 SkAutoSTArray<4, SkScalar> fIntervals; 206 }; 207 208 bool applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const; 209 210 SkStrokeRec fStrokeRec; 211 sk_sp<SkPathEffect> fPathEffect; 212 DashInfo fDashInfo; 213 }; 214 215 #endif 216