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" | 10 #include "GrBatch.h" |
11 | 11 #include "GrBatchTarget.h" |
12 #include "GrBufferAllocPool.h" | |
12 #include "GrGeometryProcessor.h" | 13 #include "GrGeometryProcessor.h" |
13 #include "GrContext.h" | 14 #include "GrContext.h" |
14 #include "GrCoordTransform.h" | 15 #include "GrCoordTransform.h" |
15 #include "GrDefaultGeoProcFactory.h" | 16 #include "GrDefaultGeoProcFactory.h" |
16 #include "GrDrawTarget.h" | 17 #include "GrDrawTarget.h" |
17 #include "GrDrawTargetCaps.h" | 18 #include "GrDrawTargetCaps.h" |
18 #include "GrInvariantOutput.h" | 19 #include "GrInvariantOutput.h" |
19 #include "GrProcessor.h" | 20 #include "GrProcessor.h" |
20 #include "GrStrokeInfo.h" | 21 #include "GrStrokeInfo.h" |
21 #include "SkGr.h" | 22 #include "SkGr.h" |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
107 vec.scale(inv); | 108 vec.scale(inv); |
108 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); | 109 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); |
109 if (ptsRot) { | 110 if (ptsRot) { |
110 rotMatrix->mapPoints(ptsRot, pts, 2); | 111 rotMatrix->mapPoints(ptsRot, pts, 2); |
111 // correction for numerical issues if map doesn't make ptsRot exactly ho rizontal | 112 // correction for numerical issues if map doesn't make ptsRot exactly ho rizontal |
112 ptsRot[1].fY = pts[0].fY; | 113 ptsRot[1].fY = pts[0].fY; |
113 } | 114 } |
114 } | 115 } |
115 | 116 |
116 // Assumes phase < sum of all intervals | 117 // Assumes phase < sum of all intervals |
117 static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { | 118 static SkScalar calc_start_adjustment(const SkScalar intervals[2], SkScalar phas e) { |
118 SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]); | 119 SkASSERT(phase < intervals[0] + intervals[1]); |
119 if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) { | 120 if (phase >= intervals[0] && phase != 0) { |
120 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; | 121 SkScalar srcIntervalLen = intervals[0] + intervals[1]; |
121 return srcIntervalLen - info.fPhase; | 122 return srcIntervalLen - phase; |
122 } | 123 } |
123 return 0; | 124 return 0; |
124 } | 125 } |
125 | 126 |
126 static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const Sk Point pts[2], | 127 static SkScalar calc_end_adjustment(const SkScalar intervals[2], const SkPoint p ts[2], |
127 SkScalar phase, SkScalar* endingInt) { | 128 SkScalar phase, SkScalar* endingInt) { |
128 if (pts[1].fX <= pts[0].fX) { | 129 if (pts[1].fX <= pts[0].fX) { |
129 return 0; | 130 return 0; |
130 } | 131 } |
131 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; | 132 SkScalar srcIntervalLen = intervals[0] + intervals[1]; |
132 SkScalar totalLen = pts[1].fX - pts[0].fX; | 133 SkScalar totalLen = pts[1].fX - pts[0].fX; |
133 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); | 134 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); |
134 SkScalar numFullIntervals = SkScalarFloorToScalar(temp); | 135 SkScalar numFullIntervals = SkScalarFloorToScalar(temp); |
135 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; | 136 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; |
136 temp = SkScalarDiv(*endingInt, srcIntervalLen); | 137 temp = SkScalarDiv(*endingInt, srcIntervalLen); |
137 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; | 138 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; |
138 if (0 == *endingInt) { | 139 if (0 == *endingInt) { |
139 *endingInt = srcIntervalLen; | 140 *endingInt = srcIntervalLen; |
140 } | 141 } |
141 if (*endingInt > info.fIntervals[0]) { | 142 if (*endingInt > intervals[0]) { |
142 if (0 == info.fIntervals[0]) { | 143 if (0 == intervals[0]) { |
143 *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps) | 144 *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps) |
144 } | 145 } |
145 return *endingInt - info.fIntervals[0]; | 146 return *endingInt - intervals[0]; |
146 } | 147 } |
147 return 0; | 148 return 0; |
148 } | 149 } |
149 | 150 |
150 enum DashCap { | 151 enum DashCap { |
151 kRound_DashCap, | 152 kRound_DashCap, |
152 kNonRound_DashCap, | 153 kNonRound_DashCap, |
153 }; | 154 }; |
154 | 155 |
155 static int kDashVertices = 4; | 156 static int kDashVertices = 4; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
230 * An GrGeometryProcessor that renders a dashed line. | 231 * An GrGeometryProcessor that renders a dashed line. |
231 * This GrGeometryProcessor is meant for dashed lines that only have a single on /off interval pair. | 232 * This GrGeometryProcessor is meant for dashed lines that only have a single on /off interval pair. |
232 * Bounding geometry is rendered and the effect computes coverage based on the f ragment's | 233 * Bounding geometry is rendered and the effect computes coverage based on the f ragment's |
233 * position relative to the dashed line. | 234 * position relative to the dashed line. |
234 */ | 235 */ |
235 static GrGeometryProcessor* create_dash_gp(GrColor, | 236 static GrGeometryProcessor* create_dash_gp(GrColor, |
236 GrPrimitiveEdgeType edgeType, | 237 GrPrimitiveEdgeType edgeType, |
237 DashCap cap, | 238 DashCap cap, |
238 const SkMatrix& localMatrix); | 239 const SkMatrix& localMatrix); |
239 | 240 |
241 class DashBatch : public GrBatch { | |
242 public: | |
243 struct Geometry { | |
244 GrColor fColor; | |
245 SkMatrix fViewMatrix; | |
246 SkMatrix fSrcRotInv; | |
247 SkPoint fPtsRot[2]; | |
248 SkScalar fSrcStrokeWidth; | |
249 SkScalar fPhase; | |
250 SkScalar fIntervals[2]; | |
251 SkDEBUGCODE(SkRect fDevBounds;) | |
252 }; | |
253 | |
254 static GrBatch* Create(const Geometry& geometry, SkPaint::Cap cap, bool useA A, bool fullDash) { | |
255 return SkNEW_ARGS(DashBatch, (geometry, cap, useAA, fullDash)); | |
256 } | |
257 | |
258 const char* name() const SK_OVERRIDE { return "DashBatch"; } | |
259 | |
260 void getInvariantOutputColor(GrInitInvariantOutput* out) const SK_OVERRIDE { | |
261 // When this is called on a batch, there is only one geometry bundle | |
262 out->setKnownFourComponents(fGeoData[0].fColor); | |
263 } | |
264 void getInvariantOutputCoverage(GrInitInvariantOutput* out) const SK_OVERRID E { | |
265 out->setUnknownSingleComponent(); | |
266 } | |
267 | |
268 void initBatchTracker(const GrPipelineInfo& init) SK_OVERRIDE { | |
269 // Handle any color overrides | |
270 if (init.fColorIgnored) { | |
271 fGeoData[0].fColor = GrColor_ILLEGAL; | |
272 } else if (GrColor_ILLEGAL != init.fOverrideColor) { | |
273 fGeoData[0].fColor = init.fOverrideColor; | |
274 } | |
275 | |
276 // setup batch properties | |
277 fBatch.fColorIgnored = init.fColorIgnored; | |
278 fBatch.fColor = fGeoData[0].fColor; | |
279 fBatch.fUsesLocalCoords = init.fUsesLocalCoords; | |
280 fBatch.fCoverageIgnored = init.fCoverageIgnored; | |
281 } | |
282 | |
283 struct DashDraw { | |
284 SkScalar fStartOffset; | |
285 SkScalar fStrokeWidth; | |
286 SkScalar fLineLength; | |
287 SkScalar fHalfDevStroke; | |
288 SkScalar fDevBloat; | |
289 bool fLineDone; | |
290 bool fHasStartRect; | |
291 bool fHasEndRect; | |
292 }; | |
293 | |
294 void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline ) SK_OVERRIDE { | |
295 int instanceCount = fGeoData.count(); | |
296 | |
297 SkMatrix invert; | |
298 if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { | |
299 SkDebugf("Failed to invert\n"); | |
300 return; | |
301 } | |
302 | |
303 SkPaint::Cap cap = this->cap(); | |
304 | |
305 SkAutoTUnref<const GrGeometryProcessor> gp; | |
306 | |
307 bool isRoundCap = SkPaint::kRound_Cap == cap; | |
308 DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap; | |
309 if (this->fullDash()) { | |
310 GrPrimitiveEdgeType edgeType = this->useAA() ? kFillAA_GrProcessorEd geType : | |
311 kFillBW_GrProcessorEd geType; | |
312 gp.reset(create_dash_gp(this->color(), edgeType, capType, invert)); | |
313 } else { | |
314 // Set up the vertex data for the line and start/end dashes | |
315 gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kP osition_GPType, | |
316 this->color(), | |
317 SkMatrix::I(), | |
318 invert)); | |
319 } | |
320 | |
321 batchTarget->initDraw(gp, pipeline); | |
322 | |
323 // TODO remove this when batch is everywhere | |
324 GrPipelineInfo init; | |
325 init.fColorIgnored = fBatch.fColorIgnored; | |
326 init.fOverrideColor = GrColor_ILLEGAL; | |
327 init.fCoverageIgnored = fBatch.fCoverageIgnored; | |
328 init.fUsesLocalCoords = this->usesLocalCoords(); | |
329 gp->initBatchTracker(batchTarget->currentBatchTracker(), init); | |
330 | |
331 bool useAA = this->useAA(); | |
332 bool fullDash = this->fullDash(); | |
333 | |
334 // We do two passes over all of the dashes. First we setup the start, e nd, and bounds, | |
335 // rectangles. We preserve all of this work in the rects / draws arrays below. Then we | |
336 // iterate again over these decomposed dashes to generate vertices | |
337 SkSTArray<128, SkRect, true> rects; | |
338 SkSTArray<128, DashDraw, true> draws; | |
339 | |
340 int totalRectCount = 0; | |
341 for (int i = 0; i < instanceCount; i++) { | |
342 Geometry& args = fGeoData[i]; | |
343 | |
344 // Scale corrections of intervals and stroke from view matrix | |
345 SkScalar parallelScale; | |
346 SkScalar perpScale; | |
347 calc_dash_scaling(¶llelScale, &perpScale, args.fViewMatrix, args .fPtsRot); | |
egdaniel
2015/02/27 15:13:18
do we really want recalculate this for all the das
| |
348 | |
349 bool hasCap = SkPaint::kButt_Cap != cap && 0 != args.fSrcStrokeWidth ; | |
350 | |
351 // We always want to at least stroke out half a pixel on each side i n device space | |
352 // so 0.5f / perpScale gives us this min in src space | |
353 SkScalar halfSrcStroke = SkMaxScalar(args.fSrcStrokeWidth * 0.5f, 0. 5f / perpScale); | |
354 | |
355 SkScalar strokeAdj; | |
356 if (!hasCap) { | |
357 strokeAdj = 0.f; | |
358 } else { | |
359 strokeAdj = halfSrcStroke; | |
360 } | |
361 | |
362 SkScalar startAdj = 0; | |
363 | |
364 SkMatrix& combinedMatrix = args.fSrcRotInv; | |
365 combinedMatrix.postConcat(args.fViewMatrix); | |
366 | |
367 bool lineDone = false; | |
368 | |
369 // Too simplify the algorithm, we always push back rects for start a nd end rect. | |
370 // Otherwise we'd have to track start / end rects for each individua l geometry | |
371 SkRect& bounds = rects.push_back(); | |
372 SkRect& startRect = rects.push_back(); | |
373 SkRect& endRect = rects.push_back(); | |
374 | |
375 bool hasStartRect = false; | |
376 // If we are using AA, check to see if we are drawing a partial dash at the start. If so | |
377 // draw it separately here and adjust our start point accordingly | |
378 if (useAA) { | |
379 if (args.fPhase > 0 && args.fPhase < args.fIntervals[0]) { | |
380 SkPoint startPts[2]; | |
381 startPts[0] = args.fPtsRot[0]; | |
382 startPts[1].fY = startPts[0].fY; | |
383 startPts[1].fX = SkMinScalar(startPts[0].fX + args.fInterval s[0] - args.fPhase, | |
384 args.fPtsRot[1].fX); | |
385 startRect.set(startPts, 2); | |
386 startRect.outset(strokeAdj, halfSrcStroke); | |
387 | |
388 hasStartRect = true; | |
389 startAdj = args.fIntervals[0] + args.fIntervals[1] - args.fP hase; | |
390 } | |
391 } | |
392 | |
393 // adjustments for start and end of bounding rect so we only draw da sh intervals | |
394 // contained in the original line segment. | |
395 startAdj += calc_start_adjustment(args.fIntervals, args.fPhase); | |
396 if (startAdj != 0) { | |
397 args.fPtsRot[0].fX += startAdj; | |
398 args.fPhase = 0; | |
399 } | |
400 SkScalar endingInterval = 0; | |
401 SkScalar endAdj = calc_end_adjustment(args.fIntervals, args.fPtsRot, args.fPhase, | |
402 &endingInterval); | |
403 args.fPtsRot[1].fX -= endAdj; | |
404 if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) { | |
405 lineDone = true; | |
406 } | |
407 | |
408 bool hasEndRect = false; | |
409 // If we are using AA, check to see if we are drawing a partial dash at then end. If so | |
410 // draw it separately here and adjust our end point accordingly | |
411 if (useAA && !lineDone) { | |
412 // If we adjusted the end then we will not be drawing a partial dash at the end. | |
413 // If we didn't adjust the end point then we just need to make s ure the ending | |
414 // dash isn't a full dash | |
415 if (0 == endAdj && endingInterval != args.fIntervals[0]) { | |
416 SkPoint endPts[2]; | |
417 endPts[1] = args.fPtsRot[1]; | |
418 endPts[0].fY = endPts[1].fY; | |
419 endPts[0].fX = endPts[1].fX - endingInterval; | |
420 | |
421 endRect.set(endPts, 2); | |
422 endRect.outset(strokeAdj, halfSrcStroke); | |
423 | |
424 hasEndRect = true; | |
425 endAdj = endingInterval + args.fIntervals[1]; | |
426 | |
427 args.fPtsRot[1].fX -= endAdj; | |
428 if (args.fPtsRot[0].fX >= args.fPtsRot[1].fX) { | |
429 lineDone = true; | |
430 } | |
431 } | |
432 } | |
433 | |
434 if (startAdj != 0) { | |
435 args.fPhase = 0; | |
436 } | |
437 | |
438 // Change the dashing info from src space into device space | |
439 SkScalar* devIntervals = args.fIntervals; | |
440 devIntervals[0] = args.fIntervals[0] * parallelScale; | |
441 devIntervals[1] = args.fIntervals[1] * parallelScale; | |
442 SkScalar devPhase = args.fPhase * parallelScale; | |
443 SkScalar strokeWidth = args.fSrcStrokeWidth * perpScale; | |
444 | |
445 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { | |
446 strokeWidth = 1.f; | |
447 } | |
448 | |
449 SkScalar halfDevStroke = strokeWidth * 0.5f; | |
450 | |
451 if (SkPaint::kSquare_Cap == cap && 0 != args.fSrcStrokeWidth) { | |
452 // add cap to on interval and remove from off interval | |
453 devIntervals[0] += strokeWidth; | |
454 devIntervals[1] -= strokeWidth; | |
455 } | |
456 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; | |
457 | |
458 SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; | |
459 SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; | |
460 | |
461 SkScalar devBloat = useAA ? 0.5f : 0.f; | |
462 | |
463 if (devIntervals[1] <= 0.f && useAA) { | |
464 // Case when we end up drawing a solid AA rect | |
465 // Reset the start rect to draw this single solid rect | |
466 // but it requires to upload a new intervals uniform so we can m imic | |
467 // one giant dash | |
468 args.fPtsRot[0].fX -= hasStartRect ? startAdj : 0; | |
469 args.fPtsRot[1].fX += hasEndRect ? endAdj : 0; | |
470 startRect.set(args.fPtsRot, 2); | |
471 startRect.outset(strokeAdj, halfSrcStroke); | |
472 hasStartRect = true; | |
473 hasEndRect = false; | |
474 lineDone = true; | |
475 | |
476 SkPoint devicePts[2]; | |
477 args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2); | |
478 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[ 1]); | |
479 if (hasCap) { | |
480 lineLength += 2.f * halfDevStroke; | |
481 } | |
482 devIntervals[0] = lineLength; | |
483 } | |
484 | |
485 totalRectCount += !lineDone ? 1 : 0; | |
486 totalRectCount += hasStartRect ? 1 : 0; | |
487 totalRectCount += hasEndRect ? 1 : 0; | |
488 | |
489 if (SkPaint::kRound_Cap == cap && 0 != args.fSrcStrokeWidth) { | |
490 // need to adjust this for round caps to correctly set the dashP os attrib on | |
491 // vertices | |
492 startOffset -= halfDevStroke; | |
493 } | |
494 | |
495 DashDraw& draw = draws.push_back(); | |
496 if (!lineDone) { | |
497 SkPoint devicePts[2]; | |
498 args.fViewMatrix.mapPoints(devicePts, args.fPtsRot, 2); | |
499 draw.fLineLength = SkPoint::Distance(devicePts[0], devicePts[1]) ; | |
500 if (hasCap) { | |
501 draw.fLineLength += 2.f * halfDevStroke; | |
502 } | |
503 | |
504 bounds.set(args.fPtsRot[0].fX, args.fPtsRot[0].fY, | |
505 args.fPtsRot[1].fX, args.fPtsRot[1].fY); | |
506 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); | |
507 } | |
508 | |
509 if (hasStartRect) { | |
510 SkASSERT(useAA); // so that we know bloatX and bloatY have been set | |
511 startRect.outset(bloatX, bloatY); | |
512 } | |
513 | |
514 if (hasEndRect) { | |
515 SkASSERT(useAA); // so that we know bloatX and bloatY have been set | |
516 endRect.outset(bloatX, bloatY); | |
517 } | |
518 | |
519 draw.fStartOffset = startOffset; | |
520 draw.fDevBloat = devBloat; | |
521 draw.fHalfDevStroke = halfDevStroke; | |
522 draw.fStrokeWidth = strokeWidth; | |
523 draw.fHasStartRect = hasStartRect; | |
524 draw.fLineDone = lineDone; | |
525 draw.fHasEndRect = hasEndRect; | |
526 } | |
527 | |
528 const GrVertexBuffer* vertexBuffer; | |
529 int firstVertex; | |
530 | |
531 size_t vertexStride = gp->getVertexStride(); | |
532 void* vertices = batchTarget->vertexPool()->makeSpace(vertexStride, | |
533 totalRectCount * k VertsPerDash, | |
534 &vertexBuffer, | |
535 &firstVertex); | |
536 | |
537 int curVIdx = 0; | |
538 int rectIndex = 0; | |
539 for (int i = 0; i < instanceCount; i++) { | |
540 Geometry& args = fGeoData[i]; | |
541 | |
542 if (!draws[i].fLineDone) { | |
543 if (fullDash) { | |
544 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args. fSrcRotInv, | |
545 draws[i].fStartOffset, draws[i].fDevBloat, | |
546 draws[i].fLineLength, draws[i].fHalfDevStr oke, | |
547 args.fIntervals[0], args.fIntervals[1], dr aws[i].fStrokeWidth, | |
548 capType, gp->getVertexStride()); | |
549 } else { | |
550 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); | |
551 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
552 setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRo tInv, verts); | |
553 } | |
554 curVIdx += 4; | |
555 } | |
556 rectIndex++; | |
557 | |
558 if (draws[i].fHasStartRect) { | |
559 if (fullDash) { | |
560 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args. fSrcRotInv, | |
561 draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0], | |
562 draws[i].fHalfDevStroke, args.fIntervals[0 ], | |
563 args.fIntervals[1], draws[i].fStrokeWidth, capType, | |
564 gp->getVertexStride()); | |
565 } else { | |
566 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); | |
567 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
568 setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRo tInv, verts); | |
569 } | |
570 | |
571 curVIdx += 4; | |
572 } | |
573 rectIndex++; | |
574 | |
575 if (draws[i].fHasEndRect) { | |
576 if (fullDash) { | |
577 setup_dashed_rect(rects[rectIndex], vertices, curVIdx, args. fSrcRotInv, | |
578 draws[i].fStartOffset, draws[i].fDevBloat, args.fIntervals[0], | |
579 draws[i].fHalfDevStroke, args.fIntervals[0 ], | |
580 args.fIntervals[1], draws[i].fStrokeWidth, capType, | |
581 gp->getVertexStride()); | |
582 } else { | |
583 SkPoint* verts = reinterpret_cast<SkPoint*>(vertices); | |
584 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
585 setup_dashed_rect_pos(rects[rectIndex], curVIdx, args.fSrcRo tInv, verts); | |
586 } | |
587 curVIdx += 4; | |
588 } | |
589 rectIndex++; | |
590 } | |
591 | |
592 const GrIndexBuffer* quadIndexBuffer = batchTarget->quadIndexBuffer(); | |
egdaniel
2015/02/27 15:13:18
rename from quad
| |
593 | |
594 GrDrawTarget::DrawInfo drawInfo; | |
595 drawInfo.setPrimitiveType(kTriangles_GrPrimitiveType); | |
596 drawInfo.setStartVertex(0); | |
597 drawInfo.setStartIndex(0); | |
598 drawInfo.setVerticesPerInstance(kVertsPerDash); | |
599 drawInfo.setIndicesPerInstance(kIndicesPerDash); | |
600 drawInfo.adjustStartVertex(firstVertex); | |
601 drawInfo.setVertexBuffer(vertexBuffer); | |
602 drawInfo.setIndexBuffer(quadIndexBuffer); | |
egdaniel
2015/02/27 15:13:18
quad
| |
603 | |
604 int maxInstancesPerDraw = quadIndexBuffer->maxQuads(); | |
egdaniel
2015/02/27 15:13:18
quads
| |
605 while (totalRectCount) { | |
606 drawInfo.setInstanceCount(SkTMin(totalRectCount, maxInstancesPerDraw )); | |
607 drawInfo.setVertexCount(drawInfo.instanceCount() * drawInfo.vertices PerInstance()); | |
608 drawInfo.setIndexCount(drawInfo.instanceCount() * drawInfo.indicesPe rInstance()); | |
609 | |
610 batchTarget->draw(drawInfo); | |
611 | |
612 drawInfo.setStartVertex(drawInfo.startVertex() + drawInfo.vertexCoun t()); | |
613 totalRectCount -= drawInfo.instanceCount(); | |
614 } | |
615 } | |
616 | |
617 SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } | |
618 | |
619 private: | |
620 DashBatch(const Geometry& geometry, SkPaint::Cap cap, bool useAA, bool fullD ash) { | |
621 this->initClassID<DashBatch>(); | |
622 fGeoData.push_back(geometry); | |
623 | |
624 fBatch.fUseAA = useAA; | |
625 fBatch.fCap = cap; | |
626 fBatch.fFullDash = fullDash; | |
627 } | |
628 | |
629 bool onCombineIfPossible(GrBatch* t) SK_OVERRIDE { | |
630 DashBatch* that = t->cast<DashBatch>(); | |
631 | |
632 if (this->useAA() != that->useAA()) { | |
633 return false; | |
634 } | |
635 | |
636 if (this->fullDash() != that->fullDash()) { | |
637 return false; | |
638 } | |
639 | |
640 if (this->cap() != that->cap()) { | |
641 return false; | |
642 } | |
643 | |
644 // TODO vertex color | |
645 if (this->color() != that->color()) { | |
646 return false; | |
647 } | |
648 | |
649 SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); | |
650 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->vi ewMatrix())) { | |
651 return false; | |
652 } | |
653 | |
654 fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()) ; | |
655 return true; | |
656 } | |
657 | |
658 GrColor color() const { return fBatch.fColor; } | |
659 bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } | |
660 const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } | |
661 bool useAA() const { return fBatch.fUseAA; } | |
662 bool fullDash() const { return fBatch.fFullDash; } | |
663 SkPaint::Cap cap() const { return fBatch.fCap; } | |
664 | |
665 struct BatchTracker { | |
666 GrColor fColor; | |
667 bool fUsesLocalCoords; | |
668 bool fColorIgnored; | |
669 bool fCoverageIgnored; | |
670 SkPaint::Cap fCap; | |
671 bool fUseAA; | |
672 bool fFullDash; | |
673 }; | |
674 | |
675 static const int kVertsPerDash = 4; | |
676 static const int kIndicesPerDash = 6; | |
677 | |
678 BatchTracker fBatch; | |
679 SkSTArray<1, Geometry, true> fGeoData; | |
680 }; | |
681 | |
682 | |
240 bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target, | 683 bool GrDashingEffect::DrawDashLine(GrGpu* gpu, GrDrawTarget* target, |
241 GrPipelineBuilder* pipelineBuilder, GrColor c olor, | 684 GrPipelineBuilder* pipelineBuilder, GrColor c olor, |
242 const SkMatrix& viewMatrix, const SkPoint pts [2], | 685 const SkMatrix& viewMatrix, const SkPoint pts [2], |
243 const GrPaint& paint, const GrStrokeInfo& str okeInfo) { | 686 const GrPaint& paint, const GrStrokeInfo& str okeInfo) { |
244 if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatr ix)) { | 687 if (!can_fast_path_dash(pts, strokeInfo, *target, *pipelineBuilder, viewMatr ix)) { |
245 return false; | 688 return false; |
246 } | 689 } |
247 | 690 |
248 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); | 691 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); |
249 | 692 |
250 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); | 693 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); |
251 | 694 |
252 SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); | 695 SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); |
253 | 696 |
254 // the phase should be normalized to be [0, sum of all intervals) | 697 // the phase should be normalized to be [0, sum of all intervals) |
255 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fInterv als[1]); | 698 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fInterv als[1]); |
256 | 699 |
257 SkScalar srcPhase = info.fPhase; | 700 //SkScalar srcPhase = info.fPhase; |
egdaniel
2015/02/27 15:13:18
remove line?
| |
258 | 701 |
259 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[ 1].fX | 702 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[ 1].fX |
260 SkMatrix srcRotInv; | 703 SkMatrix srcRotInv; |
261 SkPoint ptsRot[2]; | 704 SkPoint ptsRot[2]; |
262 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { | 705 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { |
263 SkMatrix rotMatrix; | 706 SkMatrix rotMatrix; |
264 align_to_x_axis(pts, &rotMatrix, ptsRot); | 707 align_to_x_axis(pts, &rotMatrix, ptsRot); |
265 if(!rotMatrix.invert(&srcRotInv)) { | 708 if(!rotMatrix.invert(&srcRotInv)) { |
266 SkDebugf("Failed to create invertible rotation matrix!\n"); | 709 SkDebugf("Failed to create invertible rotation matrix!\n"); |
267 return false; | 710 return false; |
268 } | 711 } |
269 } else { | 712 } else { |
270 srcRotInv.reset(); | 713 srcRotInv.reset(); |
271 memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); | 714 memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); |
272 } | 715 } |
273 | 716 |
274 bool useAA = paint.isAntiAlias(); | 717 // We have to do some throwaway work to determine if we need to fulldash |
275 | |
276 // Scale corrections of intervals and stroke from view matrix | |
277 SkScalar parallelScale; | 718 SkScalar parallelScale; |
278 SkScalar perpScale; | 719 SkScalar perpScale; |
279 calc_dash_scaling(¶llelScale, &perpScale, viewMatrix, ptsRot); | 720 calc_dash_scaling(¶llelScale, &perpScale, viewMatrix, ptsRot); |
280 | 721 |
281 bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth; | 722 SkScalar endInterval = info.fIntervals[1] * parallelScale; |
egdaniel
2015/02/27 15:13:18
can this be off interval? end interval doesn't rea
| |
282 | |
283 // We always want to at least stroke out half a pixel on each side in device space | |
284 // so 0.5f / perpScale gives us this min in src space | |
285 SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale ); | |
286 | |
287 SkScalar strokeAdj; | |
288 if (!hasCap) { | |
289 strokeAdj = 0.f; | |
290 } else { | |
291 strokeAdj = halfSrcStroke; | |
292 } | |
293 | |
294 SkScalar startAdj = 0; | |
295 | |
296 SkMatrix combinedMatrix = srcRotInv; | |
297 combinedMatrix.postConcat(viewMatrix); | |
298 | |
299 bool lineDone = false; | |
300 SkRect startRect; | |
301 bool hasStartRect = false; | |
302 // If we are using AA, check to see if we are drawing a partial dash at the start. If so | |
303 // draw it separately here and adjust our start point accordingly | |
304 if (useAA) { | |
305 if (srcPhase > 0 && srcPhase < info.fIntervals[0]) { | |
306 SkPoint startPts[2]; | |
307 startPts[0] = ptsRot[0]; | |
308 startPts[1].fY = startPts[0].fY; | |
309 startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - s rcPhase, | |
310 ptsRot[1].fX); | |
311 startRect.set(startPts, 2); | |
312 startRect.outset(strokeAdj, halfSrcStroke); | |
313 | |
314 hasStartRect = true; | |
315 startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; | |
316 } | |
317 } | |
318 | |
319 // adjustments for start and end of bounding rect so we only draw dash inter vals | |
320 // contained in the original line segment. | |
321 startAdj += calc_start_adjustment(info); | |
322 if (startAdj != 0) { | |
323 ptsRot[0].fX += startAdj; | |
324 srcPhase = 0; | |
325 } | |
326 SkScalar endingInterval = 0; | |
327 SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterva l); | |
328 ptsRot[1].fX -= endAdj; | |
329 if (ptsRot[0].fX >= ptsRot[1].fX) { | |
330 lineDone = true; | |
331 } | |
332 | |
333 SkRect endRect; | |
334 bool hasEndRect = false; | |
335 // If we are using AA, check to see if we are drawing a partial dash at then end. If so | |
336 // draw it separately here and adjust our end point accordingly | |
337 if (useAA && !lineDone) { | |
338 // If we adjusted the end then we will not be drawing a partial dash at the end. | |
339 // If we didn't adjust the end point then we just need to make sure the ending | |
340 // dash isn't a full dash | |
341 if (0 == endAdj && endingInterval != info.fIntervals[0]) { | |
342 SkPoint endPts[2]; | |
343 endPts[1] = ptsRot[1]; | |
344 endPts[0].fY = endPts[1].fY; | |
345 endPts[0].fX = endPts[1].fX - endingInterval; | |
346 | |
347 endRect.set(endPts, 2); | |
348 endRect.outset(strokeAdj, halfSrcStroke); | |
349 | |
350 hasEndRect = true; | |
351 endAdj = endingInterval + info.fIntervals[1]; | |
352 | |
353 ptsRot[1].fX -= endAdj; | |
354 if (ptsRot[0].fX >= ptsRot[1].fX) { | |
355 lineDone = true; | |
356 } | |
357 } | |
358 } | |
359 | |
360 if (startAdj != 0) { | |
361 srcPhase = 0; | |
362 } | |
363 | |
364 // Change the dashing info from src space into device space | |
365 SkScalar devIntervals[2]; | |
366 devIntervals[0] = info.fIntervals[0] * parallelScale; | |
367 devIntervals[1] = info.fIntervals[1] * parallelScale; | |
368 SkScalar devPhase = srcPhase * parallelScale; | |
369 SkScalar strokeWidth = srcStrokeWidth * perpScale; | 723 SkScalar strokeWidth = srcStrokeWidth * perpScale; |
370 | 724 |
371 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { | |
372 strokeWidth = 1.f; | |
373 } | |
374 | |
375 SkScalar halfDevStroke = strokeWidth * 0.5f; | |
376 | |
377 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { | 725 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { |
378 // add cap to on interveal and remove from off interval | 726 // add cap to on interveal and remove from off interval |
379 devIntervals[0] += strokeWidth; | 727 endInterval -= strokeWidth; |
380 devIntervals[1] -= strokeWidth; | 728 } |
381 } | 729 |
382 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; | 730 bool useAA = paint.isAntiAlias(); |
383 | 731 bool fullDash = endInterval > 0.f || useAA; |
egdaniel
2015/02/27 15:13:18
Add todo to do a real rect call in case where full
| |
384 SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; | 732 |
385 SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; | 733 DashBatch::Geometry geometry; |
386 | 734 geometry.fColor = color; |
387 SkScalar devBloat = useAA ? 0.5f : 0.f; | 735 geometry.fViewMatrix = viewMatrix; |
388 | 736 geometry.fSrcRotInv = srcRotInv; |
389 if (devIntervals[1] <= 0.f && useAA) { | 737 geometry.fPtsRot[0] = ptsRot[0]; |
390 // Case when we end up drawing a solid AA rect | 738 geometry.fPtsRot[1] = ptsRot[1]; |
391 // Reset the start rect to draw this single solid rect | 739 geometry.fSrcStrokeWidth = srcStrokeWidth; |
392 // but it requires to upload a new intervals uniform so we can mimic | 740 geometry.fPhase = info.fPhase; |
393 // one giant dash | 741 geometry.fIntervals[0] = info.fIntervals[0]; |
394 ptsRot[0].fX -= hasStartRect ? startAdj : 0; | 742 geometry.fIntervals[1] = info.fIntervals[1]; |
395 ptsRot[1].fX += hasEndRect ? endAdj : 0; | 743 //SkDEBUGCODE(fDevBounds;) |
egdaniel
2015/02/27 15:13:18
delete?
| |
396 startRect.set(ptsRot, 2); | 744 |
397 startRect.outset(strokeAdj, halfSrcStroke); | 745 SkAutoTUnref<GrBatch> batch(DashBatch::Create(geometry, cap, useAA, fullDash )); |
398 hasStartRect = true; | 746 target->drawBatch(pipelineBuilder, batch); |
399 hasEndRect = false; | 747 |
400 lineDone = true; | |
401 | |
402 SkPoint devicePts[2]; | |
403 viewMatrix.mapPoints(devicePts, ptsRot, 2); | |
404 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); | |
405 if (hasCap) { | |
406 lineLength += 2.f * halfDevStroke; | |
407 } | |
408 devIntervals[0] = lineLength; | |
409 } | |
410 | |
411 // reset to device coordinates | |
412 SkMatrix invert; | |
413 if (!viewMatrix.invert(&invert)) { | |
414 SkDebugf("Failed to invert\n"); | |
415 return false; | |
416 } | |
417 | |
418 bool isRoundCap = SkPaint::kRound_Cap == cap; | |
419 DashCap capType = isRoundCap ? kRound_DashCap : kNonRound_DashCap; | |
420 | |
421 SkAutoTUnref<const GrGeometryProcessor> gp; | |
422 bool fullDash = devIntervals[1] > 0.f || useAA; | |
423 if (fullDash) { | |
424 SkPathEffect::DashInfo devInfo; | |
425 devInfo.fPhase = devPhase; | |
426 devInfo.fCount = 2; | |
427 devInfo.fIntervals = devIntervals; | |
428 GrPrimitiveEdgeType edgeType = useAA ? kFillAA_GrProcessorEdgeType : | |
429 kFillBW_GrProcessorEdgeType; | |
430 gp.reset(create_dash_gp(color, edgeType, capType, invert)); | |
431 } else { | |
432 // Set up the vertex data for the line and start/end dashes | |
433 gp.reset(GrDefaultGeoProcFactory::Create(GrDefaultGeoProcFactory::kPosit ion_GPType, | |
434 color, | |
435 SkMatrix::I(), | |
436 invert)); | |
437 } | |
438 | |
439 int totalRectCnt = 0; | |
440 | |
441 totalRectCnt += !lineDone ? 1 : 0; | |
442 totalRectCnt += hasStartRect ? 1 : 0; | |
443 totalRectCnt += hasEndRect ? 1 : 0; | |
444 | |
445 GrDrawTarget::AutoReleaseGeometry geo(target, | |
446 totalRectCnt * 4, | |
447 gp->getVertexStride(), 0); | |
448 if (!geo.succeeded()) { | |
449 SkDebugf("Failed to get space for vertices!\n"); | |
450 return false; | |
451 } | |
452 | |
453 int curVIdx = 0; | |
454 | |
455 if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) { | |
456 // need to adjust this for round caps to correctly set the dashPos attri b on vertices | |
457 startOffset -= halfDevStroke; | |
458 } | |
459 | |
460 // Draw interior part of dashed line | |
461 if (!lineDone) { | |
462 SkPoint devicePts[2]; | |
463 viewMatrix.mapPoints(devicePts, ptsRot, 2); | |
464 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); | |
465 if (hasCap) { | |
466 lineLength += 2.f * halfDevStroke; | |
467 } | |
468 | |
469 SkRect bounds; | |
470 bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); | |
471 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); | |
472 if (fullDash) { | |
473 setup_dashed_rect(bounds, geo.vertices(), curVIdx, combinedMatrix, s tartOffset, | |
474 devBloat, lineLength, halfDevStroke, devIntervals[ 0], devIntervals[1], | |
475 strokeWidth, capType, gp->getVertexStride()); | |
476 } else { | |
477 SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); | |
478 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
479 setup_dashed_rect_pos(bounds, curVIdx, combinedMatrix, verts); | |
480 } | |
481 curVIdx += 4; | |
482 } | |
483 | |
484 if (hasStartRect) { | |
485 SkASSERT(useAA); // so that we know bloatX and bloatY have been set | |
486 startRect.outset(bloatX, bloatY); | |
487 if (fullDash) { | |
488 setup_dashed_rect(startRect, geo.vertices(), curVIdx, combinedMatrix , startOffset, | |
489 devBloat, devIntervals[0], halfDevStroke, devInter vals[0], | |
490 devIntervals[1], strokeWidth, capType, gp->getVert exStride()); | |
491 } else { | |
492 SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); | |
493 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
494 setup_dashed_rect_pos(startRect, curVIdx, combinedMatrix, verts); | |
495 } | |
496 | |
497 curVIdx += 4; | |
498 } | |
499 | |
500 if (hasEndRect) { | |
501 SkASSERT(useAA); // so that we know bloatX and bloatY have been set | |
502 endRect.outset(bloatX, bloatY); | |
503 if (fullDash) { | |
504 setup_dashed_rect(endRect, geo.vertices(), curVIdx, combinedMatrix, startOffset, | |
505 devBloat, devIntervals[0], halfDevStroke, devInter vals[0], | |
506 devIntervals[1], strokeWidth, capType, gp->getVert exStride()); | |
507 } else { | |
508 SkPoint* verts = reinterpret_cast<SkPoint*>(geo.vertices()); | |
509 SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); | |
510 setup_dashed_rect_pos(endRect, curVIdx, combinedMatrix, verts); | |
511 } | |
512 | |
513 } | |
514 | |
515 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); | |
516 target->drawIndexedInstances(pipelineBuilder, gp, kTriangles_GrPrimitiveType , totalRectCnt, | |
517 4, 6); | |
518 target->resetIndexSource(); | |
519 return true; | 748 return true; |
520 } | 749 } |
521 | 750 |
522 ////////////////////////////////////////////////////////////////////////////// | 751 ////////////////////////////////////////////////////////////////////////////// |
523 | 752 |
524 class GLDashingCircleEffect; | 753 class GLDashingCircleEffect; |
525 | 754 |
526 struct DashingCircleBatchTracker { | 755 struct DashingCircleBatchTracker { |
527 GrGPInput fInputColorType; | 756 GrGPInput fInputColorType; |
528 GrColor fColor; | 757 GrColor fColor; |
(...skipping 501 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1030 switch (cap) { | 1259 switch (cap) { |
1031 case kRound_DashCap: | 1260 case kRound_DashCap: |
1032 return DashingCircleEffect::Create(color, edgeType, localMatrix); | 1261 return DashingCircleEffect::Create(color, edgeType, localMatrix); |
1033 case kNonRound_DashCap: | 1262 case kNonRound_DashCap: |
1034 return DashingLineEffect::Create(color, edgeType, localMatrix); | 1263 return DashingLineEffect::Create(color, edgeType, localMatrix); |
1035 default: | 1264 default: |
1036 SkFAIL("Unexpected dashed cap."); | 1265 SkFAIL("Unexpected dashed cap."); |
1037 } | 1266 } |
1038 return NULL; | 1267 return NULL; |
1039 } | 1268 } |
OLD | NEW |