Home | History | Annotate | Download | only in gm
      1 
      2 /*
      3  * Copyright 2013 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 // This test only works with the GPU backend.
     10 
     11 #include "gm.h"
     12 
     13 #if SK_SUPPORT_GPU
     14 
     15 #include "GrContext.h"
     16 #include "GrPathUtils.h"
     17 #include "GrTest.h"
     18 #include "SkColorPriv.h"
     19 #include "SkDevice.h"
     20 #include "SkGeometry.h"
     21 
     22 #include "effects/GrBezierEffect.h"
     23 
     24 // Position & KLM line eq values. These are the vertex attributes for Bezier curves. The last value
     25 // of the Vec4f is ignored.
     26 extern const GrVertexAttrib kAttribs[] = {
     27     {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding},
     28     {kVec4f_GrVertexAttribType, sizeof(GrPoint), kEffect_GrVertexAttribBinding}
     29 };
     30 
     31 static inline SkScalar eval_line(const SkPoint& p, const SkScalar lineEq[3], SkScalar sign) {
     32     return sign * (lineEq[0] * p.fX + lineEq[1] * p.fY + lineEq[2]);
     33 }
     34 
     35 namespace skiagm {
     36 /**
     37  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
     38  */
     39 class BezierCubicEffects : public GM {
     40 public:
     41     BezierCubicEffects() {
     42         this->setBGColor(0xFFFFFFFF);
     43     }
     44 
     45 protected:
     46     virtual SkString onShortName() SK_OVERRIDE {
     47         return SkString("bezier_cubic_effects");
     48     }
     49 
     50     virtual SkISize onISize() SK_OVERRIDE {
     51         return make_isize(800, 800);
     52     }
     53 
     54     virtual uint32_t onGetFlags() const SK_OVERRIDE {
     55         // This is a GPU-specific GM.
     56         return kGPUOnly_Flag;
     57     }
     58 
     59 
     60     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
     61         SkBaseDevice* device = canvas->getTopDevice();
     62         GrRenderTarget* rt = device->accessRenderTarget();
     63         if (NULL == rt) {
     64             return;
     65         }
     66         GrContext* context = rt->getContext();
     67         if (NULL == context) {
     68             return;
     69         }
     70 
     71         struct Vertex {
     72             SkPoint fPosition;
     73             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
     74         };
     75 
     76         static const int kNumCubics = 15;
     77         SkRandom rand;
     78 
     79         // Mult by 3 for each edge effect type
     80         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumCubics*3)));
     81         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumCubics*3) / numCols);
     82         SkScalar w = SkIntToScalar(rt->width()) / numCols;
     83         SkScalar h = SkIntToScalar(rt->height()) / numRows;
     84         int row = 0;
     85         int col = 0;
     86 
     87         for (int i = 0; i < kNumCubics; ++i) {
     88             SkPoint baseControlPts[] = {
     89                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
     90                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
     91                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
     92                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
     93             };
     94             for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
     95                 SkScalar x = SkScalarMul(col, w);
     96                 SkScalar y = SkScalarMul(row, h);
     97                 SkPoint controlPts[] = {
     98                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
     99                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    100                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY},
    101                     {x + baseControlPts[3].fX, y + baseControlPts[3].fY}
    102                 };
    103                 SkPoint chopped[10];
    104                 SkScalar klmEqs[9];
    105                 SkScalar klmSigns[3];
    106                 int cnt = GrPathUtils::chopCubicAtLoopIntersection(controlPts,
    107                                                                    chopped,
    108                                                                    klmEqs,
    109                                                                    klmSigns);
    110 
    111                 SkPaint ctrlPtPaint;
    112                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    113                 for (int i = 0; i < 4; ++i) {
    114                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
    115                 }
    116 
    117                 SkPaint polyPaint;
    118                 polyPaint.setColor(0xffA0A0A0);
    119                 polyPaint.setStrokeWidth(0);
    120                 polyPaint.setStyle(SkPaint::kStroke_Style);
    121                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 4, controlPts, polyPaint);
    122 
    123                 SkPaint choppedPtPaint;
    124                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    125 
    126                 for (int c = 0; c < cnt; ++c) {
    127                     SkPoint* pts = chopped + 3 * c;
    128 
    129                     for (int i = 0; i < 4; ++i) {
    130                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
    131                     }
    132 
    133                     SkRect bounds;
    134                     bounds.set(pts, 4);
    135 
    136                     SkPaint boundsPaint;
    137                     boundsPaint.setColor(0xff808080);
    138                     boundsPaint.setStrokeWidth(0);
    139                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    140                     canvas->drawRect(bounds, boundsPaint);
    141 
    142                     Vertex verts[4];
    143                     verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
    144                                                   bounds.fRight, bounds.fBottom,
    145                                                   sizeof(Vertex));
    146                     for (int v = 0; v < 4; ++v) {
    147                         verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, klmSigns[c]);
    148                         verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, klmSigns[c]);
    149                         verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
    150                     }
    151 
    152                     GrTestTarget tt;
    153                     context->getTestTarget(&tt);
    154                     if (NULL == tt.target()) {
    155                         continue;
    156                     }
    157                     GrDrawState* drawState = tt.target()->drawState();
    158                     drawState->setVertexAttribs<kAttribs>(2);
    159 
    160                     SkAutoTUnref<GrEffectRef> effect(GrCubicEffect::Create(
    161                             GrBezierEdgeType(edgeType), *tt.target()->caps()));
    162                     if (!effect) {
    163                         continue;
    164                     }
    165                     drawState->addCoverageEffect(effect, 1);
    166                     drawState->setRenderTarget(rt);
    167                     drawState->setColor(0xff000000);
    168 
    169                     tt.target()->setVertexSourceToArray(verts, 4);
    170                     tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
    171                     tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
    172                 }
    173                 ++col;
    174                 if (numCols == col) {
    175                     col = 0;
    176                     ++row;
    177                 }
    178             }
    179         }
    180     }
    181 
    182 private:
    183     typedef GM INHERITED;
    184 };
    185 
    186 //////////////////////////////////////////////////////////////////////////////
    187 
    188 /**
    189  * This GM directly exercises effects that draw Bezier curves in the GPU backend.
    190  */
    191 class BezierConicEffects : public GM {
    192 public:
    193     BezierConicEffects() {
    194         this->setBGColor(0xFFFFFFFF);
    195     }
    196 
    197 protected:
    198     virtual SkString onShortName() SK_OVERRIDE {
    199         return SkString("bezier_conic_effects");
    200     }
    201 
    202     virtual SkISize onISize() SK_OVERRIDE {
    203         return make_isize(800, 800);
    204     }
    205 
    206     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    207         // This is a GPU-specific GM.
    208         return kGPUOnly_Flag;
    209     }
    210 
    211 
    212     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
    213         SkBaseDevice* device = canvas->getTopDevice();
    214         GrRenderTarget* rt = device->accessRenderTarget();
    215         if (NULL == rt) {
    216             return;
    217         }
    218         GrContext* context = rt->getContext();
    219         if (NULL == context) {
    220             return;
    221         }
    222 
    223         struct Vertex {
    224             SkPoint fPosition;
    225             float   fKLM[4]; // The last value is ignored. The effect expects a vec4f.
    226         };
    227 
    228         static const int kNumConics = 10;
    229         SkRandom rand;
    230 
    231         // Mult by 3 for each edge effect type
    232         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumConics*3)));
    233         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumConics*3) / numCols);
    234         SkScalar w = SkIntToScalar(rt->width()) / numCols;
    235         SkScalar h = SkIntToScalar(rt->height()) / numRows;
    236         int row = 0;
    237         int col = 0;
    238 
    239         for (int i = 0; i < kNumConics; ++i) {
    240             SkPoint baseControlPts[] = {
    241                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    242                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    243                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
    244             };
    245             SkScalar weight = rand.nextRangeF(0.f, 2.f);
    246             for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
    247                 SkScalar x = SkScalarMul(col, w);
    248                 SkScalar y = SkScalarMul(row, h);
    249                 SkPoint controlPts[] = {
    250                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
    251                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    252                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
    253                 };
    254                 SkConic dst[4];
    255                 SkScalar klmEqs[9];
    256                 int cnt = chop_conic(controlPts, dst, weight);
    257                 GrPathUtils::getConicKLM(controlPts, weight, klmEqs);
    258 
    259                 SkPaint ctrlPtPaint;
    260                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    261                 for (int i = 0; i < 3; ++i) {
    262                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
    263                 }
    264 
    265                 SkPaint polyPaint;
    266                 polyPaint.setColor(0xffA0A0A0);
    267                 polyPaint.setStrokeWidth(0);
    268                 polyPaint.setStyle(SkPaint::kStroke_Style);
    269                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
    270 
    271                 SkPaint choppedPtPaint;
    272                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    273 
    274                 for (int c = 0; c < cnt; ++c) {
    275                     SkPoint* pts = dst[c].fPts;
    276                     for (int i = 0; i < 3; ++i) {
    277                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
    278                     }
    279 
    280                     SkRect bounds;
    281                     //SkPoint bPts[] = {{0.f, 0.f}, {800.f, 800.f}};
    282                     //bounds.set(bPts, 2);
    283                     bounds.set(pts, 3);
    284 
    285                     SkPaint boundsPaint;
    286                     boundsPaint.setColor(0xff808080);
    287                     boundsPaint.setStrokeWidth(0);
    288                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    289                     canvas->drawRect(bounds, boundsPaint);
    290 
    291                     Vertex verts[4];
    292                     verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
    293                                                   bounds.fRight, bounds.fBottom,
    294                                                   sizeof(Vertex));
    295                     for (int v = 0; v < 4; ++v) {
    296                         verts[v].fKLM[0] = eval_line(verts[v].fPosition, klmEqs + 0, 1.f);
    297                         verts[v].fKLM[1] = eval_line(verts[v].fPosition, klmEqs + 3, 1.f);
    298                         verts[v].fKLM[2] = eval_line(verts[v].fPosition, klmEqs + 6, 1.f);
    299                     }
    300 
    301                     GrTestTarget tt;
    302                     context->getTestTarget(&tt);
    303                     if (NULL == tt.target()) {
    304                         continue;
    305                     }
    306                     GrDrawState* drawState = tt.target()->drawState();
    307                     drawState->setVertexAttribs<kAttribs>(2);
    308 
    309                     SkAutoTUnref<GrEffectRef> effect(GrConicEffect::Create(
    310                             GrBezierEdgeType(edgeType), *tt.target()->caps()));
    311                     if (!effect) {
    312                         continue;
    313                     }
    314                     drawState->addCoverageEffect(effect, 1);
    315                     drawState->setRenderTarget(rt);
    316                     drawState->setColor(0xff000000);
    317 
    318                     tt.target()->setVertexSourceToArray(verts, 4);
    319                     tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
    320                     tt.target()->drawIndexed(kTriangleFan_GrPrimitiveType, 0, 0, 4, 6);
    321                 }
    322                 ++col;
    323                 if (numCols == col) {
    324                     col = 0;
    325                     ++row;
    326                 }
    327             }
    328         }
    329     }
    330 
    331 private:
    332     // Uses the max curvature function for quads to estimate
    333     // where to chop the conic. If the max curvature is not
    334     // found along the curve segment it will return 1 and
    335     // dst[0] is the original conic. If it returns 2 the dst[0]
    336     // and dst[1] are the two new conics.
    337     int split_conic(const SkPoint src[3], SkConic dst[2], const SkScalar weight) {
    338         SkScalar t = SkFindQuadMaxCurvature(src);
    339         if (t == 0) {
    340             if (dst) {
    341                 dst[0].set(src, weight);
    342             }
    343             return 1;
    344         } else {
    345             if (dst) {
    346                 SkConic conic;
    347                 conic.set(src, weight);
    348                 conic.chopAt(t, dst);
    349             }
    350             return 2;
    351         }
    352     }
    353 
    354     // Calls split_conic on the entire conic and then once more on each subsection.
    355     // Most cases will result in either 1 conic (chop point is not within t range)
    356     // or 3 points (split once and then one subsection is split again).
    357     int chop_conic(const SkPoint src[3], SkConic dst[4], const SkScalar weight) {
    358         SkConic dstTemp[2];
    359         int conicCnt = split_conic(src, dstTemp, weight);
    360         if (2 == conicCnt) {
    361             int conicCnt2 = split_conic(dstTemp[0].fPts, dst, dstTemp[0].fW);
    362             conicCnt = conicCnt2 + split_conic(dstTemp[1].fPts, &dst[conicCnt2], dstTemp[1].fW);
    363         } else {
    364             dst[0] = dstTemp[0];
    365         }
    366         return conicCnt;
    367     }
    368 
    369     typedef GM INHERITED;
    370 };
    371 
    372 //////////////////////////////////////////////////////////////////////////////
    373 /**
    374  * This GM directly exercises effects that draw Bezier quad curves in the GPU backend.
    375  */
    376 class BezierQuadEffects : public GM {
    377 public:
    378     BezierQuadEffects() {
    379         this->setBGColor(0xFFFFFFFF);
    380     }
    381 
    382 protected:
    383     virtual SkString onShortName() SK_OVERRIDE {
    384         return SkString("bezier_quad_effects");
    385     }
    386 
    387     virtual SkISize onISize() SK_OVERRIDE {
    388         return make_isize(800, 800);
    389     }
    390 
    391     virtual uint32_t onGetFlags() const SK_OVERRIDE {
    392         // This is a GPU-specific GM.
    393         return kGPUOnly_Flag;
    394     }
    395 
    396 
    397     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
    398         SkBaseDevice* device = canvas->getTopDevice();
    399         GrRenderTarget* rt = device->accessRenderTarget();
    400         if (NULL == rt) {
    401             return;
    402         }
    403         GrContext* context = rt->getContext();
    404         if (NULL == context) {
    405             return;
    406         }
    407 
    408         struct Vertex {
    409             SkPoint fPosition;
    410             float   fUV[4]; // The last two values are ignored. The effect expects a vec4f.
    411         };
    412 
    413         static const int kNumQuads = 5;
    414         SkRandom rand;
    415 
    416         int numCols = SkScalarCeilToInt(SkScalarSqrt(SkIntToScalar(kNumQuads*3)));
    417         int numRows = SkScalarCeilToInt(SkIntToScalar(kNumQuads*3) / numCols);
    418         SkScalar w = SkIntToScalar(rt->width()) / numCols;
    419         SkScalar h = SkIntToScalar(rt->height()) / numRows;
    420         int row = 0;
    421         int col = 0;
    422 
    423         for (int i = 0; i < kNumQuads; ++i) {
    424             SkPoint baseControlPts[] = {
    425                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    426                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)},
    427                 {rand.nextRangeF(0.f, w), rand.nextRangeF(0.f, h)}
    428             };
    429             for(int edgeType = kFillAA_GrBezierEdgeType; edgeType < 3; ++edgeType) {
    430                 SkScalar x = SkScalarMul(col, w);
    431                 SkScalar y = SkScalarMul(row, h);
    432                 SkPoint controlPts[] = {
    433                     {x + baseControlPts[0].fX, y + baseControlPts[0].fY},
    434                     {x + baseControlPts[1].fX, y + baseControlPts[1].fY},
    435                     {x + baseControlPts[2].fX, y + baseControlPts[2].fY}
    436                 };
    437                 SkPoint chopped[5];
    438                 int cnt = SkChopQuadAtMaxCurvature(controlPts, chopped);
    439 
    440                 SkPaint ctrlPtPaint;
    441                 ctrlPtPaint.setColor(rand.nextU() | 0xFF000000);
    442                 for (int i = 0; i < 3; ++i) {
    443                     canvas->drawCircle(controlPts[i].fX, controlPts[i].fY, 6.f, ctrlPtPaint);
    444                 }
    445 
    446                 SkPaint polyPaint;
    447                 polyPaint.setColor(0xffA0A0A0);
    448                 polyPaint.setStrokeWidth(0);
    449                 polyPaint.setStyle(SkPaint::kStroke_Style);
    450                 canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, controlPts, polyPaint);
    451 
    452                 SkPaint choppedPtPaint;
    453                 choppedPtPaint.setColor(~ctrlPtPaint.getColor() | 0xFF000000);
    454 
    455                 for (int c = 0; c < cnt; ++c) {
    456                     SkPoint* pts = chopped + 2 * c;
    457 
    458                     for (int i = 0; i < 3; ++i) {
    459                         canvas->drawCircle(pts[i].fX, pts[i].fY, 3.f, choppedPtPaint);
    460                     }
    461 
    462                     SkRect bounds;
    463                     bounds.set(pts, 3);
    464 
    465                     SkPaint boundsPaint;
    466                     boundsPaint.setColor(0xff808080);
    467                     boundsPaint.setStrokeWidth(0);
    468                     boundsPaint.setStyle(SkPaint::kStroke_Style);
    469                     canvas->drawRect(bounds, boundsPaint);
    470 
    471                     Vertex verts[4];
    472                     verts[0].fPosition.setRectFan(bounds.fLeft, bounds.fTop,
    473                                                   bounds.fRight, bounds.fBottom,
    474                                                   sizeof(Vertex));
    475 
    476                     GrPathUtils::QuadUVMatrix DevToUV(pts);
    477                     DevToUV.apply<4, sizeof(Vertex), sizeof(GrPoint)>(verts);
    478 
    479                     GrTestTarget tt;
    480                     context->getTestTarget(&tt);
    481                     if (NULL == tt.target()) {
    482                         continue;
    483                     }
    484                     GrDrawState* drawState = tt.target()->drawState();
    485                     drawState->setVertexAttribs<kAttribs>(2);
    486                     SkAutoTUnref<GrEffectRef> effect(GrQuadEffect::Create(
    487                             GrBezierEdgeType(edgeType), *tt.target()->caps()));
    488                     if (!effect) {
    489                         continue;
    490                     }
    491                     drawState->addCoverageEffect(effect, 1);
    492                     drawState->setRenderTarget(rt);
    493                     drawState->setColor(0xff000000);
    494 
    495                     tt.target()->setVertexSourceToArray(verts, 4);
    496                     tt.target()->setIndexSourceToBuffer(context->getQuadIndexBuffer());
    497                     tt.target()->drawIndexed(kTriangles_GrPrimitiveType, 0, 0, 4, 6);
    498                 }
    499                 ++col;
    500                 if (numCols == col) {
    501                     col = 0;
    502                     ++row;
    503                 }
    504             }
    505         }
    506     }
    507 
    508 private:
    509     typedef GM INHERITED;
    510 };
    511 
    512 DEF_GM( return SkNEW(BezierCubicEffects); )
    513 DEF_GM( return SkNEW(BezierConicEffects); )
    514 DEF_GM( return SkNEW(BezierQuadEffects); )
    515 
    516 }
    517 
    518 #endif
    519