• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
26 namespace {
27 extern const GrVertexAttrib kAttribs[] = {
28     {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
29     {kVec4f_GrVertexAttribType, sizeof(SkPoint), kGeometryProcessor_GrVertexAttribBinding}
30 };
31 }
32 
eval_line(const SkPoint & p,const SkScalar lineEq[3],SkScalar sign)33 static 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 
37 namespace skiagm {
38 /**
39  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
40  */
41 class BezierCubicEffects : public GM {
42 public:
BezierCubicEffects()43     BezierCubicEffects() {
44         this->setBGColor(0xFFFFFFFF);
45     }
46 
47 protected:
onShortName()48     virtual SkString onShortName() SK_OVERRIDE {
49         return SkString("bezier_cubic_effects");
50     }
51 
onISize()52     virtual SkISize onISize() SK_OVERRIDE {
53         return SkISize::Make(800, 800);
54     }
55 
onGetFlags() const56     virtual uint32_t onGetFlags() const SK_OVERRIDE {
57         // This is a GPU-specific GM.
58         return kGPUOnly_Flag;
59     }
60 
61 
onDraw(SkCanvas * canvas)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 
190 private:
191     typedef GM INHERITED;
192 };
193 
194 //////////////////////////////////////////////////////////////////////////////
195 
196 /**
197  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
198  */
199 class BezierConicEffects : public GM {
200 public:
BezierConicEffects()201     BezierConicEffects() {
202         this->setBGColor(0xFFFFFFFF);
203     }
204 
205 protected:
onShortName()206     virtual SkString onShortName() SK_OVERRIDE {
207         return SkString("bezier_conic_effects");
208     }
209 
onISize()210     virtual SkISize onISize() SK_OVERRIDE {
211         return SkISize::Make(800, 800);
212     }
213 
onGetFlags() const214     virtual uint32_t onGetFlags() const SK_OVERRIDE {
215         // This is a GPU-specific GM.
216         return kGPUOnly_Flag;
217     }
218 
219 
onDraw(SkCanvas * canvas)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 
345 private:
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.
split_conic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)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).
chop_conic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)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  */
390 class BezierQuadEffects : public GM {
391 public:
BezierQuadEffects()392     BezierQuadEffects() {
393         this->setBGColor(0xFFFFFFFF);
394     }
395 
396 protected:
onShortName()397     virtual SkString onShortName() SK_OVERRIDE {
398         return SkString("bezier_quad_effects");
399     }
400 
onISize()401     virtual SkISize onISize() SK_OVERRIDE {
402         return SkISize::Make(800, 800);
403     }
404 
onGetFlags() const405     virtual uint32_t onGetFlags() const SK_OVERRIDE {
406         // This is a GPU-specific GM.
407         return kGPUOnly_Flag;
408     }
409 
410 
onDraw(SkCanvas * canvas)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 
529 private:
530     typedef GM INHERITED;
531 };
532 
533 DEF_GM( return SkNEW(BezierCubicEffects); )
534 DEF_GM( return SkNEW(BezierConicEffects); )
535 DEF_GM( return SkNEW(BezierQuadEffects); )
536 
537 }
538 
539 #endif
540