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