1/* 2 * Copyright 2015 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 "GrAAStrokeRectOp.h" 9 10#include "GrDefaultGeoProcFactory.h" 11#include "GrOpFlushState.h" 12#include "GrResourceKey.h" 13#include "GrResourceProvider.h" 14#include "SkStrokeRec.h" 15 16GR_DECLARE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey); 17GR_DECLARE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey); 18 19static void set_inset_fan(SkPoint* pts, size_t stride, const SkRect& r, SkScalar dx, SkScalar dy) { 20 pts->setRectFan(r.fLeft + dx, r.fTop + dy, r.fRight - dx, r.fBottom - dy, stride); 21} 22 23// We support all hairlines, bevels, and miters, but not round joins. Also, check whether the miter 24// limit makes a miter join effectively beveled. 25inline static bool allowed_stroke(const SkStrokeRec& stroke, bool* isMiter) { 26 SkASSERT(stroke.getStyle() == SkStrokeRec::kStroke_Style || 27 stroke.getStyle() == SkStrokeRec::kHairline_Style); 28 // For hairlines, make bevel and round joins appear the same as mitered ones. 29 if (!stroke.getWidth()) { 30 *isMiter = true; 31 return true; 32 } 33 if (stroke.getJoin() == SkPaint::kBevel_Join) { 34 *isMiter = false; 35 return true; 36 } 37 if (stroke.getJoin() == SkPaint::kMiter_Join) { 38 *isMiter = stroke.getMiter() >= SK_ScalarSqrt2; 39 return true; 40 } 41 return false; 42} 43 44static void compute_rects(SkRect* devOutside, SkRect* devOutsideAssist, SkRect* devInside, 45 bool* isDegenerate, const SkMatrix& viewMatrix, const SkRect& rect, 46 SkScalar strokeWidth, bool miterStroke) { 47 SkRect devRect; 48 viewMatrix.mapRect(&devRect, rect); 49 50 SkVector devStrokeSize; 51 if (strokeWidth > 0) { 52 devStrokeSize.set(strokeWidth, strokeWidth); 53 viewMatrix.mapVectors(&devStrokeSize, 1); 54 devStrokeSize.setAbs(devStrokeSize); 55 } else { 56 devStrokeSize.set(SK_Scalar1, SK_Scalar1); 57 } 58 59 const SkScalar dx = devStrokeSize.fX; 60 const SkScalar dy = devStrokeSize.fY; 61 const SkScalar rx = SkScalarHalf(dx); 62 const SkScalar ry = SkScalarHalf(dy); 63 64 *devOutside = devRect; 65 *devOutsideAssist = devRect; 66 *devInside = devRect; 67 68 devOutside->outset(rx, ry); 69 devInside->inset(rx, ry); 70 71 // If we have a degenerate stroking rect(ie the stroke is larger than inner rect) then we 72 // make a degenerate inside rect to avoid double hitting. We will also jam all of the points 73 // together when we render these rects. 74 SkScalar spare; 75 { 76 SkScalar w = devRect.width() - dx; 77 SkScalar h = devRect.height() - dy; 78 spare = SkTMin(w, h); 79 } 80 81 *isDegenerate = spare <= 0; 82 if (*isDegenerate) { 83 devInside->fLeft = devInside->fRight = devRect.centerX(); 84 devInside->fTop = devInside->fBottom = devRect.centerY(); 85 } 86 87 // For bevel-stroke, use 2 SkRect instances(devOutside and devOutsideAssist) 88 // to draw the outside of the octagon. Because there are 8 vertices on the outer 89 // edge, while vertex number of inner edge is 4, the same as miter-stroke. 90 if (!miterStroke) { 91 devOutside->inset(0, ry); 92 devOutsideAssist->outset(0, ry); 93 } 94} 95 96static sk_sp<GrGeometryProcessor> create_stroke_rect_gp(bool tweakAlphaForCoverage, 97 const SkMatrix& viewMatrix, 98 bool usesLocalCoords) { 99 using namespace GrDefaultGeoProcFactory; 100 101 Coverage::Type coverageType; 102 if (tweakAlphaForCoverage) { 103 coverageType = Coverage::kSolid_Type; 104 } else { 105 coverageType = Coverage::kAttribute_Type; 106 } 107 LocalCoords::Type localCoordsType = 108 usesLocalCoords ? LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type; 109 return MakeForDeviceSpace(Color::kPremulGrColorAttribute_Type, coverageType, localCoordsType, 110 viewMatrix); 111} 112 113class AAStrokeRectOp final : public GrMeshDrawOp { 114public: 115 DEFINE_OP_CLASS_ID 116 117 AAStrokeRectOp(GrColor color, const SkMatrix& viewMatrix, const SkRect& devOutside, 118 const SkRect& devInside) 119 : INHERITED(ClassID()), fViewMatrix(viewMatrix) { 120 SkASSERT(!devOutside.isEmpty()); 121 SkASSERT(!devInside.isEmpty()); 122 123 fRects.emplace_back(RectInfo{color, devOutside, devOutside, devInside, false}); 124 this->setBounds(devOutside, HasAABloat::kYes, IsZeroArea::kNo); 125 fMiterStroke = true; 126 } 127 128 static std::unique_ptr<GrMeshDrawOp> Make(GrColor color, const SkMatrix& viewMatrix, 129 const SkRect& rect, const SkStrokeRec& stroke) { 130 bool isMiter; 131 if (!allowed_stroke(stroke, &isMiter)) { 132 return nullptr; 133 } 134 135 AAStrokeRectOp* op = new AAStrokeRectOp(); 136 op->fMiterStroke = isMiter; 137 RectInfo& info = op->fRects.push_back(); 138 compute_rects(&info.fDevOutside, &info.fDevOutsideAssist, &info.fDevInside, 139 &info.fDegenerate, viewMatrix, rect, stroke.getWidth(), isMiter); 140 info.fColor = color; 141 if (isMiter) { 142 op->setBounds(info.fDevOutside, HasAABloat::kYes, IsZeroArea::kNo); 143 } else { 144 // The outer polygon of the bevel stroke is an octagon specified by the points of a 145 // pair of overlapping rectangles where one is wide and the other is narrow. 146 SkRect bounds = info.fDevOutside; 147 bounds.joinPossiblyEmptyRect(info.fDevOutsideAssist); 148 op->setBounds(bounds, HasAABloat::kYes, IsZeroArea::kNo); 149 } 150 op->fViewMatrix = viewMatrix; 151 return std::unique_ptr<GrMeshDrawOp>(op); 152 } 153 154 const char* name() const override { return "AAStrokeRect"; } 155 156 SkString dumpInfo() const override { 157 SkString string; 158 for (const auto& info : fRects) { 159 string.appendf( 160 "Color: 0x%08x, ORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 161 "AssistORect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], " 162 "IRect [L: %.2f, T: %.2f, R: %.2f, B: %.2f], Degen: %d", 163 info.fColor, info.fDevOutside.fLeft, info.fDevOutside.fTop, 164 info.fDevOutside.fRight, info.fDevOutside.fBottom, info.fDevOutsideAssist.fLeft, 165 info.fDevOutsideAssist.fTop, info.fDevOutsideAssist.fRight, 166 info.fDevOutsideAssist.fBottom, info.fDevInside.fLeft, info.fDevInside.fTop, 167 info.fDevInside.fRight, info.fDevInside.fBottom, info.fDegenerate); 168 } 169 string.append(DumpPipelineInfo(*this->pipeline())); 170 string.append(INHERITED::dumpInfo()); 171 return string; 172 } 173 174private: 175 AAStrokeRectOp() : INHERITED(ClassID()) {} 176 177 void getFragmentProcessorAnalysisInputs(GrPipelineAnalysisColor* color, 178 GrPipelineAnalysisCoverage* coverage) const override { 179 color->setToConstant(fRects[0].fColor); 180 *coverage = GrPipelineAnalysisCoverage::kSingleChannel; 181 } 182 void applyPipelineOptimizations(const GrPipelineOptimizations&) override; 183 void onPrepareDraws(Target*) const override; 184 185 static const int kMiterIndexCnt = 3 * 24; 186 static const int kMiterVertexCnt = 16; 187 static const int kNumMiterRectsInIndexBuffer = 256; 188 189 static const int kBevelIndexCnt = 48 + 36 + 24; 190 static const int kBevelVertexCnt = 24; 191 static const int kNumBevelRectsInIndexBuffer = 256; 192 193 static const GrBuffer* GetIndexBuffer(GrResourceProvider* resourceProvider, bool miterStroke); 194 195 bool usesLocalCoords() const { return fUsesLocalCoords; } 196 bool canTweakAlphaForCoverage() const { return fCanTweakAlphaForCoverage; } 197 const SkMatrix& viewMatrix() const { return fViewMatrix; } 198 bool miterStroke() const { return fMiterStroke; } 199 200 bool onCombineIfPossible(GrOp* t, const GrCaps&) override; 201 202 void generateAAStrokeRectGeometry(void* vertices, 203 size_t offset, 204 size_t vertexStride, 205 int outerVertexNum, 206 int innerVertexNum, 207 GrColor color, 208 const SkRect& devOutside, 209 const SkRect& devOutsideAssist, 210 const SkRect& devInside, 211 bool miterStroke, 212 bool degenerate, 213 bool tweakAlphaForCoverage) const; 214 215 // TODO support AA rotated stroke rects by copying around view matrices 216 struct RectInfo { 217 GrColor fColor; 218 SkRect fDevOutside; 219 SkRect fDevOutsideAssist; 220 SkRect fDevInside; 221 bool fDegenerate; 222 }; 223 224 SkSTArray<1, RectInfo, true> fRects; 225 bool fUsesLocalCoords; 226 bool fCanTweakAlphaForCoverage; 227 SkMatrix fViewMatrix; 228 bool fMiterStroke; 229 230 typedef GrMeshDrawOp INHERITED; 231}; 232 233void AAStrokeRectOp::applyPipelineOptimizations(const GrPipelineOptimizations& optimizations) { 234 optimizations.getOverrideColorIfSet(&fRects[0].fColor); 235 236 fUsesLocalCoords = optimizations.readsLocalCoords(); 237 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage(); 238 fCanTweakAlphaForCoverage = optimizations.canTweakAlphaForCoverage(); 239} 240 241void AAStrokeRectOp::onPrepareDraws(Target* target) const { 242 bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); 243 244 sk_sp<GrGeometryProcessor> gp(create_stroke_rect_gp(canTweakAlphaForCoverage, 245 this->viewMatrix(), 246 this->usesLocalCoords())); 247 if (!gp) { 248 SkDebugf("Couldn't create GrGeometryProcessor\n"); 249 return; 250 } 251 252 size_t vertexStride = gp->getVertexStride(); 253 254 SkASSERT(canTweakAlphaForCoverage 255 ? vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) 256 : vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); 257 int innerVertexNum = 4; 258 int outerVertexNum = this->miterStroke() ? 4 : 8; 259 int verticesPerInstance = (outerVertexNum + innerVertexNum) * 2; 260 int indicesPerInstance = this->miterStroke() ? kMiterIndexCnt : kBevelIndexCnt; 261 int instanceCount = fRects.count(); 262 263 const sk_sp<const GrBuffer> indexBuffer( 264 GetIndexBuffer(target->resourceProvider(), this->miterStroke())); 265 InstancedHelper helper; 266 void* vertices = 267 helper.init(target, kTriangles_GrPrimitiveType, vertexStride, indexBuffer.get(), 268 verticesPerInstance, indicesPerInstance, instanceCount); 269 if (!vertices || !indexBuffer) { 270 SkDebugf("Could not allocate vertices\n"); 271 return; 272 } 273 274 for (int i = 0; i < instanceCount; i++) { 275 const RectInfo& info = fRects[i]; 276 this->generateAAStrokeRectGeometry(vertices, 277 i * verticesPerInstance * vertexStride, 278 vertexStride, 279 outerVertexNum, 280 innerVertexNum, 281 info.fColor, 282 info.fDevOutside, 283 info.fDevOutsideAssist, 284 info.fDevInside, 285 fMiterStroke, 286 info.fDegenerate, 287 canTweakAlphaForCoverage); 288 } 289 helper.recordDraw(target, gp.get()); 290} 291 292const GrBuffer* AAStrokeRectOp::GetIndexBuffer(GrResourceProvider* resourceProvider, 293 bool miterStroke) { 294 if (miterStroke) { 295 // clang-format off 296 static const uint16_t gMiterIndices[] = { 297 0 + 0, 1 + 0, 5 + 0, 5 + 0, 4 + 0, 0 + 0, 298 1 + 0, 2 + 0, 6 + 0, 6 + 0, 5 + 0, 1 + 0, 299 2 + 0, 3 + 0, 7 + 0, 7 + 0, 6 + 0, 2 + 0, 300 3 + 0, 0 + 0, 4 + 0, 4 + 0, 7 + 0, 3 + 0, 301 302 0 + 4, 1 + 4, 5 + 4, 5 + 4, 4 + 4, 0 + 4, 303 1 + 4, 2 + 4, 6 + 4, 6 + 4, 5 + 4, 1 + 4, 304 2 + 4, 3 + 4, 7 + 4, 7 + 4, 6 + 4, 2 + 4, 305 3 + 4, 0 + 4, 4 + 4, 4 + 4, 7 + 4, 3 + 4, 306 307 0 + 8, 1 + 8, 5 + 8, 5 + 8, 4 + 8, 0 + 8, 308 1 + 8, 2 + 8, 6 + 8, 6 + 8, 5 + 8, 1 + 8, 309 2 + 8, 3 + 8, 7 + 8, 7 + 8, 6 + 8, 2 + 8, 310 3 + 8, 0 + 8, 4 + 8, 4 + 8, 7 + 8, 3 + 8, 311 }; 312 // clang-format on 313 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gMiterIndices) == kMiterIndexCnt); 314 GR_DEFINE_STATIC_UNIQUE_KEY(gMiterIndexBufferKey); 315 return resourceProvider->findOrCreateInstancedIndexBuffer( 316 gMiterIndices, kMiterIndexCnt, kNumMiterRectsInIndexBuffer, kMiterVertexCnt, 317 gMiterIndexBufferKey); 318 } else { 319 /** 320 * As in miter-stroke, index = a + b, and a is the current index, b is the shift 321 * from the first index. The index layout: 322 * outer AA line: 0~3, 4~7 323 * outer edge: 8~11, 12~15 324 * inner edge: 16~19 325 * inner AA line: 20~23 326 * Following comes a bevel-stroke rect and its indices: 327 * 328 * 4 7 329 * ********************************* 330 * * ______________________________ * 331 * * / 12 15 \ * 332 * * / \ * 333 * 0 * |8 16_____________________19 11 | * 3 334 * * | | | | * 335 * * | | **************** | | * 336 * * | | * 20 23 * | | * 337 * * | | * * | | * 338 * * | | * 21 22 * | | * 339 * * | | **************** | | * 340 * * | |____________________| | * 341 * 1 * |9 17 18 10| * 2 342 * * \ / * 343 * * \13 __________________________14/ * 344 * * * 345 * ********************************** 346 * 5 6 347 */ 348 // clang-format off 349 static const uint16_t gBevelIndices[] = { 350 // Draw outer AA, from outer AA line to outer edge, shift is 0. 351 0 + 0, 1 + 0, 9 + 0, 9 + 0, 8 + 0, 0 + 0, 352 1 + 0, 5 + 0, 13 + 0, 13 + 0, 9 + 0, 1 + 0, 353 5 + 0, 6 + 0, 14 + 0, 14 + 0, 13 + 0, 5 + 0, 354 6 + 0, 2 + 0, 10 + 0, 10 + 0, 14 + 0, 6 + 0, 355 2 + 0, 3 + 0, 11 + 0, 11 + 0, 10 + 0, 2 + 0, 356 3 + 0, 7 + 0, 15 + 0, 15 + 0, 11 + 0, 3 + 0, 357 7 + 0, 4 + 0, 12 + 0, 12 + 0, 15 + 0, 7 + 0, 358 4 + 0, 0 + 0, 8 + 0, 8 + 0, 12 + 0, 4 + 0, 359 360 // Draw the stroke, from outer edge to inner edge, shift is 8. 361 0 + 8, 1 + 8, 9 + 8, 9 + 8, 8 + 8, 0 + 8, 362 1 + 8, 5 + 8, 9 + 8, 363 5 + 8, 6 + 8, 10 + 8, 10 + 8, 9 + 8, 5 + 8, 364 6 + 8, 2 + 8, 10 + 8, 365 2 + 8, 3 + 8, 11 + 8, 11 + 8, 10 + 8, 2 + 8, 366 3 + 8, 7 + 8, 11 + 8, 367 7 + 8, 4 + 8, 8 + 8, 8 + 8, 11 + 8, 7 + 8, 368 4 + 8, 0 + 8, 8 + 8, 369 370 // Draw the inner AA, from inner edge to inner AA line, shift is 16. 371 0 + 16, 1 + 16, 5 + 16, 5 + 16, 4 + 16, 0 + 16, 372 1 + 16, 2 + 16, 6 + 16, 6 + 16, 5 + 16, 1 + 16, 373 2 + 16, 3 + 16, 7 + 16, 7 + 16, 6 + 16, 2 + 16, 374 3 + 16, 0 + 16, 4 + 16, 4 + 16, 7 + 16, 3 + 16, 375 }; 376 // clang-format on 377 GR_STATIC_ASSERT(SK_ARRAY_COUNT(gBevelIndices) == kBevelIndexCnt); 378 379 GR_DEFINE_STATIC_UNIQUE_KEY(gBevelIndexBufferKey); 380 return resourceProvider->findOrCreateInstancedIndexBuffer( 381 gBevelIndices, kBevelIndexCnt, kNumBevelRectsInIndexBuffer, kBevelVertexCnt, 382 gBevelIndexBufferKey); 383 } 384} 385 386bool AAStrokeRectOp::onCombineIfPossible(GrOp* t, const GrCaps& caps) { 387 AAStrokeRectOp* that = t->cast<AAStrokeRectOp>(); 388 389 if (!GrPipeline::CanCombine(*this->pipeline(), this->bounds(), *that->pipeline(), 390 that->bounds(), caps)) { 391 return false; 392 } 393 394 // TODO combine across miterstroke changes 395 if (this->miterStroke() != that->miterStroke()) { 396 return false; 397 } 398 399 // We apply the viewmatrix to the rect points on the cpu. However, if the pipeline uses 400 // local coords then we won't be able to combine. We could actually upload the viewmatrix 401 // using vertex attributes in these cases, but haven't investigated that 402 if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { 403 return false; 404 } 405 406 // In the event of two ops, one who can tweak, one who cannot, we just fall back to not 407 // tweaking. 408 if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { 409 fCanTweakAlphaForCoverage = false; 410 } 411 412 fRects.push_back_n(that->fRects.count(), that->fRects.begin()); 413 this->joinBounds(*that); 414 return true; 415} 416 417static void setup_scale(int* scale, SkScalar inset) { 418 if (inset < SK_ScalarHalf) { 419 *scale = SkScalarFloorToInt(512.0f * inset / (inset + SK_ScalarHalf)); 420 SkASSERT(*scale >= 0 && *scale <= 255); 421 } else { 422 *scale = 0xff; 423 } 424} 425 426void AAStrokeRectOp::generateAAStrokeRectGeometry(void* vertices, 427 size_t offset, 428 size_t vertexStride, 429 int outerVertexNum, 430 int innerVertexNum, 431 GrColor color, 432 const SkRect& devOutside, 433 const SkRect& devOutsideAssist, 434 const SkRect& devInside, 435 bool miterStroke, 436 bool degenerate, 437 bool tweakAlphaForCoverage) const { 438 intptr_t verts = reinterpret_cast<intptr_t>(vertices) + offset; 439 440 // We create vertices for four nested rectangles. There are two ramps from 0 to full 441 // coverage, one on the exterior of the stroke and the other on the interior. 442 // The following pointers refer to the four rects, from outermost to innermost. 443 SkPoint* fan0Pos = reinterpret_cast<SkPoint*>(verts); 444 SkPoint* fan1Pos = reinterpret_cast<SkPoint*>(verts + outerVertexNum * vertexStride); 445 SkPoint* fan2Pos = reinterpret_cast<SkPoint*>(verts + 2 * outerVertexNum * vertexStride); 446 SkPoint* fan3Pos = reinterpret_cast<SkPoint*>( 447 verts + (2 * outerVertexNum + innerVertexNum) * vertexStride); 448 449#ifndef SK_IGNORE_THIN_STROKED_RECT_FIX 450 // TODO: this only really works if the X & Y margins are the same all around 451 // the rect (or if they are all >= 1.0). 452 SkScalar inset; 453 if (!degenerate) { 454 inset = SkMinScalar(SK_Scalar1, devOutside.fRight - devInside.fRight); 455 inset = SkMinScalar(inset, devInside.fLeft - devOutside.fLeft); 456 inset = SkMinScalar(inset, devInside.fTop - devOutside.fTop); 457 if (miterStroke) { 458 inset = SK_ScalarHalf * SkMinScalar(inset, devOutside.fBottom - devInside.fBottom); 459 } else { 460 inset = SK_ScalarHalf * 461 SkMinScalar(inset, devOutsideAssist.fBottom - devInside.fBottom); 462 } 463 SkASSERT(inset >= 0); 464 } else { 465 // TODO use real devRect here 466 inset = SkMinScalar(devOutside.width(), SK_Scalar1); 467 inset = SK_ScalarHalf * 468 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height())); 469 } 470#else 471 SkScalar inset; 472 if (!degenerate) { 473 inset = SK_ScalarHalf; 474 } else { 475 // TODO use real devRect here 476 inset = SkMinScalar(devOutside.width(), SK_Scalar1); 477 inset = SK_ScalarHalf * 478 SkMinScalar(inset, SkTMax(devOutside.height(), devOutsideAssist.height())); 479 } 480#endif 481 482 if (miterStroke) { 483 // outermost 484 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); 485 // inner two 486 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); 487 if (!degenerate) { 488 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); 489 // innermost 490 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); 491 } else { 492 // When the interior rect has become degenerate we smoosh to a single point 493 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom); 494 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight, 495 devInside.fBottom, vertexStride); 496 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight, 497 devInside.fBottom, vertexStride); 498 } 499 } else { 500 SkPoint* fan0AssistPos = reinterpret_cast<SkPoint*>(verts + 4 * vertexStride); 501 SkPoint* fan1AssistPos = 502 reinterpret_cast<SkPoint*>(verts + (outerVertexNum + 4) * vertexStride); 503 // outermost 504 set_inset_fan(fan0Pos, vertexStride, devOutside, -SK_ScalarHalf, -SK_ScalarHalf); 505 set_inset_fan(fan0AssistPos, vertexStride, devOutsideAssist, -SK_ScalarHalf, 506 -SK_ScalarHalf); 507 // outer one of the inner two 508 set_inset_fan(fan1Pos, vertexStride, devOutside, inset, inset); 509 set_inset_fan(fan1AssistPos, vertexStride, devOutsideAssist, inset, inset); 510 if (!degenerate) { 511 // inner one of the inner two 512 set_inset_fan(fan2Pos, vertexStride, devInside, -inset, -inset); 513 // innermost 514 set_inset_fan(fan3Pos, vertexStride, devInside, SK_ScalarHalf, SK_ScalarHalf); 515 } else { 516 // When the interior rect has become degenerate we smoosh to a single point 517 SkASSERT(devInside.fLeft == devInside.fRight && devInside.fTop == devInside.fBottom); 518 fan2Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight, 519 devInside.fBottom, vertexStride); 520 fan3Pos->setRectFan(devInside.fLeft, devInside.fTop, devInside.fRight, 521 devInside.fBottom, vertexStride); 522 } 523 } 524 525 // Make verts point to vertex color and then set all the color and coverage vertex attrs 526 // values. The outermost rect has 0 coverage 527 verts += sizeof(SkPoint); 528 for (int i = 0; i < outerVertexNum; ++i) { 529 if (tweakAlphaForCoverage) { 530 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = 0; 531 } else { 532 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 533 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = 0; 534 } 535 } 536 537 // scale is the coverage for the the inner two rects. 538 int scale; 539 setup_scale(&scale, inset); 540 541 float innerCoverage = GrNormalizeByteToFloat(scale); 542 GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); 543 544 verts += outerVertexNum * vertexStride; 545 for (int i = 0; i < outerVertexNum + innerVertexNum; ++i) { 546 if (tweakAlphaForCoverage) { 547 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 548 } else { 549 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 550 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; 551 } 552 } 553 554 // The innermost rect has 0 coverage, unless we are degenerate, in which case we must apply the 555 // scaled coverage 556 verts += (outerVertexNum + innerVertexNum) * vertexStride; 557 if (!degenerate) { 558 innerCoverage = 0; 559 scaledColor = 0; 560 } 561 562 for (int i = 0; i < innerVertexNum; ++i) { 563 if (tweakAlphaForCoverage) { 564 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; 565 } else { 566 *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; 567 *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = innerCoverage; 568 } 569 } 570} 571 572namespace GrAAStrokeRectOp { 573 574std::unique_ptr<GrMeshDrawOp> MakeFillBetweenRects(GrColor color, 575 const SkMatrix& viewMatrix, 576 const SkRect& devOutside, 577 const SkRect& devInside) { 578 return std::unique_ptr<GrMeshDrawOp>( 579 new AAStrokeRectOp(color, viewMatrix, devOutside, devInside)); 580} 581 582std::unique_ptr<GrMeshDrawOp> Make(GrColor color, 583 const SkMatrix& viewMatrix, 584 const SkRect& rect, 585 const SkStrokeRec& stroke) { 586 return AAStrokeRectOp::Make(color, viewMatrix, rect, stroke); 587} 588} 589 590/////////////////////////////////////////////////////////////////////////////////////////////////// 591 592#if GR_TEST_UTILS 593 594#include "GrDrawOpTest.h" 595 596DRAW_OP_TEST_DEFINE(AAStrokeRectOp) { 597 bool miterStroke = random->nextBool(); 598 599 // Create either a empty rect or a non-empty rect. 600 SkRect rect = 601 random->nextBool() ? SkRect::MakeXYWH(10, 10, 50, 40) : SkRect::MakeXYWH(6, 7, 0, 0); 602 SkScalar minDim = SkMinScalar(rect.width(), rect.height()); 603 SkScalar strokeWidth = random->nextUScalar1() * minDim; 604 605 GrColor color = GrRandomColor(random); 606 607 SkStrokeRec rec(SkStrokeRec::kFill_InitStyle); 608 rec.setStrokeStyle(strokeWidth); 609 rec.setStrokeParams(SkPaint::kButt_Cap, 610 miterStroke ? SkPaint::kMiter_Join : SkPaint::kBevel_Join, 1.f); 611 SkMatrix matrix = GrTest::TestMatrixRectStaysRect(random); 612 return GrAAStrokeRectOp::Make(color, matrix, rect, rec); 613} 614 615#endif 616