1 /* 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 10 #include "../GrAARectRenderer.h" 11 12 #include "GrGeometryProcessor.h" 13 #include "gl/builders/GrGLFullProgramBuilder.h" 14 #include "gl/GrGLProcessor.h" 15 #include "gl/GrGLGeometryProcessor.h" 16 #include "gl/GrGLSL.h" 17 #include "GrContext.h" 18 #include "GrCoordTransform.h" 19 #include "GrDrawTarget.h" 20 #include "GrDrawTargetCaps.h" 21 #include "GrProcessor.h" 22 #include "GrGpu.h" 23 #include "GrStrokeInfo.h" 24 #include "GrTBackendProcessorFactory.h" 25 #include "SkGr.h" 26 27 /////////////////////////////////////////////////////////////////////////////// 28 29 // Returns whether or not the gpu can fast path the dash line effect. 30 static bool can_fast_path_dash(const SkPoint pts[2], const GrStrokeInfo& strokeInfo, 31 const GrDrawTarget& target, const SkMatrix& viewMatrix) { 32 if (target.getDrawState().getRenderTarget()->isMultisampled()) { 33 return false; 34 } 35 36 // Pts must be either horizontal or vertical in src space 37 if (pts[0].fX != pts[1].fX && pts[0].fY != pts[1].fY) { 38 return false; 39 } 40 41 // May be able to relax this to include skew. As of now cannot do perspective 42 // because of the non uniform scaling of bloating a rect 43 if (!viewMatrix.preservesRightAngles()) { 44 return false; 45 } 46 47 if (!strokeInfo.isDashed() || 2 != strokeInfo.dashCount()) { 48 return false; 49 } 50 51 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); 52 if (0 == info.fIntervals[0] && 0 == info.fIntervals[1]) { 53 return false; 54 } 55 56 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); 57 // Current we do don't handle Round or Square cap dashes 58 if (SkPaint::kRound_Cap == cap && info.fIntervals[0] != 0.f) { 59 return false; 60 } 61 62 return true; 63 } 64 65 namespace { 66 67 struct DashLineVertex { 68 SkPoint fPos; 69 SkPoint fDashPos; 70 }; 71 72 extern const GrVertexAttrib gDashLineNoAAVertexAttribs[] = { 73 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding } 74 }; 75 76 extern const GrVertexAttrib gDashLineVertexAttribs[] = { 77 { kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding }, 78 { kVec2f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding }, 79 }; 80 81 }; 82 static void calc_dash_scaling(SkScalar* parallelScale, SkScalar* perpScale, 83 const SkMatrix& viewMatrix, const SkPoint pts[2]) { 84 SkVector vecSrc = pts[1] - pts[0]; 85 SkScalar magSrc = vecSrc.length(); 86 SkScalar invSrc = magSrc ? SkScalarInvert(magSrc) : 0; 87 vecSrc.scale(invSrc); 88 89 SkVector vecSrcPerp; 90 vecSrc.rotateCW(&vecSrcPerp); 91 viewMatrix.mapVectors(&vecSrc, 1); 92 viewMatrix.mapVectors(&vecSrcPerp, 1); 93 94 // parallelScale tells how much to scale along the line parallel to the dash line 95 // perpScale tells how much to scale in the direction perpendicular to the dash line 96 *parallelScale = vecSrc.length(); 97 *perpScale = vecSrcPerp.length(); 98 } 99 100 // calculates the rotation needed to aligned pts to the x axis with pts[0] < pts[1] 101 // Stores the rotation matrix in rotMatrix, and the mapped points in ptsRot 102 static void align_to_x_axis(const SkPoint pts[2], SkMatrix* rotMatrix, SkPoint ptsRot[2] = NULL) { 103 SkVector vec = pts[1] - pts[0]; 104 SkScalar mag = vec.length(); 105 SkScalar inv = mag ? SkScalarInvert(mag) : 0; 106 107 vec.scale(inv); 108 rotMatrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); 109 if (ptsRot) { 110 rotMatrix->mapPoints(ptsRot, pts, 2); 111 // correction for numerical issues if map doesn't make ptsRot exactly horizontal 112 ptsRot[1].fY = pts[0].fY; 113 } 114 } 115 116 // Assumes phase < sum of all intervals 117 static SkScalar calc_start_adjustment(const SkPathEffect::DashInfo& info) { 118 SkASSERT(info.fPhase < info.fIntervals[0] + info.fIntervals[1]); 119 if (info.fPhase >= info.fIntervals[0] && info.fPhase != 0) { 120 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; 121 return srcIntervalLen - info.fPhase; 122 } 123 return 0; 124 } 125 126 static SkScalar calc_end_adjustment(const SkPathEffect::DashInfo& info, const SkPoint pts[2], 127 SkScalar phase, SkScalar* endingInt) { 128 if (pts[1].fX <= pts[0].fX) { 129 return 0; 130 } 131 SkScalar srcIntervalLen = info.fIntervals[0] + info.fIntervals[1]; 132 SkScalar totalLen = pts[1].fX - pts[0].fX; 133 SkScalar temp = SkScalarDiv(totalLen, srcIntervalLen); 134 SkScalar numFullIntervals = SkScalarFloorToScalar(temp); 135 *endingInt = totalLen - numFullIntervals * srcIntervalLen + phase; 136 temp = SkScalarDiv(*endingInt, srcIntervalLen); 137 *endingInt = *endingInt - SkScalarFloorToScalar(temp) * srcIntervalLen; 138 if (0 == *endingInt) { 139 *endingInt = srcIntervalLen; 140 } 141 if (*endingInt > info.fIntervals[0]) { 142 if (0 == info.fIntervals[0]) { 143 *endingInt -= 0.01f; // make sure we capture the last zero size pnt (used if has caps) 144 } 145 return *endingInt - info.fIntervals[0]; 146 } 147 return 0; 148 } 149 150 static void setup_dashed_rect(const SkRect& rect, DashLineVertex* verts, int idx, const SkMatrix& matrix, 151 SkScalar offset, SkScalar bloat, SkScalar len, SkScalar stroke) { 152 153 SkScalar startDashX = offset - bloat; 154 SkScalar endDashX = offset + len + bloat; 155 SkScalar startDashY = -stroke - bloat; 156 SkScalar endDashY = stroke + bloat; 157 verts[idx].fDashPos = SkPoint::Make(startDashX , startDashY); 158 verts[idx + 1].fDashPos = SkPoint::Make(startDashX, endDashY); 159 verts[idx + 2].fDashPos = SkPoint::Make(endDashX, endDashY); 160 verts[idx + 3].fDashPos = SkPoint::Make(endDashX, startDashY); 161 162 verts[idx].fPos = SkPoint::Make(rect.fLeft, rect.fTop); 163 verts[idx + 1].fPos = SkPoint::Make(rect.fLeft, rect.fBottom); 164 verts[idx + 2].fPos = SkPoint::Make(rect.fRight, rect.fBottom); 165 verts[idx + 3].fPos = SkPoint::Make(rect.fRight, rect.fTop); 166 167 matrix.mapPointsWithStride(&verts[idx].fPos, sizeof(DashLineVertex), 4); 168 } 169 170 171 bool GrDashingEffect::DrawDashLine(const SkPoint pts[2], const GrPaint& paint, 172 const GrStrokeInfo& strokeInfo, GrGpu* gpu, 173 GrDrawTarget* target, const SkMatrix& vm) { 174 175 if (!can_fast_path_dash(pts, strokeInfo, *target, vm)) { 176 return false; 177 } 178 179 const SkPathEffect::DashInfo& info = strokeInfo.getDashInfo(); 180 181 SkPaint::Cap cap = strokeInfo.getStrokeRec().getCap(); 182 183 SkScalar srcStrokeWidth = strokeInfo.getStrokeRec().getWidth(); 184 185 // the phase should be normalized to be [0, sum of all intervals) 186 SkASSERT(info.fPhase >= 0 && info.fPhase < info.fIntervals[0] + info.fIntervals[1]); 187 188 SkScalar srcPhase = info.fPhase; 189 190 // Rotate the src pts so they are aligned horizontally with pts[0].fX < pts[1].fX 191 SkMatrix srcRotInv; 192 SkPoint ptsRot[2]; 193 if (pts[0].fY != pts[1].fY || pts[0].fX > pts[1].fX) { 194 SkMatrix rotMatrix; 195 align_to_x_axis(pts, &rotMatrix, ptsRot); 196 if(!rotMatrix.invert(&srcRotInv)) { 197 GrPrintf("Failed to create invertible rotation matrix!\n"); 198 return false; 199 } 200 } else { 201 srcRotInv.reset(); 202 memcpy(ptsRot, pts, 2 * sizeof(SkPoint)); 203 } 204 205 bool useAA = paint.isAntiAlias(); 206 207 // Scale corrections of intervals and stroke from view matrix 208 SkScalar parallelScale; 209 SkScalar perpScale; 210 calc_dash_scaling(¶llelScale, &perpScale, vm, ptsRot); 211 212 bool hasCap = SkPaint::kButt_Cap != cap && 0 != srcStrokeWidth; 213 214 // We always want to at least stroke out half a pixel on each side in device space 215 // so 0.5f / perpScale gives us this min in src space 216 SkScalar halfSrcStroke = SkMaxScalar(srcStrokeWidth * 0.5f, 0.5f / perpScale); 217 218 SkScalar strokeAdj; 219 if (!hasCap) { 220 strokeAdj = 0.f; 221 } else { 222 strokeAdj = halfSrcStroke; 223 } 224 225 SkScalar startAdj = 0; 226 227 SkMatrix combinedMatrix = srcRotInv; 228 combinedMatrix.postConcat(vm); 229 230 bool lineDone = false; 231 SkRect startRect; 232 bool hasStartRect = false; 233 // If we are using AA, check to see if we are drawing a partial dash at the start. If so 234 // draw it separately here and adjust our start point accordingly 235 if (useAA) { 236 if (srcPhase > 0 && srcPhase < info.fIntervals[0]) { 237 SkPoint startPts[2]; 238 startPts[0] = ptsRot[0]; 239 startPts[1].fY = startPts[0].fY; 240 startPts[1].fX = SkMinScalar(startPts[0].fX + info.fIntervals[0] - srcPhase, 241 ptsRot[1].fX); 242 startRect.set(startPts, 2); 243 startRect.outset(strokeAdj, halfSrcStroke); 244 245 hasStartRect = true; 246 startAdj = info.fIntervals[0] + info.fIntervals[1] - srcPhase; 247 } 248 } 249 250 // adjustments for start and end of bounding rect so we only draw dash intervals 251 // contained in the original line segment. 252 startAdj += calc_start_adjustment(info); 253 if (startAdj != 0) { 254 ptsRot[0].fX += startAdj; 255 srcPhase = 0; 256 } 257 SkScalar endingInterval = 0; 258 SkScalar endAdj = calc_end_adjustment(info, ptsRot, srcPhase, &endingInterval); 259 ptsRot[1].fX -= endAdj; 260 if (ptsRot[0].fX >= ptsRot[1].fX) { 261 lineDone = true; 262 } 263 264 SkRect endRect; 265 bool hasEndRect = false; 266 // If we are using AA, check to see if we are drawing a partial dash at then end. If so 267 // draw it separately here and adjust our end point accordingly 268 if (useAA && !lineDone) { 269 // If we adjusted the end then we will not be drawing a partial dash at the end. 270 // If we didn't adjust the end point then we just need to make sure the ending 271 // dash isn't a full dash 272 if (0 == endAdj && endingInterval != info.fIntervals[0]) { 273 SkPoint endPts[2]; 274 endPts[1] = ptsRot[1]; 275 endPts[0].fY = endPts[1].fY; 276 endPts[0].fX = endPts[1].fX - endingInterval; 277 278 endRect.set(endPts, 2); 279 endRect.outset(strokeAdj, halfSrcStroke); 280 281 hasEndRect = true; 282 endAdj = endingInterval + info.fIntervals[1]; 283 284 ptsRot[1].fX -= endAdj; 285 if (ptsRot[0].fX >= ptsRot[1].fX) { 286 lineDone = true; 287 } 288 } 289 } 290 291 if (startAdj != 0) { 292 srcPhase = 0; 293 } 294 295 // Change the dashing info from src space into device space 296 SkScalar devIntervals[2]; 297 devIntervals[0] = info.fIntervals[0] * parallelScale; 298 devIntervals[1] = info.fIntervals[1] * parallelScale; 299 SkScalar devPhase = srcPhase * parallelScale; 300 SkScalar strokeWidth = srcStrokeWidth * perpScale; 301 302 if ((strokeWidth < 1.f && !useAA) || 0.f == strokeWidth) { 303 strokeWidth = 1.f; 304 } 305 306 SkScalar halfDevStroke = strokeWidth * 0.5f; 307 308 if (SkPaint::kSquare_Cap == cap && 0 != srcStrokeWidth) { 309 // add cap to on interveal and remove from off interval 310 devIntervals[0] += strokeWidth; 311 devIntervals[1] -= strokeWidth; 312 } 313 SkScalar startOffset = devIntervals[1] * 0.5f + devPhase; 314 315 SkScalar bloatX = useAA ? 0.5f / parallelScale : 0.f; 316 SkScalar bloatY = useAA ? 0.5f / perpScale : 0.f; 317 318 SkScalar devBloat = useAA ? 0.5f : 0.f; 319 320 GrDrawState* drawState = target->drawState(); 321 if (devIntervals[1] <= 0.f && useAA) { 322 // Case when we end up drawing a solid AA rect 323 // Reset the start rect to draw this single solid rect 324 // but it requires to upload a new intervals uniform so we can mimic 325 // one giant dash 326 ptsRot[0].fX -= hasStartRect ? startAdj : 0; 327 ptsRot[1].fX += hasEndRect ? endAdj : 0; 328 startRect.set(ptsRot, 2); 329 startRect.outset(strokeAdj, halfSrcStroke); 330 hasStartRect = true; 331 hasEndRect = false; 332 lineDone = true; 333 334 SkPoint devicePts[2]; 335 vm.mapPoints(devicePts, ptsRot, 2); 336 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); 337 if (hasCap) { 338 lineLength += 2.f * halfDevStroke; 339 } 340 devIntervals[0] = lineLength; 341 } 342 if (devIntervals[1] > 0.f || useAA) { 343 SkPathEffect::DashInfo devInfo; 344 devInfo.fPhase = devPhase; 345 devInfo.fCount = 2; 346 devInfo.fIntervals = devIntervals; 347 GrPrimitiveEdgeType edgeType= useAA ? kFillAA_GrProcessorEdgeType : 348 kFillBW_GrProcessorEdgeType; 349 bool isRoundCap = SkPaint::kRound_Cap == cap; 350 GrDashingEffect::DashCap capType = isRoundCap ? GrDashingEffect::kRound_DashCap : 351 GrDashingEffect::kNonRound_DashCap; 352 drawState->setGeometryProcessor( 353 GrDashingEffect::Create(edgeType, devInfo, strokeWidth, capType))->unref(); 354 355 // Set up the vertex data for the line and start/end dashes 356 drawState->setVertexAttribs<gDashLineVertexAttribs>(SK_ARRAY_COUNT(gDashLineVertexAttribs), 357 sizeof(DashLineVertex)); 358 } else { 359 // Set up the vertex data for the line and start/end dashes 360 drawState->setVertexAttribs<gDashLineNoAAVertexAttribs>( 361 SK_ARRAY_COUNT(gDashLineNoAAVertexAttribs), sizeof(DashLineVertex)); 362 } 363 364 int totalRectCnt = 0; 365 366 totalRectCnt += !lineDone ? 1 : 0; 367 totalRectCnt += hasStartRect ? 1 : 0; 368 totalRectCnt += hasEndRect ? 1 : 0; 369 370 GrDrawTarget::AutoReleaseGeometry geo(target, totalRectCnt * 4, 0); 371 if (!geo.succeeded()) { 372 GrPrintf("Failed to get space for vertices!\n"); 373 return false; 374 } 375 376 DashLineVertex* verts = reinterpret_cast<DashLineVertex*>(geo.vertices()); 377 378 int curVIdx = 0; 379 380 if (SkPaint::kRound_Cap == cap && 0 != srcStrokeWidth) { 381 // need to adjust this for round caps to correctly set the dashPos attrib on vertices 382 startOffset -= halfDevStroke; 383 } 384 385 // Draw interior part of dashed line 386 if (!lineDone) { 387 SkPoint devicePts[2]; 388 vm.mapPoints(devicePts, ptsRot, 2); 389 SkScalar lineLength = SkPoint::Distance(devicePts[0], devicePts[1]); 390 if (hasCap) { 391 lineLength += 2.f * halfDevStroke; 392 } 393 394 SkRect bounds; 395 bounds.set(ptsRot[0].fX, ptsRot[0].fY, ptsRot[1].fX, ptsRot[1].fY); 396 bounds.outset(bloatX + strokeAdj, bloatY + halfSrcStroke); 397 setup_dashed_rect(bounds, verts, curVIdx, combinedMatrix, startOffset, devBloat, 398 lineLength, halfDevStroke); 399 curVIdx += 4; 400 } 401 402 if (hasStartRect) { 403 SkASSERT(useAA); // so that we know bloatX and bloatY have been set 404 startRect.outset(bloatX, bloatY); 405 setup_dashed_rect(startRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, 406 devIntervals[0], halfDevStroke); 407 curVIdx += 4; 408 } 409 410 if (hasEndRect) { 411 SkASSERT(useAA); // so that we know bloatX and bloatY have been set 412 endRect.outset(bloatX, bloatY); 413 setup_dashed_rect(endRect, verts, curVIdx, combinedMatrix, startOffset, devBloat, 414 devIntervals[0], halfDevStroke); 415 } 416 417 target->setIndexSourceToBuffer(gpu->getContext()->getQuadIndexBuffer()); 418 target->drawIndexedInstances(kTriangles_GrPrimitiveType, totalRectCnt, 4, 6); 419 target->resetIndexSource(); 420 return true; 421 } 422 423 ////////////////////////////////////////////////////////////////////////////// 424 425 class GLDashingCircleEffect; 426 /* 427 * This effect will draw a dotted line (defined as a dashed lined with round caps and no on 428 * interval). The radius of the dots is given by the strokeWidth and the spacing by the DashInfo. 429 * Both of the previous two parameters are in device space. This effect also requires the setting of 430 * a vec2 vertex attribute for the the four corners of the bounding rect. This attribute is the 431 * "dash position" of each vertex. In other words it is the vertex coords (in device space) if we 432 * transform the line to be horizontal, with the start of line at the origin then shifted to the 433 * right by half the off interval. The line then goes in the positive x direction. 434 */ 435 class DashingCircleEffect : public GrGeometryProcessor { 436 public: 437 typedef SkPathEffect::DashInfo DashInfo; 438 439 static GrGeometryProcessor* Create(GrPrimitiveEdgeType edgeType, 440 const DashInfo& info, 441 SkScalar radius); 442 443 virtual ~DashingCircleEffect(); 444 445 static const char* Name() { return "DashingCircleEffect"; } 446 447 const GrShaderVar& inCoord() const { return fInCoord; } 448 449 GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; } 450 451 SkScalar getRadius() const { return fRadius; } 452 453 SkScalar getCenterX() const { return fCenterX; } 454 455 SkScalar getIntervalLength() const { return fIntervalLength; } 456 457 typedef GLDashingCircleEffect GLProcessor; 458 459 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 460 461 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE; 462 463 private: 464 DashingCircleEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, SkScalar radius); 465 466 virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE; 467 468 GrPrimitiveEdgeType fEdgeType; 469 const GrShaderVar& fInCoord; 470 SkScalar fIntervalLength; 471 SkScalar fRadius; 472 SkScalar fCenterX; 473 474 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 475 476 typedef GrGeometryProcessor INHERITED; 477 }; 478 479 ////////////////////////////////////////////////////////////////////////////// 480 481 class GLDashingCircleEffect : public GrGLGeometryProcessor { 482 public: 483 GLDashingCircleEffect(const GrBackendProcessorFactory&, const GrProcessor&); 484 485 virtual void emitCode(GrGLFullProgramBuilder* builder, 486 const GrGeometryProcessor& geometryProcessor, 487 const GrProcessorKey& key, 488 const char* outputColor, 489 const char* inputColor, 490 const TransformedCoordsArray&, 491 const TextureSamplerArray&) SK_OVERRIDE; 492 493 static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*); 494 495 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE; 496 497 private: 498 GrGLProgramDataManager::UniformHandle fParamUniform; 499 SkScalar fPrevRadius; 500 SkScalar fPrevCenterX; 501 SkScalar fPrevIntervalLength; 502 typedef GrGLGeometryProcessor INHERITED; 503 }; 504 505 GLDashingCircleEffect::GLDashingCircleEffect(const GrBackendProcessorFactory& factory, 506 const GrProcessor&) 507 : INHERITED (factory) { 508 fPrevRadius = SK_ScalarMin; 509 fPrevCenterX = SK_ScalarMin; 510 fPrevIntervalLength = SK_ScalarMax; 511 } 512 513 void GLDashingCircleEffect::emitCode(GrGLFullProgramBuilder* builder, 514 const GrGeometryProcessor& geometryProcessor, 515 const GrProcessorKey& key, 516 const char* outputColor, 517 const char* inputColor, 518 const TransformedCoordsArray&, 519 const TextureSamplerArray& samplers) { 520 const DashingCircleEffect& dce = geometryProcessor.cast<DashingCircleEffect>(); 521 const char *paramName; 522 // The param uniforms, xyz, refer to circle radius - 0.5, cicles center x coord, and 523 // the total interval length of the dash. 524 fParamUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, 525 kVec3f_GrSLType, 526 "params", 527 ¶mName); 528 529 const char *vsCoordName, *fsCoordName; 530 builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName); 531 532 GrGLVertexShaderBuilder* vsBuilder = builder->getVertexShaderBuilder(); 533 vsBuilder->codeAppendf("\t%s = %s;\n", vsCoordName, dce.inCoord().c_str()); 534 535 // transforms all points so that we can compare them to our test circle 536 GrGLProcessorFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 537 fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s.z) * %s.z;\n", 538 fsCoordName, fsCoordName, paramName, paramName); 539 fsBuilder->codeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName); 540 fsBuilder->codeAppendf("\t\tvec2 center = vec2(%s.y, 0.0);\n", paramName); 541 fsBuilder->codeAppend("\t\tfloat dist = length(center - fragPosShifted);\n"); 542 if (GrProcessorEdgeTypeIsAA(dce.getEdgeType())) { 543 fsBuilder->codeAppendf("\t\tfloat diff = dist - %s.x;\n", paramName); 544 fsBuilder->codeAppend("\t\tdiff = 1.0 - diff;\n"); 545 fsBuilder->codeAppend("\t\tfloat alpha = clamp(diff, 0.0, 1.0);\n"); 546 } else { 547 fsBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n"); 548 fsBuilder->codeAppendf("\t\talpha *= dist < %s.x + 0.5 ? 1.0 : 0.0;\n", paramName); 549 } 550 fsBuilder->codeAppendf("\t\t%s = %s;\n", outputColor, 551 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); 552 } 553 554 void GLDashingCircleEffect::setData(const GrGLProgramDataManager& pdman 555 , const GrProcessor& processor) { 556 const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>(); 557 SkScalar radius = dce.getRadius(); 558 SkScalar centerX = dce.getCenterX(); 559 SkScalar intervalLength = dce.getIntervalLength(); 560 if (radius != fPrevRadius || centerX != fPrevCenterX || intervalLength != fPrevIntervalLength) { 561 pdman.set3f(fParamUniform, radius - 0.5f, centerX, intervalLength); 562 fPrevRadius = radius; 563 fPrevCenterX = centerX; 564 fPrevIntervalLength = intervalLength; 565 } 566 } 567 568 void GLDashingCircleEffect::GenKey(const GrProcessor& processor, const GrGLCaps&, 569 GrProcessorKeyBuilder* b) { 570 const DashingCircleEffect& dce = processor.cast<DashingCircleEffect>(); 571 b->add32(dce.getEdgeType()); 572 } 573 574 ////////////////////////////////////////////////////////////////////////////// 575 576 GrGeometryProcessor* DashingCircleEffect::Create(GrPrimitiveEdgeType edgeType, const DashInfo& info, 577 SkScalar radius) { 578 if (info.fCount != 2 || info.fIntervals[0] != 0) { 579 return NULL; 580 } 581 582 return SkNEW_ARGS(DashingCircleEffect, (edgeType, info, radius)); 583 } 584 585 DashingCircleEffect::~DashingCircleEffect() {} 586 587 void DashingCircleEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 588 *validFlags = 0; 589 } 590 591 const GrBackendGeometryProcessorFactory& DashingCircleEffect::getFactory() const { 592 return GrTBackendGeometryProcessorFactory<DashingCircleEffect>::getInstance(); 593 } 594 595 DashingCircleEffect::DashingCircleEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, 596 SkScalar radius) 597 : fEdgeType(edgeType) 598 , fInCoord(this->addVertexAttrib(GrShaderVar("inCoord", 599 kVec2f_GrSLType, 600 GrShaderVar::kAttribute_TypeModifier))) { 601 SkScalar onLen = info.fIntervals[0]; 602 SkScalar offLen = info.fIntervals[1]; 603 fIntervalLength = onLen + offLen; 604 fRadius = radius; 605 fCenterX = SkScalarHalf(offLen); 606 } 607 608 bool DashingCircleEffect::onIsEqual(const GrProcessor& other) const { 609 const DashingCircleEffect& dce = other.cast<DashingCircleEffect>(); 610 return (fEdgeType == dce.fEdgeType && 611 fIntervalLength == dce.fIntervalLength && 612 fRadius == dce.fRadius && 613 fCenterX == dce.fCenterX); 614 } 615 616 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingCircleEffect); 617 618 GrGeometryProcessor* DashingCircleEffect::TestCreate(SkRandom* random, 619 GrContext*, 620 const GrDrawTargetCaps& caps, 621 GrTexture*[]) { 622 GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan( 623 kGrProcessorEdgeTypeCnt)); 624 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); 625 DashInfo info; 626 info.fCount = 2; 627 SkAutoTArray<SkScalar> intervals(info.fCount); 628 info.fIntervals = intervals.get(); 629 info.fIntervals[0] = 0; 630 info.fIntervals[1] = random->nextRangeScalar(0, 10.f); 631 info.fPhase = random->nextRangeScalar(0, info.fIntervals[1]); 632 633 return DashingCircleEffect::Create(edgeType, info, strokeWidth); 634 } 635 636 ////////////////////////////////////////////////////////////////////////////// 637 638 class GLDashingLineEffect; 639 640 /* 641 * This effect will draw a dashed line. The width of the dash is given by the strokeWidth and the 642 * length and spacing by the DashInfo. Both of the previous two parameters are in device space. 643 * This effect also requires the setting of a vec2 vertex attribute for the the four corners of the 644 * bounding rect. This attribute is the "dash position" of each vertex. In other words it is the 645 * vertex coords (in device space) if we transform the line to be horizontal, with the start of 646 * line at the origin then shifted to the right by half the off interval. The line then goes in the 647 * positive x direction. 648 */ 649 class DashingLineEffect : public GrGeometryProcessor { 650 public: 651 typedef SkPathEffect::DashInfo DashInfo; 652 653 static GrGeometryProcessor* Create(GrPrimitiveEdgeType edgeType, 654 const DashInfo& info, 655 SkScalar strokeWidth); 656 657 virtual ~DashingLineEffect(); 658 659 static const char* Name() { return "DashingEffect"; } 660 661 const GrShaderVar& inCoord() const { return fInCoord; } 662 663 GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; } 664 665 const SkRect& getRect() const { return fRect; } 666 667 SkScalar getIntervalLength() const { return fIntervalLength; } 668 669 typedef GLDashingLineEffect GLProcessor; 670 671 virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE; 672 673 virtual const GrBackendGeometryProcessorFactory& getFactory() const SK_OVERRIDE; 674 675 private: 676 DashingLineEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, SkScalar strokeWidth); 677 678 virtual bool onIsEqual(const GrProcessor& other) const SK_OVERRIDE; 679 680 GrPrimitiveEdgeType fEdgeType; 681 const GrShaderVar& fInCoord; 682 SkRect fRect; 683 SkScalar fIntervalLength; 684 685 GR_DECLARE_GEOMETRY_PROCESSOR_TEST; 686 687 typedef GrGeometryProcessor INHERITED; 688 }; 689 690 ////////////////////////////////////////////////////////////////////////////// 691 692 class GLDashingLineEffect : public GrGLGeometryProcessor { 693 public: 694 GLDashingLineEffect(const GrBackendProcessorFactory&, const GrProcessor&); 695 696 virtual void emitCode(GrGLFullProgramBuilder* builder, 697 const GrGeometryProcessor& geometryProcessor, 698 const GrProcessorKey& key, 699 const char* outputColor, 700 const char* inputColor, 701 const TransformedCoordsArray&, 702 const TextureSamplerArray&) SK_OVERRIDE; 703 704 static inline void GenKey(const GrProcessor&, const GrGLCaps&, GrProcessorKeyBuilder*); 705 706 virtual void setData(const GrGLProgramDataManager&, const GrProcessor&) SK_OVERRIDE; 707 708 private: 709 GrGLProgramDataManager::UniformHandle fRectUniform; 710 GrGLProgramDataManager::UniformHandle fIntervalUniform; 711 SkRect fPrevRect; 712 SkScalar fPrevIntervalLength; 713 typedef GrGLGeometryProcessor INHERITED; 714 }; 715 716 GLDashingLineEffect::GLDashingLineEffect(const GrBackendProcessorFactory& factory, 717 const GrProcessor&) 718 : INHERITED (factory) { 719 fPrevRect.fLeft = SK_ScalarNaN; 720 fPrevIntervalLength = SK_ScalarMax; 721 } 722 723 void GLDashingLineEffect::emitCode(GrGLFullProgramBuilder* builder, 724 const GrGeometryProcessor& geometryProcessor, 725 const GrProcessorKey& key, 726 const char* outputColor, 727 const char* inputColor, 728 const TransformedCoordsArray&, 729 const TextureSamplerArray& samplers) { 730 const DashingLineEffect& de = geometryProcessor.cast<DashingLineEffect>(); 731 const char *rectName; 732 // The rect uniform's xyzw refer to (left + 0.5, top + 0.5, right - 0.5, bottom - 0.5), 733 // respectively. 734 fRectUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, 735 kVec4f_GrSLType, 736 "rect", 737 &rectName); 738 const char *intervalName; 739 // The interval uniform's refers to the total length of the interval (on + off) 740 fIntervalUniform = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility, 741 kFloat_GrSLType, 742 "interval", 743 &intervalName); 744 745 const char *vsCoordName, *fsCoordName; 746 builder->addVarying(kVec2f_GrSLType, "Coord", &vsCoordName, &fsCoordName); 747 GrGLVertexShaderBuilder* vsBuilder = builder->getVertexShaderBuilder(); 748 vsBuilder->codeAppendf("\t%s = %s;\n", vsCoordName, de.inCoord().c_str()); 749 750 // transforms all points so that we can compare them to our test rect 751 GrGLProcessorFragmentShaderBuilder* fsBuilder = builder->getFragmentShaderBuilder(); 752 fsBuilder->codeAppendf("\t\tfloat xShifted = %s.x - floor(%s.x / %s) * %s;\n", 753 fsCoordName, fsCoordName, intervalName, intervalName); 754 fsBuilder->codeAppendf("\t\tvec2 fragPosShifted = vec2(xShifted, %s.y);\n", fsCoordName); 755 if (GrProcessorEdgeTypeIsAA(de.getEdgeType())) { 756 // The amount of coverage removed in x and y by the edges is computed as a pair of negative 757 // numbers, xSub and ySub. 758 fsBuilder->codeAppend("\t\tfloat xSub, ySub;\n"); 759 fsBuilder->codeAppendf("\t\txSub = min(fragPosShifted.x - %s.x, 0.0);\n", rectName); 760 fsBuilder->codeAppendf("\t\txSub += min(%s.z - fragPosShifted.x, 0.0);\n", rectName); 761 fsBuilder->codeAppendf("\t\tySub = min(fragPosShifted.y - %s.y, 0.0);\n", rectName); 762 fsBuilder->codeAppendf("\t\tySub += min(%s.w - fragPosShifted.y, 0.0);\n", rectName); 763 // Now compute coverage in x and y and multiply them to get the fraction of the pixel 764 // covered. 765 fsBuilder->codeAppendf("\t\tfloat alpha = (1.0 + max(xSub, -1.0)) * (1.0 + max(ySub, -1.0));\n"); 766 } else { 767 // Assuming the bounding geometry is tight so no need to check y values 768 fsBuilder->codeAppendf("\t\tfloat alpha = 1.0;\n"); 769 fsBuilder->codeAppendf("\t\talpha *= (fragPosShifted.x - %s.x) > -0.5 ? 1.0 : 0.0;\n", rectName); 770 fsBuilder->codeAppendf("\t\talpha *= (%s.z - fragPosShifted.x) >= -0.5 ? 1.0 : 0.0;\n", rectName); 771 } 772 fsBuilder->codeAppendf("\t\t%s = %s;\n", outputColor, 773 (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str()); 774 } 775 776 void GLDashingLineEffect::setData(const GrGLProgramDataManager& pdman, 777 const GrProcessor& processor) { 778 const DashingLineEffect& de = processor.cast<DashingLineEffect>(); 779 const SkRect& rect = de.getRect(); 780 SkScalar intervalLength = de.getIntervalLength(); 781 if (rect != fPrevRect || intervalLength != fPrevIntervalLength) { 782 pdman.set4f(fRectUniform, rect.fLeft + 0.5f, rect.fTop + 0.5f, 783 rect.fRight - 0.5f, rect.fBottom - 0.5f); 784 pdman.set1f(fIntervalUniform, intervalLength); 785 fPrevRect = rect; 786 fPrevIntervalLength = intervalLength; 787 } 788 } 789 790 void GLDashingLineEffect::GenKey(const GrProcessor& processor, const GrGLCaps&, 791 GrProcessorKeyBuilder* b) { 792 const DashingLineEffect& de = processor.cast<DashingLineEffect>(); 793 b->add32(de.getEdgeType()); 794 } 795 796 ////////////////////////////////////////////////////////////////////////////// 797 798 GrGeometryProcessor* DashingLineEffect::Create(GrPrimitiveEdgeType edgeType, 799 const DashInfo& info, 800 SkScalar strokeWidth) { 801 if (info.fCount != 2) { 802 return NULL; 803 } 804 805 return SkNEW_ARGS(DashingLineEffect, (edgeType, info, strokeWidth)); 806 } 807 808 DashingLineEffect::~DashingLineEffect() {} 809 810 void DashingLineEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const { 811 *validFlags = 0; 812 } 813 814 const GrBackendGeometryProcessorFactory& DashingLineEffect::getFactory() const { 815 return GrTBackendGeometryProcessorFactory<DashingLineEffect>::getInstance(); 816 } 817 818 DashingLineEffect::DashingLineEffect(GrPrimitiveEdgeType edgeType, const DashInfo& info, 819 SkScalar strokeWidth) 820 : fEdgeType(edgeType) 821 , fInCoord(this->addVertexAttrib(GrShaderVar("inCoord", 822 kVec2f_GrSLType, 823 GrShaderVar::kAttribute_TypeModifier))) { 824 SkScalar onLen = info.fIntervals[0]; 825 SkScalar offLen = info.fIntervals[1]; 826 SkScalar halfOffLen = SkScalarHalf(offLen); 827 SkScalar halfStroke = SkScalarHalf(strokeWidth); 828 fIntervalLength = onLen + offLen; 829 fRect.set(halfOffLen, -halfStroke, halfOffLen + onLen, halfStroke); 830 } 831 832 bool DashingLineEffect::onIsEqual(const GrProcessor& other) const { 833 const DashingLineEffect& de = other.cast<DashingLineEffect>(); 834 return (fEdgeType == de.fEdgeType && 835 fRect == de.fRect && 836 fIntervalLength == de.fIntervalLength); 837 } 838 839 GR_DEFINE_GEOMETRY_PROCESSOR_TEST(DashingLineEffect); 840 841 GrGeometryProcessor* DashingLineEffect::TestCreate(SkRandom* random, 842 GrContext*, 843 const GrDrawTargetCaps& caps, 844 GrTexture*[]) { 845 GrPrimitiveEdgeType edgeType = static_cast<GrPrimitiveEdgeType>(random->nextULessThan( 846 kGrProcessorEdgeTypeCnt)); 847 SkScalar strokeWidth = random->nextRangeScalar(0, 100.f); 848 DashInfo info; 849 info.fCount = 2; 850 SkAutoTArray<SkScalar> intervals(info.fCount); 851 info.fIntervals = intervals.get(); 852 info.fIntervals[0] = random->nextRangeScalar(0, 10.f); 853 info.fIntervals[1] = random->nextRangeScalar(0, 10.f); 854 info.fPhase = random->nextRangeScalar(0, info.fIntervals[0] + info.fIntervals[1]); 855 856 return DashingLineEffect::Create(edgeType, info, strokeWidth); 857 } 858 859 ////////////////////////////////////////////////////////////////////////////// 860 861 GrGeometryProcessor* GrDashingEffect::Create(GrPrimitiveEdgeType edgeType, 862 const SkPathEffect::DashInfo& info, 863 SkScalar strokeWidth, 864 GrDashingEffect::DashCap cap) { 865 switch (cap) { 866 case GrDashingEffect::kRound_DashCap: 867 return DashingCircleEffect::Create(edgeType, info, SkScalarHalf(strokeWidth)); 868 case GrDashingEffect::kNonRound_DashCap: 869 return DashingLineEffect::Create(edgeType, info, strokeWidth); 870 default: 871 SkFAIL("Unexpected dashed cap."); 872 } 873 return NULL; 874 } 875