• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/GrDefaultGeoProcFactory.h"
13 #include "src/gpu/GrDrawOpTest.h"
14 #include "src/gpu/GrGpu.h"
15 #include "src/gpu/GrOpFlushState.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/GrGLSLGeometryProcessor.h"
22 #include "src/gpu/glsl/GrGLSLVarying.h"
23 #include "src/gpu/ops/GrLatticeOp.h"
24 #include "src/gpu/ops/GrMeshDrawOp.h"
25 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
26 
27 namespace {
28 
29 class LatticeGP : public GrGeometryProcessor {
30 public:
Make(GrGpu * gpu,const GrTextureProxy * proxy,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)31     static sk_sp<GrGeometryProcessor> Make(GrGpu* gpu,
32                                            const GrTextureProxy* proxy,
33                                            sk_sp<GrColorSpaceXform> csxf,
34                                            GrSamplerState::Filter filter,
35                                            bool wideColor) {
36         return sk_sp<GrGeometryProcessor>(
37                 new LatticeGP(gpu, proxy, std::move(csxf), filter, wideColor));
38     }
39 
name() const40     const char* name() const override { return "LatticeGP"; }
41 
getGLSLProcessorKey(const GrShaderCaps &,GrProcessorKeyBuilder * b) const42     void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const override {
43         b->add32(GrColorSpaceXform::XformKey(fColorSpaceXform.get()));
44     }
45 
createGLSLInstance(const GrShaderCaps & caps) const46     GrGLSLPrimitiveProcessor* createGLSLInstance(const GrShaderCaps& caps) const override {
47         class GLSLProcessor : public GrGLSLGeometryProcessor {
48         public:
49             void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor& proc,
50                          FPCoordTransformIter&& transformIter) override {
51                 const auto& latticeGP = proc.cast<LatticeGP>();
52                 this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
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->appendTextureLookupAndModulate(
81                         args.fOutputColor,
82                         args.fTexSamplers[0],
83                         "clamp(textureCoords, textureDomain.xy, textureDomain.zw)",
84                         kFloat2_GrSLType,
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:
LatticeGP(GrGpu * gpu,const GrTextureProxy * proxy,sk_sp<GrColorSpaceXform> csxf,GrSamplerState::Filter filter,bool wideColor)95     LatticeGP(GrGpu* gpu, const GrTextureProxy* proxy, sk_sp<GrColorSpaceXform> csxf,
96               GrSamplerState::Filter filter, bool wideColor)
97             : INHERITED(kLatticeGP_ClassID), fColorSpaceXform(std::move(csxf)) {
98 
99         GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
100                                                      filter);
101         uint32_t extraSamplerKey = gpu->getExtraSamplerKeyForProgram(samplerState,
102                                                                      proxy->backendFormat());
103 
104         fSampler.reset(proxy->textureType(), samplerState, proxy->textureSwizzle(),
105                        extraSamplerKey);
106         this->setTextureSamplerCnt(1);
107         fInPosition = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
108         fInTextureCoords = {"textureCoords", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
109         fInTextureDomain = {"textureDomain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
110         fInColor = MakeColorAttribute("color", wideColor);
111         this->setVertexAttributes(&fInPosition, 4);
112     }
113 
onTextureSampler(int) const114     const TextureSampler& onTextureSampler(int) const override { return fSampler; }
115 
116     Attribute fInPosition;
117     Attribute fInTextureCoords;
118     Attribute fInTextureDomain;
119     Attribute fInColor;
120 
121     sk_sp<GrColorSpaceXform> fColorSpaceXform;
122     TextureSampler fSampler;
123 
124     typedef GrGeometryProcessor INHERITED;
125 };
126 
127 class NonAALatticeOp final : public GrMeshDrawOp {
128 private:
129     using Helper = GrSimpleMeshDrawOpHelper;
130 
131 public:
132     DEFINE_OP_CLASS_ID
133 
134     static const int kVertsPerRect = 4;
135     static const int kIndicesPerRect = 6;
136 
Make(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXForm,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)137     static std::unique_ptr<GrDrawOp> Make(GrRecordingContext* context,
138                                           GrPaint&& paint,
139                                           const SkMatrix& viewMatrix,
140                                           sk_sp<GrTextureProxy> proxy,
141                                           sk_sp<GrColorSpaceXform> colorSpaceXForm,
142                                           GrSamplerState::Filter filter,
143                                           std::unique_ptr<SkLatticeIter> iter,
144                                           const SkRect& dst) {
145         SkASSERT(proxy);
146         return Helper::FactoryHelper<NonAALatticeOp>(context, std::move(paint), viewMatrix,
147                                                      std::move(proxy),
148                                                      std::move(colorSpaceXForm), filter,
149                                                      std::move(iter), dst);
150     }
151 
NonAALatticeOp(Helper::MakeArgs & helperArgs,const SkPMColor4f & color,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)152     NonAALatticeOp(Helper::MakeArgs& helperArgs, const SkPMColor4f& color,
153                    const SkMatrix& viewMatrix, sk_sp<GrTextureProxy> proxy,
154                    sk_sp<GrColorSpaceXform> colorSpaceXform, GrSamplerState::Filter filter,
155                    std::unique_ptr<SkLatticeIter> iter, const SkRect& dst)
156             : INHERITED(ClassID())
157             , fHelper(helperArgs, GrAAType::kNone)
158             , fProxy(std::move(proxy))
159             , fColorSpaceXform(std::move(colorSpaceXform))
160             , fFilter(filter) {
161         Patch& patch = fPatches.push_back();
162         patch.fViewMatrix = viewMatrix;
163         patch.fColor = color;
164         patch.fIter = std::move(iter);
165         patch.fDst = dst;
166 
167         // setup bounds
168         this->setTransformedBounds(patch.fDst, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo);
169     }
170 
name() const171     const char* name() const override { return "NonAALatticeOp"; }
172 
visitProxies(const VisitProxyFunc & func) const173     void visitProxies(const VisitProxyFunc& func) const override {
174         bool mipped = (GrSamplerState::Filter::kMipMap == fFilter);
175         func(fProxy.get(), GrMipMapped(mipped));
176         fHelper.visitProxies(func);
177     }
178 
179 #ifdef SK_DEBUG
dumpInfo() const180     SkString dumpInfo() const override {
181         SkString str;
182 
183         for (int i = 0; i < fPatches.count(); ++i) {
184             str.appendf("%d: Color: 0x%08x Dst [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n", i,
185                         fPatches[i].fColor.toBytes_RGBA(), fPatches[i].fDst.fLeft,
186                         fPatches[i].fDst.fTop, fPatches[i].fDst.fRight, fPatches[i].fDst.fBottom);
187         }
188 
189         str += fHelper.dumpInfo();
190         str += INHERITED::dumpInfo();
191         return str;
192     }
193 #endif
194 
fixedFunctionFlags() const195     FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
196 
finalize(const GrCaps & caps,const GrAppliedClip * clip,bool hasMixedSampledCoverage,GrClampType clampType)197     GrProcessorSet::Analysis finalize(
198             const GrCaps& caps, const GrAppliedClip* clip, bool hasMixedSampledCoverage,
199             GrClampType clampType) override {
200         auto opaque = fPatches[0].fColor.isOpaque() && GrPixelConfigIsOpaque(fProxy->config())
201                               ? GrProcessorAnalysisColor::Opaque::kYes
202                               : GrProcessorAnalysisColor::Opaque::kNo;
203         auto analysisColor = GrProcessorAnalysisColor(opaque);
204         auto result = fHelper.finalizeProcessors(
205                 caps, clip, hasMixedSampledCoverage, clampType, GrProcessorAnalysisCoverage::kNone,
206                 &analysisColor);
207         analysisColor.isConstant(&fPatches[0].fColor);
208         fWideColor = SkPMColor4fNeedsWideColor(fPatches[0].fColor, clampType, caps);
209         return result;
210     }
211 
212 private:
onPrepareDraws(Target * target)213     void onPrepareDraws(Target* target) override {
214         GrGpu* gpu = target->resourceProvider()->priv().gpu();
215         auto gp = LatticeGP::Make(gpu, fProxy.get(), fColorSpaceXform, fFilter, fWideColor);
216         if (!gp) {
217             SkDebugf("Couldn't create GrGeometryProcessor\n");
218             return;
219         }
220 
221         int patchCnt = fPatches.count();
222         int numRects = 0;
223         for (int i = 0; i < patchCnt; i++) {
224             numRects += fPatches[i].fIter->numRectsToDraw();
225         }
226 
227         if (!numRects) {
228             return;
229         }
230 
231         const size_t kVertexStride = gp->vertexStride();
232         sk_sp<const GrBuffer> indexBuffer = target->resourceProvider()->refQuadIndexBuffer();
233         if (!indexBuffer) {
234             SkDebugf("Could not allocate indices\n");
235             return;
236         }
237         PatternHelper helper(target, GrPrimitiveType::kTriangles, kVertexStride,
238                              std::move(indexBuffer), kVertsPerRect, kIndicesPerRect, numRects);
239         GrVertexWriter vertices{helper.vertices()};
240         if (!vertices.fPtr) {
241             SkDebugf("Could not allocate vertices\n");
242             return;
243         }
244 
245         for (int i = 0; i < patchCnt; i++) {
246             const Patch& patch = fPatches[i];
247 
248             GrVertexColor patchColor(patch.fColor, fWideColor);
249 
250             // Apply the view matrix here if it is scale-translate.  Otherwise, we need to
251             // wait until we've created the dst rects.
252             bool isScaleTranslate = patch.fViewMatrix.isScaleTranslate();
253             if (isScaleTranslate) {
254                 patch.fIter->mapDstScaleTranslate(patch.fViewMatrix);
255             }
256 
257             SkIRect srcR;
258             SkRect dstR;
259             SkPoint* patchPositions = reinterpret_cast<SkPoint*>(vertices.fPtr);
260             Sk4f scales(1.f / fProxy->width(), 1.f / fProxy->height(),
261                         1.f / fProxy->width(), 1.f / fProxy->height());
262             static const Sk4f kDomainOffsets(0.5f, 0.5f, -0.5f, -0.5f);
263             static const Sk4f kFlipOffsets(0.f, 1.f, 0.f, 1.f);
264             static const Sk4f kFlipMuls(1.f, -1.f, 1.f, -1.f);
265             while (patch.fIter->next(&srcR, &dstR)) {
266                 Sk4f coords(SkIntToScalar(srcR.fLeft), SkIntToScalar(srcR.fTop),
267                             SkIntToScalar(srcR.fRight), SkIntToScalar(srcR.fBottom));
268                 Sk4f domain = coords + kDomainOffsets;
269                 coords *= scales;
270                 domain *= scales;
271                 if (fProxy->origin() == kBottomLeft_GrSurfaceOrigin) {
272                     coords = kFlipMuls * coords + kFlipOffsets;
273                     domain = SkNx_shuffle<0, 3, 2, 1>(kFlipMuls * domain + kFlipOffsets);
274                 }
275                 SkRect texDomain;
276                 SkRect texCoords;
277                 domain.store(&texDomain);
278                 coords.store(&texCoords);
279 
280                 vertices.writeQuad(GrVertexWriter::TriStripFromRect(dstR),
281                                    GrVertexWriter::TriStripFromRect(texCoords),
282                                    texDomain,
283                                    patchColor);
284             }
285 
286             // If we didn't handle it above, apply the matrix here.
287             if (!isScaleTranslate) {
288                 SkMatrixPriv::MapPointsWithStride(patch.fViewMatrix, patchPositions, kVertexStride,
289                                                   kVertsPerRect * patch.fIter->numRectsToDraw());
290             }
291         }
292         auto fixedDynamicState = target->makeFixedDynamicState(1);
293         fixedDynamicState->fPrimitiveProcessorTextures[0] = fProxy.get();
294         helper.recordDraw(target, std::move(gp), fixedDynamicState);
295     }
296 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)297     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
298         fHelper.executeDrawsAndUploads(this, flushState, chainBounds);
299     }
300 
onCombineIfPossible(GrOp * t,const GrCaps & caps)301     CombineResult onCombineIfPossible(GrOp* t, const GrCaps& caps) override {
302         NonAALatticeOp* that = t->cast<NonAALatticeOp>();
303         if (fProxy != that->fProxy) {
304             return CombineResult::kCannotCombine;
305         }
306         if (fFilter != that->fFilter) {
307             return CombineResult::kCannotCombine;
308         }
309         if (GrColorSpaceXform::Equals(fColorSpaceXform.get(), that->fColorSpaceXform.get())) {
310             return CombineResult::kCannotCombine;
311         }
312         if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
313             return CombineResult::kCannotCombine;
314         }
315 
316         fPatches.move_back_n(that->fPatches.count(), that->fPatches.begin());
317         fWideColor |= that->fWideColor;
318         return CombineResult::kMerged;
319     }
320 
321     struct Patch {
322         SkMatrix fViewMatrix;
323         std::unique_ptr<SkLatticeIter> fIter;
324         SkRect fDst;
325         SkPMColor4f fColor;
326     };
327 
328     Helper fHelper;
329     SkSTArray<1, Patch, true> fPatches;
330     sk_sp<GrTextureProxy> fProxy;
331     sk_sp<GrColorSpaceXform> fColorSpaceXform;
332     GrSamplerState::Filter fFilter;
333     bool fWideColor;
334 
335     typedef GrMeshDrawOp INHERITED;
336 };
337 
338 }  // anonymous namespace
339 
340 namespace GrLatticeOp {
MakeNonAA(GrRecordingContext * context,GrPaint && paint,const SkMatrix & viewMatrix,sk_sp<GrTextureProxy> proxy,sk_sp<GrColorSpaceXform> colorSpaceXform,GrSamplerState::Filter filter,std::unique_ptr<SkLatticeIter> iter,const SkRect & dst)341 std::unique_ptr<GrDrawOp> MakeNonAA(GrRecordingContext* context,
342                                     GrPaint&& paint,
343                                     const SkMatrix& viewMatrix,
344                                     sk_sp<GrTextureProxy> proxy,
345                                     sk_sp<GrColorSpaceXform> colorSpaceXform,
346                                     GrSamplerState::Filter filter,
347                                     std::unique_ptr<SkLatticeIter> iter,
348                                     const SkRect& dst) {
349     return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(proxy),
350                                 std::move(colorSpaceXform), filter, std::move(iter), dst);
351 }
352 };
353 
354 #if GR_TEST_UTILS
355 #include "src/gpu/GrProxyProvider.h"
356 #include "src/gpu/GrRecordingContextPriv.h"
357 
358 /** Randomly divides subset into count divs. */
init_random_divs(int divs[],int count,int subsetStart,int subsetStop,SkRandom * random)359 static void init_random_divs(int divs[], int count, int subsetStart, int subsetStop,
360                              SkRandom* random) {
361     // Rules for lattice divs: Must be strictly increasing and in the range
362     // [subsetStart, subsetStop).
363     // Not terribly efficient alg for generating random divs:
364     // 1) Start with minimum legal pixels between each div.
365     // 2) Randomly assign the remaining pixels of the subset to divs.
366     // 3) Convert from pixel counts to div offsets.
367 
368     // 1) Initially each divs[i] represents the number of pixels between
369     // div i-1 and i. The initial div is allowed to be at subsetStart. There
370     // must be one pixel spacing between subsequent divs.
371     divs[0] = 0;
372     for (int i = 1; i < count; ++i) {
373         divs[i] = 1;
374     }
375     // 2) Assign the remaining subset pixels to fall
376     int subsetLength = subsetStop - subsetStart;
377     for (int i = 0; i < subsetLength - count; ++i) {
378         // +1 because count divs means count+1 intervals.
379         int entry = random->nextULessThan(count + 1);
380         // We don't have an entry to  to store the count after the last div
381         if (entry < count) {
382             divs[entry]++;
383         }
384     }
385     // 3) Now convert the counts between divs to pixel indices, incorporating the subset's offset.
386     int offset = subsetStart;
387     for (int i = 0; i < count; ++i) {
388         divs[i] += offset;
389         offset = divs[i];
390     }
391 }
392 
GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp)393 GR_DRAW_OP_TEST_DEFINE(NonAALatticeOp) {
394     SkCanvas::Lattice lattice;
395     // We loop because our random lattice code can produce an invalid lattice in the case where
396     // there is a single div separator in both x and y and both are aligned with the left and top
397     // edge of the image subset, respectively.
398     std::unique_ptr<int[]> xdivs;
399     std::unique_ptr<int[]> ydivs;
400     std::unique_ptr<SkCanvas::Lattice::RectType[]> flags;
401     std::unique_ptr<SkColor[]> colors;
402     SkIRect subset;
403     GrSurfaceDesc desc;
404     desc.fConfig = kRGBA_8888_GrPixelConfig;
405     desc.fWidth = random->nextRangeU(1, 1000);
406     desc.fHeight = random->nextRangeU(1, 1000);
407     GrSurfaceOrigin origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin
408                                                 : kBottomLeft_GrSurfaceOrigin;
409     const GrBackendFormat format =
410             context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
411                                                             GrRenderable::kNo);
412     auto proxy = context->priv().proxyProvider()->createProxy(format, desc, GrRenderable::kNo, 1,
413                                                               origin, SkBackingFit::kExact,
414                                                               SkBudgeted::kYes, GrProtected::kNo);
415 
416     do {
417         if (random->nextBool()) {
418             subset.fLeft = random->nextULessThan(desc.fWidth);
419             subset.fRight = random->nextRangeU(subset.fLeft + 1, desc.fWidth);
420             subset.fTop = random->nextULessThan(desc.fHeight);
421             subset.fBottom = random->nextRangeU(subset.fTop + 1, desc.fHeight);
422         } else {
423             subset.setXYWH(0, 0, desc.fWidth, desc.fHeight);
424         }
425         // SkCanvas::Lattice allows bounds to be null. However, SkCanvas creates a temp Lattice with
426         // a non-null bounds before creating a SkLatticeIter since SkLatticeIter requires a bounds.
427         lattice.fBounds = &subset;
428         lattice.fXCount = random->nextRangeU(1, subset.width());
429         lattice.fYCount = random->nextRangeU(1, subset.height());
430         xdivs.reset(new int[lattice.fXCount]);
431         ydivs.reset(new int[lattice.fYCount]);
432         init_random_divs(xdivs.get(), lattice.fXCount, subset.fLeft, subset.fRight, random);
433         init_random_divs(ydivs.get(), lattice.fYCount, subset.fTop, subset.fBottom, random);
434         lattice.fXDivs = xdivs.get();
435         lattice.fYDivs = ydivs.get();
436         bool hasFlags = random->nextBool();
437         if (hasFlags) {
438             int n = (lattice.fXCount + 1) * (lattice.fYCount + 1);
439             flags.reset(new SkCanvas::Lattice::RectType[n]);
440             colors.reset(new SkColor[n]);
441             for (int i = 0; i < n; ++i) {
442                 flags[i] = random->nextBool() ? SkCanvas::Lattice::kTransparent
443                                               : SkCanvas::Lattice::kDefault;
444             }
445             lattice.fRectTypes = flags.get();
446             lattice.fColors = colors.get();
447         } else {
448             lattice.fRectTypes = nullptr;
449             lattice.fColors = nullptr;
450         }
451     } while (!SkLatticeIter::Valid(desc.fWidth, desc.fHeight, lattice));
452     SkRect dst;
453     dst.fLeft = random->nextRangeScalar(-2000.5f, 1000.f);
454     dst.fTop = random->nextRangeScalar(-2000.5f, 1000.f);
455     dst.fRight = dst.fLeft + random->nextRangeScalar(0.5f, 1000.f);
456     dst.fBottom = dst.fTop + random->nextRangeScalar(0.5f, 1000.f);
457     std::unique_ptr<SkLatticeIter> iter(new SkLatticeIter(lattice, dst));
458     SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
459     auto csxf = GrTest::TestColorXform(random);
460     GrSamplerState::Filter filter =
461             random->nextBool() ? GrSamplerState::Filter::kNearest : GrSamplerState::Filter::kBilerp;
462     return NonAALatticeOp::Make(context, std::move(paint), viewMatrix, std::move(proxy),
463                                 std::move(csxf), filter, std::move(iter), dst);
464 }
465 
466 #endif
467