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