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
dump_quad_info(int index,const GrPerspQuad & deviceQuad,const GrPerspQuad & localQuad,const SkPMColor4f & color,GrQuadAAFlags aaFlags)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:
Make(GrContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags edgeAA,const GrUserStencilSettings * stencilSettings,const GrPerspQuad & deviceQuad,GrQuadType deviceQuadType,const GrPerspQuad & localQuad,GrQuadType localQuadType)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.
FillRectOp(Helper::MakeArgs args,SkPMColor4f paintColor,GrAAType aaType,GrQuadAAFlags edgeFlags,const GrUserStencilSettings * stencil,const GrPerspQuad & deviceQuad,GrQuadType deviceQuadType,const GrPerspQuad & localQuad,GrQuadType localQuadType)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
name() const97 const char* name() const override { return "FillRectOp"; }
98
visitProxies(const VisitProxyFunc & func,VisitorType) const99 void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
100 return fHelper.visitProxies(func);
101 }
102
103 #ifdef SK_DEBUG
dumpInfo() const104 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
finalize(const GrCaps & caps,const GrAppliedClip * clip)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
fixedFunctionFlags() const157 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
onPrepareDraws(Target * target)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
onCombineIfPossible(GrOp * t,const GrCaps & caps)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.
addQuad(const GrPerspQuad & deviceQuad,const GrPerspQuad & localQuad,GrQuadType localQuadType,const SkPMColor4f & color,GrQuadAAFlags edgeAA,GrAAType aaType)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
quadCount() const308 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
MakePerEdge(GrContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags edgeAA,const SkMatrix & viewMatrix,const SkRect & rect,const GrUserStencilSettings * stencilSettings)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
MakePerEdgeWithLocalMatrix(GrContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags edgeAA,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect,const GrUserStencilSettings * stencilSettings)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
MakePerEdgeWithLocalRect(GrContext * context,GrPaint && paint,GrAAType aaType,GrQuadAAFlags edgeAA,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & localRect,const GrUserStencilSettings * stencilSettings)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
MakeSet(GrContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const GrRenderTargetContext::QuadSetEntry quads[],int cnt,const GrUserStencilSettings * stencilSettings)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
Make(GrContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const SkRect & rect,const GrUserStencilSettings * stencil)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
MakeWithLocalMatrix(GrContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const SkMatrix & localMatrix,const SkRect & rect,const GrUserStencilSettings * stencil)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
MakeWithLocalRect(GrContext * context,GrPaint && paint,GrAAType aaType,const SkMatrix & viewMatrix,const SkRect & rect,const SkRect & localRect,const GrUserStencilSettings * stencil)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
GR_DRAW_OP_TEST_DEFINE(FillRectOp)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