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