• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
KeySize(const GrStyle & style,Apply apply,uint32_t flags)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  
WriteKey(uint32_t * key,const GrStyle & style,Apply apply,SkScalar scale,uint32_t flags)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  
initPathEffect(sk_sp<SkPathEffect> pe)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  
applyPathEffect(SkPath * dst,SkStrokeRec * strokeRec,const SkPath & src) const129  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  
applyPathEffectToPath(SkPath * dst,SkStrokeRec * remainingStroke,const SkPath & src,SkScalar resScale) const158  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  
applyToPath(SkPath * dst,SkStrokeRec::InitStyle * style,const SkPath & src,SkScalar resScale) const170  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