1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2014 Google Inc.
3cb93a386Sopenharmony_ci * Copyright 2017 ARM Ltd.
4cb93a386Sopenharmony_ci *
5cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
6cb93a386Sopenharmony_ci * found in the LICENSE file.
7cb93a386Sopenharmony_ci */
8cb93a386Sopenharmony_ci
9cb93a386Sopenharmony_ci#include "src/gpu/ops/SmallPathRenderer.h"
10cb93a386Sopenharmony_ci
11cb93a386Sopenharmony_ci#include "include/core/SkPaint.h"
12cb93a386Sopenharmony_ci#include "src/core/SkAutoPixmapStorage.h"
13cb93a386Sopenharmony_ci#include "src/core/SkDistanceFieldGen.h"
14cb93a386Sopenharmony_ci#include "src/core/SkDraw.h"
15cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h"
16cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h"
17cb93a386Sopenharmony_ci#include "src/core/SkPointPriv.h"
18cb93a386Sopenharmony_ci#include "src/core/SkRasterClip.h"
19cb93a386Sopenharmony_ci#include "src/gpu/BufferWriter.h"
20cb93a386Sopenharmony_ci#include "src/gpu/GrBuffer.h"
21cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h"
22cb93a386Sopenharmony_ci#include "src/gpu/GrDistanceFieldGenFromVector.h"
23cb93a386Sopenharmony_ci#include "src/gpu/GrDrawOpTest.h"
24cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h"
25cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBitmapTextGeoProc.h"
26cb93a386Sopenharmony_ci#include "src/gpu/effects/GrDistanceFieldGeoProc.h"
27cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuad.h"
28cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrStyledShape.h"
29cb93a386Sopenharmony_ci#include "src/gpu/ops/GrMeshDrawOp.h"
30cb93a386Sopenharmony_ci#include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
31cb93a386Sopenharmony_ci#include "src/gpu/ops/SmallPathAtlasMgr.h"
32cb93a386Sopenharmony_ci#include "src/gpu/ops/SmallPathShapeData.h"
33cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h"
34cb93a386Sopenharmony_ci
35cb93a386Sopenharmony_cinamespace skgpu::v1 {
36cb93a386Sopenharmony_ci
37cb93a386Sopenharmony_cinamespace {
38cb93a386Sopenharmony_ci
39cb93a386Sopenharmony_ci// mip levels
40cb93a386Sopenharmony_cistatic constexpr SkScalar kIdealMinMIP = 12;
41cb93a386Sopenharmony_cistatic constexpr SkScalar kMaxMIP = 162;
42cb93a386Sopenharmony_ci
43cb93a386Sopenharmony_cistatic constexpr SkScalar kMaxDim = 73;
44cb93a386Sopenharmony_cistatic constexpr SkScalar kMinSize = SK_ScalarHalf;
45cb93a386Sopenharmony_cistatic constexpr SkScalar kMaxSize = 2*kMaxMIP;
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci////////////////////////////////////////////////////////////////////////////////
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ci// padding around path bounds to allow for antialiased pixels
50cb93a386Sopenharmony_cistatic const int kAntiAliasPad = 1;
51cb93a386Sopenharmony_ci
52cb93a386Sopenharmony_ciclass SmallPathOp final : public GrMeshDrawOp {
53cb93a386Sopenharmony_ciprivate:
54cb93a386Sopenharmony_ci    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_cipublic:
57cb93a386Sopenharmony_ci    DEFINE_OP_CLASS_ID
58cb93a386Sopenharmony_ci
59cb93a386Sopenharmony_ci    static GrOp::Owner Make(GrRecordingContext* context,
60cb93a386Sopenharmony_ci                            GrPaint&& paint,
61cb93a386Sopenharmony_ci                            const GrStyledShape& shape,
62cb93a386Sopenharmony_ci                            const SkMatrix& viewMatrix,
63cb93a386Sopenharmony_ci                            bool gammaCorrect,
64cb93a386Sopenharmony_ci                            const GrUserStencilSettings* stencilSettings) {
65cb93a386Sopenharmony_ci        return Helper::FactoryHelper<SmallPathOp>(context, std::move(paint), shape, viewMatrix,
66cb93a386Sopenharmony_ci                                                  gammaCorrect, stencilSettings);
67cb93a386Sopenharmony_ci    }
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_ci    SmallPathOp(GrProcessorSet* processorSet, const SkPMColor4f& color, const GrStyledShape& shape,
70cb93a386Sopenharmony_ci                const SkMatrix& viewMatrix, bool gammaCorrect,
71cb93a386Sopenharmony_ci                const GrUserStencilSettings* stencilSettings)
72cb93a386Sopenharmony_ci            : INHERITED(ClassID())
73cb93a386Sopenharmony_ci            , fHelper(processorSet, GrAAType::kCoverage, stencilSettings) {
74cb93a386Sopenharmony_ci        SkASSERT(shape.hasUnstyledKey());
75cb93a386Sopenharmony_ci        // Compute bounds
76cb93a386Sopenharmony_ci        this->setTransformedBounds(shape.bounds(), viewMatrix, HasAABloat::kYes, IsHairline::kNo);
77cb93a386Sopenharmony_ci
78cb93a386Sopenharmony_ci#if defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
79cb93a386Sopenharmony_ci        fUsesDistanceField = true;
80cb93a386Sopenharmony_ci#else
81cb93a386Sopenharmony_ci        // only use distance fields on desktop and Android framework to save space in the atlas
82cb93a386Sopenharmony_ci        fUsesDistanceField = this->bounds().width() > kMaxMIP || this->bounds().height() > kMaxMIP;
83cb93a386Sopenharmony_ci#endif
84cb93a386Sopenharmony_ci        // always use distance fields if in perspective
85cb93a386Sopenharmony_ci        fUsesDistanceField = fUsesDistanceField || viewMatrix.hasPerspective();
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci        fShapes.emplace_back(Entry{color, shape, viewMatrix});
88cb93a386Sopenharmony_ci
89cb93a386Sopenharmony_ci        fGammaCorrect = gammaCorrect;
90cb93a386Sopenharmony_ci    }
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci    const char* name() const override { return "SmallPathOp"; }
93cb93a386Sopenharmony_ci
94cb93a386Sopenharmony_ci    void visitProxies(const GrVisitProxyFunc& func) const override {
95cb93a386Sopenharmony_ci        fHelper.visitProxies(func);
96cb93a386Sopenharmony_ci    }
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_ci    FixedFunctionFlags fixedFunctionFlags() const override { return fHelper.fixedFunctionFlags(); }
99cb93a386Sopenharmony_ci
100cb93a386Sopenharmony_ci    GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
101cb93a386Sopenharmony_ci                                      GrClampType clampType) override {
102cb93a386Sopenharmony_ci        return fHelper.finalizeProcessors(caps, clip, clampType,
103cb93a386Sopenharmony_ci                                          GrProcessorAnalysisCoverage::kSingleChannel,
104cb93a386Sopenharmony_ci                                          &fShapes.front().fColor, &fWideColor);
105cb93a386Sopenharmony_ci    }
106cb93a386Sopenharmony_ci
107cb93a386Sopenharmony_ciprivate:
108cb93a386Sopenharmony_ci    struct FlushInfo {
109cb93a386Sopenharmony_ci        sk_sp<const GrBuffer> fVertexBuffer;
110cb93a386Sopenharmony_ci        sk_sp<const GrBuffer> fIndexBuffer;
111cb93a386Sopenharmony_ci        GrGeometryProcessor*  fGeometryProcessor;
112cb93a386Sopenharmony_ci        const GrSurfaceProxy** fPrimProcProxies;
113cb93a386Sopenharmony_ci        int fVertexOffset;
114cb93a386Sopenharmony_ci        int fInstancesToFlush;
115cb93a386Sopenharmony_ci    };
116cb93a386Sopenharmony_ci
117cb93a386Sopenharmony_ci    GrProgramInfo* programInfo() override {
118cb93a386Sopenharmony_ci        // TODO [PI]: implement
119cb93a386Sopenharmony_ci        return nullptr;
120cb93a386Sopenharmony_ci    }
121cb93a386Sopenharmony_ci
122cb93a386Sopenharmony_ci    void onCreateProgramInfo(const GrCaps*,
123cb93a386Sopenharmony_ci                             SkArenaAlloc*,
124cb93a386Sopenharmony_ci                             const GrSurfaceProxyView& writeView,
125cb93a386Sopenharmony_ci                             bool usesMSAASurface,
126cb93a386Sopenharmony_ci                             GrAppliedClip&&,
127cb93a386Sopenharmony_ci                             const GrDstProxyView&,
128cb93a386Sopenharmony_ci                             GrXferBarrierFlags renderPassXferBarriers,
129cb93a386Sopenharmony_ci                             GrLoadOp colorLoadOp) override {
130cb93a386Sopenharmony_ci        // We cannot surface the SmallPathOp's programInfo at record time. As currently
131cb93a386Sopenharmony_ci        // implemented, the GP is modified at flush time based on the number of pages in the
132cb93a386Sopenharmony_ci        // atlas.
133cb93a386Sopenharmony_ci    }
134cb93a386Sopenharmony_ci
135cb93a386Sopenharmony_ci    void onPrePrepareDraws(GrRecordingContext*,
136cb93a386Sopenharmony_ci                           const GrSurfaceProxyView& writeView,
137cb93a386Sopenharmony_ci                           GrAppliedClip*,
138cb93a386Sopenharmony_ci                           const GrDstProxyView&,
139cb93a386Sopenharmony_ci                           GrXferBarrierFlags renderPassXferBarriers,
140cb93a386Sopenharmony_ci                           GrLoadOp colorLoadOp) override {
141cb93a386Sopenharmony_ci        // TODO [PI]: implement
142cb93a386Sopenharmony_ci    }
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci    void onPrepareDraws(GrMeshDrawTarget* target) override {
145cb93a386Sopenharmony_ci        int instanceCount = fShapes.count();
146cb93a386Sopenharmony_ci
147cb93a386Sopenharmony_ci        auto atlasMgr = target->smallPathAtlasManager();
148cb93a386Sopenharmony_ci        if (!atlasMgr) {
149cb93a386Sopenharmony_ci            return;
150cb93a386Sopenharmony_ci        }
151cb93a386Sopenharmony_ci
152cb93a386Sopenharmony_ci        static constexpr int kMaxTextures = GrDistanceFieldPathGeoProc::kMaxTextures;
153cb93a386Sopenharmony_ci        static_assert(GrBitmapTextGeoProc::kMaxTextures == kMaxTextures);
154cb93a386Sopenharmony_ci
155cb93a386Sopenharmony_ci        FlushInfo flushInfo;
156cb93a386Sopenharmony_ci        flushInfo.fPrimProcProxies = target->allocPrimProcProxyPtrs(kMaxTextures);
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci        int numActiveProxies;
159cb93a386Sopenharmony_ci        const GrSurfaceProxyView* views = atlasMgr->getViews(&numActiveProxies);
160cb93a386Sopenharmony_ci        for (int i = 0; i < numActiveProxies; ++i) {
161cb93a386Sopenharmony_ci            // This op does not know its atlas proxies when it is added to a OpsTasks, so the
162cb93a386Sopenharmony_ci            // proxies don't get added during the visitProxies call. Thus we add them here.
163cb93a386Sopenharmony_ci            flushInfo.fPrimProcProxies[i] = views[i].proxy();
164cb93a386Sopenharmony_ci            target->sampledProxyArray()->push_back(views[i].proxy());
165cb93a386Sopenharmony_ci        }
166cb93a386Sopenharmony_ci
167cb93a386Sopenharmony_ci        // Setup GrGeometryProcessor
168cb93a386Sopenharmony_ci        const SkMatrix& ctm = fShapes[0].fViewMatrix;
169cb93a386Sopenharmony_ci        if (fUsesDistanceField) {
170cb93a386Sopenharmony_ci            uint32_t flags = 0;
171cb93a386Sopenharmony_ci            // Still need to key off of ctm to pick the right shader for the transformed quad
172cb93a386Sopenharmony_ci            flags |= ctm.isScaleTranslate() ? kScaleOnly_DistanceFieldEffectFlag : 0;
173cb93a386Sopenharmony_ci            flags |= ctm.isSimilarity() ? kSimilarity_DistanceFieldEffectFlag : 0;
174cb93a386Sopenharmony_ci            flags |= fGammaCorrect ? kGammaCorrect_DistanceFieldEffectFlag : 0;
175cb93a386Sopenharmony_ci
176cb93a386Sopenharmony_ci            const SkMatrix* matrix;
177cb93a386Sopenharmony_ci            SkMatrix invert;
178cb93a386Sopenharmony_ci            if (ctm.hasPerspective()) {
179cb93a386Sopenharmony_ci                matrix = &ctm;
180cb93a386Sopenharmony_ci            } else if (fHelper.usesLocalCoords()) {
181cb93a386Sopenharmony_ci                if (!ctm.invert(&invert)) {
182cb93a386Sopenharmony_ci                    return;
183cb93a386Sopenharmony_ci                }
184cb93a386Sopenharmony_ci                matrix = &invert;
185cb93a386Sopenharmony_ci            } else {
186cb93a386Sopenharmony_ci                matrix = &SkMatrix::I();
187cb93a386Sopenharmony_ci            }
188cb93a386Sopenharmony_ci            flushInfo.fGeometryProcessor = GrDistanceFieldPathGeoProc::Make(
189cb93a386Sopenharmony_ci                    target->allocator(), *target->caps().shaderCaps(), *matrix, fWideColor,
190cb93a386Sopenharmony_ci                    views, numActiveProxies, GrSamplerState::Filter::kLinear,
191cb93a386Sopenharmony_ci                    flags);
192cb93a386Sopenharmony_ci        } else {
193cb93a386Sopenharmony_ci            SkMatrix invert;
194cb93a386Sopenharmony_ci            if (fHelper.usesLocalCoords()) {
195cb93a386Sopenharmony_ci                if (!ctm.invert(&invert)) {
196cb93a386Sopenharmony_ci                    return;
197cb93a386Sopenharmony_ci                }
198cb93a386Sopenharmony_ci            }
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci            flushInfo.fGeometryProcessor = GrBitmapTextGeoProc::Make(
201cb93a386Sopenharmony_ci                    target->allocator(), *target->caps().shaderCaps(), this->color(), fWideColor,
202cb93a386Sopenharmony_ci                    views, numActiveProxies, GrSamplerState::Filter::kNearest,
203cb93a386Sopenharmony_ci                    kA8_GrMaskFormat, invert, false);
204cb93a386Sopenharmony_ci        }
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci        // allocate vertices
207cb93a386Sopenharmony_ci        const size_t kVertexStride = flushInfo.fGeometryProcessor->vertexStride();
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ci        // We need to make sure we don't overflow a 32 bit int when we request space in the
210cb93a386Sopenharmony_ci        // makeVertexSpace call below.
211cb93a386Sopenharmony_ci        if (instanceCount > SK_MaxS32 / GrResourceProvider::NumVertsPerNonAAQuad()) {
212cb93a386Sopenharmony_ci            return;
213cb93a386Sopenharmony_ci        }
214cb93a386Sopenharmony_ci        VertexWriter vertices{target->makeVertexSpace(
215cb93a386Sopenharmony_ci            kVertexStride, GrResourceProvider::NumVertsPerNonAAQuad() * instanceCount,
216cb93a386Sopenharmony_ci            &flushInfo.fVertexBuffer, &flushInfo.fVertexOffset)};
217cb93a386Sopenharmony_ci
218cb93a386Sopenharmony_ci        flushInfo.fIndexBuffer = target->resourceProvider()->refNonAAQuadIndexBuffer();
219cb93a386Sopenharmony_ci        if (!vertices || !flushInfo.fIndexBuffer) {
220cb93a386Sopenharmony_ci            SkDebugf("Could not allocate vertices\n");
221cb93a386Sopenharmony_ci            return;
222cb93a386Sopenharmony_ci        }
223cb93a386Sopenharmony_ci
224cb93a386Sopenharmony_ci        flushInfo.fInstancesToFlush = 0;
225cb93a386Sopenharmony_ci        for (int i = 0; i < instanceCount; i++) {
226cb93a386Sopenharmony_ci            const Entry& args = fShapes[i];
227cb93a386Sopenharmony_ci
228cb93a386Sopenharmony_ci            skgpu::v1::SmallPathShapeData* shapeData;
229cb93a386Sopenharmony_ci            if (fUsesDistanceField) {
230cb93a386Sopenharmony_ci                // get mip level
231cb93a386Sopenharmony_ci                SkScalar maxScale;
232cb93a386Sopenharmony_ci                const SkRect& bounds = args.fShape.bounds();
233cb93a386Sopenharmony_ci                if (args.fViewMatrix.hasPerspective()) {
234cb93a386Sopenharmony_ci                    // approximate the scale since we can't get it from the matrix
235cb93a386Sopenharmony_ci                    SkRect xformedBounds;
236cb93a386Sopenharmony_ci                    args.fViewMatrix.mapRect(&xformedBounds, bounds);
237cb93a386Sopenharmony_ci                    maxScale = SkScalarAbs(std::max(xformedBounds.width() / bounds.width(),
238cb93a386Sopenharmony_ci                                                  xformedBounds.height() / bounds.height()));
239cb93a386Sopenharmony_ci                } else {
240cb93a386Sopenharmony_ci                    maxScale = SkScalarAbs(args.fViewMatrix.getMaxScale());
241cb93a386Sopenharmony_ci                }
242cb93a386Sopenharmony_ci                SkScalar maxDim = std::max(bounds.width(), bounds.height());
243cb93a386Sopenharmony_ci                // We try to create the DF at a 2^n scaled path resolution (1/2, 1, 2, 4, etc.)
244cb93a386Sopenharmony_ci                // In the majority of cases this will yield a crisper rendering.
245cb93a386Sopenharmony_ci                SkScalar mipScale = 1.0f;
246cb93a386Sopenharmony_ci                // Our mipscale is the maxScale clamped to the next highest power of 2
247cb93a386Sopenharmony_ci                if (maxScale <= SK_ScalarHalf) {
248cb93a386Sopenharmony_ci                    SkScalar log = SkScalarFloorToScalar(SkScalarLog2(SkScalarInvert(maxScale)));
249cb93a386Sopenharmony_ci                    mipScale = SkScalarPow(2, -log);
250cb93a386Sopenharmony_ci                } else if (maxScale > SK_Scalar1) {
251cb93a386Sopenharmony_ci                    SkScalar log = SkScalarCeilToScalar(SkScalarLog2(maxScale));
252cb93a386Sopenharmony_ci                    mipScale = SkScalarPow(2, log);
253cb93a386Sopenharmony_ci                }
254cb93a386Sopenharmony_ci                // Log2 isn't very precise at values close to a power of 2,
255cb93a386Sopenharmony_ci                // so add a little tolerance here. A little bit of scaling up is fine.
256cb93a386Sopenharmony_ci                SkASSERT(maxScale <= mipScale + SK_ScalarNearlyZero);
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci                SkScalar mipSize = mipScale*SkScalarAbs(maxDim);
259cb93a386Sopenharmony_ci                // For sizes less than kIdealMinMIP we want to use as large a distance field as we can
260cb93a386Sopenharmony_ci                // so we can preserve as much detail as possible. However, we can't scale down more
261cb93a386Sopenharmony_ci                // than a 1/4 of the size without artifacts. So the idea is that we pick the mipsize
262cb93a386Sopenharmony_ci                // just bigger than the ideal, and then scale down until we are no more than 4x the
263cb93a386Sopenharmony_ci                // original mipsize.
264cb93a386Sopenharmony_ci                if (mipSize < kIdealMinMIP) {
265cb93a386Sopenharmony_ci                    SkScalar newMipSize = mipSize;
266cb93a386Sopenharmony_ci                    do {
267cb93a386Sopenharmony_ci                        newMipSize *= 2;
268cb93a386Sopenharmony_ci                    } while (newMipSize < kIdealMinMIP);
269cb93a386Sopenharmony_ci                    while (newMipSize > 4 * mipSize) {
270cb93a386Sopenharmony_ci                        newMipSize *= 0.25f;
271cb93a386Sopenharmony_ci                    }
272cb93a386Sopenharmony_ci                    mipSize = newMipSize;
273cb93a386Sopenharmony_ci                }
274cb93a386Sopenharmony_ci
275cb93a386Sopenharmony_ci                SkScalar desiredDimension = std::min(mipSize, kMaxMIP);
276cb93a386Sopenharmony_ci                int ceilDesiredDimension = SkScalarCeilToInt(desiredDimension);
277cb93a386Sopenharmony_ci
278cb93a386Sopenharmony_ci                // check to see if df path is cached
279cb93a386Sopenharmony_ci                shapeData = atlasMgr->findOrCreate(args.fShape, ceilDesiredDimension);
280cb93a386Sopenharmony_ci                if (!shapeData->fAtlasLocator.plotLocator().isValid()) {
281cb93a386Sopenharmony_ci                    SkScalar scale = desiredDimension / maxDim;
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci                    if (!this->addDFPathToAtlas(target,
284cb93a386Sopenharmony_ci                                                &flushInfo,
285cb93a386Sopenharmony_ci                                                atlasMgr,
286cb93a386Sopenharmony_ci                                                shapeData,
287cb93a386Sopenharmony_ci                                                args.fShape,
288cb93a386Sopenharmony_ci                                                ceilDesiredDimension,
289cb93a386Sopenharmony_ci                                                scale)) {
290cb93a386Sopenharmony_ci                        atlasMgr->deleteCacheEntry(shapeData);
291cb93a386Sopenharmony_ci                        continue;
292cb93a386Sopenharmony_ci                    }
293cb93a386Sopenharmony_ci                }
294cb93a386Sopenharmony_ci            } else {
295cb93a386Sopenharmony_ci                // check to see if bitmap path is cached
296cb93a386Sopenharmony_ci                shapeData = atlasMgr->findOrCreate(args.fShape, args.fViewMatrix);
297cb93a386Sopenharmony_ci                if (!shapeData->fAtlasLocator.plotLocator().isValid()) {
298cb93a386Sopenharmony_ci                    if (!this->addBMPathToAtlas(target,
299cb93a386Sopenharmony_ci                                                &flushInfo,
300cb93a386Sopenharmony_ci                                                atlasMgr,
301cb93a386Sopenharmony_ci                                                shapeData,
302cb93a386Sopenharmony_ci                                                args.fShape,
303cb93a386Sopenharmony_ci                                                args.fViewMatrix)) {
304cb93a386Sopenharmony_ci                        atlasMgr->deleteCacheEntry(shapeData);
305cb93a386Sopenharmony_ci                        continue;
306cb93a386Sopenharmony_ci                    }
307cb93a386Sopenharmony_ci                }
308cb93a386Sopenharmony_ci            }
309cb93a386Sopenharmony_ci
310cb93a386Sopenharmony_ci            auto uploadTarget = target->deferredUploadTarget();
311cb93a386Sopenharmony_ci            atlasMgr->setUseToken(shapeData, uploadTarget->tokenTracker()->nextDrawToken());
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_ci            this->writePathVertices(vertices, GrVertexColor(args.fColor, fWideColor),
314cb93a386Sopenharmony_ci                                    args.fViewMatrix, shapeData);
315cb93a386Sopenharmony_ci            flushInfo.fInstancesToFlush++;
316cb93a386Sopenharmony_ci        }
317cb93a386Sopenharmony_ci
318cb93a386Sopenharmony_ci        this->flush(target, &flushInfo);
319cb93a386Sopenharmony_ci    }
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci    bool addToAtlasWithRetry(GrMeshDrawTarget* target,
322cb93a386Sopenharmony_ci                             FlushInfo* flushInfo,
323cb93a386Sopenharmony_ci                             skgpu::v1::SmallPathAtlasMgr* atlasMgr,
324cb93a386Sopenharmony_ci                             int width, int height, const void* image,
325cb93a386Sopenharmony_ci                             const SkRect& bounds, int srcInset,
326cb93a386Sopenharmony_ci                             skgpu::v1::SmallPathShapeData* shapeData) const {
327cb93a386Sopenharmony_ci        auto resourceProvider = target->resourceProvider();
328cb93a386Sopenharmony_ci        auto uploadTarget = target->deferredUploadTarget();
329cb93a386Sopenharmony_ci
330cb93a386Sopenharmony_ci        auto code = atlasMgr->addToAtlas(resourceProvider, uploadTarget, width, height,
331cb93a386Sopenharmony_ci                                         image, &shapeData->fAtlasLocator);
332cb93a386Sopenharmony_ci        if (GrDrawOpAtlas::ErrorCode::kError == code) {
333cb93a386Sopenharmony_ci            return false;
334cb93a386Sopenharmony_ci        }
335cb93a386Sopenharmony_ci
336cb93a386Sopenharmony_ci        if (GrDrawOpAtlas::ErrorCode::kTryAgain == code) {
337cb93a386Sopenharmony_ci            this->flush(target, flushInfo);
338cb93a386Sopenharmony_ci
339cb93a386Sopenharmony_ci            code = atlasMgr->addToAtlas(resourceProvider, uploadTarget, width, height,
340cb93a386Sopenharmony_ci                                        image, &shapeData->fAtlasLocator);
341cb93a386Sopenharmony_ci        }
342cb93a386Sopenharmony_ci
343cb93a386Sopenharmony_ci        shapeData->fAtlasLocator.insetSrc(srcInset);
344cb93a386Sopenharmony_ci        shapeData->fBounds = bounds;
345cb93a386Sopenharmony_ci
346cb93a386Sopenharmony_ci        return GrDrawOpAtlas::ErrorCode::kSucceeded == code;
347cb93a386Sopenharmony_ci    }
348cb93a386Sopenharmony_ci
349cb93a386Sopenharmony_ci    bool addDFPathToAtlas(GrMeshDrawTarget* target,
350cb93a386Sopenharmony_ci                          FlushInfo* flushInfo,
351cb93a386Sopenharmony_ci                          skgpu::v1::SmallPathAtlasMgr* atlasMgr,
352cb93a386Sopenharmony_ci                          skgpu::v1::SmallPathShapeData* shapeData,
353cb93a386Sopenharmony_ci                          const GrStyledShape& shape,
354cb93a386Sopenharmony_ci                          uint32_t dimension,
355cb93a386Sopenharmony_ci                          SkScalar scale) const {
356cb93a386Sopenharmony_ci
357cb93a386Sopenharmony_ci        const SkRect& bounds = shape.bounds();
358cb93a386Sopenharmony_ci
359cb93a386Sopenharmony_ci        // generate bounding rect for bitmap draw
360cb93a386Sopenharmony_ci        SkRect scaledBounds = bounds;
361cb93a386Sopenharmony_ci        // scale to mip level size
362cb93a386Sopenharmony_ci        scaledBounds.fLeft *= scale;
363cb93a386Sopenharmony_ci        scaledBounds.fTop *= scale;
364cb93a386Sopenharmony_ci        scaledBounds.fRight *= scale;
365cb93a386Sopenharmony_ci        scaledBounds.fBottom *= scale;
366cb93a386Sopenharmony_ci        // subtract out integer portion of origin
367cb93a386Sopenharmony_ci        // (SDF created will be placed with fractional offset burnt in)
368cb93a386Sopenharmony_ci        SkScalar dx = SkScalarFloorToScalar(scaledBounds.fLeft);
369cb93a386Sopenharmony_ci        SkScalar dy = SkScalarFloorToScalar(scaledBounds.fTop);
370cb93a386Sopenharmony_ci        scaledBounds.offset(-dx, -dy);
371cb93a386Sopenharmony_ci        // get integer boundary
372cb93a386Sopenharmony_ci        SkIRect devPathBounds;
373cb93a386Sopenharmony_ci        scaledBounds.roundOut(&devPathBounds);
374cb93a386Sopenharmony_ci        // place devBounds at origin with padding to allow room for antialiasing
375cb93a386Sopenharmony_ci        int width = devPathBounds.width() + 2 * kAntiAliasPad;
376cb93a386Sopenharmony_ci        int height = devPathBounds.height() + 2 * kAntiAliasPad;
377cb93a386Sopenharmony_ci        devPathBounds = SkIRect::MakeWH(width, height);
378cb93a386Sopenharmony_ci        SkScalar translateX = kAntiAliasPad - dx;
379cb93a386Sopenharmony_ci        SkScalar translateY = kAntiAliasPad - dy;
380cb93a386Sopenharmony_ci
381cb93a386Sopenharmony_ci        // draw path to bitmap
382cb93a386Sopenharmony_ci        SkMatrix drawMatrix;
383cb93a386Sopenharmony_ci        drawMatrix.setScale(scale, scale);
384cb93a386Sopenharmony_ci        drawMatrix.postTranslate(translateX, translateY);
385cb93a386Sopenharmony_ci
386cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.fLeft == 0);
387cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.fTop == 0);
388cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.width() > 0);
389cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.height() > 0);
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci        // setup signed distance field storage
392cb93a386Sopenharmony_ci        SkIRect dfBounds = devPathBounds.makeOutset(SK_DistanceFieldPad, SK_DistanceFieldPad);
393cb93a386Sopenharmony_ci        width = dfBounds.width();
394cb93a386Sopenharmony_ci        height = dfBounds.height();
395cb93a386Sopenharmony_ci        // TODO We should really generate this directly into the plot somehow
396cb93a386Sopenharmony_ci        SkAutoSMalloc<1024> dfStorage(width * height * sizeof(unsigned char));
397cb93a386Sopenharmony_ci
398cb93a386Sopenharmony_ci        SkPath path;
399cb93a386Sopenharmony_ci        shape.asPath(&path);
400cb93a386Sopenharmony_ci        // Generate signed distance field directly from SkPath
401cb93a386Sopenharmony_ci        bool succeed = GrGenerateDistanceFieldFromPath((unsigned char*)dfStorage.get(),
402cb93a386Sopenharmony_ci                                                       path, drawMatrix, width, height,
403cb93a386Sopenharmony_ci                                                       width * sizeof(unsigned char));
404cb93a386Sopenharmony_ci        if (!succeed) {
405cb93a386Sopenharmony_ci            // setup bitmap backing
406cb93a386Sopenharmony_ci            SkAutoPixmapStorage dst;
407cb93a386Sopenharmony_ci            if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) {
408cb93a386Sopenharmony_ci                return false;
409cb93a386Sopenharmony_ci            }
410cb93a386Sopenharmony_ci            sk_bzero(dst.writable_addr(), dst.computeByteSize());
411cb93a386Sopenharmony_ci
412cb93a386Sopenharmony_ci            // rasterize path
413cb93a386Sopenharmony_ci            SkPaint paint;
414cb93a386Sopenharmony_ci            paint.setStyle(SkPaint::kFill_Style);
415cb93a386Sopenharmony_ci            paint.setAntiAlias(true);
416cb93a386Sopenharmony_ci
417cb93a386Sopenharmony_ci            SkDraw draw;
418cb93a386Sopenharmony_ci
419cb93a386Sopenharmony_ci            SkRasterClip rasterClip;
420cb93a386Sopenharmony_ci            rasterClip.setRect(devPathBounds);
421cb93a386Sopenharmony_ci            draw.fRC = &rasterClip;
422cb93a386Sopenharmony_ci            SkSimpleMatrixProvider matrixProvider(drawMatrix);
423cb93a386Sopenharmony_ci            draw.fMatrixProvider = &matrixProvider;
424cb93a386Sopenharmony_ci            draw.fDst = dst;
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_ci            draw.drawPathCoverage(path, paint);
427cb93a386Sopenharmony_ci
428cb93a386Sopenharmony_ci            // Generate signed distance field
429cb93a386Sopenharmony_ci            SkGenerateDistanceFieldFromA8Image((unsigned char*)dfStorage.get(),
430cb93a386Sopenharmony_ci                                               (const unsigned char*)dst.addr(),
431cb93a386Sopenharmony_ci                                               dst.width(), dst.height(), dst.rowBytes());
432cb93a386Sopenharmony_ci        }
433cb93a386Sopenharmony_ci
434cb93a386Sopenharmony_ci        SkRect drawBounds = SkRect::Make(devPathBounds).makeOffset(-translateX, -translateY);
435cb93a386Sopenharmony_ci        drawBounds.fLeft /= scale;
436cb93a386Sopenharmony_ci        drawBounds.fTop /= scale;
437cb93a386Sopenharmony_ci        drawBounds.fRight /= scale;
438cb93a386Sopenharmony_ci        drawBounds.fBottom /= scale;
439cb93a386Sopenharmony_ci
440cb93a386Sopenharmony_ci        return this->addToAtlasWithRetry(target, flushInfo, atlasMgr,
441cb93a386Sopenharmony_ci                                         width, height, dfStorage.get(),
442cb93a386Sopenharmony_ci                                         drawBounds, SK_DistanceFieldPad, shapeData);
443cb93a386Sopenharmony_ci    }
444cb93a386Sopenharmony_ci
445cb93a386Sopenharmony_ci    bool addBMPathToAtlas(GrMeshDrawTarget* target,
446cb93a386Sopenharmony_ci                          FlushInfo* flushInfo,
447cb93a386Sopenharmony_ci                          skgpu::v1::SmallPathAtlasMgr* atlasMgr,
448cb93a386Sopenharmony_ci                          skgpu::v1::SmallPathShapeData* shapeData,
449cb93a386Sopenharmony_ci                          const GrStyledShape& shape,
450cb93a386Sopenharmony_ci                          const SkMatrix& ctm) const {
451cb93a386Sopenharmony_ci        const SkRect& bounds = shape.bounds();
452cb93a386Sopenharmony_ci        if (bounds.isEmpty()) {
453cb93a386Sopenharmony_ci            return false;
454cb93a386Sopenharmony_ci        }
455cb93a386Sopenharmony_ci        SkMatrix drawMatrix(ctm);
456cb93a386Sopenharmony_ci        SkScalar tx = ctm.getTranslateX();
457cb93a386Sopenharmony_ci        SkScalar ty = ctm.getTranslateY();
458cb93a386Sopenharmony_ci        tx -= SkScalarFloorToScalar(tx);
459cb93a386Sopenharmony_ci        ty -= SkScalarFloorToScalar(ty);
460cb93a386Sopenharmony_ci        drawMatrix.set(SkMatrix::kMTransX, tx);
461cb93a386Sopenharmony_ci        drawMatrix.set(SkMatrix::kMTransY, ty);
462cb93a386Sopenharmony_ci        SkRect shapeDevBounds;
463cb93a386Sopenharmony_ci        drawMatrix.mapRect(&shapeDevBounds, bounds);
464cb93a386Sopenharmony_ci        SkScalar dx = SkScalarFloorToScalar(shapeDevBounds.fLeft);
465cb93a386Sopenharmony_ci        SkScalar dy = SkScalarFloorToScalar(shapeDevBounds.fTop);
466cb93a386Sopenharmony_ci
467cb93a386Sopenharmony_ci        // get integer boundary
468cb93a386Sopenharmony_ci        SkIRect devPathBounds;
469cb93a386Sopenharmony_ci        shapeDevBounds.roundOut(&devPathBounds);
470cb93a386Sopenharmony_ci        // place devBounds at origin with padding to allow room for antialiasing
471cb93a386Sopenharmony_ci        int width = devPathBounds.width() + 2 * kAntiAliasPad;
472cb93a386Sopenharmony_ci        int height = devPathBounds.height() + 2 * kAntiAliasPad;
473cb93a386Sopenharmony_ci        devPathBounds = SkIRect::MakeWH(width, height);
474cb93a386Sopenharmony_ci        SkScalar translateX = kAntiAliasPad - dx;
475cb93a386Sopenharmony_ci        SkScalar translateY = kAntiAliasPad - dy;
476cb93a386Sopenharmony_ci
477cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.fLeft == 0);
478cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.fTop == 0);
479cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.width() > 0);
480cb93a386Sopenharmony_ci        SkASSERT(devPathBounds.height() > 0);
481cb93a386Sopenharmony_ci
482cb93a386Sopenharmony_ci        SkPath path;
483cb93a386Sopenharmony_ci        shape.asPath(&path);
484cb93a386Sopenharmony_ci        // setup bitmap backing
485cb93a386Sopenharmony_ci        SkAutoPixmapStorage dst;
486cb93a386Sopenharmony_ci        if (!dst.tryAlloc(SkImageInfo::MakeA8(devPathBounds.width(), devPathBounds.height()))) {
487cb93a386Sopenharmony_ci            return false;
488cb93a386Sopenharmony_ci        }
489cb93a386Sopenharmony_ci        sk_bzero(dst.writable_addr(), dst.computeByteSize());
490cb93a386Sopenharmony_ci
491cb93a386Sopenharmony_ci        // rasterize path
492cb93a386Sopenharmony_ci        SkPaint paint;
493cb93a386Sopenharmony_ci        paint.setStyle(SkPaint::kFill_Style);
494cb93a386Sopenharmony_ci        paint.setAntiAlias(true);
495cb93a386Sopenharmony_ci
496cb93a386Sopenharmony_ci        SkDraw draw;
497cb93a386Sopenharmony_ci
498cb93a386Sopenharmony_ci        SkRasterClip rasterClip;
499cb93a386Sopenharmony_ci        rasterClip.setRect(devPathBounds);
500cb93a386Sopenharmony_ci        draw.fRC = &rasterClip;
501cb93a386Sopenharmony_ci        drawMatrix.postTranslate(translateX, translateY);
502cb93a386Sopenharmony_ci        SkSimpleMatrixProvider matrixProvider(drawMatrix);
503cb93a386Sopenharmony_ci        draw.fMatrixProvider = &matrixProvider;
504cb93a386Sopenharmony_ci        draw.fDst = dst;
505cb93a386Sopenharmony_ci
506cb93a386Sopenharmony_ci        draw.drawPathCoverage(path, paint);
507cb93a386Sopenharmony_ci
508cb93a386Sopenharmony_ci        SkRect drawBounds = SkRect::Make(devPathBounds).makeOffset(-translateX, -translateY);
509cb93a386Sopenharmony_ci
510cb93a386Sopenharmony_ci        return this->addToAtlasWithRetry(target, flushInfo, atlasMgr,
511cb93a386Sopenharmony_ci                                         dst.width(), dst.height(), dst.addr(),
512cb93a386Sopenharmony_ci                                         drawBounds, 0, shapeData);
513cb93a386Sopenharmony_ci    }
514cb93a386Sopenharmony_ci
515cb93a386Sopenharmony_ci    void writePathVertices(VertexWriter& vertices,
516cb93a386Sopenharmony_ci                           const GrVertexColor& color,
517cb93a386Sopenharmony_ci                           const SkMatrix& ctm,
518cb93a386Sopenharmony_ci                           const skgpu::v1::SmallPathShapeData* shapeData) const {
519cb93a386Sopenharmony_ci        SkRect translatedBounds(shapeData->fBounds);
520cb93a386Sopenharmony_ci        if (!fUsesDistanceField) {
521cb93a386Sopenharmony_ci            translatedBounds.offset(SkScalarFloorToScalar(ctm.get(SkMatrix::kMTransX)),
522cb93a386Sopenharmony_ci                                    SkScalarFloorToScalar(ctm.get(SkMatrix::kMTransY)));
523cb93a386Sopenharmony_ci        }
524cb93a386Sopenharmony_ci
525cb93a386Sopenharmony_ci        // set up texture coordinates
526cb93a386Sopenharmony_ci        auto texCoords = VertexWriter::TriStripFromUVs(shapeData->fAtlasLocator.getUVs());
527cb93a386Sopenharmony_ci
528cb93a386Sopenharmony_ci        if (fUsesDistanceField && !ctm.hasPerspective()) {
529cb93a386Sopenharmony_ci            vertices.writeQuad(GrQuad::MakeFromRect(translatedBounds, ctm),
530cb93a386Sopenharmony_ci                               color,
531cb93a386Sopenharmony_ci                               texCoords);
532cb93a386Sopenharmony_ci        } else {
533cb93a386Sopenharmony_ci            vertices.writeQuad(VertexWriter::TriStripFromRect(translatedBounds),
534cb93a386Sopenharmony_ci                               color,
535cb93a386Sopenharmony_ci                               texCoords);
536cb93a386Sopenharmony_ci        }
537cb93a386Sopenharmony_ci    }
538cb93a386Sopenharmony_ci
539cb93a386Sopenharmony_ci    void flush(GrMeshDrawTarget* target, FlushInfo* flushInfo) const {
540cb93a386Sopenharmony_ci        auto atlasMgr = target->smallPathAtlasManager();
541cb93a386Sopenharmony_ci        if (!atlasMgr) {
542cb93a386Sopenharmony_ci            return;
543cb93a386Sopenharmony_ci        }
544cb93a386Sopenharmony_ci
545cb93a386Sopenharmony_ci        int numActiveProxies;
546cb93a386Sopenharmony_ci        const GrSurfaceProxyView* views = atlasMgr->getViews(&numActiveProxies);
547cb93a386Sopenharmony_ci
548cb93a386Sopenharmony_ci        GrGeometryProcessor* gp = flushInfo->fGeometryProcessor;
549cb93a386Sopenharmony_ci        if (gp->numTextureSamplers() != numActiveProxies) {
550cb93a386Sopenharmony_ci            for (int i = gp->numTextureSamplers(); i < numActiveProxies; ++i) {
551cb93a386Sopenharmony_ci                flushInfo->fPrimProcProxies[i] = views[i].proxy();
552cb93a386Sopenharmony_ci                // This op does not know its atlas proxies when it is added to a OpsTasks, so the
553cb93a386Sopenharmony_ci                // proxies don't get added during the visitProxies call. Thus we add them here.
554cb93a386Sopenharmony_ci                target->sampledProxyArray()->push_back(views[i].proxy());
555cb93a386Sopenharmony_ci            }
556cb93a386Sopenharmony_ci            // During preparation the number of atlas pages has increased.
557cb93a386Sopenharmony_ci            // Update the proxies used in the GP to match.
558cb93a386Sopenharmony_ci            if (fUsesDistanceField) {
559cb93a386Sopenharmony_ci                reinterpret_cast<GrDistanceFieldPathGeoProc*>(gp)->addNewViews(
560cb93a386Sopenharmony_ci                        views, numActiveProxies, GrSamplerState::Filter::kLinear);
561cb93a386Sopenharmony_ci            } else {
562cb93a386Sopenharmony_ci                reinterpret_cast<GrBitmapTextGeoProc*>(gp)->addNewViews(
563cb93a386Sopenharmony_ci                        views, numActiveProxies, GrSamplerState::Filter::kNearest);
564cb93a386Sopenharmony_ci            }
565cb93a386Sopenharmony_ci        }
566cb93a386Sopenharmony_ci
567cb93a386Sopenharmony_ci        if (flushInfo->fInstancesToFlush) {
568cb93a386Sopenharmony_ci            GrSimpleMesh* mesh = target->allocMesh();
569cb93a386Sopenharmony_ci            mesh->setIndexedPatterned(flushInfo->fIndexBuffer,
570cb93a386Sopenharmony_ci                                      GrResourceProvider::NumIndicesPerNonAAQuad(),
571cb93a386Sopenharmony_ci                                      flushInfo->fInstancesToFlush,
572cb93a386Sopenharmony_ci                                      GrResourceProvider::MaxNumNonAAQuads(),
573cb93a386Sopenharmony_ci                                      flushInfo->fVertexBuffer,
574cb93a386Sopenharmony_ci                                      GrResourceProvider::NumVertsPerNonAAQuad(),
575cb93a386Sopenharmony_ci                                      flushInfo->fVertexOffset);
576cb93a386Sopenharmony_ci            target->recordDraw(flushInfo->fGeometryProcessor, mesh, 1, flushInfo->fPrimProcProxies,
577cb93a386Sopenharmony_ci                               GrPrimitiveType::kTriangles);
578cb93a386Sopenharmony_ci            flushInfo->fVertexOffset += GrResourceProvider::NumVertsPerNonAAQuad() *
579cb93a386Sopenharmony_ci                                        flushInfo->fInstancesToFlush;
580cb93a386Sopenharmony_ci            flushInfo->fInstancesToFlush = 0;
581cb93a386Sopenharmony_ci        }
582cb93a386Sopenharmony_ci    }
583cb93a386Sopenharmony_ci
584cb93a386Sopenharmony_ci    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
585cb93a386Sopenharmony_ci        auto pipeline = fHelper.createPipeline(flushState);
586cb93a386Sopenharmony_ci
587cb93a386Sopenharmony_ci        flushState->executeDrawsAndUploadsForMeshDrawOp(this, chainBounds, pipeline,
588cb93a386Sopenharmony_ci                                                        fHelper.stencilSettings());
589cb93a386Sopenharmony_ci    }
590cb93a386Sopenharmony_ci
591cb93a386Sopenharmony_ci    const SkPMColor4f& color() const { return fShapes[0].fColor; }
592cb93a386Sopenharmony_ci    bool usesDistanceField() const { return fUsesDistanceField; }
593cb93a386Sopenharmony_ci
594cb93a386Sopenharmony_ci    CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
595cb93a386Sopenharmony_ci        SmallPathOp* that = t->cast<SmallPathOp>();
596cb93a386Sopenharmony_ci        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds())) {
597cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
598cb93a386Sopenharmony_ci        }
599cb93a386Sopenharmony_ci
600cb93a386Sopenharmony_ci        if (this->usesDistanceField() != that->usesDistanceField()) {
601cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
602cb93a386Sopenharmony_ci        }
603cb93a386Sopenharmony_ci
604cb93a386Sopenharmony_ci        const SkMatrix& thisCtm = this->fShapes[0].fViewMatrix;
605cb93a386Sopenharmony_ci        const SkMatrix& thatCtm = that->fShapes[0].fViewMatrix;
606cb93a386Sopenharmony_ci
607cb93a386Sopenharmony_ci        if (thisCtm.hasPerspective() != thatCtm.hasPerspective()) {
608cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
609cb93a386Sopenharmony_ci        }
610cb93a386Sopenharmony_ci
611cb93a386Sopenharmony_ci        // We can position on the cpu unless we're in perspective,
612cb93a386Sopenharmony_ci        // but also need to make sure local matrices are identical
613cb93a386Sopenharmony_ci        if ((thisCtm.hasPerspective() || fHelper.usesLocalCoords()) &&
614cb93a386Sopenharmony_ci            !SkMatrixPriv::CheapEqual(thisCtm, thatCtm)) {
615cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
616cb93a386Sopenharmony_ci        }
617cb93a386Sopenharmony_ci
618cb93a386Sopenharmony_ci        // Depending on the ctm we may have a different shader for SDF paths
619cb93a386Sopenharmony_ci        if (this->usesDistanceField()) {
620cb93a386Sopenharmony_ci            if (thisCtm.isScaleTranslate() != thatCtm.isScaleTranslate() ||
621cb93a386Sopenharmony_ci                thisCtm.isSimilarity() != thatCtm.isSimilarity()) {
622cb93a386Sopenharmony_ci                return CombineResult::kCannotCombine;
623cb93a386Sopenharmony_ci            }
624cb93a386Sopenharmony_ci        }
625cb93a386Sopenharmony_ci
626cb93a386Sopenharmony_ci        fShapes.push_back_n(that->fShapes.count(), that->fShapes.begin());
627cb93a386Sopenharmony_ci        fWideColor |= that->fWideColor;
628cb93a386Sopenharmony_ci        return CombineResult::kMerged;
629cb93a386Sopenharmony_ci    }
630cb93a386Sopenharmony_ci
631cb93a386Sopenharmony_ci#if GR_TEST_UTILS
632cb93a386Sopenharmony_ci    SkString onDumpInfo() const override {
633cb93a386Sopenharmony_ci        SkString string;
634cb93a386Sopenharmony_ci        for (const auto& geo : fShapes) {
635cb93a386Sopenharmony_ci            string.appendf("Color: 0x%08x\n", geo.fColor.toBytes_RGBA());
636cb93a386Sopenharmony_ci        }
637cb93a386Sopenharmony_ci        string += fHelper.dumpInfo();
638cb93a386Sopenharmony_ci        return string;
639cb93a386Sopenharmony_ci    }
640cb93a386Sopenharmony_ci#endif
641cb93a386Sopenharmony_ci
642cb93a386Sopenharmony_ci    bool fUsesDistanceField;
643cb93a386Sopenharmony_ci
644cb93a386Sopenharmony_ci    struct Entry {
645cb93a386Sopenharmony_ci        SkPMColor4f   fColor;
646cb93a386Sopenharmony_ci        GrStyledShape fShape;
647cb93a386Sopenharmony_ci        SkMatrix      fViewMatrix;
648cb93a386Sopenharmony_ci    };
649cb93a386Sopenharmony_ci
650cb93a386Sopenharmony_ci    SkSTArray<1, Entry> fShapes;
651cb93a386Sopenharmony_ci    Helper fHelper;
652cb93a386Sopenharmony_ci    bool fGammaCorrect;
653cb93a386Sopenharmony_ci    bool fWideColor;
654cb93a386Sopenharmony_ci
655cb93a386Sopenharmony_ci    using INHERITED = GrMeshDrawOp;
656cb93a386Sopenharmony_ci};
657cb93a386Sopenharmony_ci
658cb93a386Sopenharmony_ci} // anonymous namespace
659cb93a386Sopenharmony_ci
660cb93a386Sopenharmony_ci///////////////////////////////////////////////////////////////////////////////////////////////////
661cb93a386Sopenharmony_ci
662cb93a386Sopenharmony_ciPathRenderer::CanDrawPath SmallPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const {
663cb93a386Sopenharmony_ci    if (!args.fCaps->shaderCaps()->shaderDerivativeSupport()) {
664cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
665cb93a386Sopenharmony_ci    }
666cb93a386Sopenharmony_ci    // If the shape has no key then we won't get any reuse.
667cb93a386Sopenharmony_ci    if (!args.fShape->hasUnstyledKey()) {
668cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
669cb93a386Sopenharmony_ci    }
670cb93a386Sopenharmony_ci    // This only supports filled paths, however, the caller may apply the style to make a filled
671cb93a386Sopenharmony_ci    // path and try again.
672cb93a386Sopenharmony_ci    if (!args.fShape->style().isSimpleFill()) {
673cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
674cb93a386Sopenharmony_ci    }
675cb93a386Sopenharmony_ci    // This does non-inverse coverage-based antialiased fills.
676cb93a386Sopenharmony_ci    if (GrAAType::kCoverage != args.fAAType) {
677cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
678cb93a386Sopenharmony_ci    }
679cb93a386Sopenharmony_ci    // TODO: Support inverse fill
680cb93a386Sopenharmony_ci    if (args.fShape->inverseFilled()) {
681cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
682cb93a386Sopenharmony_ci    }
683cb93a386Sopenharmony_ci
684cb93a386Sopenharmony_ci    SkScalar scaleFactors[2] = { 1, 1 };
685cb93a386Sopenharmony_ci    // TODO: handle perspective distortion
686cb93a386Sopenharmony_ci    if (!args.fViewMatrix->hasPerspective() && !args.fViewMatrix->getMinMaxScales(scaleFactors)) {
687cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
688cb93a386Sopenharmony_ci    }
689cb93a386Sopenharmony_ci    // For affine transformations, too much shear can produce artifacts.
690cb93a386Sopenharmony_ci    if (!scaleFactors[0] || scaleFactors[1]/scaleFactors[0] > 4) {
691cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
692cb93a386Sopenharmony_ci    }
693cb93a386Sopenharmony_ci    // Only support paths with bounds within kMaxDim by kMaxDim,
694cb93a386Sopenharmony_ci    // scaled to have bounds within kMaxSize by kMaxSize.
695cb93a386Sopenharmony_ci    // The goal is to accelerate rendering of lots of small paths that may be scaling.
696cb93a386Sopenharmony_ci    SkRect bounds = args.fShape->styledBounds();
697cb93a386Sopenharmony_ci    SkScalar minDim = std::min(bounds.width(), bounds.height());
698cb93a386Sopenharmony_ci    SkScalar maxDim = std::max(bounds.width(), bounds.height());
699cb93a386Sopenharmony_ci    SkScalar minSize = minDim * SkScalarAbs(scaleFactors[0]);
700cb93a386Sopenharmony_ci    SkScalar maxSize = maxDim * SkScalarAbs(scaleFactors[1]);
701cb93a386Sopenharmony_ci    if (maxDim > kMaxDim || kMinSize > minSize || maxSize > kMaxSize) {
702cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
703cb93a386Sopenharmony_ci    }
704cb93a386Sopenharmony_ci
705cb93a386Sopenharmony_ci    return CanDrawPath::kYes;
706cb93a386Sopenharmony_ci}
707cb93a386Sopenharmony_ci
708cb93a386Sopenharmony_cibool SmallPathRenderer::onDrawPath(const DrawPathArgs& args) {
709cb93a386Sopenharmony_ci    GR_AUDIT_TRAIL_AUTO_FRAME(args.fContext->priv().auditTrail(),
710cb93a386Sopenharmony_ci                              "SmallPathRenderer::onDrawPath");
711cb93a386Sopenharmony_ci
712cb93a386Sopenharmony_ci    // we've already bailed on inverse filled paths, so this is safe
713cb93a386Sopenharmony_ci    SkASSERT(!args.fShape->isEmpty());
714cb93a386Sopenharmony_ci    SkASSERT(args.fShape->hasUnstyledKey());
715cb93a386Sopenharmony_ci
716cb93a386Sopenharmony_ci    GrOp::Owner op = SmallPathOp::Make(
717cb93a386Sopenharmony_ci            args.fContext, std::move(args.fPaint), *args.fShape, *args.fViewMatrix,
718cb93a386Sopenharmony_ci            args.fGammaCorrect, args.fUserStencilSettings);
719cb93a386Sopenharmony_ci    args.fSurfaceDrawContext->addDrawOp(args.fClip, std::move(op));
720cb93a386Sopenharmony_ci
721cb93a386Sopenharmony_ci    return true;
722cb93a386Sopenharmony_ci}
723cb93a386Sopenharmony_ci
724cb93a386Sopenharmony_ci} // namespace skgpu::v1
725cb93a386Sopenharmony_ci
726cb93a386Sopenharmony_ci#if GR_TEST_UTILS
727cb93a386Sopenharmony_ci
728cb93a386Sopenharmony_ciGR_DRAW_OP_TEST_DEFINE(SmallPathOp) {
729cb93a386Sopenharmony_ci    SkMatrix viewMatrix = GrTest::TestMatrix(random);
730cb93a386Sopenharmony_ci    bool gammaCorrect = random->nextBool();
731cb93a386Sopenharmony_ci
732cb93a386Sopenharmony_ci    // This path renderer only allows fill styles.
733cb93a386Sopenharmony_ci    GrStyledShape shape(GrTest::TestPath(random), GrStyle::SimpleFill());
734cb93a386Sopenharmony_ci    return skgpu::v1::SmallPathOp::Make(context, std::move(paint), shape, viewMatrix, gammaCorrect,
735cb93a386Sopenharmony_ci                                        GrGetRandomStencil(random, context));
736cb93a386Sopenharmony_ci}
737cb93a386Sopenharmony_ci
738cb93a386Sopenharmony_ci#endif // GR_TEST_UTILS
739