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