Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2015 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 #include "gm.h"
      9 #include "SkPathPriv.h"
     10 
     11 static void create_ngon(int n, SkPoint* pts, SkScalar width, SkScalar height) {
     12     float angleStep = 360.0f / n, angle = 0.0f, sin, cos;
     13     if ((n % 2) == 1) {
     14         angle = angleStep/2.0f;
     15     }
     16 
     17     for (int i = 0; i < n; ++i) {
     18         sin = SkScalarSinCos(SkDegreesToRadians(angle), &cos);
     19         pts[i].fX = -sin * width;
     20         pts[i].fY = cos * height;
     21         angle += angleStep;
     22     }
     23 }
     24 
     25 namespace skiagm {
     26 
     27 // This GM is intended to exercise Ganesh's handling of convex line-only
     28 // paths
     29 class ConvexLineOnlyPathsGM : public GM {
     30 public:
     31     ConvexLineOnlyPathsGM() {
     32         this->setBGColor(0xFFFFFFFF);
     33     }
     34 
     35 protected:
     36     SkString onShortName() override { return SkString("convex-lineonly-paths"); }
     37     SkISize onISize() override { return SkISize::Make(kGMWidth, kGMHeight); }
     38     bool runAsBench() const override { return true; }
     39 
     40     static SkPath GetPath(int index, int offset, SkPath::Direction dir) {
     41         // narrow rect
     42         const SkPoint gPoints0[] = {
     43             { -1.5f, -50.0f },
     44             {  1.5f, -50.0f },
     45             {  1.5f,  50.0f },
     46             { -1.5f,  50.0f }
     47         };
     48         // narrow rect on an angle
     49         const SkPoint gPoints1[] = {
     50             { -50.0f, -49.0f },
     51             { -49.0f, -50.0f },
     52             {  50.0f,  49.0f },
     53             {  49.0f,  50.0f }
     54         };
     55         // trap - narrow on top - wide on bottom
     56         const SkPoint gPoints2[] = {
     57             { -10.0f, -50.0f },
     58             {  10.0f, -50.0f },
     59             {  50.0f,  50.0f },
     60             { -50.0f,  50.0f }
     61         };
     62         // wide skewed rect
     63         const SkPoint gPoints3[] = {
     64             { -50.0f, -50.0f },
     65             {   0.0f, -50.0f },
     66             {  50.0f,  50.0f },
     67             {   0.0f,  50.0f }
     68         };
     69         // thin rect with colinear-ish lines
     70         const SkPoint gPoints4[] = {
     71             { -6.0f, -50.0f },
     72             {  4.0f, -50.0f },
     73             {  5.0f, -25.0f },
     74             {  6.0f,   0.0f },
     75             {  5.0f,  25.0f },
     76             {  4.0f,  50.0f },
     77             { -4.0f,  50.0f }
     78         };
     79         // degenerate
     80         const SkPoint gPoints5[] = {
     81             { -0.025f, -0.025f  },
     82             {  0.025f, -0.025f  },
     83             {  0.025f,  0.025f },
     84             { -0.025f,  0.025f }
     85         };
     86         // Triangle in which the first point should fuse with last
     87         const SkPoint gPoints6[] = {
     88             { -20.0f, -13.0f },
     89             { -20.0f, -13.05f },
     90             {  20.0f, -13.0f },
     91             {  20.0f,  27.0f }
     92         };
     93         // thin rect with colinear lines
     94         const SkPoint gPoints7[] = {
     95             { -10.0f, -50.0f },
     96             {  10.0f, -50.0f },
     97             {  10.0f, -25.0f },
     98             {  10.0f,   0.0f },
     99             {  10.0f,  25.0f },
    100             {  10.0f,  50.0f },
    101             { -10.0f,  50.0f }
    102         };
    103         // capped teardrop
    104         const SkPoint gPoints8[] = {
    105             {  50.00f,  50.00f },
    106             {   0.00f,  50.00f },
    107             { -15.45f,  47.55f },
    108             { -29.39f,  40.45f },
    109             { -40.45f,  29.39f },
    110             { -47.55f,  15.45f },
    111             { -50.00f,   0.00f },
    112             { -47.55f, -15.45f },
    113             { -40.45f, -29.39f },
    114             { -29.39f, -40.45f },
    115             { -15.45f, -47.55f },
    116             {   0.00f, -50.00f },
    117             {  50.00f, -50.00f }
    118         };
    119         // teardrop
    120         const SkPoint gPoints9[] = {
    121             {   4.39f,  40.45f },
    122             {  -9.55f,  47.55f },
    123             { -25.00f,  50.00f },
    124             { -40.45f,  47.55f },
    125             { -54.39f,  40.45f },
    126             { -65.45f,  29.39f },
    127             { -72.55f,  15.45f },
    128             { -75.00f,   0.00f },
    129             { -72.55f, -15.45f },
    130             { -65.45f, -29.39f },
    131             { -54.39f, -40.45f },
    132             { -40.45f, -47.55f },
    133             { -25.0f,  -50.0f },
    134             {  -9.55f, -47.55f },
    135             {   4.39f, -40.45f },
    136             {  75.00f,   0.00f }
    137         };
    138         // clipped triangle
    139         const SkPoint gPoints10[] = {
    140             { -10.0f, -50.0f },
    141             {  10.0f, -50.0f },
    142             {  50.0f,  31.0f },
    143             {  40.0f,  50.0f },
    144             { -40.0f,  50.0f },
    145             { -50.0f,  31.0f },
    146         };
    147 
    148         const SkPoint* gPoints[] = {
    149             gPoints0, gPoints1, gPoints2, gPoints3, gPoints4, gPoints5, gPoints6,
    150             gPoints7, gPoints8, gPoints9, gPoints10,
    151         };
    152 
    153         const size_t gSizes[] = {
    154             SK_ARRAY_COUNT(gPoints0),
    155             SK_ARRAY_COUNT(gPoints1),
    156             SK_ARRAY_COUNT(gPoints2),
    157             SK_ARRAY_COUNT(gPoints3),
    158             SK_ARRAY_COUNT(gPoints4),
    159             SK_ARRAY_COUNT(gPoints5),
    160             SK_ARRAY_COUNT(gPoints6),
    161             SK_ARRAY_COUNT(gPoints7),
    162             SK_ARRAY_COUNT(gPoints8),
    163             SK_ARRAY_COUNT(gPoints9),
    164             SK_ARRAY_COUNT(gPoints10),
    165         };
    166         static_assert(SK_ARRAY_COUNT(gSizes) == SK_ARRAY_COUNT(gPoints), "array_mismatch");
    167 
    168         SkAutoTDeleteArray<SkPoint> data(nullptr);
    169         const SkPoint* points;
    170         int numPts;
    171         if (index < (int) SK_ARRAY_COUNT(gPoints)) {
    172             // manually specified
    173             points = gPoints[index];
    174             numPts = (int) gSizes[index];
    175         } else {
    176             // procedurally generated
    177             SkScalar width = kMaxPathHeight/2;
    178             SkScalar height = kMaxPathHeight/2;
    179             switch (index-SK_ARRAY_COUNT(gPoints)) {
    180             case 0:
    181                 numPts = 3;
    182                 break;
    183             case 1:
    184                 numPts = 4;
    185                 break;
    186             case 2:
    187                 numPts = 5;
    188                 break;
    189             case 3:             // squashed pentagon
    190                 numPts = 5;
    191                 width = kMaxPathHeight/5;
    192                 break;
    193             case 4:
    194                 numPts = 6;
    195                 break;
    196             case 5:
    197                 numPts = 8;
    198                 break;
    199             case 6:              // squashed octogon
    200                 numPts = 8;
    201                 width = kMaxPathHeight/5;
    202                 break;
    203             case 7:
    204                 numPts = 20;
    205                 break;
    206             case 8:
    207                 numPts = 100;
    208                 break;
    209             default:
    210                 numPts = 3;
    211                 break;
    212             }
    213 
    214             data.reset(new SkPoint[numPts]);
    215 
    216             create_ngon(numPts, data.get(), width, height);
    217             points = data.get();
    218         }
    219 
    220         SkPath path;
    221 
    222         if (SkPath::kCW_Direction == dir) {
    223             path.moveTo(points[0]);
    224             for (int i = 1; i < numPts; ++i) {
    225                 path.lineTo(points[i]);
    226             }
    227         } else {
    228             path.moveTo(points[numPts-1]);
    229             for (int i = numPts-2; i >= 0; --i) {
    230                 path.lineTo(points[i]);
    231             }
    232         }
    233 
    234         path.close();
    235 #ifdef SK_DEBUG
    236         // Each path this method returns should be convex, only composed of
    237         // lines, wound the right direction, and short enough to fit in one
    238         // of the GMs rows.
    239         SkASSERT(path.isConvex());
    240         SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks());
    241         SkPathPriv::FirstDirection actualDir;
    242         SkASSERT(SkPathPriv::CheapComputeFirstDirection(path, &actualDir));
    243         SkASSERT(SkPathPriv::AsFirstDirection(dir) == actualDir);
    244         SkRect bounds = path.getBounds();
    245         SkASSERT(SkScalarNearlyEqual(bounds.centerX(), 0.0f));
    246         SkASSERT(bounds.height() <= kMaxPathHeight);
    247 #endif
    248         return path;
    249     }
    250 
    251     // Draw a single path several times, shrinking it, flipping its direction
    252     // and changing its start vertex each time.
    253     void drawPath(SkCanvas* canvas, int index, SkPoint* offset) {
    254 
    255         SkPoint center;
    256         {
    257             SkPath path = GetPath(index, 0, SkPath::kCW_Direction);
    258             if (offset->fX+path.getBounds().width() > kGMWidth) {
    259                 offset->fX = 0;
    260                 offset->fY += kMaxPathHeight;
    261             }
    262             center = { offset->fX + SkScalarHalf(path.getBounds().width()), offset->fY};
    263             offset->fX += path.getBounds().width();
    264         }
    265 
    266         const SkColor colors[2] = { SK_ColorBLACK, SK_ColorWHITE };
    267         const SkPath::Direction dirs[2] = { SkPath::kCW_Direction, SkPath::kCCW_Direction };
    268         const float scales[] = { 1.0f, 0.75f, 0.5f, 0.25f, 0.1f, 0.01f, 0.001f };
    269 
    270         SkPaint paint;
    271         paint.setAntiAlias(true);
    272 
    273         for (size_t i = 0; i < SK_ARRAY_COUNT(scales); ++i) {
    274             SkPath path = GetPath(index, (int) i, dirs[i%2]);
    275 
    276             canvas->save();
    277                 canvas->translate(center.fX, center.fY);
    278                 canvas->scale(scales[i], scales[i]);
    279                 paint.setColor(colors[i%2]);
    280                 canvas->drawPath(path, paint);
    281             canvas->restore();
    282         }
    283     }
    284 
    285     void onDraw(SkCanvas* canvas) override {
    286         // the right edge of the last drawn path
    287         SkPoint offset = { 0, SkScalarHalf(kMaxPathHeight) };
    288 
    289         for (int i = 0; i < kNumPaths; ++i) {
    290             this->drawPath(canvas, i, &offset);
    291         }
    292 
    293         // Repro for crbug.com/472723 (Missing AA on portions of graphic with GPU rasterization)
    294         {
    295             canvas->translate(356.0f, 50.0f);
    296 
    297             SkPaint p;
    298             p.setAntiAlias(true);
    299 
    300             SkPath p1;
    301             p1.moveTo(60.8522949f, 364.671021f);
    302             p1.lineTo(59.4380493f, 364.671021f);
    303             p1.lineTo(385.414276f, 690.647217f);
    304             p1.lineTo(386.121399f, 689.940125f);
    305             canvas->drawPath(p1, p);
    306         }
    307     }
    308 
    309 private:
    310     static const int kNumPaths      = 20;
    311     static const int kMaxPathHeight = 100;
    312     static const int kGMWidth       = 512;
    313     static const int kGMHeight      = 512;
    314 
    315     typedef GM INHERITED;
    316 };
    317 
    318 //////////////////////////////////////////////////////////////////////////////
    319 
    320 DEF_GM(return new ConvexLineOnlyPathsGM;)
    321 }
    322