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