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