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