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 "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:
     29     FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
     30 
     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 
     36     void visitProxies(const VisitProxyFunc& func, VisitorType) const override {
     37         fProcessorSet.visitProxies(func);
     38     }
     39 
     40 protected:
     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 
     51     Target::PipelineAndFixedDynamicState makePipeline(Target* target) {
     52         return target->makePipeline(0, std::move(fProcessorSet), target->detachAppliedClip());
     53     }
     54 
     55     sk_sp<const GrGeometryProcessor> gp() const { return fGeometryProcessor; }
     56 
     57     const SkRect& rect() const { return fRect; }
     58     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 
     77     const char* name() const override { return "BezierConicTestOp"; }
     78 
     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 
     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 
    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:
    133     BezierConicEffects() {
    134         this->setBGColor(0xFFFFFFFF);
    135     }
    136 
    137 protected:
    138     SkString onShortName() override {
    139         return SkString("bezier_conic_effects");
    140     }
    141 
    142     SkISize onISize() override {
    143         return SkISize::Make(800, 800);
    144     }
    145 
    146 
    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.
    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).
    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
    299     const char* name() const override { return "BezierQuadTestOp"; }
    300 
    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 
    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 
    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:
    350     BezierQuadEffects() {
    351         this->setBGColor(0xFFFFFFFF);
    352     }
    353 
    354 protected:
    355     SkString onShortName() override {
    356         return SkString("bezier_quad_effects");
    357     }
    358 
    359     SkISize onISize() override {
    360         return SkISize::Make(800, 800);
    361     }
    362 
    363 
    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