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