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