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 
     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 
     31     const char* name() const override { return "BezierCubicOrConicTestOp"; }
     32 
     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:
     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 
     55     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:
     87     BezierCubicEffects() {
     88         this->setBGColor(0xFFFFFFFF);
     89     }
     90 
     91 protected:
     92     SkString onShortName() override {
     93         return SkString("bezier_cubic_effects");
     94     }
     95 
     96     SkISize onISize() override {
     97         return SkISize::Make(800, 800);
     98     }
     99 
    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:
    226     BezierConicEffects() {
    227         this->setBGColor(0xFFFFFFFF);
    228     }
    229 
    230 protected:
    231     SkString onShortName() override {
    232         return SkString("bezier_conic_effects");
    233     }
    234 
    235     SkISize onISize() override {
    236         return SkISize::Make(800, 800);
    237     }
    238 
    239 
    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.
    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).
    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
    397     const char* name() const override { return "BezierQuadTestOp"; }
    398 
    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:
    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 
    418     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:
    447     BezierQuadEffects() {
    448         this->setBGColor(0xFFFFFFFF);
    449     }
    450 
    451 protected:
    452     SkString onShortName() override {
    453         return SkString("bezier_quad_effects");
    454     }
    455 
    456     SkISize onISize() override {
    457         return SkISize::Make(800, 800);
    458     }
    459 
    460 
    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