• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = &subset;
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