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