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