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