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 #include "gm.h"
      9 #include "SkCanvas.h"
     10 #include "SkPath.h"
     11 #include "SkTArray.h"
     12 
     13 namespace skiagm {
     14 
     15 class HairlinesGM : public GM {
     16 protected:
     17 
     18 
     19     SkString onShortName() override {
     20         return SkString("hairlines");
     21     }
     22 
     23     SkISize onISize() override { return SkISize::Make(1250, 1250); }
     24 
     25     void onOnceBeforeDraw() override {
     26         {
     27             SkPath* lineAnglesPath = &fPaths.push_back();
     28             enum {
     29                 kNumAngles = 15,
     30                 kRadius = 40,
     31             };
     32             for (int i = 0; i < kNumAngles; ++i) {
     33                 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
     34                 SkScalar x = kRadius * SkScalarCos(angle);
     35                 SkScalar y = kRadius * SkScalarSin(angle);
     36                 lineAnglesPath->moveTo(x, y);
     37                 lineAnglesPath->lineTo(-x, -y);
     38             }
     39         }
     40 
     41         {
     42             SkPath* kindaTightQuad = &fPaths.push_back();
     43             kindaTightQuad->moveTo(0, -10 * SK_Scalar1);
     44             kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0);
     45         }
     46 
     47         {
     48             SkPath* tightQuad = &fPaths.push_back();
     49             tightQuad->moveTo(0, -5 * SK_Scalar1);
     50             tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0);
     51         }
     52 
     53         {
     54             SkPath* tighterQuad = &fPaths.push_back();
     55             tighterQuad->moveTo(0, -2 * SK_Scalar1);
     56             tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0);
     57         }
     58 
     59         {
     60             SkPath* unevenTighterQuad = &fPaths.push_back();
     61             unevenTighterQuad->moveTo(0, -1 * SK_Scalar1);
     62             SkPoint p;
     63             p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75));
     64             unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY);
     65         }
     66 
     67         {
     68             SkPath* reallyTightQuad = &fPaths.push_back();
     69             reallyTightQuad->moveTo(0, -1 * SK_Scalar1);
     70             reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0);
     71         }
     72 
     73         {
     74             SkPath* closedQuad = &fPaths.push_back();
     75             closedQuad->moveTo(0, -0);
     76             closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0);
     77         }
     78 
     79         {
     80             SkPath* unevenClosedQuad = &fPaths.push_back();
     81             unevenClosedQuad->moveTo(0, -0);
     82             unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100),
     83                                      SkIntToScalar(75), SkIntToScalar(75));
     84         }
     85 
     86         // Two problem cases for gpu hairline renderer found by shapeops testing. These used
     87         // to assert that the computed bounding box didn't contain all the vertices.
     88         {
     89             SkPath* problem1 = &fPaths.push_back();
     90             problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6));
     91             problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6),
     92                               SkIntToScalar(5), SkIntToScalar(4),
     93                               SkIntToScalar(4), SkIntToScalar(0));
     94             problem1->close();
     95         }
     96 
     97         {
     98             SkPath* problem2 = &fPaths.push_back();
     99             problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1));
    100             problem2->lineTo(4.32787323f, 1.67212653f);
    101             problem2->cubicTo(2.75223875f, 3.24776125f,
    102                               3.00581908f, 4.51236057f,
    103                               3.7580452f, 4.37367964f);
    104             problem2->cubicTo(4.66472578f, 3.888381f,
    105                               5.f, 2.875f,
    106                               5.f, 1.f);
    107             problem2->close();
    108         }
    109 
    110         // Three paths that show the same bug (missing end caps)
    111         {
    112             // A caret (crbug.com/131770)
    113             SkPath* bug0 = &fPaths.push_back();
    114             bug0->moveTo(6.5f,5.5f);
    115             bug0->lineTo(3.5f,0.5f);
    116             bug0->moveTo(0.5f,5.5f);
    117             bug0->lineTo(3.5f,0.5f);
    118         }
    119 
    120         {
    121             // An X (crbug.com/137317)
    122             SkPath* bug1 = &fPaths.push_back();
    123 
    124             bug1->moveTo(1, 1);
    125             bug1->lineTo(6, 6);
    126             bug1->moveTo(1, 6);
    127             bug1->lineTo(6, 1);
    128         }
    129 
    130         {
    131             // A right angle (crbug.com/137465 and crbug.com/256776)
    132             SkPath* bug2 = &fPaths.push_back();
    133 
    134             bug2->moveTo(5.5f, 5.5f);
    135             bug2->lineTo(5.5f, 0.5f);
    136             bug2->lineTo(0.5f, 0.5f);
    137         }
    138 
    139         {
    140             // Arc example to test imperfect truncation bug (crbug.com/295626)
    141             constexpr SkScalar kRad = SkIntToScalar(2000);
    142             constexpr SkScalar kStartAngle = 262.59717f;
    143             constexpr SkScalar kSweepAngle = SkScalarHalf(17.188717f);
    144 
    145             SkPath* bug = &fPaths.push_back();
    146 
    147             // Add a circular arc
    148             SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
    149             bug->addArc(circle, kStartAngle, kSweepAngle);
    150 
    151             // Now add the chord that should cap the circular arc
    152             SkScalar cosV, sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle), &cosV);
    153 
    154             SkPoint p0 = SkPoint::Make(kRad * cosV, kRad * sinV);
    155 
    156             sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle + kSweepAngle), &cosV);
    157 
    158             SkPoint p1 = SkPoint::Make(kRad * cosV, kRad * sinV);
    159 
    160             bug->moveTo(p0);
    161             bug->lineTo(p1);
    162         }
    163     }
    164 
    165     void onDraw(SkCanvas* canvas) override {
    166         constexpr SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
    167         constexpr SkScalar kWidths[] = { 0, 0.5f, 1.5f };
    168 
    169         enum {
    170             kMargin = 5,
    171         };
    172         int wrapX = 1250 - kMargin;
    173 
    174         SkScalar maxH = 0;
    175         canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
    176         canvas->save();
    177 
    178         SkScalar x = SkIntToScalar(kMargin);
    179         for (int p = 0; p < fPaths.count(); ++p) {
    180             for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
    181                 for (int aa = 0; aa < 2; ++aa) {
    182                     for (size_t w = 0; w < SK_ARRAY_COUNT(kWidths); w++) {
    183                         const SkRect& bounds = fPaths[p].getBounds();
    184 
    185                         if (x + bounds.width() > wrapX) {
    186                             canvas->restore();
    187                             canvas->translate(0, maxH + SkIntToScalar(kMargin));
    188                             canvas->save();
    189                             maxH = 0;
    190                             x = SkIntToScalar(kMargin);
    191                         }
    192 
    193                         SkPaint paint;
    194                         paint.setARGB(kAlphaValue[a], 0, 0, 0);
    195                         paint.setAntiAlias(SkToBool(aa));
    196                         paint.setStyle(SkPaint::kStroke_Style);
    197                         paint.setStrokeWidth(kWidths[w]);
    198 
    199                         canvas->save();
    200                         canvas->translate(-bounds.fLeft, -bounds.fTop);
    201                         canvas->drawPath(fPaths[p], paint);
    202                         canvas->restore();
    203 
    204                         maxH = SkMaxScalar(maxH, bounds.height());
    205 
    206                         SkScalar dx = bounds.width() + SkIntToScalar(kMargin);
    207                         x += dx;
    208                         canvas->translate(dx, 0);
    209                     }
    210                 }
    211             }
    212         }
    213         canvas->restore();
    214     }
    215 
    216 private:
    217     SkTArray<SkPath> fPaths;
    218     typedef GM INHERITED;
    219 };
    220 
    221 static void draw_squarehair_tests(SkCanvas* canvas, SkScalar width, SkPaint::Cap cap, bool aa) {
    222     SkPaint paint;
    223     paint.setStrokeCap(cap);
    224     paint.setStrokeWidth(width);
    225     paint.setAntiAlias(aa);
    226     paint.setStyle(SkPaint::kStroke_Style);
    227     canvas->drawLine(10, 10, 20, 10, paint);
    228     canvas->drawLine(30, 10, 30, 20, paint);
    229     canvas->drawLine(40, 10, 50, 20, paint);
    230     SkPath path;
    231     path.moveTo(60, 10);
    232     path.quadTo(60, 20, 70, 20);
    233     path.conicTo(70, 10, 80, 10, 0.707f);
    234     canvas->drawPath(path, paint);
    235     path.reset();
    236     path.moveTo(90, 10);
    237     path.cubicTo(90, 20, 100, 20, 100, 10);
    238     path.lineTo(110, 10);
    239     canvas->drawPath(path, paint);
    240     canvas->translate(0, 30);
    241 }
    242 
    243 DEF_SIMPLE_GM(squarehair, canvas, 240, 360) {
    244     const bool aliases[] = { false, true };
    245     const SkScalar widths[] = { 0, 0.999f, 1, 1.001f };
    246     const SkPaint::Cap caps[] = { SkPaint::kButt_Cap, SkPaint::kSquare_Cap, SkPaint::kRound_Cap };
    247     for (auto alias : aliases) {
    248         canvas->save();
    249         for (auto width : widths) {
    250             for (auto cap : caps) {
    251                 draw_squarehair_tests(canvas, width, cap, alias);
    252             }
    253         }
    254         canvas->restore();
    255         canvas->translate(120, 0);
    256     }
    257 }
    258 
    259 //////////////////////////////////////////////////////////////////////////////
    260 
    261 static GM* MyFactory(void*) { return new HairlinesGM; }
    262 static GMRegistry reg(MyFactory);
    263 
    264 }
    265