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