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 #include "GrStyle.h" 9 #include "SkDashPathPriv.h" 10 11 int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) { 12 GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); 13 int size = 0; 14 if (style.isDashed()) { 15 // One scalar for scale, one for dash phase, and one for each dash value. 16 size += 2 + style.dashIntervalCnt(); 17 } else if (style.pathEffect()) { 18 // No key for a generic path effect. 19 return -1; 20 } 21 22 if (Apply::kPathEffectOnly == apply) { 23 return size; 24 } 25 26 if (style.strokeRec().needToApply()) { 27 // One for res scale, one for style/cap/join, one for miter limit, and one for width. 28 size += 4; 29 } 30 return size; 31 } 32 33 void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale, 34 uint32_t flags) { 35 SkASSERT(key); 36 SkASSERT(KeySize(style, apply) >= 0); 37 GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); 38 39 int i = 0; 40 // The scale can influence both the path effect and stroking. We want to preserve the 41 // property that the following two are equal: 42 // 1. WriteKey with apply == kPathEffectAndStrokeRec 43 // 2. WriteKey with apply == kPathEffectOnly followed by WriteKey of a GrStyle made 44 // from SkStrokeRec output by the the path effect (and no additional path effect). 45 // Since the scale can affect both parts of 2 we write it into the key twice. 46 if (style.isDashed()) { 47 GR_STATIC_ASSERT(sizeof(style.dashPhase()) == sizeof(uint32_t)); 48 SkScalar phase = style.dashPhase(); 49 memcpy(&key[i++], &scale, sizeof(SkScalar)); 50 memcpy(&key[i++], &phase, sizeof(SkScalar)); 51 52 int32_t count = style.dashIntervalCnt(); 53 // Dash count should always be even. 54 SkASSERT(0 == (count & 0x1)); 55 const SkScalar *intervals = style.dashIntervals(); 56 int intervalByteCnt = count * sizeof(SkScalar); 57 memcpy(&key[i], intervals, intervalByteCnt); 58 i += count; 59 } else { 60 SkASSERT(!style.pathEffect()); 61 } 62 63 if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) { 64 memcpy(&key[i++], &scale, sizeof(SkScalar)); 65 enum { 66 kStyleBits = 2, 67 kJoinBits = 2, 68 kCapBits = 32 - kStyleBits - kJoinBits, 69 70 kJoinShift = kStyleBits, 71 kCapShift = kJoinShift + kJoinBits, 72 }; 73 GR_STATIC_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits)); 74 GR_STATIC_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits)); 75 GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits)); 76 // The cap type only matters for unclosed shapes. However, a path effect could unclose 77 // the shape before it is stroked. 78 SkPaint::Cap cap = SkPaint::kDefault_Cap; 79 if (!(flags & kClosed_KeyFlag) || style.pathEffect()) { 80 cap = style.strokeRec().getCap(); 81 } 82 SkScalar miter = -1.f; 83 SkPaint::Join join = SkPaint::kDefault_Join; 84 85 // Dashing will not insert joins but other path effects may. 86 if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) { 87 join = style.strokeRec().getJoin(); 88 // Miter limit only affects miter joins 89 if (SkPaint::kMiter_Join == join) { 90 miter = style.strokeRec().getMiter(); 91 } 92 } 93 94 key[i++] = style.strokeRec().getStyle() | 95 join << kJoinShift | 96 cap << kCapShift; 97 98 memcpy(&key[i++], &miter, sizeof(miter)); 99 100 SkScalar width = style.strokeRec().getWidth(); 101 memcpy(&key[i++], &width, sizeof(width)); 102 } 103 SkASSERT(KeySize(style, apply) == i); 104 } 105 106 void GrStyle::initPathEffect(sk_sp<SkPathEffect> pe) { 107 SkASSERT(!fPathEffect); 108 SkASSERT(SkPathEffect::kNone_DashType == fDashInfo.fType); 109 SkASSERT(0 == fDashInfo.fIntervals.count()); 110 if (!pe) { 111 return; 112 } 113 SkPathEffect::DashInfo info; 114 if (SkPathEffect::kDash_DashType == pe->asADash(&info)) { 115 SkStrokeRec::Style recStyle = fStrokeRec.getStyle(); 116 if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) { 117 fDashInfo.fType = SkPathEffect::kDash_DashType; 118 fDashInfo.fIntervals.reset(info.fCount); 119 fDashInfo.fPhase = info.fPhase; 120 info.fIntervals = fDashInfo.fIntervals.get(); 121 pe->asADash(&info); 122 fPathEffect = std::move(pe); 123 } 124 } else { 125 fPathEffect = std::move(pe); 126 } 127 } 128 129 bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const { 130 if (!fPathEffect) { 131 return false; 132 } 133 if (SkPathEffect::kDash_DashType == fDashInfo.fType) { 134 // We apply the dash ourselves here rather than using the path effect. This is so that 135 // we can control whether the dasher applies the strokeRec for special cases. Our keying 136 // depends on the strokeRec being applied separately. 137 SkScalar phase = fDashInfo.fPhase; 138 const SkScalar* intervals = fDashInfo.fIntervals.get(); 139 int intervalCnt = fDashInfo.fIntervals.count(); 140 SkScalar initialLength; 141 int initialIndex; 142 SkScalar intervalLength; 143 SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength, 144 &initialIndex, &intervalLength); 145 if (!SkDashPath::InternalFilter(dst, src, strokeRec, 146 nullptr, intervals, intervalCnt, 147 initialLength, initialIndex, intervalLength, 148 SkDashPath::StrokeRecApplication::kDisallow)) { 149 return false; 150 } 151 } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) { 152 return false; 153 } 154 dst->setIsVolatile(true); 155 return true; 156 } 157 158 bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke, 159 const SkPath &src, SkScalar resScale) const { 160 SkASSERT(dst); 161 SkStrokeRec strokeRec = fStrokeRec; 162 strokeRec.setResScale(resScale); 163 if (!this->applyPathEffect(dst, &strokeRec, src)) { 164 return false; 165 } 166 *remainingStroke = strokeRec; 167 return true; 168 } 169 170 bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src, 171 SkScalar resScale) const { 172 SkASSERT(style); 173 SkASSERT(dst); 174 SkStrokeRec strokeRec = fStrokeRec; 175 strokeRec.setResScale(resScale); 176 const SkPath* pathForStrokeRec = &src; 177 if (this->applyPathEffect(dst, &strokeRec, src)) { 178 pathForStrokeRec = dst; 179 } else if (fPathEffect) { 180 return false; 181 } 182 if (strokeRec.needToApply()) { 183 if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) { 184 return false; 185 } 186 dst->setIsVolatile(true); 187 *style = SkStrokeRec::kFill_InitStyle; 188 } else if (!fPathEffect) { 189 // Nothing to do for path effect or stroke, fail. 190 return false; 191 } else { 192 SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() || 193 SkStrokeRec::kHairline_Style == strokeRec.getStyle()); 194 *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style 195 ? SkStrokeRec::kFill_InitStyle 196 : SkStrokeRec::kHairline_InitStyle; 197 } 198 return true; 199 } 200