Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2016 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 "SkAnimTimer.h"
     10 #include "SkPath.h"
     11 #include "SkDashPathEffect.h"
     12 
     13 int dash1[] = { 1, 1 };
     14 int dash2[] = { 1, 3 };
     15 int dash3[] = { 1, 1, 3, 3 };
     16 int dash4[] = { 1, 3, 2, 4 };
     17 
     18 struct DashExample {
     19     int* pattern;
     20     int length;
     21 } dashExamples[] = {
     22     { dash1, SK_ARRAY_COUNT(dash1) },
     23     { dash2, SK_ARRAY_COUNT(dash2) },
     24     { dash3, SK_ARRAY_COUNT(dash3) },
     25     { dash4, SK_ARRAY_COUNT(dash4) }
     26 };
     27 
     28 
     29 class DashCircleGM : public skiagm::GM {
     30 public:
     31     DashCircleGM() : fRotation(0) { }
     32 
     33 protected:
     34     SkString onShortName() override { return SkString("dashcircle"); }
     35 
     36     SkISize onISize() override { return SkISize::Make(900, 1200); }
     37 
     38     void onDraw(SkCanvas* canvas) override {
     39         SkPaint refPaint;
     40         refPaint.setAntiAlias(true);
     41         refPaint.setColor(0xFFbf3f7f);
     42         refPaint.setStyle(SkPaint::kStroke_Style);
     43         refPaint.setStrokeWidth(1);
     44         const SkScalar radius = 125;
     45         SkRect oval = SkRect::MakeLTRB(-radius - 20, -radius - 20, radius + 20, radius + 20);
     46         SkPath circle;
     47         circle.addCircle(0, 0, radius);
     48         SkScalar circumference = radius * SK_ScalarPI * 2;
     49         int wedges[] = { 6, 12, 36 };
     50         canvas->translate(radius+20, radius+20);
     51         for (int wedge : wedges) {
     52             SkScalar arcLength = 360.f / wedge;
     53             canvas->save();
     54             for (const DashExample& dashExample : dashExamples) {
     55                 SkPath refPath;
     56                 int dashUnits = 0;
     57                 for (int index = 0; index < dashExample.length; ++index) {
     58                     dashUnits += dashExample.pattern[index];
     59                 }
     60                 SkScalar unitLength = arcLength / dashUnits;
     61                 SkScalar angle = 0;
     62                 for (int index = 0; index < wedge; ++index) {
     63                     for (int i2 = 0; i2 < dashExample.length; i2 += 2) {
     64                         SkScalar span = dashExample.pattern[i2] * unitLength;
     65                         refPath.moveTo(0, 0);
     66                         refPath.arcTo(oval, angle, span, false);
     67                         refPath.close();
     68                         angle += span + (dashExample.pattern[i2 + 1]) * unitLength;
     69                     }
     70                 }
     71                 canvas->save();
     72                 canvas->rotate(fRotation);
     73                 canvas->drawPath(refPath, refPaint);
     74                 canvas->restore();
     75                 SkPaint p;
     76                 p.setAntiAlias(true);
     77                 p.setStyle(SkPaint::kStroke_Style);
     78                 p.setStrokeWidth(10);
     79                 SkScalar intervals[4];
     80                 int intervalCount = dashExample.length;
     81                 SkScalar dashLength = circumference / wedge / dashUnits;
     82                 for (int index = 0; index < dashExample.length; ++index) {
     83                     intervals[index] = dashExample.pattern[index] * dashLength;
     84                 }
     85                 p.setPathEffect(SkDashPathEffect::Make(intervals, intervalCount, 0));
     86                 canvas->save();
     87                 canvas->rotate(fRotation);
     88                 canvas->drawPath(circle, p);
     89                 canvas->restore();
     90                 canvas->translate(0, radius * 2 + 50);
     91             }
     92             canvas->restore();
     93             canvas->translate(radius * 2 + 50, 0);
     94         }
     95     }
     96 
     97     bool onAnimate(const SkAnimTimer& timer) override {
     98         constexpr SkScalar kDesiredDurationSecs = 100.0f;
     99 
    100         fRotation = timer.scaled(360.0f/kDesiredDurationSecs, 360.0f);
    101         return true;
    102     }
    103 
    104 private:
    105     SkScalar fRotation;
    106 
    107     typedef GM INHERITED;
    108 };
    109 
    110 DEF_GM(return new DashCircleGM; )
    111 
    112 class DashCircle2GM : public skiagm::GM {
    113 public:
    114     DashCircle2GM() {}
    115 
    116 protected:
    117     SkString onShortName() override { return SkString("dashcircle2"); }
    118 
    119     SkISize onISize() override { return SkISize::Make(635, 900); }
    120 
    121     void onDraw(SkCanvas* canvas) override {
    122         // These intervals are defined relative to tau.
    123         static constexpr SkScalar kIntervals[][2]{
    124                 {0.333f, 0.333f},
    125                 {0.015f, 0.015f},
    126                 {0.01f , 0.09f },
    127                 {0.097f, 0.003f},
    128                 {0.02f , 0.04f },
    129                 {0.1f  , 0.2f  },
    130                 {0.25f , 0.25f },
    131                 {0.6f  , 0.7f  }, // adds to > 1
    132                 {1.2f  , 0.8f  }, // on is > 1
    133                 {0.1f  , 1.1f  }, // off is > 1*/
    134         };
    135 
    136         static constexpr int kN = SK_ARRAY_COUNT(kIntervals);
    137         static constexpr SkScalar kRadius = 20.f;
    138         static constexpr SkScalar kStrokeWidth = 15.f;
    139         static constexpr SkScalar kPad = 5.f;
    140         static constexpr SkRect kCircle = {-kRadius, -kRadius, kRadius, kRadius};
    141 
    142         static constexpr SkScalar kThinRadius = kRadius * 1.5;
    143         static constexpr SkRect kThinCircle = {-kThinRadius, -kThinRadius,
    144                                                 kThinRadius,  kThinRadius};
    145         static constexpr SkScalar kThinStrokeWidth = 0.4f;
    146 
    147         sk_sp<SkPathEffect> deffects[SK_ARRAY_COUNT(kIntervals)];
    148         sk_sp<SkPathEffect> thinDEffects[SK_ARRAY_COUNT(kIntervals)];
    149         for (int i = 0; i < kN; ++i) {
    150             static constexpr SkScalar kTau = 2 * SK_ScalarPI;
    151             static constexpr SkScalar kCircumference = kRadius * kTau;
    152             SkScalar scaledIntervals[2] = {kCircumference * kIntervals[i][0],
    153                                            kCircumference * kIntervals[i][1]};
    154             deffects[i] = SkDashPathEffect::Make(
    155                     scaledIntervals, 2, kCircumference * fPhaseDegrees * kTau / 360.f);
    156             static constexpr SkScalar kThinCircumference = kThinRadius * kTau;
    157             scaledIntervals[0] = kThinCircumference * kIntervals[i][0];
    158             scaledIntervals[1] = kThinCircumference * kIntervals[i][1];
    159             thinDEffects[i] = SkDashPathEffect::Make(
    160                     scaledIntervals, 2, kThinCircumference * fPhaseDegrees * kTau / 360.f);
    161         }
    162 
    163         SkMatrix rotate;
    164         rotate.setRotate(25.f);
    165         static const SkMatrix kMatrices[]{
    166                 SkMatrix::I(),
    167                 SkMatrix::MakeScale(1.2f),
    168                 SkMatrix::MakeAll(1, 0, 0, 0, -1, 0, 0, 0, 1),  // y flipper
    169                 SkMatrix::MakeAll(-1, 0, 0, 0, 1, 0, 0, 0, 1),  // x flipper
    170                 SkMatrix::MakeScale(0.7f),
    171                 rotate,
    172                 SkMatrix::Concat(
    173                         SkMatrix::Concat(SkMatrix::MakeAll(-1, 0, 0, 0, 1, 0, 0, 0, 1), rotate),
    174                         rotate)
    175         };
    176 
    177         SkPaint paint;
    178         paint.setAntiAlias(true);
    179         paint.setStrokeWidth(kStrokeWidth);
    180         paint.setStyle(SkPaint::kStroke_Style);
    181 
    182         // Compute the union of bounds of all of our test cases.
    183         SkRect bounds = SkRect::MakeEmpty();
    184         static const SkRect kBounds = kThinCircle.makeOutset(kThinStrokeWidth / 2.f,
    185                                                              kThinStrokeWidth / 2.f);
    186         for (const auto& m : kMatrices) {
    187             SkRect devBounds;
    188             m.mapRect(&devBounds, kBounds);
    189             bounds.join(devBounds);
    190         }
    191 
    192         canvas->save();
    193         canvas->translate(-bounds.fLeft + kPad, -bounds.fTop + kPad);
    194         for (size_t i = 0; i < SK_ARRAY_COUNT(deffects); ++i) {
    195             canvas->save();
    196             for (const auto& m : kMatrices) {
    197                 canvas->save();
    198                 canvas->concat(m);
    199 
    200                 paint.setPathEffect(deffects[i]);
    201                 paint.setStrokeWidth(kStrokeWidth);
    202                 canvas->drawOval(kCircle, paint);
    203 
    204                 paint.setPathEffect(thinDEffects[i]);
    205                 paint.setStrokeWidth(kThinStrokeWidth);
    206                 canvas->drawOval(kThinCircle, paint);
    207 
    208                 canvas->restore();
    209                 canvas->translate(bounds.width() + kPad, 0);
    210             }
    211             canvas->restore();
    212             canvas->translate(0, bounds.height() + kPad);
    213         }
    214         canvas->restore();
    215     }
    216 
    217 protected:
    218     bool onAnimate(const SkAnimTimer& timer) override {
    219         fPhaseDegrees = timer.secs();
    220         return true;
    221     }
    222 
    223     // Init with a non-zero phase for when run as a non-animating GM.
    224     SkScalar fPhaseDegrees = 12.f;
    225 };
    226 
    227 DEF_GM(return new DashCircle2GM;)
    228 
    229 DEF_SIMPLE_GM(maddash, canvas, 1600, 1600) {
    230     canvas->drawRect({0, 0, 1600, 1600}, SkPaint());
    231     SkPaint p;
    232     p.setColor(SK_ColorRED);
    233     p.setAntiAlias(true);
    234     p.setStyle(SkPaint::kStroke_Style);
    235     p.setStrokeWidth(380);
    236 
    237     SkScalar intvls[] = { 2.5, 10 /* 1200 */ };
    238     p.setPathEffect(SkDashPathEffect::Make(intvls, 2, 0));
    239 
    240     canvas->drawCircle(400, 400, 200, p);
    241 
    242     SkPath path;
    243     path.moveTo(800, 400);
    244     path.quadTo(1000, 400, 1000, 600);
    245     path.quadTo(1000, 800, 800, 800);
    246     path.quadTo(600, 800, 600, 600);
    247     path.quadTo(600, 400, 800, 400);
    248     path.close();
    249     canvas->translate(350, 150);
    250     p.setStrokeWidth(320);
    251     canvas->drawPath(path, p);
    252 
    253     path.reset();
    254     path.moveTo(800, 400);
    255     path.cubicTo(900, 400, 1000, 500, 1000, 600);
    256     path.cubicTo(1000, 700, 900, 800, 800, 800);
    257     path.cubicTo(700, 800, 600, 700, 600, 600);
    258     path.cubicTo(600, 500, 700, 400, 800, 400);
    259     path.close();
    260     canvas->translate(-550, 500);
    261     p.setStrokeWidth(300);
    262     canvas->drawPath(path, p);
    263 }
    264