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