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