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     virtual uint32_t onGetFlags() const SK_OVERRIDE {
     17         return kSkipTiled_Flag;
     18     }
     19 
     20 
     21     virtual SkString onShortName() SK_OVERRIDE {
     22         return SkString("hairlines");
     23     }
     24 
     25     virtual SkISize onISize() { return SkISize::Make(800, 600); }
     26 
     27     virtual void onOnceBeforeDraw() SK_OVERRIDE {
     28         {
     29             SkPath* lineAnglesPath = &fPaths.push_back();
     30             enum {
     31                 kNumAngles = 15,
     32                 kRadius = 40,
     33             };
     34             for (int i = 0; i < kNumAngles; ++i) {
     35                 SkScalar angle = SK_ScalarPI * SkIntToScalar(i) / kNumAngles;
     36                 SkScalar x = kRadius * SkScalarCos(angle);
     37                 SkScalar y = kRadius * SkScalarSin(angle);
     38                 lineAnglesPath->moveTo(x, y);
     39                 lineAnglesPath->lineTo(-x, -y);
     40             }
     41         }
     42 
     43         {
     44             SkPath* kindaTightQuad = &fPaths.push_back();
     45             kindaTightQuad->moveTo(0, -10 * SK_Scalar1);
     46             kindaTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -10 * SK_Scalar1, 0);
     47         }
     48 
     49         {
     50             SkPath* tightQuad = &fPaths.push_back();
     51             tightQuad->moveTo(0, -5 * SK_Scalar1);
     52             tightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -5 * SK_Scalar1, 0);
     53         }
     54 
     55         {
     56             SkPath* tighterQuad = &fPaths.push_back();
     57             tighterQuad->moveTo(0, -2 * SK_Scalar1);
     58             tighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -2 * SK_Scalar1, 0);
     59         }
     60 
     61         {
     62             SkPath* unevenTighterQuad = &fPaths.push_back();
     63             unevenTighterQuad->moveTo(0, -1 * SK_Scalar1);
     64             SkPoint p;
     65             p.set(-2 * SK_Scalar1 + 3 * SkIntToScalar(102) / 4, SkIntToScalar(75));
     66             unevenTighterQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), p.fX, p.fY);
     67         }
     68 
     69         {
     70             SkPath* reallyTightQuad = &fPaths.push_back();
     71             reallyTightQuad->moveTo(0, -1 * SK_Scalar1);
     72             reallyTightQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), -1 * SK_Scalar1, 0);
     73         }
     74 
     75         {
     76             SkPath* closedQuad = &fPaths.push_back();
     77             closedQuad->moveTo(0, -0);
     78             closedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100), 0, 0);
     79         }
     80 
     81         {
     82             SkPath* unevenClosedQuad = &fPaths.push_back();
     83             unevenClosedQuad->moveTo(0, -0);
     84             unevenClosedQuad->quadTo(SkIntToScalar(100), SkIntToScalar(100),
     85                                      SkIntToScalar(75), SkIntToScalar(75));
     86         }
     87 
     88         // Two problem cases for gpu hairline renderer found by shapeops testing. These used
     89         // to assert that the computed bounding box didn't contain all the vertices.
     90         {
     91             SkPath* problem1 = &fPaths.push_back();
     92             problem1->moveTo(SkIntToScalar(4), SkIntToScalar(6));
     93             problem1->cubicTo(SkIntToScalar(5), SkIntToScalar(6),
     94                               SkIntToScalar(5), SkIntToScalar(4),
     95                               SkIntToScalar(4), SkIntToScalar(0));
     96             problem1->close();
     97         }
     98 
     99         {
    100             SkPath* problem2 = &fPaths.push_back();
    101             problem2->moveTo(SkIntToScalar(5), SkIntToScalar(1));
    102             problem2->lineTo(4.32787323f, 1.67212653f);
    103             problem2->cubicTo(2.75223875f, 3.24776125f,
    104                               3.00581908f, 4.51236057f,
    105                               3.7580452f, 4.37367964f);
    106             problem2->cubicTo(4.66472578f, 3.888381f,
    107                               5.f, 2.875f,
    108                               5.f, 1.f);
    109             problem2->close();
    110         }
    111 
    112         // Three paths that show the same bug (missing end caps)
    113         {
    114             // A caret (crbug.com/131770)
    115             SkPath* bug0 = &fPaths.push_back();
    116             bug0->moveTo(6.5f,5.5f);
    117             bug0->lineTo(3.5f,0.5f);
    118             bug0->moveTo(0.5f,5.5f);
    119             bug0->lineTo(3.5f,0.5f);
    120         }
    121 
    122         {
    123             // An X (crbug.com/137317)
    124             SkPath* bug1 = &fPaths.push_back();
    125 
    126             bug1->moveTo(1, 1);
    127             bug1->lineTo(6, 6);
    128             bug1->moveTo(1, 6);
    129             bug1->lineTo(6, 1);
    130         }
    131 
    132         {
    133             // A right angle (crbug.com/137465 and crbug.com/256776)
    134             SkPath* bug2 = &fPaths.push_back();
    135 
    136             bug2->moveTo(5.5f, 5.5f);
    137             bug2->lineTo(5.5f, 0.5f);
    138             bug2->lineTo(0.5f, 0.5f);
    139         }
    140 
    141         {
    142             // Arc example to test imperfect truncation bug (crbug.com/295626)
    143             static const SkScalar kRad = SkIntToScalar(2000);
    144             static const SkScalar kStartAngle = 262.59717f;
    145             static const SkScalar kSweepAngle = SkScalarHalf(17.188717f);
    146 
    147             SkPath* bug = &fPaths.push_back();
    148 
    149             // Add a circular arc
    150             SkRect circle = SkRect::MakeLTRB(-kRad, -kRad, kRad, kRad);
    151             bug->addArc(circle, kStartAngle, kSweepAngle);
    152 
    153             // Now add the chord that should cap the circular arc
    154             SkScalar cosV, sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle), &cosV);
    155 
    156             SkPoint p0 = SkPoint::Make(kRad * cosV, kRad * sinV);
    157 
    158             sinV = SkScalarSinCos(SkDegreesToRadians(kStartAngle + kSweepAngle), &cosV);
    159 
    160             SkPoint p1 = SkPoint::Make(kRad * cosV, kRad * sinV);
    161 
    162             bug->moveTo(p0);
    163             bug->lineTo(p1);
    164         }
    165     }
    166 
    167     virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
    168         static const SkAlpha kAlphaValue[] = { 0xFF, 0x40 };
    169 
    170         enum {
    171             kMargin = 5,
    172         };
    173         int wrapX = canvas->getDeviceSize().fWidth - kMargin;
    174 
    175         SkScalar maxH = 0;
    176         canvas->translate(SkIntToScalar(kMargin), SkIntToScalar(kMargin));
    177         canvas->save();
    178 
    179         SkScalar x = SkIntToScalar(kMargin);
    180         for (int p = 0; p < fPaths.count(); ++p) {
    181             for (size_t a = 0; a < SK_ARRAY_COUNT(kAlphaValue); ++a) {
    182                 for (int aa = 0; aa < 2; ++aa) {
    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(0);
    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         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