/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrStyle.h" #include "SkDashPathPriv.h" int GrStyle::KeySize(const GrStyle &style, Apply apply, uint32_t flags) { GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); int size = 0; if (style.isDashed()) { // One scalar for scale, one for dash phase, and one for each dash value. size += 2 + style.dashIntervalCnt(); } else if (style.pathEffect()) { // No key for a generic path effect. return -1; } if (Apply::kPathEffectOnly == apply) { return size; } if (style.strokeRec().needToApply()) { // One for res scale, one for style/cap/join, one for miter limit, and one for width. size += 4; } return size; } void GrStyle::WriteKey(uint32_t *key, const GrStyle &style, Apply apply, SkScalar scale, uint32_t flags) { SkASSERT(key); SkASSERT(KeySize(style, apply) >= 0); GR_STATIC_ASSERT(sizeof(uint32_t) == sizeof(SkScalar)); int i = 0; // The scale can influence both the path effect and stroking. We want to preserve the // property that the following two are equal: // 1. WriteKey with apply == kPathEffectAndStrokeRec // 2. WriteKey with apply == kPathEffectOnly followed by WriteKey of a GrStyle made // from SkStrokeRec output by the the path effect (and no additional path effect). // Since the scale can affect both parts of 2 we write it into the key twice. if (style.isDashed()) { GR_STATIC_ASSERT(sizeof(style.dashPhase()) == sizeof(uint32_t)); SkScalar phase = style.dashPhase(); memcpy(&key[i++], &scale, sizeof(SkScalar)); memcpy(&key[i++], &phase, sizeof(SkScalar)); int32_t count = style.dashIntervalCnt(); // Dash count should always be even. SkASSERT(0 == (count & 0x1)); const SkScalar *intervals = style.dashIntervals(); int intervalByteCnt = count * sizeof(SkScalar); memcpy(&key[i], intervals, intervalByteCnt); i += count; } else { SkASSERT(!style.pathEffect()); } if (Apply::kPathEffectAndStrokeRec == apply && style.strokeRec().needToApply()) { memcpy(&key[i++], &scale, sizeof(SkScalar)); enum { kStyleBits = 2, kJoinBits = 2, kCapBits = 32 - kStyleBits - kJoinBits, kJoinShift = kStyleBits, kCapShift = kJoinShift + kJoinBits, }; GR_STATIC_ASSERT(SkStrokeRec::kStyleCount <= (1 << kStyleBits)); GR_STATIC_ASSERT(SkPaint::kJoinCount <= (1 << kJoinBits)); GR_STATIC_ASSERT(SkPaint::kCapCount <= (1 << kCapBits)); // The cap type only matters for unclosed shapes. However, a path effect could unclose // the shape before it is stroked. SkPaint::Cap cap = SkPaint::kDefault_Cap; if (!(flags & kClosed_KeyFlag) || style.pathEffect()) { cap = style.strokeRec().getCap(); } SkScalar miter = -1.f; SkPaint::Join join = SkPaint::kDefault_Join; // Dashing will not insert joins but other path effects may. if (!(flags & kNoJoins_KeyFlag) || style.hasNonDashPathEffect()) { join = style.strokeRec().getJoin(); // Miter limit only affects miter joins if (SkPaint::kMiter_Join == join) { miter = style.strokeRec().getMiter(); } } key[i++] = style.strokeRec().getStyle() | join << kJoinShift | cap << kCapShift; memcpy(&key[i++], &miter, sizeof(miter)); SkScalar width = style.strokeRec().getWidth(); memcpy(&key[i++], &width, sizeof(width)); } SkASSERT(KeySize(style, apply) == i); } void GrStyle::initPathEffect(sk_sp<SkPathEffect> pe) { SkASSERT(!fPathEffect); SkASSERT(SkPathEffect::kNone_DashType == fDashInfo.fType); SkASSERT(0 == fDashInfo.fIntervals.count()); if (!pe) { return; } SkPathEffect::DashInfo info; if (SkPathEffect::kDash_DashType == pe->asADash(&info)) { SkStrokeRec::Style recStyle = fStrokeRec.getStyle(); if (recStyle != SkStrokeRec::kFill_Style && recStyle != SkStrokeRec::kStrokeAndFill_Style) { fDashInfo.fType = SkPathEffect::kDash_DashType; fDashInfo.fIntervals.reset(info.fCount); fDashInfo.fPhase = info.fPhase; info.fIntervals = fDashInfo.fIntervals.get(); pe->asADash(&info); fPathEffect = std::move(pe); } } else { fPathEffect = std::move(pe); } } bool GrStyle::applyPathEffect(SkPath* dst, SkStrokeRec* strokeRec, const SkPath& src) const { if (!fPathEffect) { return false; } if (SkPathEffect::kDash_DashType == fDashInfo.fType) { // We apply the dash ourselves here rather than using the path effect. This is so that // we can control whether the dasher applies the strokeRec for special cases. Our keying // depends on the strokeRec being applied separately. SkScalar phase = fDashInfo.fPhase; const SkScalar* intervals = fDashInfo.fIntervals.get(); int intervalCnt = fDashInfo.fIntervals.count(); SkScalar initialLength; int initialIndex; SkScalar intervalLength; SkDashPath::CalcDashParameters(phase, intervals, intervalCnt, &initialLength, &initialIndex, &intervalLength); if (!SkDashPath::InternalFilter(dst, src, strokeRec, nullptr, intervals, intervalCnt, initialLength, initialIndex, intervalLength, SkDashPath::StrokeRecApplication::kDisallow)) { return false; } } else if (!fPathEffect->filterPath(dst, src, strokeRec, nullptr)) { return false; } dst->setIsVolatile(true); return true; } bool GrStyle::applyPathEffectToPath(SkPath *dst, SkStrokeRec *remainingStroke, const SkPath &src, SkScalar resScale) const { SkASSERT(dst); SkStrokeRec strokeRec = fStrokeRec; strokeRec.setResScale(resScale); if (!this->applyPathEffect(dst, &strokeRec, src)) { return false; } *remainingStroke = strokeRec; return true; } bool GrStyle::applyToPath(SkPath* dst, SkStrokeRec::InitStyle* style, const SkPath& src, SkScalar resScale) const { SkASSERT(style); SkASSERT(dst); SkStrokeRec strokeRec = fStrokeRec; strokeRec.setResScale(resScale); const SkPath* pathForStrokeRec = &src; if (this->applyPathEffect(dst, &strokeRec, src)) { pathForStrokeRec = dst; } else if (fPathEffect) { return false; } if (strokeRec.needToApply()) { if (!strokeRec.applyToPath(dst, *pathForStrokeRec)) { return false; } dst->setIsVolatile(true); *style = SkStrokeRec::kFill_InitStyle; } else if (!fPathEffect) { // Nothing to do for path effect or stroke, fail. return false; } else { SkASSERT(SkStrokeRec::kFill_Style == strokeRec.getStyle() || SkStrokeRec::kHairline_Style == strokeRec.getStyle()); *style = strokeRec.getStyle() == SkStrokeRec::kFill_Style ? SkStrokeRec::kFill_InitStyle : SkStrokeRec::kHairline_InitStyle; } return true; }