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