1 /* 2 * Copyright 2018 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 "GrFillRectOp.h" 9 10 #include "GrGeometryProcessor.h" 11 #include "GrMeshDrawOp.h" 12 #include "GrPaint.h" 13 #include "GrQuad.h" 14 #include "GrQuadPerEdgeAA.h" 15 #include "GrSimpleMeshDrawOpHelper.h" 16 #include "SkMatrix.h" 17 #include "SkRect.h" 18 #include "glsl/GrGLSLColorSpaceXformHelper.h" 19 #include "glsl/GrGLSLGeometryProcessor.h" 20 #include "glsl/GrGLSLVarying.h" 21 22 namespace { 23 24 using VertexSpec = GrQuadPerEdgeAA::VertexSpec; 25 using ColorType = GrQuadPerEdgeAA::ColorType; 26 27 #ifdef SK_DEBUG 28 static SkString dump_quad_info(int index, const GrPerspQuad& deviceQuad, 29 const GrPerspQuad& localQuad, const SkPMColor4f& color, 30 GrQuadAAFlags aaFlags) { 31 SkString str; 32 str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n" 33 " device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), " 34 "(%.2f, %.2f, %.2f)],\n" 35 " local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), " 36 "(%.2f, %.2f, %.2f)]\n", 37 index, color.fR, color.fG, color.fB, color.fA, 38 (uint32_t) (aaFlags & GrQuadAAFlags::kLeft), 39 (uint32_t) (aaFlags & GrQuadAAFlags::kTop), 40 (uint32_t) (aaFlags & GrQuadAAFlags::kRight), 41 (uint32_t) (aaFlags & GrQuadAAFlags::kBottom), 42 deviceQuad.x(0), deviceQuad.y(0), deviceQuad.w(0), 43 deviceQuad.x(1), deviceQuad.y(1), deviceQuad.w(1), 44 deviceQuad.x(2), deviceQuad.y(2), deviceQuad.w(2), 45 deviceQuad.x(3), deviceQuad.y(3), deviceQuad.w(3), 46 localQuad.x(0), localQuad.y(0), localQuad.w(0), 47 localQuad.x(1), localQuad.y(1), localQuad.w(1), 48 localQuad.x(2), localQuad.y(2), localQuad.w(2), 49 localQuad.x(3), localQuad.y(3), localQuad.w(3)); 50 return str; 51 } 52 #endif 53 54 class FillRectOp final : public GrMeshDrawOp { 55 private: 56 using Helper = GrSimpleMeshDrawOpHelperWithStencil; 57 58 public: 59 static std::unique_ptr<GrDrawOp> Make(GrContext* context, 60 GrPaint&& paint, 61 GrAAType aaType, 62 GrQuadAAFlags edgeAA, 63 const GrUserStencilSettings* stencilSettings, 64 const GrPerspQuad& deviceQuad, 65 GrQuadType deviceQuadType, 66 const GrPerspQuad& localQuad, 67 GrQuadType localQuadType) { 68 // Clean up deviations between aaType and edgeAA 69 GrResolveAATypeForQuad(aaType, edgeAA, deviceQuad, deviceQuadType, &aaType, &edgeAA); 70 return Helper::FactoryHelper<FillRectOp>(context, std::move(paint), aaType, edgeAA, 71 stencilSettings, deviceQuad, deviceQuadType, localQuad, localQuadType); 72 } 73 74 // aaType is passed to Helper in the initializer list, so incongruities between aaType and 75 // edgeFlags must be resolved prior to calling this constructor. 76 FillRectOp(Helper::MakeArgs args, SkPMColor4f paintColor, GrAAType aaType, 77 GrQuadAAFlags edgeFlags, const GrUserStencilSettings* stencil, 78 const GrPerspQuad& deviceQuad, GrQuadType deviceQuadType, 79 const GrPerspQuad& localQuad, GrQuadType localQuadType) 80 : INHERITED(ClassID()) 81 , fHelper(args, aaType, stencil) 82 , fWideColor(!SkPMColor4fFitsInBytes(paintColor)) { 83 // The color stored with the quad is the clear color if a scissor-clear is decided upon 84 // when executing the op. 85 fDeviceQuads.push_back(deviceQuad, deviceQuadType, { paintColor, edgeFlags }); 86 87 if (!fHelper.isTrivial()) { 88 // Conservatively keep track of the local coordinates; it may be that the paint doesn't 89 // need them after analysis is finished. If the paint is known to be solid up front they 90 // can be skipped entirely. 91 fLocalQuads.push_back(localQuad, localQuadType); 92 } 93 this->setBounds(deviceQuad.bounds(deviceQuadType), 94 HasAABloat(aaType == GrAAType::kCoverage), IsZeroArea::kNo); 95 } 96 97 const char* name() const override { return "FillRectOp"; } 98 99 void visitProxies(const VisitProxyFunc& func, VisitorType) const override { 100 return fHelper.visitProxies(func); 101 } 102 103 #ifdef SK_DEBUG 104 SkString dumpInfo() const override { 105 SkString str; 106 str.appendf("# draws: %u\n", this->quadCount()); 107 str.appendf("Device quad type: %u, local quad type: %u\n", 108 (uint32_t) fDeviceQuads.quadType(), (uint32_t) fLocalQuads.quadType()); 109 str += fHelper.dumpInfo(); 110 GrPerspQuad device, local; 111 for (int i = 0; i < this->quadCount(); i++) { 112 device = fDeviceQuads[i]; 113 const ColorAndAA& info = fDeviceQuads.metadata(i); 114 if (!fHelper.isTrivial()) { 115 local = fLocalQuads[i]; 116 } 117 str += dump_quad_info(i, device, local, info.fColor, info.fAAFlags); 118 } 119 str += INHERITED::dumpInfo(); 120 return str; 121 } 122 #endif 123 124 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override { 125 // Initialize aggregate color analysis with the first quad's color (which always exists) 126 SkASSERT(this->quadCount() > 0); 127 GrProcessorAnalysisColor quadColors(fDeviceQuads.metadata(0).fColor); 128 // Then combine the colors of any additional quads (e.g. from MakeSet) 129 for (int i = 1; i < this->quadCount(); ++i) { 130 quadColors = GrProcessorAnalysisColor::Combine(quadColors, 131 fDeviceQuads.metadata(i).fColor); 132 if (quadColors.isUnknown()) { 133 // No point in accumulating additional starting colors, combining cannot make it 134 // less unknown. 135 break; 136 } 137 } 138 139 // If the AA type is coverage, it will be a single value per pixel; if it's not coverage AA 140 // then the coverage is always 1.0, so specify kNone for more optimal blending. 141 GrProcessorAnalysisCoverage coverage = fHelper.aaType() == GrAAType::kCoverage ? 142 GrProcessorAnalysisCoverage::kSingleChannel : 143 GrProcessorAnalysisCoverage::kNone; 144 auto result = fHelper.finalizeProcessors(caps, clip, coverage, &quadColors); 145 // If there is a constant color after analysis, that means all of the quads should be set 146 // to the same color (even if they started out with different colors). 147 SkPMColor4f colorOverride; 148 if (quadColors.isConstant(&colorOverride)) { 149 for (int i = 0; i < this->quadCount(); ++i) { 150 fDeviceQuads.metadata(i).fColor = colorOverride; 151 } 152 } 153 154 return result; 155 } 156 157 FixedFunctionFlags fixedFunctionFlags() const override { 158 // Since the AA type of the whole primitive is kept consistent with the per edge AA flags 159 // the helper's fixed function flags are appropriate. 160 return fHelper.fixedFunctionFlags(); 161 } 162 163 DEFINE_OP_CLASS_ID 164 165 private: 166 // For GrFillRectOp::MakeSet's use of addQuad 167 friend std::unique_ptr<GrDrawOp> GrFillRectOp::MakeSet(GrContext* context, GrPaint&& paint, 168 GrAAType aaType, const SkMatrix& viewMatrix, 169 const GrRenderTargetContext::QuadSetEntry quads[], int quadCount, 170 const GrUserStencilSettings* stencilSettings); 171 172 void onPrepareDraws(Target* target) override { 173 TRACE_EVENT0("skia", TRACE_FUNC); 174 175 using Domain = GrQuadPerEdgeAA::Domain; 176 static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty(); 177 178 VertexSpec vertexSpec(fDeviceQuads.quadType(), 179 fWideColor ? ColorType::kHalf : ColorType::kByte, 180 fLocalQuads.quadType(), fHelper.usesLocalCoords(), Domain::kNo, 181 fHelper.aaType(), fHelper.compatibleWithAlphaAsCoverage()); 182 // Make sure that if the op thought it was a solid color, the vertex spec does not use 183 // local coords. 184 SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords()); 185 186 sk_sp<GrGeometryProcessor> gp = GrQuadPerEdgeAA::MakeProcessor(vertexSpec); 187 size_t vertexSize = gp->vertexStride(); 188 189 sk_sp<const GrBuffer> vbuffer; 190 int vertexOffsetInBuffer = 0; 191 192 // Fill the allocated vertex data 193 void* vdata = target->makeVertexSpace( 194 vertexSize, this->quadCount() * vertexSpec.verticesPerQuad(), 195 &vbuffer, &vertexOffsetInBuffer); 196 if (!vdata) { 197 SkDebugf("Could not allocate vertices\n"); 198 return; 199 } 200 201 // vertices pointer advances through vdata based on Tessellate's return value 202 void* vertices = vdata; 203 if (fHelper.isTrivial()) { 204 SkASSERT(fLocalQuads.count() == 0); // No local coords, so send an ignored dummy quad 205 static const GrPerspQuad kIgnoredLocal(SkRect::MakeEmpty(), SkMatrix::I()); 206 for (int i = 0; i < this->quadCount(); ++i) { 207 const ColorAndAA& info = fDeviceQuads.metadata(i); 208 vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i], 209 info.fColor, kIgnoredLocal, kEmptyDomain, info.fAAFlags); 210 } 211 } else { 212 SkASSERT(fLocalQuads.count() == fDeviceQuads.count()); 213 for (int i = 0; i < this->quadCount(); ++i) { 214 const ColorAndAA& info = fDeviceQuads.metadata(i); 215 vertices = GrQuadPerEdgeAA::Tessellate(vertices, vertexSpec, fDeviceQuads[i], 216 info.fColor, fLocalQuads[i], kEmptyDomain, info.fAAFlags); 217 } 218 } 219 220 // Configure the mesh for the vertex data 221 GrMesh* mesh = target->allocMeshes(1); 222 if (!GrQuadPerEdgeAA::ConfigureMeshIndices(target, mesh, vertexSpec, this->quadCount())) { 223 SkDebugf("Could not allocate indices\n"); 224 return; 225 } 226 mesh->setVertexData(std::move(vbuffer), vertexOffsetInBuffer); 227 228 auto pipe = fHelper.makePipeline(target); 229 target->draw(std::move(gp), pipe.fPipeline, pipe.fFixedDynamicState, mesh); 230 } 231 232 CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override { 233 TRACE_EVENT0("skia", TRACE_FUNC); 234 const auto* that = t->cast<FillRectOp>(); 235 236 if ((fHelper.aaType() == GrAAType::kCoverage || 237 that->fHelper.aaType() == GrAAType::kCoverage) && 238 this->quadCount() + that->quadCount() > GrQuadPerEdgeAA::kNumAAQuadsInIndexBuffer) { 239 // This limit on batch size seems to help on Adreno devices 240 return CombineResult::kCannotCombine; 241 } 242 243 // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa draw 244 // ops together, so pass true as the last argument. 245 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) { 246 return CombineResult::kCannotCombine; 247 } 248 249 // If the paints were compatible, the trivial/solid-color state should be the same 250 SkASSERT(fHelper.isTrivial() == that->fHelper.isTrivial()); 251 252 // If the processor sets are compatible, the two ops are always compatible; it just needs to 253 // adjust the state of the op to be the more general quad and aa types of the two ops and 254 // then concatenate the per-quad data. 255 fWideColor |= that->fWideColor; 256 257 // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa 258 // types to be none and coverage, in which case this op's aa type must be lifted to coverage 259 // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed. 260 if (fHelper.aaType() == GrAAType::kNone && that->fHelper.aaType() == GrAAType::kCoverage) { 261 fHelper.setAAType(GrAAType::kCoverage); 262 } 263 264 fDeviceQuads.concat(that->fDeviceQuads); 265 if (!fHelper.isTrivial()) { 266 fLocalQuads.concat(that->fLocalQuads); 267 } 268 return CombineResult::kMerged; 269 } 270 271 // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible. 272 // But since it's avoiding the op list management, it must update the op's bounds. This is only 273 // used with quad sets, which uses the same view matrix for each quad so this assumes that the 274 // device quad type of the new quad is the same as the op's. 275 void addQuad(const GrPerspQuad& deviceQuad, const GrPerspQuad& localQuad, 276 GrQuadType localQuadType, const SkPMColor4f& color, GrQuadAAFlags edgeAA, 277 GrAAType aaType) { 278 SkASSERT(deviceQuad.quadType() <= fDeviceQuads.quadType()); 279 280 // The new quad's aa type should be the same as the first quad's or none, except when the 281 // first quad's aa type was already downgraded to none, in which case the stored type must 282 // be lifted to back to the requested type. 283 if (aaType != fHelper.aaType()) { 284 if (aaType != GrAAType::kNone) { 285 // Original quad was downgraded to non-aa, lift back up to this quad's required type 286 SkASSERT(fHelper.aaType() == GrAAType::kNone); 287 fHelper.setAAType(aaType); 288 } 289 // else the new quad could have been downgraded but the other quads can't be, so don't 290 // reset the op's accumulated aa type. 291 } 292 293 // clear compatible won't need to be updated, since device quad type and paint is the same, 294 // but this quad has a new color, so maybe update wide color 295 fWideColor |= !SkPMColor4fFitsInBytes(color); 296 297 // Update the bounds and add the quad to this op's storage 298 SkRect newBounds = this->bounds(); 299 newBounds.joinPossiblyEmptyRect(deviceQuad.bounds(fDeviceQuads.quadType())); 300 this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage), 301 IsZeroArea::kNo); 302 fDeviceQuads.push_back(deviceQuad, fDeviceQuads.quadType(), { color, edgeAA }); 303 if (!fHelper.isTrivial()) { 304 fLocalQuads.push_back(localQuad, localQuadType); 305 } 306 } 307 308 int quadCount() const { 309 // Sanity check that the parallel arrays for quad properties all have the same size 310 SkASSERT(fDeviceQuads.count() == fLocalQuads.count() || 311 (fLocalQuads.count() == 0 && fHelper.isTrivial())); 312 return fDeviceQuads.count(); 313 } 314 315 struct ColorAndAA { 316 SkPMColor4f fColor; 317 GrQuadAAFlags fAAFlags; 318 }; 319 320 Helper fHelper; 321 GrTQuadList<ColorAndAA> fDeviceQuads; 322 // No metadata attached to the local quads; this list is empty when local coords are not needed. 323 GrQuadList fLocalQuads; 324 325 unsigned fWideColor: 1; 326 327 typedef GrMeshDrawOp INHERITED; 328 }; 329 330 } // anonymous namespace 331 332 namespace GrFillRectOp { 333 334 std::unique_ptr<GrDrawOp> MakePerEdge(GrContext* context, 335 GrPaint&& paint, 336 GrAAType aaType, 337 GrQuadAAFlags edgeAA, 338 const SkMatrix& viewMatrix, 339 const SkRect& rect, 340 const GrUserStencilSettings* stencilSettings) { 341 return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, 342 GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), 343 GrPerspQuad(rect, SkMatrix::I()), GrQuadType::kRect); 344 } 345 346 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalMatrix(GrContext* context, 347 GrPaint&& paint, 348 GrAAType aaType, 349 GrQuadAAFlags edgeAA, 350 const SkMatrix& viewMatrix, 351 const SkMatrix& localMatrix, 352 const SkRect& rect, 353 const GrUserStencilSettings* stencilSettings) { 354 GrQuadType localQuadType = GrQuadTypeForTransformedRect(localMatrix); 355 return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, 356 GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), 357 GrPerspQuad(rect, localMatrix), localQuadType); 358 } 359 360 std::unique_ptr<GrDrawOp> MakePerEdgeWithLocalRect(GrContext* context, 361 GrPaint&& paint, 362 GrAAType aaType, 363 GrQuadAAFlags edgeAA, 364 const SkMatrix& viewMatrix, 365 const SkRect& rect, 366 const SkRect& localRect, 367 const GrUserStencilSettings* stencilSettings) { 368 return FillRectOp::Make(context, std::move(paint), aaType, edgeAA, stencilSettings, 369 GrPerspQuad(rect, viewMatrix), GrQuadTypeForTransformedRect(viewMatrix), 370 GrPerspQuad(localRect, SkMatrix::I()), GrQuadType::kRect); 371 } 372 373 std::unique_ptr<GrDrawOp> MakeSet(GrContext* context, 374 GrPaint&& paint, 375 GrAAType aaType, 376 const SkMatrix& viewMatrix, 377 const GrRenderTargetContext::QuadSetEntry quads[], 378 int cnt, 379 const GrUserStencilSettings* stencilSettings) { 380 // First make a draw op for the first quad in the set 381 SkASSERT(cnt > 0); 382 GrQuadType deviceQuadType = GrQuadTypeForTransformedRect(viewMatrix); 383 384 paint.setColor4f(quads[0].fColor); 385 std::unique_ptr<GrDrawOp> op = FillRectOp::Make(context, std::move(paint), aaType, 386 quads[0].fAAFlags, stencilSettings, GrPerspQuad(quads[0].fRect, viewMatrix), 387 deviceQuadType, GrPerspQuad(quads[0].fRect, quads[0].fLocalMatrix), 388 GrQuadTypeForTransformedRect(quads[0].fLocalMatrix)); 389 auto* fillRects = op->cast<FillRectOp>(); 390 391 // Accumulate remaining quads similar to onCombineIfPossible() without creating an op 392 for (int i = 1; i < cnt; ++i) { 393 GrPerspQuad deviceQuad(quads[i].fRect, viewMatrix); 394 395 GrAAType resolvedAA; 396 GrQuadAAFlags resolvedEdgeFlags; 397 GrResolveAATypeForQuad(aaType, quads[i].fAAFlags, deviceQuad, deviceQuadType, 398 &resolvedAA, &resolvedEdgeFlags); 399 400 fillRects->addQuad(deviceQuad, GrPerspQuad(quads[i].fRect, quads[i].fLocalMatrix), 401 GrQuadTypeForTransformedRect(quads[i].fLocalMatrix), quads[i].fColor, 402 resolvedEdgeFlags,resolvedAA); 403 } 404 405 return op; 406 } 407 408 std::unique_ptr<GrDrawOp> Make(GrContext* context, 409 GrPaint&& paint, 410 GrAAType aaType, 411 const SkMatrix& viewMatrix, 412 const SkRect& rect, 413 const GrUserStencilSettings* stencil) { 414 return MakePerEdge(context, std::move(paint), aaType, 415 aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone, 416 viewMatrix, rect, stencil); 417 } 418 419 std::unique_ptr<GrDrawOp> MakeWithLocalMatrix(GrContext* context, 420 GrPaint&& paint, 421 GrAAType aaType, 422 const SkMatrix& viewMatrix, 423 const SkMatrix& localMatrix, 424 const SkRect& rect, 425 const GrUserStencilSettings* stencil) { 426 return MakePerEdgeWithLocalMatrix(context, std::move(paint), aaType, 427 aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone, 428 viewMatrix, localMatrix, rect, stencil); 429 } 430 431 std::unique_ptr<GrDrawOp> MakeWithLocalRect(GrContext* context, 432 GrPaint&& paint, 433 GrAAType aaType, 434 const SkMatrix& viewMatrix, 435 const SkRect& rect, 436 const SkRect& localRect, 437 const GrUserStencilSettings* stencil) { 438 return MakePerEdgeWithLocalRect(context, std::move(paint), aaType, 439 aaType == GrAAType::kCoverage ? GrQuadAAFlags::kAll : GrQuadAAFlags::kNone, 440 viewMatrix, rect, localRect, stencil); 441 } 442 443 } // namespace GrFillRectOp 444 445 #if GR_TEST_UTILS 446 447 #include "GrDrawOpTest.h" 448 #include "SkGr.h" 449 450 GR_DRAW_OP_TEST_DEFINE(FillRectOp) { 451 SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); 452 SkRect rect = GrTest::TestRect(random); 453 454 GrAAType aaType = GrAAType::kNone; 455 if (random->nextBool()) { 456 aaType = (fsaaType == GrFSAAType::kUnifiedMSAA) ? GrAAType::kMSAA : GrAAType::kCoverage; 457 } 458 const GrUserStencilSettings* stencil = random->nextBool() ? nullptr 459 : GrGetRandomStencil(random, context); 460 461 GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; 462 aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; 463 aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; 464 aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; 465 aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; 466 467 if (random->nextBool()) { 468 if (random->nextBool()) { 469 if (random->nextBool()) { 470 // Local matrix with a set op 471 uint32_t extraQuadCt = random->nextRangeU(1, 4); 472 SkTArray<GrRenderTargetContext::QuadSetEntry> quads(extraQuadCt + 1); 473 quads.push_back( 474 {rect, SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())), 475 GrTest::TestMatrixInvertible(random), aaFlags}); 476 for (uint32_t i = 0; i < extraQuadCt; ++i) { 477 GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone; 478 aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone; 479 aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone; 480 aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone; 481 aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone; 482 483 quads.push_back( 484 {GrTest::TestRect(random), 485 SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU())), 486 GrTest::TestMatrixInvertible(random), aaFlags}); 487 } 488 489 return GrFillRectOp::MakeSet(context, std::move(paint), aaType, viewMatrix, 490 quads.begin(), quads.count(), stencil); 491 } else { 492 // Single local matrix 493 SkMatrix localMatrix = GrTest::TestMatrixInvertible(random); 494 return GrFillRectOp::MakePerEdgeWithLocalMatrix(context, std::move(paint), aaType, 495 aaFlags, viewMatrix, localMatrix, 496 rect, stencil); 497 } 498 } else { 499 // Pass local rect directly 500 SkRect localRect = GrTest::TestRect(random); 501 return GrFillRectOp::MakePerEdgeWithLocalRect(context, std::move(paint), aaType, 502 aaFlags, viewMatrix, rect, localRect, 503 stencil); 504 } 505 } else { 506 // The simplest constructor 507 return GrFillRectOp::MakePerEdge(context, std::move(paint), aaType, aaFlags, viewMatrix, 508 rect, stencil); 509 } 510 } 511 512 #endif 513