1/*
2 * Copyright 2013 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// This test only works with the GPU backend.
9
10#include "gm.h"
11
12#if SK_SUPPORT_GPU
13
14#include "GrContext.h"
15#include "GrOpFlushState.h"
16#include "GrPathUtils.h"
17#include "GrRenderTargetContextPriv.h"
18#include "GrTest.h"
19#include "SkColorPriv.h"
20#include "SkGeometry.h"
21#include "SkPoint3.h"
22#include "SkPointPriv.h"
23#include "effects/GrBezierEffect.h"
24#include "ops/GrMeshDrawOp.h"
25
26namespace skiagm {
27
28class BezierTestOp : public GrMeshDrawOp {
29public:
30    FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
31
32    RequiresDstTexture finalize(const GrCaps& caps, const GrAppliedClip* clip,
33                                GrPixelConfigIsClamped dstIsClamped) override {
34        auto analysis = fProcessorSet.finalize(fColor, GrProcessorAnalysisCoverage::kSingleChannel,
35                                               clip, false, caps, dstIsClamped, &fColor);
36        return analysis.requiresDstTexture() ? RequiresDstTexture::kYes : RequiresDstTexture::kNo;
37    }
38
39    void visitProxies(const VisitProxyFunc& func) const override {
40        fProcessorSet.visitProxies(func);
41    }
42
43protected:
44    BezierTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color, int32_t classID)
45            : INHERITED(classID)
46            , fRect(rect)
47            , fColor(color)
48            , fGeometryProcessor(std::move(gp))
49            , fProcessorSet(SkBlendMode::kSrc) {
50        this->setBounds(rect, HasAABloat::kYes, IsZeroArea::kNo);
51    }
52
53    const GrPipeline* makePipeline(Target* target) {
54        return target->makePipeline(0, std::move(fProcessorSet), target->detachAppliedClip());
55    }
56
57    const GrGeometryProcessor* gp() const { return fGeometryProcessor.get(); }
58
59    const SkRect& rect() const { return fRect; }
60    GrColor color() const { return fColor; }
61
62private:
63    bool onCombineIfPossible(GrOp* op, const GrCaps& caps) override { return false; }
64
65    SkRect fRect;
66    GrColor fColor;
67    sk_sp<GrGeometryProcessor> fGeometryProcessor;
68    GrProcessorSet fProcessorSet;
69
70    typedef GrMeshDrawOp INHERITED;
71};
72
73class BezierCubicTestOp : public BezierTestOp {
74public:
75    DEFINE_OP_CLASS_ID
76
77    const char* name() const override { return "BezierCubicTestOp"; }
78
79    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
80                                          GrColor color) {
81        return std::unique_ptr<GrDrawOp>(new BezierCubicTestOp(std::move(gp), rect, color));
82    }
83
84private:
85    BezierCubicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color)
86            : INHERITED(std::move(gp), rect, color, ClassID()) {}
87
88    void onPrepareDraws(Target* target) override {
89        QuadHelper helper;
90        size_t vertexStride = this->gp()->getVertexStride();
91        SkASSERT(vertexStride == sizeof(SkPoint));
92        SkPoint* pts = reinterpret_cast<SkPoint*>(helper.init(target, vertexStride, 1));
93        if (!pts) {
94            return;
95        }
96        SkRect rect = this->rect();
97        SkPointPriv::SetRectTriStrip(pts, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vertexStride);
98        helper.recordDraw(target, this->gp(), this->makePipeline(target));
99    }
100
101    static constexpr int kVertsPerCubic = 4;
102    static constexpr int kIndicesPerCubic = 6;
103
104    typedef BezierTestOp INHERITED;
105};
106
107/**
108 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
109 */
110class BezierCubicEffects : public GM {
111public:
112    BezierCubicEffects() {
113        this->setBGColor(0xFFFFFFFF);
114    }
115
116protected:
117    SkString onShortName() override {
118        return SkString("bezier_cubic_effects");
119    }
120
121    SkISize onISize() override {
122        return SkISize::Make(800, 800);
123    }
124
125    void onDraw(SkCanvas* canvas) override {
126        GrRenderTargetContext* renderTargetContext =
127            canvas->internal_private_accessTopLayerRenderTargetContext();
128        if (!renderTargetContext) {
129            skiagm::GM::DrawGpuOnlyMessage(canvas);
130            return;
131        }
132
133        GrContext* context = canvas->getGrContext();
134        if (!context) {
135            return;
136        }
137
138        struct Vertex {
139            SkPoint fPosition;
140            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
141        };
142
143        constexpr int kNumCubics = 15;
144        SkRandom rand;
145
146        // Mult by 3 for each edge effect type
147        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
148        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
149        SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
150        SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
151        int row = 0;
152        int col = 0;
153        constexpr GrColor color = 0xff000000;
154
155        for (int i = 0; i < kNumCubics; ++i) {
156            SkPoint baseControlPts[] = {
157                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
158                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
159                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
160                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
161            };
162            for(GrClipEdgeType edgeType : {GrClipEdgeType::kFillBW,
163                                           GrClipEdgeType::kFillAA,
164                                           GrClipEdgeType::kHairlineAA}) {
165                SkScalar x = col * w;
166                SkScalar y = row * h;
167                SkPoint controlPts[] = {
168                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
169                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
170                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
171                    {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
172                };
173                SkPoint chopped[10];
174                SkMatrix klm;
175                int loopIndex;
176                int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
177                                                                   chopped,
178                                                                   &klm,
179                                                                   &loopIndex);
180
181                SkPaint ctrlPtPaint;
182                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
183                canvas->drawCircle(controlPts[0], 8.f, ctrlPtPaint);
184                for (int i = 1; i < 4; ++i) {
185                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
186                }
187
188                SkPaint polyPaint;
189                polyPaint.setColor(0xffA0A0A0);
190                polyPaint.setStrokeWidth(0);
191                polyPaint.setStyle(SkPaint::kStroke_Style);
192                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
193
194                SkPaint choppedPtPaint;
195                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
196
197                for (int c = 0; c < cnt; ++c) {
198                    SkPoint* pts = chopped + 3 * c;
199
200                    for (int i = 0; i < 4; ++i) {
201                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
202                    }
203
204                    SkRect bounds;
205                    bounds.set(pts, 4);
206
207                    SkPaint boundsPaint;
208                    boundsPaint.setColor(0xff808080);
209                    boundsPaint.setStrokeWidth(0);
210                    boundsPaint.setStyle(SkPaint::kStroke_Style);
211                    canvas->drawRect(bounds, boundsPaint);
212
213
214                    bool flipKL = (c == loopIndex && cnt != 3);
215                    sk_sp<GrGeometryProcessor> gp = GrCubicEffect::Make(color, SkMatrix::I(), klm,
216                                                                        flipKL, edgeType,
217                                                                        *context->caps());
218                    if (!gp) {
219                        break;
220                    }
221
222                    std::unique_ptr<GrDrawOp> op =
223                            BezierCubicTestOp::Make(std::move(gp), bounds, color);
224                    renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
225                }
226                ++col;
227                if (numCols == col) {
228                    col = 0;
229                    ++row;
230                }
231            }
232        }
233    }
234
235private:
236    typedef GM INHERITED;
237};
238
239//////////////////////////////////////////////////////////////////////////////
240
241class BezierConicTestOp : public BezierTestOp {
242public:
243    DEFINE_OP_CLASS_ID
244
245    const char* name() const override { return "BezierConicTestOp"; }
246
247    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
248                                          GrColor color, const SkMatrix& klm) {
249        return std::unique_ptr<GrMeshDrawOp>(
250                new BezierConicTestOp(std::move(gp), rect, color, klm));
251    }
252
253private:
254    BezierConicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
255                      const SkMatrix& klm)
256            : INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {}
257
258    struct Vertex {
259        SkPoint fPosition;
260        float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
261    };
262
263    void onPrepareDraws(Target* target) override {
264        QuadHelper helper;
265        size_t vertexStride = this->gp()->getVertexStride();
266        SkASSERT(vertexStride == sizeof(Vertex));
267        Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
268        if (!verts) {
269            return;
270        }
271        SkRect rect = this->rect();
272        SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect.fLeft, rect.fTop, rect.fRight,
273                                     rect.fBottom, sizeof(Vertex));
274        for (int v = 0; v < 4; ++v) {
275            SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
276            fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1);
277        }
278        helper.recordDraw(target, this->gp(), this->makePipeline(target));
279    }
280
281    SkMatrix fKLM;
282
283    static constexpr int kVertsPerCubic = 4;
284    static constexpr int kIndicesPerCubic = 6;
285
286    typedef BezierTestOp INHERITED;
287};
288
289
290/**
291 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
292 */
293class BezierConicEffects : public GM {
294public:
295    BezierConicEffects() {
296        this->setBGColor(0xFFFFFFFF);
297    }
298
299protected:
300    SkString onShortName() override {
301        return SkString("bezier_conic_effects");
302    }
303
304    SkISize onISize() override {
305        return SkISize::Make(800, 800);
306    }
307
308
309    void onDraw(SkCanvas* canvas) override {
310        GrRenderTargetContext* renderTargetContext =
311            canvas->internal_private_accessTopLayerRenderTargetContext();
312        if (!renderTargetContext) {
313            skiagm::GM::DrawGpuOnlyMessage(canvas);
314            return;
315        }
316
317        GrContext* context = canvas->getGrContext();
318        if (!context) {
319            return;
320        }
321
322        struct Vertex {
323            SkPoint fPosition;
324            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
325        };
326
327        constexpr int kNumConics = 10;
328        SkRandom rand;
329
330        // Mult by 3 for each edge effect type
331        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
332        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
333        SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
334        SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
335        int row = 0;
336        int col = 0;
337        constexpr GrColor color = 0xff000000;
338
339        for (int i = 0; i < kNumConics; ++i) {
340            SkPoint baseControlPts[] = {
341                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
342                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
343                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
344            };
345            SkScalar weight = rand.nextRangeF(0.f, 2.f);
346            for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
347                sk_sp<GrGeometryProcessor> gp;
348                GrClipEdgeType et = (GrClipEdgeType)edgeType;
349                gp = GrConicEffect::Make(color, SkMatrix::I(), et,
350                                         *context->caps(), SkMatrix::I(), false);
351                if (!gp) {
352                    continue;
353                }
354
355                SkScalar x = col * w;
356                SkScalar y = row * h;
357                SkPoint controlPts[] = {
358                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
359                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
360                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
361                };
362                SkConic dst[4];
363                SkMatrix klm;
364                int cnt = chop_conic(controlPts, dst, weight);
365                GrPathUtils::getConicKLM(controlPts, weight, &klm);
366
367                SkPaint ctrlPtPaint;
368                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
369                for (int i = 0; i < 3; ++i) {
370                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
371                }
372
373                SkPaint polyPaint;
374                polyPaint.setColor(0xffA0A0A0);
375                polyPaint.setStrokeWidth(0);
376                polyPaint.setStyle(SkPaint::kStroke_Style);
377                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
378
379                SkPaint choppedPtPaint;
380                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
381
382                for (int c = 0; c < cnt; ++c) {
383                    SkPoint* pts = dst[c].fPts;
384                    for (int i = 0; i < 3; ++i) {
385                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
386                    }
387
388                    SkRect bounds;
389                    //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
390                    //bounds.set(bPts, 2);
391                    bounds.set(pts, 3);
392
393                    SkPaint boundsPaint;
394                    boundsPaint.setColor(0xff808080);
395                    boundsPaint.setStrokeWidth(0);
396                    boundsPaint.setStyle(SkPaint::kStroke_Style);
397                    canvas->drawRect(bounds, boundsPaint);
398
399                    std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(gp, bounds, color, klm);
400                    renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
401                }
402                ++col;
403                if (numCols == col) {
404                    col = 0;
405                    ++row;
406                }
407            }
408        }
409    }
410
411private:
412    // Uses the max curvature function for quads to estimate
413    // where to chop the conic. If the max curvature is not
414    // found along the curve segment it will return 1 and
415    // dst[0] is the original conic. If it returns 2 the dst[0]
416    // and dst[1] are the two new conics.
417    int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
418        SkScalar t = SkFindQuadMaxCurvature(src);
419        if (t == 0) {
420            if (dst) {
421                dst[0].set(src, weight);
422            }
423            return 1;
424        } else {
425            if (dst) {
426                SkConic conic;
427                conic.set(src, weight);
428                if (!conic.chopAt(t, dst)) {
429                    dst[0].set(src, weight);
430                    return 1;
431                }
432            }
433            return 2;
434        }
435    }
436
437    // Calls split_conic on the entire conic and then once more on each subsection.
438    // Most cases will result in either 1 conic (chop point is not within t range)
439    // or 3 points (split once and then one subsection is split again).
440    int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
441        SkConic dstTemp[2];
442        int conicCnt = split_conic(src, dstTemp, weight);
443        if (2 == conicCnt) {
444            int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
445            conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
446        } else {
447            dst[0] = dstTemp[0];
448        }
449        return conicCnt;
450    }
451
452    typedef GM INHERITED;
453};
454
455//////////////////////////////////////////////////////////////////////////////
456
457class BezierQuadTestOp : public BezierTestOp {
458public:
459    DEFINE_OP_CLASS_ID
460    const char* name() const override { return "BezierQuadTestOp"; }
461
462    static std::unique_ptr<GrDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
463                                          GrColor color, const GrPathUtils::QuadUVMatrix& devToUV) {
464        return std::unique_ptr<GrDrawOp>(new BezierQuadTestOp(std::move(gp), rect, color, devToUV));
465    }
466
467private:
468    BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
469                     const GrPathUtils::QuadUVMatrix& devToUV)
470            : INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {}
471
472    struct Vertex {
473        SkPoint fPosition;
474        float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
475    };
476
477    void onPrepareDraws(Target* target) override {
478        QuadHelper helper;
479        size_t vertexStride = this->gp()->getVertexStride();
480        SkASSERT(vertexStride == sizeof(Vertex));
481        Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
482        if (!verts) {
483            return;
484        }
485        SkRect rect = this->rect();
486        SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect.fLeft, rect.fTop, rect.fRight,
487                                     rect.fBottom, sizeof(Vertex));
488        fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
489        helper.recordDraw(target, this->gp(), this->makePipeline(target));
490    }
491
492    GrPathUtils::QuadUVMatrix fDevToUV;
493
494    static constexpr int kVertsPerCubic = 4;
495    static constexpr int kIndicesPerCubic = 6;
496
497    typedef BezierTestOp INHERITED;
498};
499
500/**
501 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
502 */
503class BezierQuadEffects : public GM {
504public:
505    BezierQuadEffects() {
506        this->setBGColor(0xFFFFFFFF);
507    }
508
509protected:
510    SkString onShortName() override {
511        return SkString("bezier_quad_effects");
512    }
513
514    SkISize onISize() override {
515        return SkISize::Make(800, 800);
516    }
517
518
519    void onDraw(SkCanvas* canvas) override {
520        GrRenderTargetContext* renderTargetContext =
521            canvas->internal_private_accessTopLayerRenderTargetContext();
522        if (!renderTargetContext) {
523            skiagm::GM::DrawGpuOnlyMessage(canvas);
524            return;
525        }
526
527        GrContext* context = canvas->getGrContext();
528        if (!context) {
529            return;
530        }
531
532        struct Vertex {
533            SkPoint fPosition;
534            float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
535        };
536
537        constexpr int kNumQuads = 5;
538        SkRandom rand;
539
540        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
541        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
542        SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
543        SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
544        int row = 0;
545        int col = 0;
546        constexpr GrColor color = 0xff000000;
547
548        for (int i = 0; i < kNumQuads; ++i) {
549            SkPoint baseControlPts[] = {
550                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
551                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
552                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
553            };
554            for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
555                sk_sp<GrGeometryProcessor> gp;
556                GrClipEdgeType et = (GrClipEdgeType)edgeType;
557                gp = GrQuadEffect::Make(color, SkMatrix::I(), et,
558                                        *context->caps(), SkMatrix::I(), false);
559                if (!gp) {
560                    continue;
561                }
562
563                SkScalar x = col * w;
564                SkScalar y = row * h;
565                SkPoint controlPts[] = {
566                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
567                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
568                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
569                };
570                SkPoint chopped[5];
571                int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
572
573                SkPaint ctrlPtPaint;
574                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
575                for (int i = 0; i < 3; ++i) {
576                    canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
577                }
578
579                SkPaint polyPaint;
580                polyPaint.setColor(0xffA0A0A0);
581                polyPaint.setStrokeWidth(0);
582                polyPaint.setStyle(SkPaint::kStroke_Style);
583                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
584
585                SkPaint choppedPtPaint;
586                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
587
588                for (int c = 0; c < cnt; ++c) {
589                    SkPoint* pts = chopped + 2 * c;
590
591                    for (int i = 0; i < 3; ++i) {
592                        canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
593                    }
594
595                    SkRect bounds;
596                    bounds.set(pts, 3);
597
598                    SkPaint boundsPaint;
599                    boundsPaint.setColor(0xff808080);
600                    boundsPaint.setStrokeWidth(0);
601                    boundsPaint.setStyle(SkPaint::kStroke_Style);
602                    canvas->drawRect(bounds, boundsPaint);
603
604                    GrPaint grPaint;
605                    grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
606
607                    GrPathUtils::QuadUVMatrix DevToUV(pts);
608
609                    std::unique_ptr<GrDrawOp> op =
610                            BezierQuadTestOp::Make(gp, bounds, color, DevToUV);
611                    renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
612                }
613                ++col;
614                if (numCols == col) {
615                    col = 0;
616                    ++row;
617                }
618            }
619        }
620    }
621
622private:
623    typedef GM INHERITED;
624};
625
626DEF_GM(return new BezierCubicEffects;)
627DEF_GM(return new BezierConicEffects;)
628DEF_GM(return new BezierQuadEffects;)
629}
630
631#endif
632