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