OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "GrDashingEffect.h" | 8 #include "GrDashingEffect.h" |
9 | 9 |
| 10 #include "../GrAARectRenderer.h" |
| 11 |
| 12 #include "effects/GrVertexEffect.h" |
10 #include "gl/GrGLEffect.h" | 13 #include "gl/GrGLEffect.h" |
| 14 #include "gl/GrGLVertexEffect.h" |
11 #include "gl/GrGLSL.h" | 15 #include "gl/GrGLSL.h" |
12 #include "GrContext.h" | 16 #include "GrContext.h" |
13 #include "GrCoordTransform.h" | 17 #include "GrCoordTransform.h" |
| 18 #include "GrDrawTarget.h" |
14 #include "GrDrawTargetCaps.h" | 19 #include "GrDrawTargetCaps.h" |
15 #include "GrEffect.h" | 20 #include "GrEffect.h" |
| 21 #include "GrGpu.h" |
| 22 #include "GrStrokeInfo.h" |
16 #include "GrTBackendEffectFactory.h" | 23 #include "GrTBackendEffectFactory.h" |
17 #include "SkGr.h" | 24 #include "SkGr.h" |
18 | 25 |
19 /////////////////////////////////////////////////////////////////////////////// | 26 /////////////////////////////////////////////////////////////////////////////// |
20 | 27 |
| 28 // Returns whether or not the gpu can fast path the dash line effect. |
| 29 static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeI
nfo, |
| 30 const GrDrawTarget& target, const SkMatrix& viewM
atrix) { |
| 31 if (target.getDrawState().getRenderTarget()->isMultisampled()) { |
| 32 return false; |
| 33 } |
| 34 |
| 35 // Pts must be either horizontal or vertical in src space |
| 36 if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) { |
| 37 return false; |
| 38 } |
| 39 |
| 40 // May be able to relax this to include skew. As of now cannot do perspectiv
e |
| 41 // because of the non uniform scaling of bloating a rect |
| 42 if (!viewMatrix.preservesRightAngles()) { |
| 43 return false; |
| 44 } |
| 45 |
| 46 if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) { |
| 47 return false; |
| 48 } |
| 49 |
| 50 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); |
| 51 if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) { |
| 52 return false; |
| 53 } |
| 54 |
| 55 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); |
| 56 // Current we do don't handle Round or Square cap dashes |
| 57 if (SkPaint::kRound_Cap == cap) { |
| 58 return false; |
| 59 } |
| 60 |
| 61 return true; |
| 62 } |
| 63 |
| 64 namespace { |
| 65 |
| 66 struct DashLineVertex { |
| 67 SkPoint fPos; |
| 68 SkPoint fDashPos; |
| 69 }; |
| 70 |
| 71 extern const GrVertexAttrib gDashLineVertexAttribs[] = { |
| 72 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBind
ing }, |
| 73 { kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBindin
g }, |
| 74 }; |
| 75 |
| 76 }; |
21 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, | 77 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, |
22 const SkMatrix& viewMatrix, const SkPoint pts[2]) { | 78 const SkMatrix& viewMatrix, const SkPoint pts[2]) { |
23 SkVector vecSrc = pts[1] - pts[0]; | 79 SkVector vecSrc = pts[1] - pts[0]; |
24 SkScalar magSrc = vecSrc.length(); | 80 SkScalar magSrc = vecSrc.length(); |
25 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0; | 81 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0; |
26 vecSrc.scale(invSrc); | 82 vecSrc.scale(invSrc); |
27 | 83 |
28 SkVector vecSrcPerp; | 84 SkVector vecSrcPerp; |
29 vecSrc.rotateCW(&vecSrcPerp); | 85 vecSrc.rotateCW(&vecSrcPerp); |
30 viewMatrix.mapVectors(&vecSrc, 1); | 86 viewMatrix.mapVectors(&vecSrc, 1); |
(...skipping 24 matching lines...) Expand all Loading... |
55 // Assumes phase < sum of all intervals | 111 // Assumes phase < sum of all intervals |
56 static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { | 112 static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { |
57 SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]); | 113 SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]); |
58 if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) { | 114 if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) { |
59 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; | 115 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; |
60 return srcIntervalLen - info.fPhase; | 116 return srcIntervalLen - info.fPhase; |
61 } | 117 } |
62 return 0; | 118 return 0; |
63 } | 119 } |
64 | 120 |
65 static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk
Point pts[2], SkScalar* endingInt) { | 121 static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk
Point pts[2], |
| 122 SkScalar phase, SkScalar* endingInt) { |
66 if (pts[1].fX <= pts[0].fX) { | 123 if (pts[1].fX <= pts[0].fX) { |
67 return 0; | 124 return 0; |
68 } | 125 } |
69 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; | 126 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; |
70 SkScalar totalLen = pts[1].fX - pts[0].fX; | 127 SkScalar totalLen = pts[1].fX - pts[0].fX; |
71 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); | 128 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); |
72 SkScalar numFullIntervals = SkScalarFloorToScalar(temp); | 129 SkScalar numFullIntervals = SkScalarFloorToScalar(temp); |
73 *endingInt = totalLen - numFullIntervals * srcIntervalLen + info.fPhase; | 130 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; |
74 temp = SkScalarDiv(*endingInt, srcIntervalLen); | 131 temp = SkScalarDiv(*endingInt, srcIntervalLen); |
75 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; | 132 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; |
76 if (0 == *endingInt) { | 133 if (0 == *endingInt) { |
77 *endingInt = srcIntervalLen; | 134 *endingInt = srcIntervalLen; |
78 } | 135 } |
79 if (*endingInt > info.fIntervals[0]) { | 136 if (*endingInt > info.fIntervals[0]) { |
80 if (0 == info.fIntervals[0]) { | 137 if (0 == info.fIntervals[0]) { |
81 *endingInt -= 0.01f; // make sure we capture the last zero size pnt
(used if has caps) | 138 *endingInt -= 0.01f; // make sure we capture the last zero size pnt
(used if has caps) |
82 } | 139 } |
83 return *endingInt - info.fIntervals[0]; | 140 return *endingInt - info.fIntervals[0]; |
84 } | 141 } |
85 return 0; | 142 return 0; |
86 } | 143 } |
87 | 144 |
88 | 145 static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx
, const SkMatrix& matrix, |
89 bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const SkPaint& paint, G
rContext* context) { | 146 SkScalar offset, SkScalar bloat, SkScalar len, SkScalar s
troke) { |
90 if (context->getRenderTarget()->isMultisampled()) { | 147 |
| 148 SkScalar startDashX = offset - bloat; |
| 149 SkScalar endDashX = offset + len + bloat; |
| 150 SkScalar startDashY = -stroke - bloat; |
| 151 SkScalar endDashY = stroke + bloat; |
| 152 verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY); |
| 153 verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY); |
| 154 verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY); |
| 155 verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY); |
| 156 |
| 157 verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop); |
| 158 verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom); |
| 159 verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom); |
| 160 verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop); |
| 161 |
| 162 matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4); |
| 163 } |
| 164 |
| 165 |
| 166 bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint, |
| 167 const GrStrokeInfo& strokeInfo, GrGpu* gpu, |
| 168 GrDrawTarget* target, const SkMatrix& vm) { |
| 169 |
| 170 if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) { |
91 return false; | 171 return false; |
92 } | 172 } |
93 | 173 |
94 const SkMatrix& viewMatrix = context->getMatrix(); | 174 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); |
95 if (!viewMatrix.preservesRightAngles()) { | 175 |
96 return false; | 176 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); |
97 } | 177 |
98 | 178 SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); |
99 const SkPathEffect* pe = paint.getPathEffect(); | |
100 SkPathEffect::DashInfo info; | |
101 SkPathEffect::DashType dashType = pe->asADash(&info); | |
102 // Must be a dash effect with 2 intervals (1 on and 1 off) | |
103 if (SkPathEffect::kDash_DashType != dashType || 2 != info.fCount) { | |
104 return false; | |
105 } | |
106 | |
107 SkPaint::Cap cap = paint.getStrokeCap(); | |
108 // Current we do don't handle Round or Square cap dashes | |
109 if (SkPaint::kRound_Cap == cap) { | |
110 return false; | |
111 } | |
112 | |
113 SkScalar srcStrokeWidth = paint.getStrokeWidth(); | |
114 | |
115 // Get all info about the dash effect | |
116 SkAutoTArray<SkScalar> intervals(info.fCount); | |
117 info.fIntervals = intervals.get(); | |
118 pe->asADash(&info); | |
119 | 179 |
120 // the phase should be normalized to be [0, sum of all intervals) | 180 // the phase should be normalized to be [0, sum of all intervals) |
121 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fInterv
als[1]); | 181 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fInterv
als[1]); |
122 | 182 |
123 SkMatrix coordTrans; | 183 SkScalar srcPhase = info.fPhase; |
124 | 184 |
125 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[
1].fX | 185 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[
1].fX |
126 SkMatrix srcRotInv; | 186 SkMatrix srcRotInv; |
127 SkPoint ptsRot[2]; | 187 SkPoint ptsRot[2]; |
128 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { | 188 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { |
129 align_to_x_axis(pts, &coordTrans, ptsRot); | 189 SkMatrix rotMatrix; |
130 if(!coordTrans.invert(&srcRotInv)) { | 190 align_to_x_axis(pts, &rotMatrix, ptsRot); |
| 191 if(!rotMatrix.invert(&srcRotInv)) { |
| 192 GrPrintf("Failed to create invertible rotation matrix!\n"); |
131 return false; | 193 return false; |
132 } | 194 } |
133 } else { | 195 } else { |
134 coordTrans.reset(); | |
135 srcRotInv.reset(); | 196 srcRotInv.reset(); |
136 memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); | 197 memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); |
137 } | 198 } |
138 | 199 |
139 GrPaint grPaint; | |
140 SkPaint2GrPaintShader(context, paint, true, &grPaint); | |
141 | |
142 bool useAA = paint.isAntiAlias(); | 200 bool useAA = paint.isAntiAlias(); |
143 | 201 |
144 // Scale corrections of intervals and stroke from view matrix | 202 // Scale corrections of intervals and stroke from view matrix |
145 SkScalar parallelScale; | 203 SkScalar parallelScale; |
146 SkScalar perpScale; | 204 SkScalar perpScale; |
147 calc_dash_scaling(¶llelScale, &perpScale, viewMatrix, ptsRot); | 205 calc_dash_scaling(¶llelScale, &perpScale, vm, ptsRot); |
148 | 206 |
149 bool hasCap = SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth; | 207 bool hasCap = SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth; |
150 | 208 |
151 // We always want to at least stroke out half a pixel on each side in device
space | 209 // We always want to at least stroke out half a pixel on each side in device
space |
152 // so 0.5f / perpScale gives us this min in src space | 210 // so 0.5f / perpScale gives us this min in src space |
153 SkScalar halfStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); | 211 SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale
); |
154 | 212 |
155 SkScalar xStroke; | 213 SkScalar strokeAdj; |
156 if (!hasCap) { | 214 if (!hasCap) { |
157 xStroke = 0.f; | 215 strokeAdj = 0.f; |
158 } else { | 216 } else { |
159 xStroke = halfStroke; | 217 strokeAdj = halfSrcStroke; |
160 } | 218 } |
161 | 219 |
| 220 SkScalar startAdj = 0; |
| 221 |
| 222 SkMatrix combinedMatrix = srcRotInv; |
| 223 combinedMatrix.postConcat(vm); |
| 224 |
| 225 bool lineDone = false; |
| 226 SkRect startRect; |
| 227 bool hasStartRect = false; |
162 // If we are using AA, check to see if we are drawing a partial dash at the
start. If so | 228 // If we are using AA, check to see if we are drawing a partial dash at the
start. If so |
163 // draw it separately here and adjust our start point accordingly | 229 // draw it separately here and adjust our start point accordingly |
164 if (useAA) { | 230 if (useAA) { |
165 if (info.fPhase > 0 && info.fPhase < info.fIntervals[0]) { | 231 if (srcPhase > 0 && srcPhase < info.fIntervals[0]) { |
166 SkPoint startPts[2]; | 232 SkPoint startPts[2]; |
167 startPts[0] = ptsRot[0]; | 233 startPts[0] = ptsRot[0]; |
168 startPts[1].fY = startPts[0].fY; | 234 startPts[1].fY = startPts[0].fY; |
169 startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - i
nfo.fPhase, | 235 startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - s
rcPhase, |
170 ptsRot[1].fX); | 236 ptsRot[1].fX); |
171 SkRect startRect; | |
172 startRect.set(startPts, 2); | 237 startRect.set(startPts, 2); |
173 startRect.outset(xStroke, halfStroke); | 238 startRect.outset(strokeAdj, halfSrcStroke); |
174 context->drawRect(grPaint, startRect, NULL, &srcRotInv); | 239 |
175 | 240 hasStartRect = true; |
176 ptsRot[0].fX += info.fIntervals[0] + info.fIntervals[1] - info.fPhas
e; | 241 startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; |
177 info.fPhase = 0; | |
178 } | 242 } |
179 } | 243 } |
180 | 244 |
181 // adjustments for start and end of bounding rect so we only draw dash inter
vals | 245 // adjustments for start and end of bounding rect so we only draw dash inter
vals |
182 // contained in the original line segment. | 246 // contained in the original line segment. |
183 SkScalar startAdj = calc_start_adjustment(info); | 247 startAdj += calc_start_adjustment(info); |
| 248 if (startAdj != 0) { |
| 249 ptsRot[0].fX += startAdj; |
| 250 srcPhase = 0; |
| 251 } |
184 SkScalar endingInterval = 0; | 252 SkScalar endingInterval = 0; |
185 SkScalar endAdj = calc_end_adjustment(info, ptsRot, &endingInterval); | 253 SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterva
l); |
186 if (ptsRot[0].fX + startAdj >= ptsRot[1].fX - endAdj) { | 254 ptsRot[1].fX -= endAdj; |
187 // Nothing left to draw so just return | 255 if (ptsRot[0].fX >= ptsRot[1].fX) { |
188 return true; | 256 lineDone = true; |
189 } | 257 } |
190 | 258 |
| 259 SkRect endRect; |
| 260 bool hasEndRect = false; |
191 // If we are using AA, check to see if we are drawing a partial dash at then
end. If so | 261 // If we are using AA, check to see if we are drawing a partial dash at then
end. If so |
192 // draw it separately here and adjust our end point accordingly | 262 // draw it separately here and adjust our end point accordingly |
193 if (useAA) { | 263 if (useAA && !lineDone) { |
194 // If we adjusted the end then we will not be drawing a partial dash at
the end. | 264 // If we adjusted the end then we will not be drawing a partial dash at
the end. |
195 // If we didn't adjust the end point then we just need to make sure the
ending | 265 // If we didn't adjust the end point then we just need to make sure the
ending |
196 // dash isn't a full dash | 266 // dash isn't a full dash |
197 if (0 == endAdj && endingInterval != info.fIntervals[0]) { | 267 if (0 == endAdj && endingInterval != info.fIntervals[0]) { |
198 | |
199 SkPoint endPts[2]; | 268 SkPoint endPts[2]; |
200 endPts[1] = ptsRot[1]; | 269 endPts[1] = ptsRot[1]; |
201 endPts[0].fY = endPts[1].fY; | 270 endPts[0].fY = endPts[1].fY; |
202 endPts[0].fX = endPts[1].fX - endingInterval; | 271 endPts[0].fX = endPts[1].fX - endingInterval; |
203 | 272 |
204 SkRect endRect; | |
205 endRect.set(endPts, 2); | 273 endRect.set(endPts, 2); |
206 endRect.outset(xStroke, halfStroke); | 274 endRect.outset(strokeAdj, halfSrcStroke); |
207 context->drawRect(grPaint, endRect, NULL, &srcRotInv); | 275 |
208 | 276 hasEndRect = true; |
209 ptsRot[1].fX -= endingInterval + info.fIntervals[1]; | 277 endAdj = endingInterval + info.fIntervals[1]; |
| 278 |
| 279 ptsRot[1].fX -= endAdj; |
210 if (ptsRot[0].fX >= ptsRot[1].fX) { | 280 if (ptsRot[0].fX >= ptsRot[1].fX) { |
211 // Nothing left to draw so just return | 281 lineDone = true; |
212 return true; | |
213 } | 282 } |
214 } | 283 } |
215 } | 284 } |
216 coordTrans.postConcat(viewMatrix); | 285 |
217 | 286 if (startAdj != 0) { |
218 SkPoint devicePts[2]; | 287 srcPhase = 0; |
219 viewMatrix.mapPoints(devicePts, ptsRot, 2); | 288 } |
220 | 289 |
221 info.fIntervals[0] *= parallelScale; | 290 // Change the dashing info from src space into device space |
222 info.fIntervals[1] *= parallelScale; | 291 SkScalar devIntervals[2]; |
223 info.fPhase *= parallelScale; | 292 devIntervals[0] = info.fIntervals[0] * parallelScale; |
| 293 devIntervals[1] = info.fIntervals[1] * parallelScale; |
| 294 SkScalar devPhase = srcPhase * parallelScale; |
224 SkScalar strokeWidth = srcStrokeWidth * perpScale; | 295 SkScalar strokeWidth = srcStrokeWidth * perpScale; |
225 | 296 |
226 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { | 297 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { |
227 strokeWidth = 1.f; | 298 strokeWidth = 1.f; |
228 } | 299 } |
229 | 300 |
230 // Set up coordTransform for device space transforms | 301 SkScalar halfDevStroke = strokeWidth * 0.5f; |
231 // We rotate the dashed line such that it is horizontal with the start point
at smaller x | |
232 // then we translate the start point to the origin | |
233 if (devicePts[0].fY != devicePts[1].fY || devicePts[0].fX > devicePts[1].fX)
{ | |
234 SkMatrix rot; | |
235 align_to_x_axis(devicePts, &rot); | |
236 coordTrans.postConcat(rot); | |
237 } | |
238 coordTrans.postTranslate(-devicePts[0].fX, -devicePts[0].fY); | |
239 coordTrans.postTranslate(info.fIntervals[1] * 0.5f + info.fPhase, 0); | |
240 | 302 |
241 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { | 303 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { |
242 // add cap to on interveal and remove from off interval | 304 // add cap to on interveal and remove from off interval |
243 info.fIntervals[0] += strokeWidth; | 305 devIntervals[0] += strokeWidth; |
244 info.fIntervals[1] -= strokeWidth; | 306 devIntervals[1] -= strokeWidth; |
245 } | 307 } |
246 | 308 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; |
247 if (info.fIntervals[1] > 0.f) { | 309 |
| 310 SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; |
| 311 SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; |
| 312 |
| 313 SkScalar devBloat = useAA ? 0.5f : 0.f; |
| 314 |
| 315 GrDrawState* drawState = target->drawState(); |
| 316 if (devIntervals[1] <= 0.f && useAA) { |
| 317 // Case when we end up drawing a solid AA rect |
| 318 // Reset the start rect to draw this single solid rect |
| 319 // but it requires to upload a new intervals uniform so we can mimic |
| 320 // one giant dash |
| 321 ptsRot[0].fX -= hasStartRect ? startAdj : 0; |
| 322 ptsRot[1].fX += hasEndRect ? endAdj : 0; |
| 323 startRect.set(ptsRot, 2); |
| 324 startRect.outset(strokeAdj, halfSrcStroke); |
| 325 hasStartRect = true; |
| 326 hasEndRect = false; |
| 327 lineDone = true; |
| 328 |
| 329 SkPoint devicePts[2]; |
| 330 vm.mapPoints(devicePts, ptsRot, 2); |
| 331 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
| 332 if (hasCap) { |
| 333 lineLength += 2.f * halfDevStroke; |
| 334 } |
| 335 devIntervals[0] = lineLength; |
| 336 } |
| 337 if (devIntervals[1] > 0.f || useAA) { |
| 338 SkPathEffect::DashInfo devInfo; |
| 339 devInfo.fPhase = devPhase; |
| 340 devInfo.fCount = 2; |
| 341 devInfo.fIntervals = devIntervals; |
248 GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType : | 342 GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType : |
249 kFillBW_GrEffectEdgeType; | 343 kFillBW_GrEffectEdgeType; |
250 grPaint.addCoverageEffect( | 344 drawState->addCoverageEffect( |
251 GrDashingEffect::Create(edgeType, info, coordTrans, strokeWidth))->u
nref(); | 345 GrDashingEffect::Create(edgeType, devInfo, strokeWidth), 1)->unref()
; |
252 grPaint.setAntiAlias(false); | 346 } |
253 } | 347 |
254 | 348 // Set up the vertex data for the line and start/end dashes |
255 SkRect rect; | 349 drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLine
VertexAttribs)); |
256 bool bloat = useAA && info.fIntervals[1] > 0.f; | 350 |
257 SkScalar bloatX = bloat ? 0.5f / parallelScale : 0.f; | 351 int totalRectCnt = 0; |
258 SkScalar bloatY = bloat ? 0.5f / perpScale : 0.f; | 352 |
259 ptsRot[0].fX += startAdj; | 353 totalRectCnt += !lineDone ? 1 : 0; |
260 ptsRot[1].fX -= endAdj; | 354 totalRectCnt += hasStartRect ? 1 : 0; |
261 if (!hasCap) { | 355 totalRectCnt += hasEndRect ? 1 : 0; |
262 xStroke = 0.f; | 356 |
263 } else { | 357 GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0); |
264 xStroke = halfStroke; | 358 if (!geo.succeeded()) { |
265 } | 359 GrPrintf("Failed to get space for vertices!\n"); |
266 rect.set(ptsRot, 2); | 360 return false; |
267 rect.outset(bloatX + xStroke, bloatY + halfStroke); | 361 } |
268 context->drawRect(grPaint, rect, NULL, &srcRotInv); | 362 |
269 | 363 DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices()); |
| 364 |
| 365 int curVIdx = 0; |
| 366 |
| 367 // Draw interior part of dashed line |
| 368 if (!lineDone) { |
| 369 SkPoint devicePts[2]; |
| 370 vm.mapPoints(devicePts, ptsRot, 2); |
| 371 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); |
| 372 if (hasCap) { |
| 373 lineLength += 2.f * halfDevStroke; |
| 374 } |
| 375 |
| 376 SkRect bounds; |
| 377 bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); |
| 378 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); |
| 379 setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, d
evBloat, |
| 380 lineLength, halfDevStroke); |
| 381 curVIdx += 4; |
| 382 } |
| 383 |
| 384 if (hasStartRect) { |
| 385 SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
| 386 startRect.outset(bloatX, bloatY); |
| 387 setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset
, devBloat, |
| 388 devIntervals[0], halfDevStroke); |
| 389 curVIdx += 4; |
| 390 } |
| 391 |
| 392 if (hasEndRect) { |
| 393 SkASSERT(useAA); // so that we know bloatX and bloatY have been set |
| 394 endRect.outset(bloatX, bloatY); |
| 395 setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset,
devBloat, |
| 396 devIntervals[0], halfDevStroke); |
| 397 } |
| 398 |
| 399 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); |
| 400 target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6)
; |
| 401 target->resetIndexSource(); |
270 return true; | 402 return true; |
271 } | 403 } |
272 | 404 |
273 ////////////////////////////////////////////////////////////////////////////// | 405 ////////////////////////////////////////////////////////////////////////////// |
274 | 406 |
275 class GLDashingLineEffect; | 407 class GLDashingLineEffect; |
276 | 408 |
277 class DashingLineEffect : public GrEffect { | 409 class DashingLineEffect : public GrVertexEffect { |
278 public: | 410 public: |
279 typedef SkPathEffect::DashInfo DashInfo; | 411 typedef SkPathEffect::DashInfo DashInfo; |
280 | 412 |
281 /** | 413 /** |
282 * The effect calculates the coverage for the case of a horizontal line in d
evice space. | 414 * The effect calculates the coverage for the case of a horizontal line in d
evice space. |
283 * The matrix that is passed in should be able to convert a line in source s
pace to a | 415 * The matrix that is passed in should be able to convert a line in source s
pace to a |
284 * horizontal line in device space. Additionally, the coord transform matrix
should translate | 416 * horizontal line in device space. Additionally, the coord transform matrix
should translate |
285 * the the start of line to origin, and the shift it along the positive x-ax
is by the phase | 417 * the the start of line to origin, and the shift it along the positive x-ax
is by the phase |
286 * and half the off interval. | 418 * and half the off interval. |
287 */ | 419 */ |
288 static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info, | 420 static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info, |
289 const SkMatrix& matrix, SkScalar strokeWidth); | 421 SkScalar strokeWidth); |
290 | 422 |
291 virtual ~DashingLineEffect(); | 423 virtual ~DashingLineEffect(); |
292 | 424 |
293 static const char* Name() { return "DashingEffect"; } | 425 static const char* Name() { return "DashingEffect"; } |
294 | 426 |
295 GrEffectEdgeType getEdgeType() const { return fEdgeType; } | 427 GrEffectEdgeType getEdgeType() const { return fEdgeType; } |
296 | 428 |
297 const SkRect& getRect() const { return fRect; } | 429 const SkRect& getRect() const { return fRect; } |
298 | 430 |
299 SkScalar getIntervalLength() const { return fIntervalLength; } | 431 SkScalar getIntervalLength() const { return fIntervalLength; } |
300 | 432 |
301 typedef GLDashingLineEffect GLEffect; | 433 typedef GLDashingLineEffect GLEffect; |
302 | 434 |
303 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags
) const SK_OVERRIDE; | 435 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags
) const SK_OVERRIDE; |
304 | 436 |
305 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; | 437 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE; |
306 | 438 |
307 private: | 439 private: |
308 DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, const SkM
atrix& matrix, | 440 DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar
strokeWidth); |
309 SkScalar strokeWidth); | |
310 | 441 |
311 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; | 442 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE; |
312 | 443 |
313 GrEffectEdgeType fEdgeType; | 444 GrEffectEdgeType fEdgeType; |
314 GrCoordTransform fCoordTransform; | |
315 SkRect fRect; | 445 SkRect fRect; |
316 SkScalar fIntervalLength; | 446 SkScalar fIntervalLength; |
317 | 447 |
318 GR_DECLARE_EFFECT_TEST; | 448 GR_DECLARE_EFFECT_TEST; |
319 | 449 |
320 typedef GrEffect INHERITED; | 450 typedef GrEffect INHERITED; |
321 }; | 451 }; |
322 | 452 |
323 ////////////////////////////////////////////////////////////////////////////// | 453 ////////////////////////////////////////////////////////////////////////////// |
324 | 454 |
325 class GLDashingLineEffect : public GrGLEffect { | 455 class GLDashingLineEffect : public GrGLVertexEffect { |
326 public: | 456 public: |
327 GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&); | 457 GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&); |
328 | 458 |
329 virtual void emitCode(GrGLShaderBuilder* builder, | 459 virtual void emitCode(GrGLFullShaderBuilder* builder, |
330 const GrDrawEffect& drawEffect, | 460 const GrDrawEffect& drawEffect, |
331 EffectKey key, | 461 EffectKey key, |
332 const char* outputColor, | 462 const char* outputColor, |
333 const char* inputColor, | 463 const char* inputColor, |
334 const TransformedCoordsArray&, | 464 const TransformedCoordsArray&, |
335 const TextureSamplerArray&) SK_OVERRIDE; | 465 const TextureSamplerArray&) SK_OVERRIDE; |
336 | 466 |
337 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); | 467 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&); |
338 | 468 |
339 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVER
RIDE; | 469 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVER
RIDE; |
340 | 470 |
341 private: | 471 private: |
342 GrGLUniformManager::UniformHandle fRectUniform; | 472 GrGLUniformManager::UniformHandle fRectUniform; |
343 GrGLUniformManager::UniformHandle fIntervalUniform; | 473 GrGLUniformManager::UniformHandle fIntervalUniform; |
344 SkRect fPrevRect; | 474 SkRect fPrevRect; |
345 SkScalar fPrevIntervalLength; | 475 SkScalar fPrevIntervalLength; |
346 typedef GrGLEffect INHERITED; | 476 typedef GrGLVertexEffect INHERITED; |
347 }; | 477 }; |
348 | 478 |
349 GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory, | 479 GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory, |
350 const GrDrawEffect& drawEffect) | 480 const GrDrawEffect& drawEffect) |
351 : INHERITED (factory) { | 481 : INHERITED (factory) { |
352 fPrevRect.fLeft = SK_ScalarNaN; | 482 fPrevRect.fLeft = SK_ScalarNaN; |
353 fPrevIntervalLength = SK_ScalarMax; | 483 fPrevIntervalLength = SK_ScalarMax; |
354 | 484 |
355 } | 485 } |
356 | 486 |
357 void GLDashingLineEffect::emitCode(GrGLShaderBuilder* builder, | 487 void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder, |
358 const GrDrawEffect& drawEffect, | 488 const GrDrawEffect& drawEffect, |
359 EffectKey key, | 489 EffectKey key, |
360 const char* outputColor, | 490 const char* outputColor, |
361 const char* inputColor, | 491 const char* inputColor, |
362 const TransformedCoordsArray& coords, | 492 const TransformedCoordsArray&, |
363 const TextureSamplerArray& samplers) { | 493 const TextureSamplerArray& samplers) { |
364 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>(); | 494 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>(); |
365 const char *rectName; | 495 const char *rectName; |
366 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bot
tom - 0.5), | 496 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bot
tom - 0.5), |
367 // respectively. | 497 // respectively. |
368 fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, | 498 fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility, |
369 kVec4f_GrSLType, | 499 kVec4f_GrSLType, |
370 "rect", | 500 "rect", |
371 &rectName); | 501 &rectName); |
372 const char *intervalName; | 502 const char *intervalName; |
373 // The interval uniform's refers to the total length of the interval (on + o
ff) | 503 // The interval uniform's refers to the total length of the interval (on + o
ff) |
374 fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibili
ty, | 504 fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibili
ty, |
375 kFloat_GrSLType, | 505 kFloat_GrSLType, |
376 "interval", | 506 "interval", |
377 &intervalName); | 507 &intervalName); |
| 508 |
| 509 const char *vsCoordName, *fsCoordName; |
| 510 builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName); |
| 511 const SkString* attr0Name = |
| 512 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]); |
| 513 builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str()); |
| 514 |
378 // transforms all points so that we can compare them to our test rect | 515 // transforms all points so that we can compare them to our test rect |
379 builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n
", | 516 builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n
", |
380 coords[0].c_str(), coords[0].c_str(), intervalName, i
ntervalName); | 517 fsCoordName, fsCoordName, intervalName, intervalName)
; |
381 builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n",
coords[0].c_str()); | 518 builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n",
fsCoordName); |
382 if (GrEffectEdgeTypeIsAA(de.getEdgeType())) { | 519 if (GrEffectEdgeTypeIsAA(de.getEdgeType())) { |
383 // The amount of coverage removed in x and y by the edges is computed as
a pair of negative | 520 // The amount of coverage removed in x and y by the edges is computed as
a pair of negative |
384 // numbers, xSub and ySub. | 521 // numbers, xSub and ySub. |
385 builder->fsCodeAppend("\t\tfloat xSub, ySub;\n"); | 522 builder->fsCodeAppend("\t\tfloat xSub, ySub;\n"); |
386 builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n"
, rectName); | 523 builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n"
, rectName); |
387 builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n
", rectName); | 524 builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n
", rectName); |
388 builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n"
, rectName); | 525 builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n"
, rectName); |
389 builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n
", rectName); | 526 builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n
", rectName); |
390 // Now compute coverage in x and y and multiply them to get the fraction
of the pixel | 527 // Now compute coverage in x and y and multiply them to get the fraction
of the pixel |
391 // covered. | 528 // covered. |
(...skipping 23 matching lines...) Expand all Loading... |
415 | 552 |
416 GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect
, | 553 GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect
, |
417 const GrGLCaps&) { | 554 const GrGLCaps&) { |
418 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>(); | 555 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>(); |
419 return de.getEdgeType(); | 556 return de.getEdgeType(); |
420 } | 557 } |
421 | 558 |
422 ////////////////////////////////////////////////////////////////////////////// | 559 ////////////////////////////////////////////////////////////////////////////// |
423 | 560 |
424 GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo
& info, | 561 GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo
& info, |
425 const SkMatrix& matrix, SkScalar strokeWidt
h) { | 562 SkScalar strokeWidth) { |
426 if (info.fCount != 2) { | 563 if (info.fCount != 2) { |
427 return NULL; | 564 return NULL; |
428 } | 565 } |
429 | 566 |
430 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect, | 567 return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(DashingLineEffect, |
431 (edgeType, info, matrix, s
trokeWidth)))); | 568 (edgeType, info, strokeWid
th)))); |
432 } | 569 } |
433 | 570 |
434 DashingLineEffect::~DashingLineEffect() {} | 571 DashingLineEffect::~DashingLineEffect() {} |
435 | 572 |
436 void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* val
idFlags) const { | 573 void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* val
idFlags) const { |
437 *validFlags = 0; | 574 *validFlags = 0; |
438 } | 575 } |
439 | 576 |
440 const GrBackendEffectFactory& DashingLineEffect::getFactory() const { | 577 const GrBackendEffectFactory& DashingLineEffect::getFactory() const { |
441 return GrTBackendEffectFactory<DashingLineEffect>::getInstance(); | 578 return GrTBackendEffectFactory<DashingLineEffect>::getInstance(); |
442 } | 579 } |
443 | 580 |
444 DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo&
info, | 581 DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo&
info, |
445 const SkMatrix& matrix, SkScalar strokeWidth) | 582 SkScalar strokeWidth) |
446 : fEdgeType(edgeType) | 583 : fEdgeType(edgeType) { |
447 , fCoordTransform(kLocal_GrCoordSet, matrix) { | |
448 SkScalar onLen = info.fIntervals[0]; | 584 SkScalar onLen = info.fIntervals[0]; |
449 SkScalar offLen = info.fIntervals[1]; | 585 SkScalar offLen = info.fIntervals[1]; |
450 SkScalar halfOffLen = SkScalarHalf(offLen); | 586 SkScalar halfOffLen = SkScalarHalf(offLen); |
451 SkScalar halfStroke = SkScalarHalf(strokeWidth); | 587 SkScalar halfStroke = SkScalarHalf(strokeWidth); |
452 fIntervalLength = onLen + offLen; | 588 fIntervalLength = onLen + offLen; |
453 fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke); | 589 fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke); |
454 | 590 |
455 addCoordTransform(&fCoordTransform); | 591 this->addVertexAttrib(kVec2f_GrSLType); |
456 } | 592 } |
457 | 593 |
458 bool DashingLineEffect::onIsEqual(const GrEffect& other) const { | 594 bool DashingLineEffect::onIsEqual(const GrEffect& other) const { |
459 const DashingLineEffect& de = CastEffect<DashingLineEffect>(other); | 595 const DashingLineEffect& de = CastEffect<DashingLineEffect>(other); |
460 return (fEdgeType == de.fEdgeType && | 596 return (fEdgeType == de.fEdgeType && |
461 fCoordTransform == de.fCoordTransform && | |
462 fRect == de.fRect && | 597 fRect == de.fRect && |
463 fIntervalLength == de.fIntervalLength); | 598 fIntervalLength == de.fIntervalLength); |
464 } | 599 } |
465 | 600 |
466 GR_DEFINE_EFFECT_TEST(DashingLineEffect); | 601 GR_DEFINE_EFFECT_TEST(DashingLineEffect); |
467 | 602 |
468 GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random, | 603 GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random, |
469 GrContext*, | 604 GrContext*, |
470 const GrDrawTargetCaps& caps, | 605 const GrDrawTargetCaps& caps, |
471 GrTexture*[]) { | 606 GrTexture*[]) { |
472 GrEffectRef* effect; | 607 GrEffectRef* effect; |
473 SkMatrix m; | |
474 m.reset(); | |
475 GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessT
han( | 608 GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessT
han( |
476 kGrEffectEdgeTypeCnt)); | 609 kGrEffectEdgeTypeCnt)); |
477 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); | 610 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); |
478 DashInfo info; | 611 DashInfo info; |
479 info.fCount = 2; | 612 info.fCount = 2; |
480 SkAutoTArray<SkScalar> intervals(info.fCount); | 613 SkAutoTArray<SkScalar> intervals(info.fCount); |
481 info.fIntervals = intervals.get(); | 614 info.fIntervals = intervals.get(); |
482 info.fIntervals[0] = random->nextRangeScalar(0, 10.f); | 615 info.fIntervals[0] = random->nextRangeScalar(0, 10.f); |
483 info.fIntervals[1] = random->nextRangeScalar(0, 10.f); | 616 info.fIntervals[1] = random->nextRangeScalar(0, 10.f); |
484 info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fInterval
s[1]); | 617 info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fInterval
s[1]); |
485 | 618 |
486 effect = DashingLineEffect::Create(edgeType, info, m, strokeWidth); | 619 effect = DashingLineEffect::Create(edgeType, info, strokeWidth); |
487 return effect; | 620 return effect; |
488 } | 621 } |
489 | 622 |
490 ////////////////////////////////////////////////////////////////////////////// | 623 ////////////////////////////////////////////////////////////////////////////// |
491 | 624 |
492 GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffe
ct::DashInfo& info, | 625 GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffe
ct::DashInfo& info, |
493 const SkMatrix& matrix, SkScalar strokeWidt
h) { | 626 SkScalar strokeWidth) { |
494 return DashingLineEffect::Create(edgeType, info, matrix, strokeWidth); | 627 return DashingLineEffect::Create(edgeType, info, strokeWidth); |
495 } | 628 } |
OLD | NEW |