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                    Vertex verts[4];
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                    GrTestTarget tt;
168                    context->getTestTarget(&tt);
169                    SkASSERT(tt.target());
170                    GrDrawState* drawState = tt.target()->drawState();
171                    drawState->setVertexAttribs<kAttribs>(2, sizeof(Vertex));
172
173                    drawState->setGeometryProcessor(gp);
174                    drawState->setRenderTarget(rt);
175                    drawState->setColor(0xff000000);
176
177                    tt.target()->setVertexSourceToArray(verts, 4);
178                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
179                    tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
180                }
181                ++col;
182                if (numCols == col) {
183                    col = 0;
184                    ++row;
185                }
186            }
187        }
188    }
189
190private:
191    typedef GM INHERITED;
192};
193
194//////////////////////////////////////////////////////////////////////////////
195
196/**
197 * This GM directly exercises effects that draw Bezier curves in the GPU backend.
198 */
199class BezierConicEffects : public GM {
200public:
201    BezierConicEffects() {
202        this->setBGColor(0xFFFFFFFF);
203    }
204
205protected:
206    virtual SkString onShortName() SK_OVERRIDE {
207        return SkString("bezier_conic_effects");
208    }
209
210    virtual SkISize onISize() SK_OVERRIDE {
211        return SkISize::Make(800, 800);
212    }
213
214    virtual uint32_t onGetFlags() const SK_OVERRIDE {
215        // This is a GPU-specific GM.
216        return kGPUOnly_Flag;
217    }
218
219
220    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
221        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
222        if (NULL == rt) {
223            return;
224        }
225        GrContext* context = rt->getContext();
226        if (NULL == context) {
227            return;
228        }
229
230        struct Vertex {
231            SkPoint fPosition;
232            float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
233        };
234
235        static const int kNumConics = 10;
236        SkRandom rand;
237
238        // Mult by 3 for each edge effect type
239        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
240        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
241        SkScalar w = SkIntToScalar(rt->width()) / numCols;
242        SkScalar h = SkIntToScalar(rt->height()) / numRows;
243        int row = 0;
244        int col = 0;
245
246        for (int i = 0; i < kNumConics; ++i) {
247            SkPoint baseControlPts[] = {
248                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
249                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
250                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
251            };
252            SkScalar weight = rand.nextRangeF(0.f, 2.f);
253            for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
254                SkAutoTUnref<GrGeometryProcessor> gp;
255                {   // scope to contain GrTestTarget
256                    GrTestTarget tt;
257                    context->getTestTarget(&tt);
258                    if (NULL == tt.target()) {
259                        continue;
260                    }
261                    GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
262                    gp.reset(GrConicEffect::Create(et, *tt.target()->caps()));
263                    if (!gp) {
264                        continue;
265                    }
266                }
267
268                SkScalar x = SkScalarMul(col, w);
269                SkScalar y = SkScalarMul(row, h);
270                SkPoint controlPts[] = {
271                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
272                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
273                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
274                };
275                SkConic dst[4];
276                SkScalar klmEqs[9];
277                int cnt = chop_conic(controlPts, dst, weight);
278                GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
279
280                SkPaint ctrlPtPaint;
281                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
282                for (int i = 0; i < 3; ++i) {
283                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
284                }
285
286                SkPaint polyPaint;
287                polyPaint.setColor(0xffA0A0A0);
288                polyPaint.setStrokeWidth(0);
289                polyPaint.setStyle(SkPaint::kStroke_Style);
290                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
291
292                SkPaint choppedPtPaint;
293                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
294
295                for (int c = 0; c < cnt; ++c) {
296                    SkPoint* pts = dst[c].fPts;
297                    for (int i = 0; i < 3; ++i) {
298                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
299                    }
300
301                    SkRect bounds;
302                    //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
303                    //bounds.set(bPts, 2);
304                    bounds.set(pts, 3);
305
306                    SkPaint boundsPaint;
307                    boundsPaint.setColor(0xff808080);
308                    boundsPaint.setStrokeWidth(0);
309                    boundsPaint.setStyle(SkPaint::kStroke_Style);
310                    canvas->drawRect(bounds, boundsPaint);
311
312                    Vertex verts[4];
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                    GrTestTarget tt;
323                    context->getTestTarget(&tt);
324                    SkASSERT(tt.target());
325                    GrDrawState* drawState = tt.target()->drawState();
326                    drawState->setVertexAttribs<kAttribs>(2, sizeof(Vertex));
327
328                    drawState->setGeometryProcessor(gp);
329                    drawState->setRenderTarget(rt);
330                    drawState->setColor(0xff000000);
331
332                    tt.target()->setVertexSourceToArray(verts, 4);
333                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
334                    tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
335                }
336                ++col;
337                if (numCols == col) {
338                    col = 0;
339                    ++row;
340                }
341            }
342        }
343    }
344
345private:
346    // Uses the max curvature function for quads to estimate
347    // where to chop the conic. If the max curvature is not
348    // found along the curve segment it will return 1 and
349    // dst[0] is the original conic. If it returns 2 the dst[0]
350    // and dst[1] are the two new conics.
351    int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
352        SkScalar t = SkFindQuadMaxCurvature(src);
353        if (t == 0) {
354            if (dst) {
355                dst[0].set(src, weight);
356            }
357            return 1;
358        } else {
359            if (dst) {
360                SkConic conic;
361                conic.set(src, weight);
362                conic.chopAt(t, dst);
363            }
364            return 2;
365        }
366    }
367
368    // Calls split_conic on the entire conic and then once more on each subsection.
369    // Most cases will result in either 1 conic (chop point is not within t range)
370    // or 3 points (split once and then one subsection is split again).
371    int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
372        SkConic dstTemp[2];
373        int conicCnt = split_conic(src, dstTemp, weight);
374        if (2 == conicCnt) {
375            int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
376            conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
377        } else {
378            dst[0] = dstTemp[0];
379        }
380        return conicCnt;
381    }
382
383    typedef GM INHERITED;
384};
385
386//////////////////////////////////////////////////////////////////////////////
387/**
388 * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
389 */
390class BezierQuadEffects : public GM {
391public:
392    BezierQuadEffects() {
393        this->setBGColor(0xFFFFFFFF);
394    }
395
396protected:
397    virtual SkString onShortName() SK_OVERRIDE {
398        return SkString("bezier_quad_effects");
399    }
400
401    virtual SkISize onISize() SK_OVERRIDE {
402        return SkISize::Make(800, 800);
403    }
404
405    virtual uint32_t onGetFlags() const SK_OVERRIDE {
406        // This is a GPU-specific GM.
407        return kGPUOnly_Flag;
408    }
409
410
411    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
412        GrRenderTarget* rt = canvas->internal_private_accessTopLayerRenderTarget();
413        if (NULL == rt) {
414            return;
415        }
416        GrContext* context = rt->getContext();
417        if (NULL == context) {
418            return;
419        }
420
421        struct Vertex {
422            SkPoint fPosition;
423            float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
424        };
425
426        static const int kNumQuads = 5;
427        SkRandom rand;
428
429        int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
430        int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
431        SkScalar w = SkIntToScalar(rt->width()) / numCols;
432        SkScalar h = SkIntToScalar(rt->height()) / numRows;
433        int row = 0;
434        int col = 0;
435
436        for (int i = 0; i < kNumQuads; ++i) {
437            SkPoint baseControlPts[] = {
438                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
439                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
440                {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
441            };
442            for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
443                SkAutoTUnref<GrGeometryProcessor> gp;
444                {   // scope to contain GrTestTarget
445                    GrTestTarget tt;
446                    context->getTestTarget(&tt);
447                    if (NULL == tt.target()) {
448                        continue;
449                    }
450                    GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
451                    gp.reset(GrQuadEffect::Create(et, *tt.target()->caps()));
452                    if (!gp) {
453                        continue;
454                    }
455                }
456
457                SkScalar x = SkScalarMul(col, w);
458                SkScalar y = SkScalarMul(row, h);
459                SkPoint controlPts[] = {
460                    {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
461                    {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
462                    {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
463                };
464                SkPoint chopped[5];
465                int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
466
467                SkPaint ctrlPtPaint;
468                ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
469                for (int i = 0; i < 3; ++i) {
470                    canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
471                }
472
473                SkPaint polyPaint;
474                polyPaint.setColor(0xffA0A0A0);
475                polyPaint.setStrokeWidth(0);
476                polyPaint.setStyle(SkPaint::kStroke_Style);
477                canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
478
479                SkPaint choppedPtPaint;
480                choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
481
482                for (int c = 0; c < cnt; ++c) {
483                    SkPoint* pts = chopped + 2 * c;
484
485                    for (int i = 0; i < 3; ++i) {
486                        canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
487                    }
488
489                    SkRect bounds;
490                    bounds.set(pts, 3);
491
492                    SkPaint boundsPaint;
493                    boundsPaint.setColor(0xff808080);
494                    boundsPaint.setStrokeWidth(0);
495                    boundsPaint.setStyle(SkPaint::kStroke_Style);
496                    canvas->drawRect(bounds, boundsPaint);
497
498                    Vertex verts[4];
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                    GrTestTarget tt;
507                    context->getTestTarget(&tt);
508                    SkASSERT(tt.target());
509                    GrDrawState* drawState = tt.target()->drawState();
510                    drawState->setVertexAttribs<kAttribs>(2, sizeof(Vertex));
511
512                    drawState->setGeometryProcessor(gp);
513                    drawState->setRenderTarget(rt);
514                    drawState->setColor(0xff000000);
515
516                    tt.target()->setVertexSourceToArray(verts, 4);
517                    tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
518                    tt.target()->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
519                }
520                ++col;
521                if (numCols == col) {
522                    col = 0;
523                    ++row;
524                }
525            }
526        }
527    }
528
529private:
530    typedef GM INHERITED;
531};
532
533DEF_GM( return SkNEW(BezierCubicEffects); )
534DEF_GM( return SkNEW(BezierConicEffects); )
535DEF_GM( return SkNEW(BezierQuadEffects); )
536
537}
538
539#endif
540