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