beziereffects.cpp revision 53a0b6cc191639925798c01e9bce853723517419
1
2/*
3 * Copyright 2013 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
9// This test only works with the GPU backend.
10
11#include "gm.h"
12
13#if SK_SUPPORT_GPU
14
15#include "GrContext.h"
16#include "GrPathUtils.h"
17#include "GrTest.h"
18#include "SkColorPriv.h"
19#include "SkDevice.h"
20#include "SkGeometry.h"
21
22#include "effects/GrBezierEffect.h"
23
24// Position & KLM line eq values. These are the vertex attributes for Bezier curves. The last value
25// of the Vec4f is ignored.
26extern const GrVertexAttrib kAttribs[] = {
27    {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
28    {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
29};
30
31static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) {
32    return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]);
33}
34
35namespace skiagm {
36/**
37 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
38 */
39class BezierCubicEffects : public GM {
40public:
41    BezierCubicEffects() {
42        this->setBGColor(0xFFFFFFFF);
43    }
44
45protected:
46    virtual SkString onShortName() SK_OVERRIDE {
47        return SkString("bezier_cubic_effects");
48    }
49
50    virtual SkISize onISize() SK_OVERRIDE {
51        return make_isize(800, 800);
52    }
53
54    virtual uint32_t onGetFlags() const SK_OVERRIDE {
55        // This is a GPU-specific GM.
56        return kGPUOnly_Flag;
57    }
58
59
60    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
61        SkDevice* device = canvas->getTopDevice();
62        GrRenderTarget* rt = device->accessRenderTarget();
63        if (NULL == rt) {
64            return;
65        }
66        GrContext* context = rt->getContext();
67        if (NULL == context) {
68            return;
69        }
70
71        struct Vertex {
72            SkPoint fPosition;
73            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
74        };
75
76        static const int kNumCubics = 15;
77        SkMWCRandom rand;
78
79        // Mult by 3 for each edge effect type
80        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
81        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
82        SkScalar w = SkIntToScalar(rt->width()) / numCols;
83        SkScalar h = SkIntToScalar(rt->height()) / numRows;
84        int row = 0;
85        int col = 0;
86
87        for (int i = 0; i < kNumCubics; ++i) {
88            SkPoint baseControlPts[] = {
89                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
90                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
91                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
92                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
93            };
94            for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
95                SkScalar x = SkScalarMul(col, w);
96                SkScalar y = SkScalarMul(row, h);
97                SkPoint controlPts[] = {
98                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
99                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
100                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
101                    {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
102                };
103                SkPoint chopped[10];
104                SkScalar klmEqs[9];
105                SkScalar klmSigns[3];
106                int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
107                                                                   chopped,
108                                                                   klmEqs,
109                                                                   klmSigns);
110
111                SkPaint ctrlPtPaint;
112                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
113                for (int i = 0; i < 4; ++i) {
114                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
115                }
116
117                SkPaint polyPaint;
118                polyPaint.setColor(0xffA0A0A0);
119                polyPaint.setStrokeWidth(0);
120                polyPaint.setStyle(SkPaint::kStroke_Style);
121                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
122
123                SkPaint choppedPtPaint;
124                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
125
126                for (int c = 0; c < cnt; ++c) {
127                    SkPoint* pts = chopped + 3 * c;
128
129                    for (int i = 0; i < 4; ++i) {
130                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
131                    }
132
133                    SkRect bounds;
134                    bounds.set(pts, 4);
135
136                    SkPaint boundsPaint;
137                    boundsPaint.setColor(0xff808080);
138                    boundsPaint.setStrokeWidth(0);
139                    boundsPaint.setStyle(SkPaint::kStroke_Style);
140                    canvas->drawRect(bounds, boundsPaint);
141
142                    Vertex verts[4];
143                    verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
144                                                  bounds.fRight, bounds.fBottom,
145                                                  sizeof(Vertex));
146                    for (int v = 0; v < 4; ++v) {
147                        verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, klmSigns[c]);
148                        verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, klmSigns[c]);
149                        verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
150                    }
151
152                    GrTestTarget tt;
153                    context->getTestTarget(&tt);
154                    if (NULL == tt.target()) {
155                        continue;
156                    }
157                    GrDrawState* drawState = tt.target()->drawState();
158                    drawState->setVertexAttribs<kAttribs>(2);
159
160                    SkAutoTUnref<GrEffectRef> effect(GrCubicEffect::Create(
161                            GrBezierEdgeType(edgeType), *tt.target()->caps()));
162                    if (!effect) {
163                        continue;
164                    }
165                    drawState->addCoverageEffect(effect, 1);
166                    drawState->setRenderTarget(rt);
167                    drawState->setColor(0xff000000);
168
169                    tt.target()->setVertexSourceToArray(verts, 4);
170                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
171                    tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
172                }
173                ++col;
174                if (numCols == col) {
175                    col = 0;
176                    ++row;
177                }
178            }
179        }
180    }
181
182private:
183    typedef GM INHERITED;
184};
185
186//////////////////////////////////////////////////////////////////////////////
187
188/**
189 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
190 */
191class BezierConicEffects : public GM {
192public:
193    BezierConicEffects() {
194        this->setBGColor(0xFFFFFFFF);
195    }
196
197protected:
198    virtual SkString onShortName() SK_OVERRIDE {
199        return SkString("bezier_conic_effects");
200    }
201
202    virtual SkISize onISize() SK_OVERRIDE {
203        return make_isize(800, 800);
204    }
205
206    virtual uint32_t onGetFlags() const SK_OVERRIDE {
207        // This is a GPU-specific GM.
208        return kGPUOnly_Flag;
209    }
210
211
212    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
213        SkDevice* device = canvas->getTopDevice();
214        GrRenderTarget* rt = device->accessRenderTarget();
215        if (NULL == rt) {
216            return;
217        }
218        GrContext* context = rt->getContext();
219        if (NULL == context) {
220            return;
221        }
222
223        struct Vertex {
224            SkPoint fPosition;
225            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
226        };
227
228        static const int kNumConics = 10;
229        SkMWCRandom rand;
230
231        // Mult by 3 for each edge effect type
232        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
233        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
234        SkScalar w = SkIntToScalar(rt->width()) / numCols;
235        SkScalar h = SkIntToScalar(rt->height()) / numRows;
236        int row = 0;
237        int col = 0;
238
239        for (int i = 0; i < kNumConics; ++i) {
240            SkPoint baseControlPts[] = {
241                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
242                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
243                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
244            };
245            SkScalar weight = rand.nextRangeF(0.f, 2.f);
246            for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
247                SkScalar x = SkScalarMul(col, w);
248                SkScalar y = SkScalarMul(row, h);
249                SkPoint controlPts[] = {
250                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
251                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
252                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
253                };
254                SkConic dst[4];
255                SkScalar klmEqs[9];
256                int cnt = chop_conic(controlPts, dst, weight);
257                GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
258
259                SkPaint ctrlPtPaint;
260                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
261                for (int i = 0; i < 3; ++i) {
262                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
263                }
264
265                SkPaint polyPaint;
266                polyPaint.setColor(0xffA0A0A0);
267                polyPaint.setStrokeWidth(0);
268                polyPaint.setStyle(SkPaint::kStroke_Style);
269                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
270
271                SkPaint choppedPtPaint;
272                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
273
274                for (int c = 0; c < cnt; ++c) {
275                    SkPoint* pts = dst[c].fPts;
276                    for (int i = 0; i < 3; ++i) {
277                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
278                    }
279
280                    SkRect bounds;
281                    //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
282                    //bounds.set(bPts, 2);
283                    bounds.set(pts, 3);
284
285                    SkPaint boundsPaint;
286                    boundsPaint.setColor(0xff808080);
287                    boundsPaint.setStrokeWidth(0);
288                    boundsPaint.setStyle(SkPaint::kStroke_Style);
289                    canvas->drawRect(bounds, boundsPaint);
290
291                    Vertex verts[4];
292                    verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
293                                                  bounds.fRight, bounds.fBottom,
294                                                  sizeof(Vertex));
295                    for (int v = 0; v < 4; ++v) {
296                        verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, 1.f);
297                        verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, 1.f);
298                        verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
299                    }
300
301                    GrTestTarget tt;
302                    context->getTestTarget(&tt);
303                    if (NULL == tt.target()) {
304                        continue;
305                    }
306                    GrDrawState* drawState = tt.target()->drawState();
307                    drawState->setVertexAttribs<kAttribs>(2);
308
309                    SkAutoTUnref<GrEffectRef> effect(GrConicEffect::Create(
310                            GrBezierEdgeType(edgeType), *tt.target()->caps()));
311                    if (!effect) {
312                        continue;
313                    }
314                    drawState->addCoverageEffect(effect, 1);
315                    drawState->setRenderTarget(rt);
316                    drawState->setColor(0xff000000);
317
318                    tt.target()->setVertexSourceToArray(verts, 4);
319                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
320                    tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
321                }
322                ++col;
323                if (numCols == col) {
324                    col = 0;
325                    ++row;
326                }
327            }
328        }
329    }
330
331private:
332    // Uses the max curvature function for quads to estimate
333    // where to chop the conic. If the max curvature is not
334    // found along the curve segment it will return 1 and
335    // dst[0] is the original conic. If it returns 2 the dst[0]
336    // and dst[1] are the two new conics.
337    int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
338        SkScalar t = SkFindQuadMaxCurvature(src);
339        if (t == 0) {
340            if (dst) {
341                dst[0].set(src, weight);
342            }
343            return 1;
344        } else {
345            if (dst) {
346                SkConic conic;
347                conic.set(src, weight);
348                conic.chopAt(t, dst);
349            }
350            return 2;
351        }
352    }
353
354    // Calls split_conic on the entire conic and then once more on each subsection.
355    // Most cases will result in either 1 conic (chop point is not within t range)
356    // or 3 points (split once and then one subsection is split again).
357    int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
358        SkConic dstTemp[2];
359        int conicCnt = split_conic(src, dstTemp, weight);
360        if (2 == conicCnt) {
361            int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
362            conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
363        } else {
364            dst[0] = dstTemp[0];
365        }
366        return conicCnt;
367    }
368
369    typedef GM INHERITED;
370};
371
372//////////////////////////////////////////////////////////////////////////////
373/**
374 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
375 */
376class BezierQuadEffects : public GM {
377public:
378    BezierQuadEffects() {
379        this->setBGColor(0xFFFFFFFF);
380    }
381
382protected:
383    virtual SkString onShortName() SK_OVERRIDE {
384        return SkString("bezier_quad_effects");
385    }
386
387    virtual SkISize onISize() SK_OVERRIDE {
388        return make_isize(800, 800);
389    }
390
391    virtual uint32_t onGetFlags() const SK_OVERRIDE {
392        // This is a GPU-specific GM.
393        return kGPUOnly_Flag;
394    }
395
396
397    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
398        SkDevice* device = canvas->getTopDevice();
399        GrRenderTarget* rt = device->accessRenderTarget();
400        if (NULL == rt) {
401            return;
402        }
403        GrContext* context = rt->getContext();
404        if (NULL == context) {
405            return;
406        }
407
408        struct Vertex {
409            SkPoint fPosition;
410            float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
411        };
412
413        static const int kNumQuads = 5;
414        SkMWCRandom rand;
415
416        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
417        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
418        SkScalar w = SkIntToScalar(rt->width()) / numCols;
419        SkScalar h = SkIntToScalar(rt->height()) / numRows;
420        int row = 0;
421        int col = 0;
422
423        for (int i = 0; i < kNumQuads; ++i) {
424            SkPoint baseControlPts[] = {
425                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
426                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
427                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
428            };
429            for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
430                SkScalar x = SkScalarMul(col, w);
431                SkScalar y = SkScalarMul(row, h);
432                SkPoint controlPts[] = {
433                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
434                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
435                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
436                };
437                SkPoint chopped[5];
438                int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
439
440                SkPaint ctrlPtPaint;
441                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
442                for (int i = 0; i < 3; ++i) {
443                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
444                }
445
446                SkPaint polyPaint;
447                polyPaint.setColor(0xffA0A0A0);
448                polyPaint.setStrokeWidth(0);
449                polyPaint.setStyle(SkPaint::kStroke_Style);
450                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
451
452                SkPaint choppedPtPaint;
453                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
454
455                for (int c = 0; c < cnt; ++c) {
456                    SkPoint* pts = chopped + 2 * c;
457
458                    for (int i = 0; i < 3; ++i) {
459                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
460                    }
461
462                    SkRect bounds;
463                    bounds.set(pts, 3);
464
465                    SkPaint boundsPaint;
466                    boundsPaint.setColor(0xff808080);
467                    boundsPaint.setStrokeWidth(0);
468                    boundsPaint.setStyle(SkPaint::kStroke_Style);
469                    canvas->drawRect(bounds, boundsPaint);
470
471                    Vertex verts[4];
472                    verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
473                                                  bounds.fRight, bounds.fBottom,
474                                                  sizeof(Vertex));
475
476                    GrPathUtils::QuadUVMatrix DevToUV(pts);
477                    DevToUV.apply<4, sizeof(Vertex), sizeof(GrPoint)>(verts);
478
479                    GrTestTarget tt;
480                    context->getTestTarget(&tt);
481                    if (NULL == tt.target()) {
482                        continue;
483                    }
484                    GrDrawState* drawState = tt.target()->drawState();
485                    drawState->setVertexAttribs<kAttribs>(2);
486                    SkAutoTUnref<GrEffectRef> effect(GrQuadEffect::Create(
487                            GrBezierEdgeType(edgeType), *tt.target()->caps()));
488                    if (!effect) {
489                        continue;
490                    }
491                    drawState->addCoverageEffect(effect, 1);
492                    drawState->setRenderTarget(rt);
493                    drawState->setColor(0xff000000);
494
495                    tt.target()->setVertexSourceToArray(verts, 4);
496                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
497                    tt.target()->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
498                }
499                ++col;
500                if (numCols == col) {
501                    col = 0;
502                    ++row;
503                }
504            }
505        }
506    }
507
508private:
509    typedef GM INHERITED;
510};
511
512DEF_GM( return SkNEW(BezierCubicEffects); )
513DEF_GM( return SkNEW(BezierConicEffects); )
514DEF_GM( return SkNEW(BezierQuadEffects); )
515
516}
517
518#endif
519