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 = ⊂
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