• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 // This test only works with the GPU backend.
9 
10 #include "gm.h"
11 
12 #if SK_SUPPORT_GPU
13 
14 #include "GrRenderTargetContextPriv.h"
15 #include "GrContext.h"
16 #include "GrPathUtils.h"
17 #include "GrTest.h"
18 #include "SkColorPriv.h"
19 #include "SkGeometry.h"
20 
21 #include "ops/GrTestMeshDrawOp.h"
22 
23 #include "effects/GrBezierEffect.h"
24 
25 namespace skiagm {
26 
27 class BezierCubicOrConicTestOp : public GrTestMeshDrawOp {
28 public:
29     DEFINE_OP_CLASS_ID
30 
name() const31     const char* name() const override { return "BezierCubicOrConicTestOp"; }
32 
Make(sk_sp<GrGeometryProcessor> gp,const SkRect & rect,GrColor color,const SkMatrix & klm,SkScalar sign)33     static std::unique_ptr<GrMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
34                                               GrColor color, const SkMatrix& klm, SkScalar sign) {
35         return std::unique_ptr<GrMeshDrawOp>(
36                 new BezierCubicOrConicTestOp(gp, rect, color, klm, sign));
37     }
38 
39 private:
BezierCubicOrConicTestOp(sk_sp<GrGeometryProcessor> gp,const SkRect & rect,GrColor color,const SkMatrix & klm,SkScalar sign)40     BezierCubicOrConicTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
41                              const SkMatrix& klm, SkScalar sign)
42         : INHERITED(ClassID(), rect, color)
43         , fKLM(klm)
44         , fRect(rect)
45         , fGeometryProcessor(std::move(gp)) {
46         if (1 != sign) {
47             fKLM.postScale(sign, sign);
48         }
49     }
50     struct Vertex {
51         SkPoint fPosition;
52         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
53     };
54 
onPrepareDraws(Target * target) const55     void onPrepareDraws(Target* target) const override {
56         QuadHelper helper;
57         size_t vertexStride = fGeometryProcessor->getVertexStride();
58         SkASSERT(vertexStride == sizeof(Vertex));
59         Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
60         if (!verts) {
61             return;
62         }
63         verts[0].fPosition.setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
64                                       sizeof(Vertex));
65         for (int v = 0; v < 4; ++v) {
66             SkScalar pt3[3] = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
67             fKLM.mapHomogeneousPoints(verts[v].fKLM, pt3, 1);
68         }
69         helper.recordDraw(target, fGeometryProcessor.get());
70     }
71 
72     SkMatrix fKLM;
73     SkRect fRect;
74     sk_sp<GrGeometryProcessor> fGeometryProcessor;
75 
76     static constexpr int kVertsPerCubic = 4;
77     static constexpr int kIndicesPerCubic = 6;
78 
79     typedef GrTestMeshDrawOp INHERITED;
80 };
81 
82 /**
83  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
84  */
85 class BezierCubicEffects : public GM {
86 public:
BezierCubicEffects()87     BezierCubicEffects() {
88         this->setBGColor(0xFFFFFFFF);
89     }
90 
91 protected:
onShortName()92     SkString onShortName() override {
93         return SkString("bezier_cubic_effects");
94     }
95 
onISize()96     SkISize onISize() override {
97         return SkISize::Make(800, 800);
98     }
99 
onDraw(SkCanvas * canvas)100     void onDraw(SkCanvas* canvas) override {
101         GrRenderTargetContext* renderTargetContext =
102             canvas->internal_private_accessTopLayerRenderTargetContext();
103         if (!renderTargetContext) {
104             skiagm::GM::DrawGpuOnlyMessage(canvas);
105             return;
106         }
107 
108         GrContext* context = canvas->getGrContext();
109         if (!context) {
110             return;
111         }
112 
113         struct Vertex {
114             SkPoint fPosition;
115             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
116         };
117 
118         constexpr int kNumCubics = 15;
119         SkRandom rand;
120 
121         // Mult by 3 for each edge effect type
122         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
123         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
124         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
125         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
126         int row = 0;
127         int col = 0;
128         constexpr GrColor color = 0xff000000;
129 
130         for (int i = 0; i < kNumCubics; ++i) {
131             SkPoint baseControlPts[] = {
132                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
133                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
134                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
135                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
136             };
137             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
138                 sk_sp<GrGeometryProcessor> gp;
139                 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
140                 gp = GrCubicEffect::Make(color, SkMatrix::I(), et, *context->caps());
141                 if (!gp) {
142                     continue;
143                 }
144                 SkScalar x = col * w;
145                 SkScalar y = row * h;
146                 SkPoint controlPts[] = {
147                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
148                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
149                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
150                     {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
151                 };
152                 SkPoint chopped[10];
153                 SkMatrix klm;
154                 int loopIndex;
155                 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
156                                                                    chopped,
157                                                                    &klm,
158                                                                    &loopIndex);
159 
160                 SkPaint ctrlPtPaint;
161                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
162                 canvas->drawCircle(controlPts[0].fX, controlPts[0].fY, 8.f, ctrlPtPaint);
163                 for (int i = 1; i < 4; ++i) {
164                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
165                 }
166 
167                 SkPaint polyPaint;
168                 polyPaint.setColor(0xffA0A0A0);
169                 polyPaint.setStrokeWidth(0);
170                 polyPaint.setStyle(SkPaint::kStroke_Style);
171                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
172 
173                 SkPaint choppedPtPaint;
174                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
175 
176                 for (int c = 0; c < cnt; ++c) {
177                     SkPoint* pts = chopped + 3 * c;
178 
179                     for (int i = 0; i < 4; ++i) {
180                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
181                     }
182 
183                     SkRect bounds;
184                     bounds.set(pts, 4);
185 
186                     SkPaint boundsPaint;
187                     boundsPaint.setColor(0xff808080);
188                     boundsPaint.setStrokeWidth(0);
189                     boundsPaint.setStyle(SkPaint::kStroke_Style);
190                     canvas->drawRect(bounds, boundsPaint);
191 
192                     GrPaint grPaint;
193                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
194 
195                     SkScalar sign = 1.0f;
196                     if (c == loopIndex && cnt != 3) {
197                         sign = -1.0f;
198                     }
199 
200                     std::unique_ptr<GrMeshDrawOp> op =
201                             BezierCubicOrConicTestOp::Make(gp, bounds, color, klm, sign);
202 
203                     renderTargetContext->priv().testingOnly_addMeshDrawOp(
204                             std::move(grPaint), GrAAType::kNone, std::move(op));
205                 }
206                 ++col;
207                 if (numCols == col) {
208                     col = 0;
209                     ++row;
210                 }
211             }
212         }
213     }
214 
215 private:
216     typedef GM INHERITED;
217 };
218 
219 //////////////////////////////////////////////////////////////////////////////
220 
221 /**
222  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
223  */
224 class BezierConicEffects : public GM {
225 public:
BezierConicEffects()226     BezierConicEffects() {
227         this->setBGColor(0xFFFFFFFF);
228     }
229 
230 protected:
onShortName()231     SkString onShortName() override {
232         return SkString("bezier_conic_effects");
233     }
234 
onISize()235     SkISize onISize() override {
236         return SkISize::Make(800, 800);
237     }
238 
239 
onDraw(SkCanvas * canvas)240     void onDraw(SkCanvas* canvas) override {
241         GrRenderTargetContext* renderTargetContext =
242             canvas->internal_private_accessTopLayerRenderTargetContext();
243         if (!renderTargetContext) {
244             skiagm::GM::DrawGpuOnlyMessage(canvas);
245             return;
246         }
247 
248         GrContext* context = canvas->getGrContext();
249         if (!context) {
250             return;
251         }
252 
253         struct Vertex {
254             SkPoint fPosition;
255             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
256         };
257 
258         constexpr int kNumConics = 10;
259         SkRandom rand;
260 
261         // Mult by 3 for each edge effect type
262         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
263         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
264         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
265         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
266         int row = 0;
267         int col = 0;
268         constexpr GrColor color = 0xff000000;
269 
270         for (int i = 0; i < kNumConics; ++i) {
271             SkPoint baseControlPts[] = {
272                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
273                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
274                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
275             };
276             SkScalar weight = rand.nextRangeF(0.f, 2.f);
277             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
278                 sk_sp<GrGeometryProcessor> gp;
279                 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
280                 gp = GrConicEffect::Make(color, SkMatrix::I(), et,
281                                          *context->caps(), SkMatrix::I(), false);
282                 if (!gp) {
283                     continue;
284                 }
285 
286                 SkScalar x = col * w;
287                 SkScalar y = row * h;
288                 SkPoint controlPts[] = {
289                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
290                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
291                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
292                 };
293                 SkConic dst[4];
294                 SkMatrix klm;
295                 int cnt = chop_conic(controlPts, dst, weight);
296                 GrPathUtils::getConicKLM(controlPts, weight, &klm);
297 
298                 SkPaint ctrlPtPaint;
299                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
300                 for (int i = 0; i < 3; ++i) {
301                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
302                 }
303 
304                 SkPaint polyPaint;
305                 polyPaint.setColor(0xffA0A0A0);
306                 polyPaint.setStrokeWidth(0);
307                 polyPaint.setStyle(SkPaint::kStroke_Style);
308                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
309 
310                 SkPaint choppedPtPaint;
311                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
312 
313                 for (int c = 0; c < cnt; ++c) {
314                     SkPoint* pts = dst[c].fPts;
315                     for (int i = 0; i < 3; ++i) {
316                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
317                     }
318 
319                     SkRect bounds;
320                     //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
321                     //bounds.set(bPts, 2);
322                     bounds.set(pts, 3);
323 
324                     SkPaint boundsPaint;
325                     boundsPaint.setColor(0xff808080);
326                     boundsPaint.setStrokeWidth(0);
327                     boundsPaint.setStyle(SkPaint::kStroke_Style);
328                     canvas->drawRect(bounds, boundsPaint);
329 
330                     GrPaint grPaint;
331                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
332 
333                     std::unique_ptr<GrMeshDrawOp> op =
334                             BezierCubicOrConicTestOp::Make(gp, bounds, color, klm, 1.f);
335 
336                     renderTargetContext->priv().testingOnly_addMeshDrawOp(
337                             std::move(grPaint), GrAAType::kNone, std::move(op));
338                 }
339                 ++col;
340                 if (numCols == col) {
341                     col = 0;
342                     ++row;
343                 }
344             }
345         }
346     }
347 
348 private:
349     // Uses the max curvature function for quads to estimate
350     // where to chop the conic. If the max curvature is not
351     // found along the curve segment it will return 1 and
352     // dst[0] is the original conic. If it returns 2 the dst[0]
353     // and dst[1] are the two new conics.
split_conic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)354     int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
355         SkScalar t = SkFindQuadMaxCurvature(src);
356         if (t == 0) {
357             if (dst) {
358                 dst[0].set(src, weight);
359             }
360             return 1;
361         } else {
362             if (dst) {
363                 SkConic conic;
364                 conic.set(src, weight);
365                 if (!conic.chopAt(t, dst)) {
366                     dst[0].set(src, weight);
367                     return 1;
368                 }
369             }
370             return 2;
371         }
372     }
373 
374     // Calls split_conic on the entire conic and then once more on each subsection.
375     // Most cases will result in either 1 conic (chop point is not within t range)
376     // or 3 points (split once and then one subsection is split again).
chop_conic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)377     int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
378         SkConic dstTemp[2];
379         int conicCnt = split_conic(src, dstTemp, weight);
380         if (2 == conicCnt) {
381             int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
382             conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
383         } else {
384             dst[0] = dstTemp[0];
385         }
386         return conicCnt;
387     }
388 
389     typedef GM INHERITED;
390 };
391 
392 //////////////////////////////////////////////////////////////////////////////
393 
394 class BezierQuadTestOp : public GrTestMeshDrawOp {
395 public:
396     DEFINE_OP_CLASS_ID
name() const397     const char* name() const override { return "BezierQuadTestOp"; }
398 
Make(sk_sp<GrGeometryProcessor> gp,const SkRect & rect,GrColor color,const GrPathUtils::QuadUVMatrix & devToUV)399     static std::unique_ptr<GrMeshDrawOp> Make(sk_sp<GrGeometryProcessor> gp, const SkRect& rect,
400                                               GrColor color,
401                                               const GrPathUtils::QuadUVMatrix& devToUV) {
402         return std::unique_ptr<GrMeshDrawOp>(new BezierQuadTestOp(gp, rect, color, devToUV));
403     }
404 
405 private:
BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp,const SkRect & rect,GrColor color,const GrPathUtils::QuadUVMatrix & devToUV)406     BezierQuadTestOp(sk_sp<GrGeometryProcessor> gp, const SkRect& rect, GrColor color,
407                      const GrPathUtils::QuadUVMatrix& devToUV)
408             : INHERITED(ClassID(), rect, color)
409             , fDevToUV(devToUV)
410             , fRect(rect)
411             , fGeometryProcessor(std::move(gp)) {}
412 
413     struct Vertex {
414         SkPoint fPosition;
415         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
416     };
417 
onPrepareDraws(Target * target) const418     void onPrepareDraws(Target* target) const override {
419         QuadHelper helper;
420         size_t vertexStride = fGeometryProcessor->getVertexStride();
421         SkASSERT(vertexStride == sizeof(Vertex));
422         Vertex* verts = reinterpret_cast<Vertex*>(helper.init(target, vertexStride, 1));
423         if (!verts) {
424             return;
425         }
426         verts[0].fPosition.setRectFan(fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
427                                       sizeof(Vertex));
428         fDevToUV.apply<4, sizeof(Vertex), sizeof(SkPoint)>(verts);
429         helper.recordDraw(target, fGeometryProcessor.get());
430     }
431 
432     GrPathUtils::QuadUVMatrix fDevToUV;
433     SkRect fRect;
434     sk_sp<GrGeometryProcessor> fGeometryProcessor;
435 
436     static constexpr int kVertsPerCubic = 4;
437     static constexpr int kIndicesPerCubic = 6;
438 
439     typedef GrTestMeshDrawOp INHERITED;
440 };
441 
442 /**
443  * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
444  */
445 class BezierQuadEffects : public GM {
446 public:
BezierQuadEffects()447     BezierQuadEffects() {
448         this->setBGColor(0xFFFFFFFF);
449     }
450 
451 protected:
onShortName()452     SkString onShortName() override {
453         return SkString("bezier_quad_effects");
454     }
455 
onISize()456     SkISize onISize() override {
457         return SkISize::Make(800, 800);
458     }
459 
460 
onDraw(SkCanvas * canvas)461     void onDraw(SkCanvas* canvas) override {
462         GrRenderTargetContext* renderTargetContext =
463             canvas->internal_private_accessTopLayerRenderTargetContext();
464         if (!renderTargetContext) {
465             skiagm::GM::DrawGpuOnlyMessage(canvas);
466             return;
467         }
468 
469         GrContext* context = canvas->getGrContext();
470         if (!context) {
471             return;
472         }
473 
474         struct Vertex {
475             SkPoint fPosition;
476             float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
477         };
478 
479         constexpr int kNumQuads = 5;
480         SkRandom rand;
481 
482         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
483         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
484         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
485         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
486         int row = 0;
487         int col = 0;
488         constexpr GrColor color = 0xff000000;
489 
490         for (int i = 0; i < kNumQuads; ++i) {
491             SkPoint baseControlPts[] = {
492                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
493                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
494                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
495             };
496             for(int edgeType = 0; edgeType < kGrProcessorEdgeTypeCnt; ++edgeType) {
497                 sk_sp<GrGeometryProcessor> gp;
498                 GrPrimitiveEdgeType et = (GrPrimitiveEdgeType)edgeType;
499                 gp = GrQuadEffect::Make(color, SkMatrix::I(), et,
500                                         *context->caps(), SkMatrix::I(), false);
501                 if (!gp) {
502                     continue;
503                 }
504 
505                 SkScalar x = col * w;
506                 SkScalar y = row * h;
507                 SkPoint controlPts[] = {
508                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
509                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
510                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
511                 };
512                 SkPoint chopped[5];
513                 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
514 
515                 SkPaint ctrlPtPaint;
516                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
517                 for (int i = 0; i < 3; ++i) {
518                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
519                 }
520 
521                 SkPaint polyPaint;
522                 polyPaint.setColor(0xffA0A0A0);
523                 polyPaint.setStrokeWidth(0);
524                 polyPaint.setStyle(SkPaint::kStroke_Style);
525                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
526 
527                 SkPaint choppedPtPaint;
528                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
529 
530                 for (int c = 0; c < cnt; ++c) {
531                     SkPoint* pts = chopped + 2 * c;
532 
533                     for (int i = 0; i < 3; ++i) {
534                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
535                     }
536 
537                     SkRect bounds;
538                     bounds.set(pts, 3);
539 
540                     SkPaint boundsPaint;
541                     boundsPaint.setColor(0xff808080);
542                     boundsPaint.setStrokeWidth(0);
543                     boundsPaint.setStyle(SkPaint::kStroke_Style);
544                     canvas->drawRect(bounds, boundsPaint);
545 
546                     GrPaint grPaint;
547                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
548 
549                     GrPathUtils::QuadUVMatrix DevToUV(pts);
550 
551                     std::unique_ptr<GrMeshDrawOp> op =
552                             BezierQuadTestOp::Make(gp, bounds, color, DevToUV);
553 
554                     renderTargetContext->priv().testingOnly_addMeshDrawOp(
555                             std::move(grPaint), GrAAType::kNone, std::move(op));
556                 }
557                 ++col;
558                 if (numCols == col) {
559                     col = 0;
560                     ++row;
561                 }
562             }
563         }
564     }
565 
566 private:
567     typedef GM INHERITED;
568 };
569 
570 DEF_GM(return new BezierCubicEffects;)
571 DEF_GM(return new BezierConicEffects;)
572 DEF_GM(return new BezierQuadEffects;)
573 }
574 
575 #endif
576