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