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