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