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