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 #include "sk_tool_utils.h"
12 
13 #include "GrContext.h"
14 #include "GrMemoryPool.h"
15 #include "GrOpFlushState.h"
16 #include "GrPathUtils.h"
17 #include "GrRenderTargetContextPriv.h"
18 #include "SkColorPriv.h"
19 #include "SkGeometry.h"
20 #include "SkPoint3.h"
21 #include "SkPointPriv.h"
22 #include "effects/GrBezierEffect.h"
23 #include "ops/GrMeshDrawOp.h"
24 
25 namespace skiagm {
26 
27 class BezierTestOp : public GrMeshDrawOp {
28 public:
fixedFunctionFlags() const29     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
30 
finalize(const GrCaps & caps,const GrAppliedClip * clip)31     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip) override {
32         return fProcessorSet.finalize(fColor, GrProcessorAnalysisCoverage::kSingleChannel, clip,
33                                       false, caps, &fColor);
34     }
35 
visitProxies(const VisitProxyFunc & func,VisitorType) const36     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
37         fProcessorSet.visitProxies(func);
38     }
39 
40 protected:
BezierTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,int32_t classID)41     BezierTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect, const SkPMColor4f& color,
42                  int32_t classID)
43             : INHERITED(classID)
44             , fRect(rect)
45             , fColor(color)
46             , fGeometryProcessor(std::move(gp))
47             , fProcessorSet(SkBlendMode::kSrc) {
48         this->setBounds(rect, HasAABloat::kYes, IsZeroArea::kNo);
49     }
50 
makePipeline(Target * target)51     Target::PipelineAndFixedDynamicState makePipeline(Target* target) {
52         return target->makePipeline(0, std::move(fProcessorSet), target->detachAppliedClip());
53     }
54 
gp() const55     sk_sp<const GrGeometryProcessor> gp() const { return fGeometryProcessor; }
56 
rect() const57     const SkRect& rect() const { return fRect; }
color() const58     const SkPMColor4f& color() const { return fColor; }
59 
60 private:
61     SkRect fRect;
62     SkPMColor4f fColor;
63     sk_sp<const GrGeometryProcessor> fGeometryProcessor;
64     GrProcessorSet fProcessorSet;
65 
66     typedef GrMeshDrawOp INHERITED;
67 };
68 
69 /**
70  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
71  */
72 
73 class BezierConicTestOp : public BezierTestOp {
74 public:
75     DEFINE_OP_CLASS_ID
76 
name() const77     const char* name() const override { return "BezierConicTestOp"; }
78 
Make(GrContext * context,sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)79     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
80                                           sk_sp<const GrGeometryProcessor> gp,
81                                           const SkRect& rect,
82                                           const SkPMColor4f& color,
83                                           const SkMatrix& klm) {
84         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
85 
86         return pool->allocate<BezierConicTestOp>(std::move(gp), rect, color, klm);
87     }
88 
89 private:
90     friend class ::GrOpMemoryPool; // for ctor
91 
BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const SkMatrix & klm)92     BezierConicTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
93                       const SkPMColor4f& color, const SkMatrix& klm)
94             : INHERITED(std::move(gp), rect, color, ClassID()), fKLM(klm) {}
95 
96     struct Vertex {
97         SkPoint fPosition;
98         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
99     };
100 
onPrepareDraws(Target * target)101     void onPrepareDraws(Target* target) override {
102         SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
103         QuadHelper helper(target, sizeof(Vertex), 1);
104         Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
105         if (!verts) {
106             return;
107         }
108         SkRect rect = this->rect();
109         SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect.fLeft, rect.fTop, rect.fRight,
110                                      rect.fBottom, sizeof(Vertex));
111         for (int v = 0; v < 4; ++v) {
112             SkPoint3 pt3 = {verts[v].fPosition.x(), verts[v].fPosition.y(), 1.f};
113             fKLM.mapHomogeneousPoints((SkPoint3* ) verts[v].fKLM, &pt3, 1);
114         }
115         auto pipe = this->makePipeline(target);
116         helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState);
117     }
118 
119     SkMatrix fKLM;
120 
121     static constexpr int kVertsPerCubic = 4;
122     static constexpr int kIndicesPerCubic = 6;
123 
124     typedef BezierTestOp INHERITED;
125 };
126 
127 
128 /**
129  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
130  */
131 class BezierConicEffects : public GM {
132 public:
BezierConicEffects()133     BezierConicEffects() {
134         this->setBGColor(0xFFFFFFFF);
135     }
136 
137 protected:
onShortName()138     SkString onShortName() override {
139         return SkString("bezier_conic_effects");
140     }
141 
onISize()142     SkISize onISize() override {
143         return SkISize::Make(800, 800);
144     }
145 
146 
onDraw(SkCanvas * canvas)147     void onDraw(SkCanvas* canvas) override {
148         GrRenderTargetContext* renderTargetContext =
149             canvas->internal_private_accessTopLayerRenderTargetContext();
150         if (!renderTargetContext) {
151             skiagm::GM::DrawGpuOnlyMessage(canvas);
152             return;
153         }
154 
155         GrContext* context = canvas->getGrContext();
156         if (!context) {
157             return;
158         }
159 
160         struct Vertex {
161             SkPoint fPosition;
162             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
163         };
164 
165         constexpr int kNumConics = 10;
166         SkRandom rand;
167 
168         // Mult by 3 for each edge effect type
169         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
170         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
171         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
172         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
173         int row = 0;
174         int col = 0;
175         SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
176 
177         for (int i = 0; i < kNumConics; ++i) {
178             SkPoint baseControlPts[] = {
179                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
180                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
181                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
182             };
183             SkScalar weight = rand.nextRangeF(0.f, 2.f);
184             for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
185                 sk_sp<GrGeometryProcessor> gp;
186                 GrClipEdgeType et = (GrClipEdgeType)edgeType;
187                 gp = GrConicEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(),
188                                          SkMatrix::I(), false);
189                 if (!gp) {
190                     continue;
191                 }
192 
193                 SkScalar x = col * w;
194                 SkScalar y = row * h;
195                 SkPoint controlPts[] = {
196                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
197                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
198                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
199                 };
200                 SkConic dst[4];
201                 SkMatrix klm;
202                 int cnt = chop_conic(controlPts, dst, weight);
203                 GrPathUtils::getConicKLM(controlPts, weight, &klm);
204 
205                 SkPaint ctrlPtPaint;
206                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
207                 for (int i = 0; i < 3; ++i) {
208                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
209                 }
210 
211                 SkPaint polyPaint;
212                 polyPaint.setColor(0xffA0A0A0);
213                 polyPaint.setStrokeWidth(0);
214                 polyPaint.setStyle(SkPaint::kStroke_Style);
215                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
216 
217                 SkPaint choppedPtPaint;
218                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
219 
220                 for (int c = 0; c < cnt; ++c) {
221                     SkPoint* pts = dst[c].fPts;
222                     for (int i = 0; i < 3; ++i) {
223                         canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
224                     }
225 
226                     SkRect bounds;
227                     //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
228                     //bounds.set(bPts, 2);
229                     bounds.set(pts, 3);
230 
231                     SkPaint boundsPaint;
232                     boundsPaint.setColor(0xff808080);
233                     boundsPaint.setStrokeWidth(0);
234                     boundsPaint.setStyle(SkPaint::kStroke_Style);
235                     canvas->drawRect(bounds, boundsPaint);
236 
237                     std::unique_ptr<GrDrawOp> op = BezierConicTestOp::Make(context, gp, bounds,
238                                                                            color, klm);
239                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
240                 }
241                 ++col;
242                 if (numCols == col) {
243                     col = 0;
244                     ++row;
245                 }
246             }
247         }
248     }
249 
250 private:
251     // Uses the max curvature function for quads to estimate
252     // where to chop the conic. If the max curvature is not
253     // found along the curve segment it will return 1 and
254     // dst[0] is the original conic. If it returns 2 the dst[0]
255     // and dst[1] are the two new conics.
split_conic(const SkPoint src[3],SkConic dst[2],const SkScalar weight)256     int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
257         SkScalar t = SkFindQuadMaxCurvature(src);
258         if (t == 0 || t == 1) {
259             if (dst) {
260                 dst[0].set(src, weight);
261             }
262             return 1;
263         } else {
264             if (dst) {
265                 SkConic conic;
266                 conic.set(src, weight);
267                 if (!conic.chopAt(t, dst)) {
268                     dst[0].set(src, weight);
269                     return 1;
270                 }
271             }
272             return 2;
273         }
274     }
275 
276     // Calls split_conic on the entire conic and then once more on each subsection.
277     // Most cases will result in either 1 conic (chop point is not within t range)
278     // or 3 points (split once and then one subsection is split again).
chop_conic(const SkPoint src[3],SkConic dst[4],const SkScalar weight)279     int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
280         SkConic dstTemp[2];
281         int conicCnt = split_conic(src, dstTemp, weight);
282         if (2 == conicCnt) {
283             int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
284             conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
285         } else {
286             dst[0] = dstTemp[0];
287         }
288         return conicCnt;
289     }
290 
291     typedef GM INHERITED;
292 };
293 
294 //////////////////////////////////////////////////////////////////////////////
295 
296 class BezierQuadTestOp : public BezierTestOp {
297 public:
298     DEFINE_OP_CLASS_ID
name() const299     const char* name() const override { return "BezierQuadTestOp"; }
300 
Make(GrContext * context,sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)301     static std::unique_ptr<GrDrawOp> Make(GrContext* context,
302                                           sk_sp<const GrGeometryProcessor> gp,
303                                           const SkRect& rect,
304                                           const SkPMColor4f& color,
305                                           const GrPathUtils::QuadUVMatrix& devToUV) {
306         GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
307 
308         return pool->allocate<BezierQuadTestOp>(std::move(gp), rect, color, devToUV);
309     }
310 
311 private:
312     friend class ::GrOpMemoryPool; // for ctor
313 
BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp,const SkRect & rect,const SkPMColor4f & color,const GrPathUtils::QuadUVMatrix & devToUV)314     BezierQuadTestOp(sk_sp<const GrGeometryProcessor> gp, const SkRect& rect,
315                      const SkPMColor4f& color, const GrPathUtils::QuadUVMatrix& devToUV)
316             : INHERITED(std::move(gp), rect, color, ClassID()), fDevToUV(devToUV) {}
317 
318     struct Vertex {
319         SkPoint fPosition;
320         float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
321     };
322 
onPrepareDraws(Target * target)323     void onPrepareDraws(Target* target) override {
324         SkASSERT(this->gp()->vertexStride() == sizeof(Vertex));
325         QuadHelper helper(target, sizeof(Vertex), 1);
326         Vertex* verts = reinterpret_cast<Vertex*>(helper.vertices());
327         if (!verts) {
328             return;
329         }
330         SkRect rect = this->rect();
331         SkPointPriv::SetRectTriStrip(&verts[0].fPosition, rect, sizeof(Vertex));
332         fDevToUV.apply(verts, 4, sizeof(Vertex), sizeof(SkPoint));
333         auto pipe = this->makePipeline(target);
334         helper.recordDraw(target, this->gp(), pipe.fPipeline, pipe.fFixedDynamicState);
335     }
336 
337     GrPathUtils::QuadUVMatrix fDevToUV;
338 
339     static constexpr int kVertsPerCubic = 4;
340     static constexpr int kIndicesPerCubic = 6;
341 
342     typedef BezierTestOp INHERITED;
343 };
344 
345 /**
346  * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
347  */
348 class BezierQuadEffects : public GM {
349 public:
BezierQuadEffects()350     BezierQuadEffects() {
351         this->setBGColor(0xFFFFFFFF);
352     }
353 
354 protected:
onShortName()355     SkString onShortName() override {
356         return SkString("bezier_quad_effects");
357     }
358 
onISize()359     SkISize onISize() override {
360         return SkISize::Make(800, 800);
361     }
362 
363 
onDraw(SkCanvas * canvas)364     void onDraw(SkCanvas* canvas) override {
365         GrRenderTargetContext* renderTargetContext =
366             canvas->internal_private_accessTopLayerRenderTargetContext();
367         if (!renderTargetContext) {
368             skiagm::GM::DrawGpuOnlyMessage(canvas);
369             return;
370         }
371 
372         GrContext* context = canvas->getGrContext();
373         if (!context) {
374             return;
375         }
376 
377         struct Vertex {
378             SkPoint fPosition;
379             float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
380         };
381 
382         constexpr int kNumQuads = 5;
383         SkRandom rand;
384 
385         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
386         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
387         SkScalar w = SkIntToScalar(renderTargetContext->width()) / numCols;
388         SkScalar h = SkIntToScalar(renderTargetContext->height()) / numRows;
389         int row = 0;
390         int col = 0;
391         SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(0xff000000);
392 
393         for (int i = 0; i < kNumQuads; ++i) {
394             SkPoint baseControlPts[] = {
395                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
396                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
397                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
398             };
399             for(int edgeType = 0; edgeType < kGrClipEdgeTypeCnt; ++edgeType) {
400                 sk_sp<GrGeometryProcessor> gp;
401                 GrClipEdgeType et = (GrClipEdgeType)edgeType;
402                 gp = GrQuadEffect::Make(color, SkMatrix::I(), et, *context->contextPriv().caps(),
403                                         SkMatrix::I(), false);
404                 if (!gp) {
405                     continue;
406                 }
407 
408                 SkScalar x = col * w;
409                 SkScalar y = row * h;
410                 SkPoint controlPts[] = {
411                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
412                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
413                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
414                 };
415                 SkPoint chopped[5];
416                 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
417 
418                 SkPaint ctrlPtPaint;
419                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
420                 for (int i = 0; i < 3; ++i) {
421                     canvas->drawCircle(controlPts[i], 6.f, ctrlPtPaint);
422                 }
423 
424                 SkPaint polyPaint;
425                 polyPaint.setColor(0xffA0A0A0);
426                 polyPaint.setStrokeWidth(0);
427                 polyPaint.setStyle(SkPaint::kStroke_Style);
428                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
429 
430                 SkPaint choppedPtPaint;
431                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
432 
433                 for (int c = 0; c < cnt; ++c) {
434                     SkPoint* pts = chopped + 2 * c;
435 
436                     for (int i = 0; i < 3; ++i) {
437                         canvas->drawCircle(pts[i], 3.f, choppedPtPaint);
438                     }
439 
440                     SkRect bounds;
441                     bounds.set(pts, 3);
442 
443                     SkPaint boundsPaint;
444                     boundsPaint.setColor(0xff808080);
445                     boundsPaint.setStrokeWidth(0);
446                     boundsPaint.setStyle(SkPaint::kStroke_Style);
447                     canvas->drawRect(bounds, boundsPaint);
448 
449                     GrPaint grPaint;
450                     grPaint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
451 
452                     GrPathUtils::QuadUVMatrix DevToUV(pts);
453 
454                     std::unique_ptr<GrDrawOp> op = BezierQuadTestOp::Make(context, gp,
455                                                                           bounds, color, DevToUV);
456                     renderTargetContext->priv().testingOnly_addDrawOp(std::move(op));
457                 }
458                 ++col;
459                 if (numCols == col) {
460                     col = 0;
461                     ++row;
462                 }
463             }
464         }
465     }
466 
467 private:
468     typedef GM INHERITED;
469 };
470 
471 DEF_GM(return new BezierConicEffects;)
472 DEF_GM(return new BezierQuadEffects;)
473 }
474