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