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