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 "src/gpu/ganesh/ops/LatticeOp.h"
9
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkRect.h"
12 #include "src/core/SkLatticeIter.h"
13 #include "src/core/SkMatrixPriv.h"
14 #include "src/gpu/BufferWriter.h"
15 #include "src/gpu/KeyBuilder.h"
16 #include "src/gpu/ganesh/GrGeometryProcessor.h"
17 #include "src/gpu/ganesh/GrGpu.h"
18 #include "src/gpu/ganesh/GrOpFlushState.h"
19 #include "src/gpu/ganesh/GrProgramInfo.h"
20 #include "src/gpu/ganesh/GrResourceProvider.h"
21 #include "src/gpu/ganesh/GrResourceProviderPriv.h"
22 #include "src/gpu/ganesh/SkGr.h"
23 #include "src/gpu/ganesh/glsl/GrGLSLColorSpaceXformHelper.h"
24 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
25 #include "src/gpu/ganesh/glsl/GrGLSLVarying.h"
26 #include "src/gpu/ganesh/ops/GrMeshDrawOp.h"
27 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
28
29 namespace skgpu::v1::LatticeOp {
30
31 namespace {
32
33 class LatticeGP : public GrGeometryProcessor {
34 public:
Make(SkArenaAlloc * arena,const GrSurfaceProxyView & view,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)35 static GrGeometryProcessor* Make(SkArenaAlloc* arena,
36 const GrSurfaceProxyView& view,
37 sk_sp<GrColorSpaceXform> csxf,
38 GrSamplerState::Filter filter,
39 bool wideColor) {
40 return arena->make([&](void* ptr) {
41 return new (ptr) LatticeGP(view, std::move(csxf), filter, wideColor);
42 });
43 }
44
name() const45 const char* name() const override { return "LatticeGP"; }
46
addToKey(const GrShaderCaps &,KeyBuilder * b) const47 void addToKey(const GrShaderCaps&, KeyBuilder* b) const override {
48 b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
49 }
50
makeProgramImpl(const GrShaderCaps &) const51 std::unique_ptr<ProgramImpl> makeProgramImpl(const GrShaderCaps&) const override {
52 class Impl : public ProgramImpl {
53 public:
54 void setData(const GrGLSLProgramDataManager& pdman,
55 const GrShaderCaps&,
56 const GrGeometryProcessor& geomProc) override {
57 const auto& latticeGP = geomProc.cast<LatticeGP>();
58 fColorSpaceXformHelper.setData(pdman, latticeGP.fColorSpaceXform.get());
59 }
60
61 private:
62 void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override {
63 using Interpolation = GrGLSLVaryingHandler::Interpolation;
64 const auto& latticeGP = args.fGeomProc.cast<LatticeGP>();
65 fColorSpaceXformHelper.emitCode(args.fUniformHandler,
66 latticeGP.fColorSpaceXform.get());
67
68 args.fVaryingHandler->emitAttributes(latticeGP);
69 WriteOutputPosition(args.fVertBuilder, gpArgs, latticeGP.fInPosition.name());
70 gpArgs->fLocalCoordVar = latticeGP.fInTextureCoords.asShaderVar();
71
72 args.fFragBuilder->codeAppend("float2 textureCoords;");
73 args.fVaryingHandler->addPassThroughAttribute(
74 latticeGP.fInTextureCoords.asShaderVar(),
75 "textureCoords");
76 args.fFragBuilder->codeAppend("float4 textureDomain;");
77 args.fVaryingHandler->addPassThroughAttribute(
78 latticeGP.fInTextureDomain.asShaderVar(),
79 "textureDomain",
80 Interpolation::kCanBeFlat);
81 args.fFragBuilder->codeAppendf("half4 %s;", args.fOutputColor);
82 args.fVaryingHandler->addPassThroughAttribute(latticeGP.fInColor.asShaderVar(),
83 args.fOutputColor,
84 Interpolation::kCanBeFlat);
85 args.fFragBuilder->codeAppendf("%s = ", args.fOutputColor);
86 args.fFragBuilder->appendTextureLookupAndBlend(
87 args.fOutputColor,
88 SkBlendMode::kModulate,
89 args.fTexSamplers[0],
90 "clamp(textureCoords, textureDomain.xy, textureDomain.zw)",
91 &fColorSpaceXformHelper);
92 args.fFragBuilder->codeAppend(";");
93 args.fFragBuilder->codeAppendf("const half4 %s = half4(1);", args.fOutputCoverage);
94 }
95
96 GrGLSLColorSpaceXformHelper fColorSpaceXformHelper;
97 };
98
99 return std::make_unique<Impl>();
100 }
101
102 private:
LatticeGP(const GrSurfaceProxyView & view,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)103 LatticeGP(const GrSurfaceProxyView& view, sk_sp<GrColorSpaceXform> csxf,
104 GrSamplerState::Filter filter, bool wideColor)
105 : INHERITED(kLatticeGP_ClassID)
106 , fColorSpaceXform(std::move(csxf)) {
107
108 fSampler.reset(GrSamplerState(GrSamplerState::WrapMode::kClamp, filter),
109 view.proxy()->backendFormat(), view.swizzle());
110 this->setTextureSamplerCnt(1);
111 fInPosition = {"position", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
112 fInTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType, SkSLType::kFloat2};
113 fInTextureDomain = {"textureDomain", kFloat4_GrVertexAttribType, SkSLType::kFloat4};
114 fInColor = MakeColorAttribute("color", wideColor);
115 this->setVertexAttributesWithImplicitOffsets(&fInPosition, 4);
116 }
117
onTextureSampler(int) const118 const TextureSampler& onTextureSampler(int) const override { return fSampler; }
119
120 Attribute fInPosition;
121 Attribute fInTextureCoords;
122 Attribute fInTextureDomain;
123 Attribute fInColor;
124
125 sk_sp<GrColorSpaceXform> fColorSpaceXform;
126 TextureSampler fSampler;
127
128 using INHERITED = GrGeometryProcessor;
129 };
130
131 class NonAALatticeOp final : public GrMeshDrawOp {
132 private:
133 using Helper = GrSimpleMeshDrawOpHelper;
134
135 public:
136 DEFINE_OP_CLASS_ID
137
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,GrSurfaceProxyView view,SkAlphaType alphaType,sk_sp<GrColorSpaceXform> colorSpaceXForm,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)138 static GrOp::Owner Make(GrRecordingContext* context,
139 GrPaint&& paint,
140 const SkMatrix& viewMatrix,
141 GrSurfaceProxyView view,
142 SkAlphaType alphaType,
143 sk_sp<GrColorSpaceXform> colorSpaceXForm,
144 GrSamplerState::Filter filter,
145 std::unique_ptr<SkLatticeIter> iter,
146 const SkRect& dst) {
147 SkASSERT(view.proxy());
148 return Helper::FactoryHelper<NonAALatticeOp>(context, std::move(paint), viewMatrix,
149 std::move(view), alphaType,
150 std::move(colorSpaceXForm), filter,
151 std::move(iter), dst);
152 }
153
NonAALatticeOp(GrProcessorSet * processorSet,const SkPMColor4f & color,const SkMatrix & viewMatrix,GrSurfaceProxyView view,SkAlphaType alphaType,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)154 NonAALatticeOp(GrProcessorSet* processorSet, const SkPMColor4f& color,
155 const SkMatrix& viewMatrix, GrSurfaceProxyView view,
156 SkAlphaType alphaType, sk_sp<GrColorSpaceXform> colorSpaceXform,
157 GrSamplerState::Filter filter, std::unique_ptr<SkLatticeIter> iter,
158 const SkRect& dst)
159 : INHERITED(ClassID())
160 , fHelper(processorSet, GrAAType::kNone)
161 , fView(std::move(view))
162 , fAlphaType(alphaType)
163 , fColorSpaceXform(std::move(colorSpaceXform))
164 , fFilter(filter) {
165 Patch& patch = fPatches.push_back();
166 patch.fViewMatrix = viewMatrix;
167 patch.fColor = color;
168 patch.fIter = std::move(iter);
169 patch.fDst = dst;
170
171 // setup bounds
172 this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsHairline::kNo);
173 }
174
name() const175 const char* name() const override { return "NonAALatticeOp"; }
176
visitProxies(const GrVisitProxyFunc & func) const177 void visitProxies(const GrVisitProxyFunc& func) const override {
178 func(fView.proxy(), GrMipmapped::kNo);
179 if (fProgramInfo) {
180 fProgramInfo->visitFPProxies(func);
181 } else {
182 fHelper.visitProxies(func);
183 }
184 }
185
fixedFunctionFlags() const186 FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
187
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)188 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
189 GrClampType clampType) override {
190 auto opaque = fPatches[0].fColor.isOpaque() && fAlphaType == kOpaque_SkAlphaType
191 ? GrProcessorAnalysisColor::Opaque::kYes
192 : GrProcessorAnalysisColor::Opaque::kNo;
193 auto analysisColor = GrProcessorAnalysisColor(opaque);
194 auto result = fHelper.finalizeProcessors(caps, clip, clampType,
195 GrProcessorAnalysisCoverage::kNone,
196 &analysisColor);
197 analysisColor.isConstant(&fPatches[0].fColor);
198 fWideColor = !fPatches[0].fColor.fitsInBytes();
199 return result;
200 }
201
202 private:
programInfo()203 GrProgramInfo* programInfo() override { return fProgramInfo; }
204
onCreateProgramInfo(const GrCaps * caps,SkArenaAlloc * arena,const GrSurfaceProxyView & writeView,bool usesMSAASurface,GrAppliedClip && appliedClip,const GrDstProxyView & dstProxyView,GrXferBarrierFlags renderPassXferBarriers,GrLoadOp colorLoadOp)205 void onCreateProgramInfo(const GrCaps* caps,
206 SkArenaAlloc* arena,
207 const GrSurfaceProxyView& writeView,
208 bool usesMSAASurface,
209 GrAppliedClip&& appliedClip,
210 const GrDstProxyView& dstProxyView,
211 GrXferBarrierFlags renderPassXferBarriers,
212 GrLoadOp colorLoadOp) override {
213
214 auto gp = LatticeGP::Make(arena, fView, fColorSpaceXform, fFilter, fWideColor);
215 if (!gp) {
216 return;
217 }
218
219 fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(caps, arena, writeView,
220 usesMSAASurface,
221 std::move(appliedClip),
222 dstProxyView, gp,
223 fHelper.detachProcessorSet(),
224 GrPrimitiveType::kTriangles,
225 renderPassXferBarriers,
226 colorLoadOp,
227 fHelper.pipelineFlags(),
228 &GrUserStencilSettings::kUnused);
229 }
230
onPrepareDraws(GrMeshDrawTarget * target)231 void onPrepareDraws(GrMeshDrawTarget* target) override {
232 if (!fProgramInfo) {
233 this->createProgramInfo(target);
234 if (!fProgramInfo) {
235 return;
236 }
237 }
238
239 int patchCnt = fPatches.size();
240 int numRects = 0;
241 for (int i = 0; i < patchCnt; i++) {
242 numRects += fPatches[i].fIter->numRectsToDraw();
243 }
244
245 if (!numRects) {
246 return;
247 }
248
249 const size_t kVertexStride = fProgramInfo->geomProc().vertexStride();
250
251 QuadHelper helper(target, kVertexStride, numRects);
252
253 VertexWriter vertices{helper.vertices()};
254 if (!vertices) {
255 SkDebugf("Could not allocate vertices\n");
256 return;
257 }
258
259 for (int i = 0; i < patchCnt; i++) {
260 const Patch& patch = fPatches[i];
261
262 VertexColor patchColor(patch.fColor, fWideColor);
263
264 // Apply the view matrix here if it is scale-translate. Otherwise, we need to
265 // wait until we've created the dst rects.
266 bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate();
267 if (isScaleTranslate) {
268 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix);
269 }
270
271 SkIRect srcR;
272 SkRect dstR;
273 skvx::float4 scales(1.f / fView.proxy()->width(), 1.f / fView.proxy()->height(),
274 1.f / fView.proxy()->width(), 1.f / fView.proxy()->height());
275 static const skvx::float4 kDomainOffsets(0.5f, 0.5f, -0.5f, -0.5f);
276 static const skvx::float4 kFlipOffsets(0.f, 1.f, 0.f, 1.f);
277 static const skvx::float4 kFlipMuls(1.f, -1.f, 1.f, -1.f);
278 while (patch.fIter->next(&srcR, &dstR)) {
279 skvx::float4 coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop),
280 SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom));
281 skvx::float4 domain = coords + kDomainOffsets;
282 coords *= scales;
283 domain *= scales;
284 if (fView.origin() == kBottomLeft_GrSurfaceOrigin) {
285 coords = kFlipMuls * coords + kFlipOffsets;
286 domain = skvx::shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets);
287 }
288 SkRect texDomain;
289 SkRect texCoords;
290 domain.store(&texDomain);
291 coords.store(&texCoords);
292
293 if (isScaleTranslate) {
294 vertices.writeQuad(VertexWriter::TriStripFromRect(dstR),
295 VertexWriter::TriStripFromRect(texCoords),
296 texDomain,
297 patchColor);
298 } else {
299 SkPoint mappedPts[4];
300 patch.fViewMatrix.mapRectToQuad(mappedPts, dstR);
301 // In the above if statement, writeQuad writes the corners as:
302 // left-top, left-bottom, right-top, right-bottom.
303 // However, mapRectToQuad returns them in the order:
304 // left-top, right-top, right-bottom, left-bottom
305 // Thus we write out the vertices to match the writeQuad path.
306 vertices << mappedPts[0]
307 << SkPoint::Make(texCoords.fLeft, texCoords.fTop)
308 << texDomain
309 << patchColor;
310 vertices << mappedPts[3]
311 << SkPoint::Make(texCoords.fLeft, texCoords.fBottom)
312 << texDomain
313 << patchColor;
314 vertices << mappedPts[1]
315 << SkPoint::Make(texCoords.fRight, texCoords.fTop)
316 << texDomain
317 << patchColor;
318 vertices << mappedPts[2]
319 << SkPoint::Make(texCoords.fRight, texCoords.fBottom)
320 << texDomain
321 << patchColor;
322 }
323 }
324 }
325
326 fMesh = helper.mesh();
327 }
328
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)329 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
330 if (!fProgramInfo || !fMesh) {
331 return;
332 }
333
334 flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
335 flushState->bindTextures(fProgramInfo->geomProc(),
336 *fView.proxy(),
337 fProgramInfo->pipeline());
338 flushState->drawMesh(*fMesh);
339 }
340
onCombineIfPossible(GrOp * t,SkArenaAlloc *,const GrCaps & caps)341 CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
342 NonAALatticeOp* that = t->cast<NonAALatticeOp>();
343 if (fView != that->fView) {
344 return CombineResult::kCannotCombine;
345 }
346 if (fFilter != that->fFilter) {
347 return CombineResult::kCannotCombine;
348 }
349 if (GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
350 return CombineResult::kCannotCombine;
351 }
352 if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
353 return CombineResult::kCannotCombine;
354 }
355
356 fPatches.move_back_n(that->fPatches.size(), that->fPatches.begin());
357 fWideColor |= that->fWideColor;
358 return CombineResult::kMerged;
359 }
360
361 #if GR_TEST_UTILS
onDumpInfo() const362 SkString onDumpInfo() const override {
363 SkString str;
364
365 for (int i = 0; i < fPatches.size(); ++i) {
366 str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
367 fPatches[i].fColor.toBytes_RGBA(), fPatches[i].fDst.fLeft,
368 fPatches[i].fDst.fTop, fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom);
369 }
370
371 str += fHelper.dumpInfo();
372 return str;
373 }
374 #endif
375
376 struct Patch {
377 SkMatrix fViewMatrix;
378 std::unique_ptr<SkLatticeIter> fIter;
379 SkRect fDst;
380 SkPMColor4f fColor;
381 };
382
383 Helper fHelper;
384 SkSTArray<1, Patch, true> fPatches;
385 GrSurfaceProxyView fView;
386 SkAlphaType fAlphaType;
387 sk_sp<GrColorSpaceXform> fColorSpaceXform;
388 GrSamplerState::Filter fFilter;
389 bool fWideColor;
390
391 GrSimpleMesh* fMesh = nullptr;
392 GrProgramInfo* fProgramInfo = nullptr;
393
394 using INHERITED = GrMeshDrawOp;
395 };
396
397 } // anonymous namespace
398
MakeNonAA(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,GrSurfaceProxyView view,SkAlphaType alphaType,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)399 GrOp::Owner MakeNonAA(GrRecordingContext* context,
400 GrPaint&& paint,
401 const SkMatrix& viewMatrix,
402 GrSurfaceProxyView view,
403 SkAlphaType alphaType,
404 sk_sp<GrColorSpaceXform> colorSpaceXform,
405 GrSamplerState::Filter filter,
406 std::unique_ptr<SkLatticeIter> iter,
407 const SkRect& dst) {
408 return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(view), alphaType,
409 std::move(colorSpaceXform), filter, std::move(iter), dst);
410 }
411
412 } // namespace skgpu::v1::LatticeOp
413
414 #if GR_TEST_UTILS
415 #include "src/gpu/ganesh/GrDrawOpTest.h"
416 #include "src/gpu/ganesh/GrProxyProvider.h"
417 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
418
419 /** Randomly divides subset into count divs. */
init_random_divs(int divs[],int count,int subsetStart,int subsetStop,SkRandom * random)420 static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop,
421 SkRandom* random) {
422 // Rules for lattice divs: Must be strictly increasing and in the range
423 // [subsetStart, subsetStop.
424 // Not terribly efficient alg for generating random divs:
425 // 1 Start with minimum legal pixels between each div.
426 // 2) Randomly assign the remaining pixels of the subset to divs.
427 // 3) Convert from pixel counts to div offsets.
428
429 // 1) Initially each divs[i] represents the number of pixels between
430 // div i-1 and i. The initial div is allowed to be at subsetStart. There
431 // must be one pixel spacing between subsequent divs.
432 divs[0] = 0;
433 for (int i = 1; i < count; ++i) {
434 divs[i] = 1;
435 }
436 // 2) Assign the remaining subset pixels to fall
437 int subsetLength = subsetStop - subsetStart;
438 for (int i = 0; i < subsetLength - count; ++i) {
439 // +1 because count divs means count+1 intervals.
440 int entry = random->nextULessThan(count + 1);
441 // We don't have an entry to to store the count after the last div
442 if (entry < count) {
443 divs[entry]++;
444 }
445 }
446 // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset.
447 int offset = subsetStart;
448 for (int i = 0; i < count; ++i) {
449 divs[i] += offset;
450 offset = divs[i];
451 }
452 }
453
GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp)454 GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) {
455 SkCanvas::Lattice lattice;
456 // We loop because our random lattice code can produce an invalid lattice in the case where
457 // there is a single div separator in both x and y and both are aligned with the left and top
458 // edge of the image subset, respectively.
459 std::unique_ptr<int[]> xdivs;
460 std::unique_ptr<int[]> ydivs;
461 std::unique_ptr<SkCanvas::Lattice::RectType[]> flags;
462 std::unique_ptr<SkColor[]> colors;
463 SkIRect subset;
464 SkISize dims;
465 dims.fWidth = random->nextRangeU(1, 1000);
466 dims.fHeight = random->nextRangeU(1, 1000);
467 GrSurfaceOrigin origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin
468 : kBottomLeft_GrSurfaceOrigin;
469 const GrBackendFormat format =
470 context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
471 GrRenderable::kNo);
472
473 auto proxy = context->priv().proxyProvider()->createProxy(format,
474 dims,
475 GrRenderable::kNo,
476 1,
477 GrMipmapped::kNo,
478 SkBackingFit::kExact,
479 skgpu::Budgeted::kYes,
480 GrProtected::kNo,
481 /*label=*/"LatticeOp");
482
483 do {
484 if (random->nextBool()) {
485 subset.fLeft = random->nextULessThan(dims.fWidth);
486 subset.fRight = random->nextRangeU(subset.fLeft + 1, dims.fWidth);
487 subset.fTop = random->nextULessThan(dims.fHeight);
488 subset.fBottom = random->nextRangeU(subset.fTop + 1, dims.fHeight);
489 } else {
490 subset.setXYWH(0, 0, dims.fWidth, dims.fHeight);
491 }
492 // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with
493 // a non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds.
494 lattice.fBounds = ⊂
495 lattice.fXCount = random->nextRangeU(1, subset.width());
496 lattice.fYCount = random->nextRangeU(1, subset.height());
497 xdivs.reset(new int[lattice.fXCount]);
498 ydivs.reset(new int[lattice.fYCount]);
499 init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random);
500 init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random);
501 lattice.fXDivs = xdivs.get();
502 lattice.fYDivs = ydivs.get();
503 bool hasFlags = random->nextBool();
504 if (hasFlags) {
505 int n = (lattice.fXCount + 1) * (lattice.fYCount + 1);
506 flags.reset(new SkCanvas::Lattice::RectType[n]);
507 colors.reset(new SkColor[n]);
508 for (int i = 0; i < n; ++i) {
509 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent
510 : SkCanvas::Lattice::kDefault;
511 }
512 lattice.fRectTypes = flags.get();
513 lattice.fColors = colors.get();
514 } else {
515 lattice.fRectTypes = nullptr;
516 lattice.fColors = nullptr;
517 }
518 } while (!SkLatticeIter::Valid(dims.fWidth, dims.fHeight, lattice));
519 SkRect dst;
520 dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f);
521 dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f);
522 dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f);
523 dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f);
524 std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst));
525 SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
526 auto csxf = GrTest::TestColorXform(random);
527 GrSamplerState::Filter filter =
528 random->nextBool() ? GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kLinear;
529
530 GrSurfaceProxyView view(
531 std::move(proxy), origin,
532 context->priv().caps()->getReadSwizzle(format, GrColorType::kRGBA_8888));
533
534 return skgpu::v1::LatticeOp::NonAALatticeOp::Make(context, std::move(paint), viewMatrix,
535 std::move(view), kPremul_SkAlphaType,
536 std::move(csxf), filter, std::move(iter),
537 dst);
538 }
539
540 #endif
541