blob: fb58b84bc384d79272597d1453689efee9ebf1f2 [file] [log] [blame]
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +00001/*
2 * Copyright 2014 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 "GrDashingEffect.h"
9
egdaniele61c4112014-06-12 10:24:21 -070010#include "../GrAARectRenderer.h"
11
12#include "effects/GrVertexEffect.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000013#include "gl/GrGLEffect.h"
egdaniele61c4112014-06-12 10:24:21 -070014#include "gl/GrGLVertexEffect.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000015#include "gl/GrGLSL.h"
16#include "GrContext.h"
17#include "GrCoordTransform.h"
egdaniele61c4112014-06-12 10:24:21 -070018#include "GrDrawTarget.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000019#include "GrDrawTargetCaps.h"
20#include "GrEffect.h"
egdaniele61c4112014-06-12 10:24:21 -070021#include "GrGpu.h"
22#include "GrStrokeInfo.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000023#include "GrTBackendEffectFactory.h"
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000024#include "SkGr.h"
25
26///////////////////////////////////////////////////////////////////////////////
27
egdaniele61c4112014-06-12 10:24:21 -070028// Returns whether or not the gpu can fast path the dash line effect.
29static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo,
30 const GrDrawTarget& target, const SkMatrix& viewMatrix) {
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 perspective
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
egdanielf767e792014-07-02 06:21:32 -070057 if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) {
egdaniele61c4112014-06-12 10:24:21 -070058 return false;
59 }
60
61 return true;
62}
63
64namespace {
65
66struct DashLineVertex {
67 SkPoint fPos;
68 SkPoint fDashPos;
69};
70
71extern const GrVertexAttrib gDashLineVertexAttribs[] = {
72 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding },
73 { kVec2f_GrVertexAttribType, sizeof(SkPoint), kEffect_GrVertexAttribBinding },
74};
75
76};
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +000077static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale,
78 const SkMatrix& viewMatrix, const SkPoint pts[2]) {
79 SkVector vecSrc = pts[1] - pts[0];
80 SkScalar magSrc = vecSrc.length();
81 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0;
82 vecSrc.scale(invSrc);
83
84 SkVector vecSrcPerp;
85 vecSrc.rotateCW(&vecSrcPerp);
86 viewMatrix.mapVectors(&vecSrc, 1);
87 viewMatrix.mapVectors(&vecSrcPerp, 1);
88
89 // parallelScale tells how much to scale along the line parallel to the dash line
90 // perpScale tells how much to scale in the direction perpendicular to the dash line
91 *parallelScale = vecSrc.length();
92 *perpScale = vecSrcPerp.length();
93}
94
95// calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1]
96// Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot
97static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) {
98 SkVector vec = pts[1] - pts[0];
99 SkScalar mag = vec.length();
100 SkScalar inv = mag ? SkScalarInvert(mag) : 0;
101
102 vec.scale(inv);
103 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
104 if (ptsRot) {
105 rotMatrix->mapPoints(ptsRot, pts, 2);
106 // correction for numerical issues if map doesn't make ptsRot exactly horizontal
107 ptsRot[1].fY = pts[0].fY;
108 }
109}
110
111// Assumes phase < sum of all intervals
112static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) {
113 SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
114 if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) {
115 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
116 return srcIntervalLen - info.fPhase;
117 }
118 return 0;
119}
120
egdaniele61c4112014-06-12 10:24:21 -0700121static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2],
122 SkScalar phase, SkScalar* endingInt) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000123 if (pts[1].fX <= pts[0].fX) {
124 return 0;
125 }
126 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1];
127 SkScalar totalLen = pts[1].fX - pts[0].fX;
128 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen);
129 SkScalar numFullIntervals = SkScalarFloorToScalar(temp);
egdaniele61c4112014-06-12 10:24:21 -0700130 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000131 temp = SkScalarDiv(*endingInt, srcIntervalLen);
132 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen;
133 if (0 == *endingInt) {
134 *endingInt = srcIntervalLen;
135 }
136 if (*endingInt > info.fIntervals[0]) {
137 if (0 == info.fIntervals[0]) {
commit-bot@chromium.orgad883402014-05-19 14:43:45 +0000138 *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps)
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000139 }
140 return *endingInt - info.fIntervals[0];
141 }
142 return 0;
143}
144
egdaniele61c4112014-06-12 10:24:21 -0700145static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix,
146 SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000147
egdaniele61c4112014-06-12 10:24:21 -0700148 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
166bool 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)) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000171 return false;
172 }
173
egdaniele61c4112014-06-12 10:24:21 -0700174 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000175
egdaniele61c4112014-06-12 10:24:21 -0700176 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000177
egdaniele61c4112014-06-12 10:24:21 -0700178 SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000179
180 // the phase should be normalized to be [0, sum of all intervals)
181 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]);
182
egdaniele61c4112014-06-12 10:24:21 -0700183 SkScalar srcPhase = info.fPhase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000184
185 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX
186 SkMatrix srcRotInv;
187 SkPoint ptsRot[2];
188 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) {
egdaniele61c4112014-06-12 10:24:21 -0700189 SkMatrix rotMatrix;
190 align_to_x_axis(pts, &rotMatrix, ptsRot);
191 if(!rotMatrix.invert(&srcRotInv)) {
192 GrPrintf("Failed to create invertible rotation matrix!\n");
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000193 return false;
194 }
195 } else {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000196 srcRotInv.reset();
197 memcpy(ptsRot, pts, 2 * sizeof(SkPoint));
198 }
199
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000200 bool useAA = paint.isAntiAlias();
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000201
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000202 // Scale corrections of intervals and stroke from view matrix
203 SkScalar parallelScale;
204 SkScalar perpScale;
egdaniele61c4112014-06-12 10:24:21 -0700205 calc_dash_scaling(&parallelScale, &perpScale, vm, ptsRot);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000206
egdanielf767e792014-07-02 06:21:32 -0700207 bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000208
209 // We always want to at least stroke out half a pixel on each side in device space
210 // so 0.5f / perpScale gives us this min in src space
egdaniele61c4112014-06-12 10:24:21 -0700211 SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000212
egdaniele61c4112014-06-12 10:24:21 -0700213 SkScalar strokeAdj;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000214 if (!hasCap) {
egdaniele61c4112014-06-12 10:24:21 -0700215 strokeAdj = 0.f;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000216 } else {
egdaniele61c4112014-06-12 10:24:21 -0700217 strokeAdj = halfSrcStroke;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000218 }
219
egdaniele61c4112014-06-12 10:24:21 -0700220 SkScalar startAdj = 0;
221
222 SkMatrix combinedMatrix = srcRotInv;
223 combinedMatrix.postConcat(vm);
224
225 bool lineDone = false;
226 SkRect startRect;
227 bool hasStartRect = false;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000228 // If we are using AA, check to see if we are drawing a partial dash at the start. If so
229 // draw it separately here and adjust our start point accordingly
230 if (useAA) {
egdaniele61c4112014-06-12 10:24:21 -0700231 if (srcPhase > 0 && srcPhase < info.fIntervals[0]) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000232 SkPoint startPts[2];
233 startPts[0] = ptsRot[0];
234 startPts[1].fY = startPts[0].fY;
egdaniele61c4112014-06-12 10:24:21 -0700235 startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000236 ptsRot[1].fX);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000237 startRect.set(startPts, 2);
egdaniele61c4112014-06-12 10:24:21 -0700238 startRect.outset(strokeAdj, halfSrcStroke);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000239
egdaniele61c4112014-06-12 10:24:21 -0700240 hasStartRect = true;
241 startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000242 }
243 }
244
245 // adjustments for start and end of bounding rect so we only draw dash intervals
246 // contained in the original line segment.
egdaniele61c4112014-06-12 10:24:21 -0700247 startAdj += calc_start_adjustment(info);
248 if (startAdj != 0) {
249 ptsRot[0].fX += startAdj;
250 srcPhase = 0;
251 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000252 SkScalar endingInterval = 0;
egdaniele61c4112014-06-12 10:24:21 -0700253 SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval);
254 ptsRot[1].fX -= endAdj;
255 if (ptsRot[0].fX >= ptsRot[1].fX) {
256 lineDone = true;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000257 }
258
egdaniele61c4112014-06-12 10:24:21 -0700259 SkRect endRect;
260 bool hasEndRect = false;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000261 // If we are using AA, check to see if we are drawing a partial dash at then end. If so
262 // draw it separately here and adjust our end point accordingly
egdaniele61c4112014-06-12 10:24:21 -0700263 if (useAA && !lineDone) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000264 // If we adjusted the end then we will not be drawing a partial dash at the end.
265 // If we didn't adjust the end point then we just need to make sure the ending
266 // dash isn't a full dash
267 if (0 == endAdj && endingInterval != info.fIntervals[0]) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000268 SkPoint endPts[2];
269 endPts[1] = ptsRot[1];
270 endPts[0].fY = endPts[1].fY;
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000271 endPts[0].fX = endPts[1].fX - endingInterval;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000272
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000273 endRect.set(endPts, 2);
egdaniele61c4112014-06-12 10:24:21 -0700274 endRect.outset(strokeAdj, halfSrcStroke);
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000275
egdaniele61c4112014-06-12 10:24:21 -0700276 hasEndRect = true;
277 endAdj = endingInterval + info.fIntervals[1];
278
279 ptsRot[1].fX -= endAdj;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000280 if (ptsRot[0].fX >= ptsRot[1].fX) {
egdaniele61c4112014-06-12 10:24:21 -0700281 lineDone = true;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000282 }
283 }
284 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000285
egdaniele61c4112014-06-12 10:24:21 -0700286 if (startAdj != 0) {
287 srcPhase = 0;
288 }
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000289
egdaniele61c4112014-06-12 10:24:21 -0700290 // Change the dashing info from src space into device space
291 SkScalar devIntervals[2];
292 devIntervals[0] = info.fIntervals[0] * parallelScale;
293 devIntervals[1] = info.fIntervals[1] * parallelScale;
294 SkScalar devPhase = srcPhase * parallelScale;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000295 SkScalar strokeWidth = srcStrokeWidth * perpScale;
296
297 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) {
298 strokeWidth = 1.f;
299 }
300
egdaniele61c4112014-06-12 10:24:21 -0700301 SkScalar halfDevStroke = strokeWidth * 0.5f;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000302
303 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) {
304 // add cap to on interveal and remove from off interval
egdaniele61c4112014-06-12 10:24:21 -0700305 devIntervals[0] += strokeWidth;
306 devIntervals[1] -= strokeWidth;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000307 }
egdaniele61c4112014-06-12 10:24:21 -0700308 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000309
egdaniele61c4112014-06-12 10:24:21 -0700310 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;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000342 GrEffectEdgeType edgeType= useAA ? kFillAA_GrEffectEdgeType :
343 kFillBW_GrEffectEdgeType;
egdanielf767e792014-07-02 06:21:32 -0700344 bool isRoundCap = SkPaint::kRound_Cap == cap;
345 GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap :
346 GrDashingEffect::kNonRound_DashCap;
egdaniele61c4112014-06-12 10:24:21 -0700347 drawState->addCoverageEffect(
egdanielf767e792014-07-02 06:21:32 -0700348 GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType), 1)->unref();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000349 }
350
egdaniele61c4112014-06-12 10:24:21 -0700351 // Set up the vertex data for the line and start/end dashes
352 drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs));
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000353
egdaniele61c4112014-06-12 10:24:21 -0700354 int totalRectCnt = 0;
355
356 totalRectCnt += !lineDone ? 1 : 0;
357 totalRectCnt += hasStartRect ? 1 : 0;
358 totalRectCnt += hasEndRect ? 1 : 0;
359
360 GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0);
361 if (!geo.succeeded()) {
362 GrPrintf("Failed to get space for vertices!\n");
363 return false;
364 }
365
366 DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices());
367
368 int curVIdx = 0;
369
egdanielf767e792014-07-02 06:21:32 -0700370 if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) {
371 // need to adjust this for round caps to correctly set the dashPos attrib on vertices
372 startOffset -= halfDevStroke;
373 }
374
egdaniele61c4112014-06-12 10:24:21 -0700375 // Draw interior part of dashed line
376 if (!lineDone) {
377 SkPoint devicePts[2];
378 vm.mapPoints(devicePts, ptsRot, 2);
379 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]);
380 if (hasCap) {
381 lineLength += 2.f * halfDevStroke;
382 }
383
384 SkRect bounds;
385 bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY);
386 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke);
387 setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat,
388 lineLength, halfDevStroke);
389 curVIdx += 4;
390 }
391
392 if (hasStartRect) {
393 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
394 startRect.outset(bloatX, bloatY);
395 setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
396 devIntervals[0], halfDevStroke);
397 curVIdx += 4;
398 }
399
400 if (hasEndRect) {
401 SkASSERT(useAA); // so that we know bloatX and bloatY have been set
402 endRect.outset(bloatX, bloatY);
403 setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat,
404 devIntervals[0], halfDevStroke);
405 }
406
407 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer());
408 target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6);
409 target->resetIndexSource();
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000410 return true;
411}
412
413//////////////////////////////////////////////////////////////////////////////
414
egdanielf767e792014-07-02 06:21:32 -0700415class GLDashingCircleEffect;
416/*
417 * This effect will draw a dotted line (defined as a dashed lined with round caps and no on
418 * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo.
419 * Both of the previous two parameters are in device space. This effect also requires the setting of
420 * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the
421 * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we
422 * transform the line to be horizontal, with the start of line at the origin then shifted to the
423 * right by half the off interval. The line then goes in the positive x direction.
424 */
425class DashingCircleEffect : public GrVertexEffect {
426public:
427 typedef SkPathEffect::DashInfo DashInfo;
428
429 static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info,
430 SkScalar radius);
431
432 virtual ~DashingCircleEffect();
433
434 static const char* Name() { return "DashingCircleEffect"; }
435
436 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
437
438 SkScalar getRadius() const { return fRadius; }
439
440 SkScalar getCenterX() const { return fCenterX; }
441
442 SkScalar getIntervalLength() const { return fIntervalLength; }
443
444 typedef GLDashingCircleEffect GLEffect;
445
446 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
447
448 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
449
450private:
451 DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar radius);
452
453 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
454
455 GrEffectEdgeType fEdgeType;
456 SkScalar fIntervalLength;
457 SkScalar fRadius;
458 SkScalar fCenterX;
459
460 GR_DECLARE_EFFECT_TEST;
461
462 typedef GrVertexEffect INHERITED;
463};
464
465//////////////////////////////////////////////////////////////////////////////
466
467class GLDashingCircleEffect : public GrGLVertexEffect {
468public:
469 GLDashingCircleEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
470
471 virtual void emitCode(GrGLFullShaderBuilder* builder,
472 const GrDrawEffect& drawEffect,
473 EffectKey key,
474 const char* outputColor,
475 const char* inputColor,
476 const TransformedCoordsArray&,
477 const TextureSamplerArray&) SK_OVERRIDE;
478
479 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
480
481 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
482
483private:
484 GrGLUniformManager::UniformHandle fParamUniform;
485 SkScalar fPrevRadius;
486 SkScalar fPrevCenterX;
487 SkScalar fPrevIntervalLength;
488 typedef GrGLVertexEffect INHERITED;
489};
490
491GLDashingCircleEffect::GLDashingCircleEffect(const GrBackendEffectFactory& factory,
492 const GrDrawEffect& drawEffect)
493 : INHERITED (factory) {
494 fPrevRadius = SK_ScalarMin;
495 fPrevCenterX = SK_ScalarMin;
496 fPrevIntervalLength = SK_ScalarMax;
497}
498
499void GLDashingCircleEffect::emitCode(GrGLFullShaderBuilder* builder,
500 const GrDrawEffect& drawEffect,
501 EffectKey key,
502 const char* outputColor,
503 const char* inputColor,
504 const TransformedCoordsArray&,
505 const TextureSamplerArray& samplers) {
506 const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
507 const char *paramName;
508 // The param uniforms, xyz, refer to circle radius - 0.5, cicles center x coord, and
509 // the total interval length of the dash.
510 fParamUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
511 kVec3f_GrSLType,
512 "params",
513 &paramName);
514
515 const char *vsCoordName, *fsCoordName;
516 builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
517 const SkString* attr0Name =
518 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
519 builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
520
521 // transforms all points so that we can compare them to our test circle
522 builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n",
523 fsCoordName, fsCoordName, paramName, paramName);
524 builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
525 builder->fsCodeAppendf("\t\tvec2 center = vec2(%s.y, 0.0);\n", paramName);
526 builder->fsCodeAppend("\t\tfloat dist = length(center - fragPosShifted);\n");
527 if (GrEffectEdgeTypeIsAA(dce.getEdgeType())) {
528 builder->fsCodeAppendf("\t\tfloat diff = dist - %s.x;\n", paramName);
egdaniela8870262014-07-02 06:56:30 -0700529 builder->fsCodeAppend("\t\tdiff = 1.0 - diff;\n");
egdanielf767e792014-07-02 06:21:32 -0700530 builder->fsCodeAppend("\t\tfloat alpha = clamp(diff, 0.0, 1.0);\n");
531 } else {
532 builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
533 builder->fsCodeAppendf("\t\talpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;\n", paramName);
534 }
535 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
536 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
537}
538
539void GLDashingCircleEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
540 const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
541 SkScalar radius = dce.getRadius();
542 SkScalar centerX = dce.getCenterX();
543 SkScalar intervalLength = dce.getIntervalLength();
544 if (radius != fPrevRadius || centerX != fPrevCenterX || intervalLength != fPrevIntervalLength) {
545 uman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength);
546 fPrevRadius = radius;
547 fPrevCenterX = centerX;
548 fPrevIntervalLength = intervalLength;
549 }
550}
551
552GrGLEffect::EffectKey GLDashingCircleEffect::GenKey(const GrDrawEffect& drawEffect,
553 const GrGLCaps&) {
554 const DashingCircleEffect& dce = drawEffect.castEffect<DashingCircleEffect>();
555 return dce.getEdgeType();
556}
557
558//////////////////////////////////////////////////////////////////////////////
559
560GrEffectRef* DashingCircleEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
561 SkScalar radius) {
562 if (info.fCount != 2 || info.fIntervals[0] != 0) {
563 return NULL;
564 }
565
bsalomon55fad7a2014-07-08 07:34:20 -0700566 return SkNEW_ARGS(DashingCircleEffect, (edgeType, info, radius));
egdanielf767e792014-07-02 06:21:32 -0700567}
568
569DashingCircleEffect::~DashingCircleEffect() {}
570
571void DashingCircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
572 *validFlags = 0;
573}
574
575const GrBackendEffectFactory& DashingCircleEffect::getFactory() const {
576 return GrTBackendEffectFactory<DashingCircleEffect>::getInstance();
577}
578
579DashingCircleEffect::DashingCircleEffect(GrEffectEdgeType edgeType, const DashInfo& info,
580 SkScalar radius)
581 : fEdgeType(edgeType) {
582 SkScalar onLen = info.fIntervals[0];
583 SkScalar offLen = info.fIntervals[1];
584 fIntervalLength = onLen + offLen;
585 fRadius = radius;
586 fCenterX = SkScalarHalf(offLen);
587
588 this->addVertexAttrib(kVec2f_GrSLType);
589}
590
591bool DashingCircleEffect::onIsEqual(const GrEffect& other) const {
592 const DashingCircleEffect& dce = CastEffect<DashingCircleEffect>(other);
593 return (fEdgeType == dce.fEdgeType &&
594 fIntervalLength == dce.fIntervalLength &&
595 fRadius == dce.fRadius &&
596 fCenterX == dce.fCenterX);
597}
598
599GR_DEFINE_EFFECT_TEST(DashingCircleEffect);
600
601GrEffectRef* DashingCircleEffect::TestCreate(SkRandom* random,
602 GrContext*,
603 const GrDrawTargetCaps& caps,
604 GrTexture*[]) {
605 GrEffectRef* effect;
606 GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
607 kGrEffectEdgeTypeCnt));
608 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
609 DashInfo info;
610 info.fCount = 2;
611 SkAutoTArray<SkScalar> intervals(info.fCount);
612 info.fIntervals = intervals.get();
613 info.fIntervals[0] = 0;
614 info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
615 info.fPhase = random->nextRangeScalar(0, info.fIntervals[1]);
616
617 effect = DashingCircleEffect::Create(edgeType, info, strokeWidth);
618 return effect;
619}
620
621//////////////////////////////////////////////////////////////////////////////
622
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000623class GLDashingLineEffect;
624
egdanielf767e792014-07-02 06:21:32 -0700625/*
626 * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the
627 * length and spacing by the DashInfo. Both of the previous two parameters are in device space.
628 * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the
629 * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the
630 * vertex coords (in device space) if we transform the line to be horizontal, with the start of
631 * line at the origin then shifted to the right by half the off interval. The line then goes in the
632 * positive x direction.
633 */
egdaniele61c4112014-06-12 10:24:21 -0700634class DashingLineEffect : public GrVertexEffect {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000635public:
636 typedef SkPathEffect::DashInfo DashInfo;
637
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000638 static GrEffectRef* Create(GrEffectEdgeType edgeType, const DashInfo& info,
egdaniele61c4112014-06-12 10:24:21 -0700639 SkScalar strokeWidth);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000640
641 virtual ~DashingLineEffect();
642
643 static const char* Name() { return "DashingEffect"; }
644
645 GrEffectEdgeType getEdgeType() const { return fEdgeType; }
646
647 const SkRect& getRect() const { return fRect; }
648
649 SkScalar getIntervalLength() const { return fIntervalLength; }
650
651 typedef GLDashingLineEffect GLEffect;
652
653 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
654
655 virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
656
657private:
egdaniele61c4112014-06-12 10:24:21 -0700658 DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000659
660 virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
661
662 GrEffectEdgeType fEdgeType;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000663 SkRect fRect;
664 SkScalar fIntervalLength;
665
666 GR_DECLARE_EFFECT_TEST;
667
egdanielf767e792014-07-02 06:21:32 -0700668 typedef GrVertexEffect INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000669};
670
671//////////////////////////////////////////////////////////////////////////////
672
egdaniele61c4112014-06-12 10:24:21 -0700673class GLDashingLineEffect : public GrGLVertexEffect {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000674public:
675 GLDashingLineEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
676
egdaniele61c4112014-06-12 10:24:21 -0700677 virtual void emitCode(GrGLFullShaderBuilder* builder,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000678 const GrDrawEffect& drawEffect,
679 EffectKey key,
680 const char* outputColor,
681 const char* inputColor,
682 const TransformedCoordsArray&,
683 const TextureSamplerArray&) SK_OVERRIDE;
684
685 static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
686
687 virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
688
689private:
690 GrGLUniformManager::UniformHandle fRectUniform;
691 GrGLUniformManager::UniformHandle fIntervalUniform;
692 SkRect fPrevRect;
693 SkScalar fPrevIntervalLength;
egdaniele61c4112014-06-12 10:24:21 -0700694 typedef GrGLVertexEffect INHERITED;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000695};
696
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000697GLDashingLineEffect::GLDashingLineEffect(const GrBackendEffectFactory& factory,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000698 const GrDrawEffect& drawEffect)
699 : INHERITED (factory) {
700 fPrevRect.fLeft = SK_ScalarNaN;
701 fPrevIntervalLength = SK_ScalarMax;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000702}
703
egdaniele61c4112014-06-12 10:24:21 -0700704void GLDashingLineEffect::emitCode(GrGLFullShaderBuilder* builder,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000705 const GrDrawEffect& drawEffect,
706 EffectKey key,
707 const char* outputColor,
708 const char* inputColor,
egdaniele61c4112014-06-12 10:24:21 -0700709 const TransformedCoordsArray&,
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000710 const TextureSamplerArray& samplers) {
711 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
712 const char *rectName;
713 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5),
714 // respectively.
715 fRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
716 kVec4f_GrSLType,
717 "rect",
718 &rectName);
719 const char *intervalName;
720 // The interval uniform's refers to the total length of the interval (on + off)
721 fIntervalUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
722 kFloat_GrSLType,
723 "interval",
724 &intervalName);
egdaniele61c4112014-06-12 10:24:21 -0700725
726 const char *vsCoordName, *fsCoordName;
727 builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName);
728 const SkString* attr0Name =
729 builder->getEffectAttributeName(drawEffect.getVertexAttribIndices()[0]);
730 builder->vsCodeAppendf("\t%s = %s;\n", vsCoordName, attr0Name->c_str());
731
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000732 // transforms all points so that we can compare them to our test rect
733 builder->fsCodeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n",
egdaniele61c4112014-06-12 10:24:21 -0700734 fsCoordName, fsCoordName, intervalName, intervalName);
735 builder->fsCodeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000736 if (GrEffectEdgeTypeIsAA(de.getEdgeType())) {
737 // The amount of coverage removed in x and y by the edges is computed as a pair of negative
738 // numbers, xSub and ySub.
739 builder->fsCodeAppend("\t\tfloat xSub, ySub;\n");
740 builder->fsCodeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName);
741 builder->fsCodeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName);
742 builder->fsCodeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName);
743 builder->fsCodeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName);
744 // Now compute coverage in x and y and multiply them to get the fraction of the pixel
745 // covered.
746 builder->fsCodeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n");
747 } else {
748 // Assuming the bounding geometry is tight so no need to check y values
749 builder->fsCodeAppendf("\t\tfloat alpha = 1.0;\n");
750 builder->fsCodeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName);
751 builder->fsCodeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName);
752 }
753 builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
754 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
755}
756
757void GLDashingLineEffect::setData(const GrGLUniformManager& uman, const GrDrawEffect& drawEffect) {
758 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
759 const SkRect& rect = de.getRect();
760 SkScalar intervalLength = de.getIntervalLength();
761 if (rect != fPrevRect || intervalLength != fPrevIntervalLength) {
762 uman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f,
763 rect.fRight - 0.5f, rect.fBottom - 0.5f);
764 uman.set1f(fIntervalUniform, intervalLength);
765 fPrevRect = rect;
766 fPrevIntervalLength = intervalLength;
767 }
768}
769
770GrGLEffect::EffectKey GLDashingLineEffect::GenKey(const GrDrawEffect& drawEffect,
771 const GrGLCaps&) {
772 const DashingLineEffect& de = drawEffect.castEffect<DashingLineEffect>();
773 return de.getEdgeType();
774}
775
776//////////////////////////////////////////////////////////////////////////////
skia.committer@gmail.com3b9e8be2014-05-20 03:05:34 +0000777
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000778GrEffectRef* DashingLineEffect::Create(GrEffectEdgeType edgeType, const DashInfo& info,
egdaniele61c4112014-06-12 10:24:21 -0700779 SkScalar strokeWidth) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000780 if (info.fCount != 2) {
781 return NULL;
782 }
783
bsalomon55fad7a2014-07-08 07:34:20 -0700784 return SkNEW_ARGS(DashingLineEffect, (edgeType, info, strokeWidth));
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000785}
786
787DashingLineEffect::~DashingLineEffect() {}
788
789void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
790 *validFlags = 0;
791}
792
793const GrBackendEffectFactory& DashingLineEffect::getFactory() const {
794 return GrTBackendEffectFactory<DashingLineEffect>::getInstance();
795}
796
797DashingLineEffect::DashingLineEffect(GrEffectEdgeType edgeType, const DashInfo& info,
egdaniele61c4112014-06-12 10:24:21 -0700798 SkScalar strokeWidth)
799 : fEdgeType(edgeType) {
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000800 SkScalar onLen = info.fIntervals[0];
801 SkScalar offLen = info.fIntervals[1];
802 SkScalar halfOffLen = SkScalarHalf(offLen);
803 SkScalar halfStroke = SkScalarHalf(strokeWidth);
804 fIntervalLength = onLen + offLen;
805 fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke);
806
egdaniele61c4112014-06-12 10:24:21 -0700807 this->addVertexAttrib(kVec2f_GrSLType);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000808}
809
810bool DashingLineEffect::onIsEqual(const GrEffect& other) const {
811 const DashingLineEffect& de = CastEffect<DashingLineEffect>(other);
812 return (fEdgeType == de.fEdgeType &&
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000813 fRect == de.fRect &&
814 fIntervalLength == de.fIntervalLength);
815}
816
817GR_DEFINE_EFFECT_TEST(DashingLineEffect);
818
819GrEffectRef* DashingLineEffect::TestCreate(SkRandom* random,
820 GrContext*,
821 const GrDrawTargetCaps& caps,
822 GrTexture*[]) {
823 GrEffectRef* effect;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000824 GrEffectEdgeType edgeType = static_cast<GrEffectEdgeType>(random->nextULessThan(
825 kGrEffectEdgeTypeCnt));
826 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f);
827 DashInfo info;
828 info.fCount = 2;
829 SkAutoTArray<SkScalar> intervals(info.fCount);
830 info.fIntervals = intervals.get();
831 info.fIntervals[0] = random->nextRangeScalar(0, 10.f);
832 info.fIntervals[1] = random->nextRangeScalar(0, 10.f);
833 info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]);
834
egdaniele61c4112014-06-12 10:24:21 -0700835 effect = DashingLineEffect::Create(edgeType, info, strokeWidth);
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000836 return effect;
837}
838
839//////////////////////////////////////////////////////////////////////////////
840
841GrEffectRef* GrDashingEffect::Create(GrEffectEdgeType edgeType, const SkPathEffect::DashInfo& info,
egdanielf767e792014-07-02 06:21:32 -0700842 SkScalar strokeWidth, GrDashingEffect::DashCap cap) {
843 switch (cap) {
844 case GrDashingEffect::kRound_DashCap:
845 return DashingCircleEffect::Create(edgeType, info, SkScalarHalf(strokeWidth));
846 case GrDashingEffect::kNonRound_DashCap:
847 return DashingLineEffect::Create(edgeType, info, strokeWidth);
848 default:
849 SkFAIL("Unexpected dashed cap.");
850 }
851 return NULL;
commit-bot@chromium.org628ed0b2014-05-19 14:32:49 +0000852}