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