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