1 /* 2 * Copyright 2015 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 "sk_tool_utils.h" 10 #include "SkAnimTimer.h" 11 #include "SkCanvas.h" 12 #include "SkPathMeasure.h" 13 #include "SkRandom.h" 14 15 class AddArcGM : public skiagm::GM { 16 public: 17 AddArcGM() : fRotate(0) {} 18 19 protected: 20 SkString onShortName() override { return SkString("addarc"); } 21 22 SkISize onISize() override { return SkISize::Make(1040, 1040); } 23 24 void onDraw(SkCanvas* canvas) override { 25 canvas->translate(20, 20); 26 27 SkRect r = SkRect::MakeWH(1000, 1000); 28 29 SkPaint paint; 30 paint.setAntiAlias(true); 31 paint.setStyle(SkPaint::kStroke_Style); 32 paint.setStrokeWidth(15); 33 34 const SkScalar inset = paint.getStrokeWidth() + 4; 35 const SkScalar sweepAngle = 345; 36 SkRandom rand; 37 38 SkScalar sign = 1; 39 while (r.width() > paint.getStrokeWidth() * 3) { 40 paint.setColor(sk_tool_utils::color_to_565(rand.nextU() | (0xFF << 24))); 41 SkScalar startAngle = rand.nextUScalar1() * 360; 42 43 SkScalar speed = SkScalarSqrt(16 / r.width()) * 0.5f; 44 startAngle += fRotate * 360 * speed * sign; 45 46 SkPath path; 47 path.addArc(r, startAngle, sweepAngle); 48 canvas->drawPath(path, paint); 49 50 r.inset(inset, inset); 51 sign = -sign; 52 } 53 } 54 55 bool onAnimate(const SkAnimTimer& timer) override { 56 fRotate = timer.scaled(1, 360); 57 return true; 58 } 59 60 private: 61 SkScalar fRotate; 62 typedef skiagm::GM INHERITED; 63 }; 64 DEF_GM( return new AddArcGM; ) 65 66 /////////////////////////////////////////////////// 67 68 #define R 400 69 70 class AddArcMeasGM : public skiagm::GM { 71 public: 72 AddArcMeasGM() {} 73 74 protected: 75 SkString onShortName() override { return SkString("addarc_meas"); } 76 77 SkISize onISize() override { return SkISize::Make(2*R + 40, 2*R + 40); } 78 79 void onDraw(SkCanvas* canvas) override { 80 canvas->translate(R + 20, R + 20); 81 82 SkPaint paint; 83 paint.setAntiAlias(true); 84 paint.setStyle(SkPaint::kStroke_Style); 85 86 SkPaint measPaint; 87 measPaint.setAntiAlias(true); 88 measPaint.setColor(SK_ColorRED); 89 90 const SkRect oval = SkRect::MakeLTRB(-R, -R, R, R); 91 canvas->drawOval(oval, paint); 92 93 for (SkScalar deg = 0; deg < 360; deg += 10) { 94 const SkScalar rad = SkDegreesToRadians(deg); 95 SkScalar rx = SkScalarCos(rad) * R; 96 SkScalar ry = SkScalarSin(rad) * R; 97 98 canvas->drawLine(0, 0, rx, ry, paint); 99 100 SkPath path; 101 path.addArc(oval, 0, deg); 102 SkPathMeasure meas(path, false); 103 SkScalar arcLen = rad * R; 104 SkPoint pos; 105 if (meas.getPosTan(arcLen, &pos, nullptr)) { 106 canvas->drawLine({0, 0}, pos, measPaint); 107 } 108 } 109 } 110 111 private: 112 typedef skiagm::GM INHERITED; 113 }; 114 DEF_GM( return new AddArcMeasGM; ) 115 116 /////////////////////////////////////////////////// 117 118 // Emphasize drawing a stroked oval (containing conics) and then scaling the results up, 119 // to ensure that we compute the stroke taking the CTM into account 120 // 121 class StrokeCircleGM : public skiagm::GM { 122 public: 123 StrokeCircleGM() : fRotate(0) {} 124 125 protected: 126 SkString onShortName() override { return SkString("strokecircle"); } 127 128 SkISize onISize() override { return SkISize::Make(520, 520); } 129 130 void onDraw(SkCanvas* canvas) override { 131 canvas->scale(20, 20); 132 canvas->translate(13, 13); 133 134 SkPaint paint; 135 paint.setAntiAlias(true); 136 paint.setStyle(SkPaint::kStroke_Style); 137 paint.setStrokeWidth(SK_Scalar1 / 2); 138 139 const SkScalar delta = paint.getStrokeWidth() * 3 / 2; 140 SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24); 141 SkRandom rand; 142 143 SkScalar sign = 1; 144 while (r.width() > paint.getStrokeWidth() * 2) { 145 SkAutoCanvasRestore acr(canvas, true); 146 canvas->rotate(fRotate * sign); 147 148 paint.setColor(sk_tool_utils::color_to_565(rand.nextU() | (0xFF << 24))); 149 canvas->drawOval(r, paint); 150 r.inset(delta, delta); 151 sign = -sign; 152 } 153 } 154 155 bool onAnimate(const SkAnimTimer& timer) override { 156 fRotate = timer.scaled(60, 360); 157 return true; 158 } 159 160 private: 161 SkScalar fRotate; 162 163 typedef skiagm::GM INHERITED; 164 }; 165 DEF_GM( return new StrokeCircleGM; ) 166 167 ////////////////////// 168 169 // Fill circles and rotate them to test our Analytic Anti-Aliasing. 170 // This test is based on StrokeCircleGM. 171 class FillCircleGM : public skiagm::GM { 172 public: 173 FillCircleGM() : fRotate(0) {} 174 175 protected: 176 SkString onShortName() override { return SkString("fillcircle"); } 177 178 SkISize onISize() override { return SkISize::Make(520, 520); } 179 180 void onDraw(SkCanvas* canvas) override { 181 canvas->scale(20, 20); 182 canvas->translate(13, 13); 183 184 SkPaint paint; 185 paint.setAntiAlias(true); 186 paint.setStyle(SkPaint::kStroke_Style); 187 paint.setStrokeWidth(SK_Scalar1 / 2); 188 189 const SkScalar strokeWidth = paint.getStrokeWidth(); 190 const SkScalar delta = strokeWidth * 3 / 2; 191 SkRect r = SkRect::MakeXYWH(-12, -12, 24, 24); 192 SkRandom rand; 193 194 // Reset style to fill. We only need stroke stype for producing delta and strokeWidth 195 paint.setStyle(SkPaint::kFill_Style); 196 197 SkScalar sign = 1; 198 while (r.width() > strokeWidth * 2) { 199 SkAutoCanvasRestore acr(canvas, true); 200 canvas->rotate(fRotate * sign); 201 paint.setColor(sk_tool_utils::color_to_565(rand.nextU() | (0xFF << 24))); 202 canvas->drawOval(r, paint); 203 r.inset(delta, delta); 204 sign = -sign; 205 } 206 } 207 208 bool onAnimate(const SkAnimTimer& timer) override { 209 fRotate = timer.scaled(60, 360); 210 return true; 211 } 212 213 private: 214 SkScalar fRotate; 215 216 typedef skiagm::GM INHERITED; 217 }; 218 DEF_GM( return new FillCircleGM; ) 219 220 ////////////////////// 221 222 static void html_canvas_arc(SkPath* path, SkScalar x, SkScalar y, SkScalar r, SkScalar start, 223 SkScalar end, bool ccw, bool callArcTo) { 224 SkRect bounds = { x - r, y - r, x + r, y + r }; 225 SkScalar sweep = ccw ? end - start : start - end; 226 if (callArcTo) 227 path->arcTo(bounds, start, sweep, false); 228 else 229 path->addArc(bounds, start, sweep); 230 } 231 232 // Lifted from canvas-arc-circumference-fill-diffs.html 233 class ManyArcsGM : public skiagm::GM { 234 public: 235 ManyArcsGM() {} 236 237 protected: 238 SkString onShortName() override { return SkString("manyarcs"); } 239 240 SkISize onISize() override { return SkISize::Make(620, 330); } 241 242 void onDraw(SkCanvas* canvas) override { 243 SkPaint paint; 244 paint.setAntiAlias(true); 245 paint.setStyle(SkPaint::kStroke_Style); 246 247 canvas->translate(10, 10); 248 249 // 20 angles. 250 SkScalar sweepAngles[] = { 251 -123.7f, -2.3f, -2, -1, -0.3f, -0.000001f, 0, 0.000001f, 0.3f, 0.7f, 252 1, 1.3f, 1.5f, 1.7f, 1.99999f, 2, 2.00001f, 2.3f, 4.3f, 3934723942837.3f 253 }; 254 for (size_t i = 0; i < SK_ARRAY_COUNT(sweepAngles); ++i) { 255 sweepAngles[i] *= 180; 256 } 257 258 SkScalar startAngles[] = { -1, -0.5f, 0, 0.5f }; 259 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) { 260 startAngles[i] *= 180; 261 } 262 263 bool anticlockwise = false; 264 SkScalar sign = 1; 265 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles) * 2; ++i) { 266 if (i == SK_ARRAY_COUNT(startAngles)) { 267 anticlockwise = true; 268 sign = -1; 269 } 270 SkScalar startAngle = startAngles[i % SK_ARRAY_COUNT(startAngles)] * sign; 271 canvas->save(); 272 for (size_t j = 0; j < SK_ARRAY_COUNT(sweepAngles); ++j) { 273 SkPath path; 274 path.moveTo(0, 2); 275 html_canvas_arc(&path, 18, 15, 10, startAngle, startAngle + (sweepAngles[j] * sign), 276 anticlockwise, true); 277 path.lineTo(0, 28); 278 canvas->drawPath(path, paint); 279 canvas->translate(30, 0); 280 } 281 canvas->restore(); 282 canvas->translate(0, 40); 283 } 284 } 285 286 private: 287 typedef skiagm::GM INHERITED; 288 }; 289 DEF_GM( return new ManyArcsGM; ) 290 291 // Lifted from https://bugs.chromium.org/p/chromium/issues/detail?id=640031 292 class TinyAngleBigRadiusArcsGM : public skiagm::GM { 293 public: 294 TinyAngleBigRadiusArcsGM() {} 295 296 protected: 297 SkString onShortName() override { return SkString("tinyanglearcs"); } 298 299 SkISize onISize() override { return SkISize::Make(620, 330); } 300 301 void onDraw(SkCanvas* canvas) override { 302 SkPaint paint; 303 paint.setAntiAlias(true); 304 paint.setStyle(SkPaint::kStroke_Style); 305 306 canvas->translate(50, 50); 307 308 SkScalar outerRadius = 100000.0f; 309 SkScalar innerRadius = outerRadius - 20.0f; 310 SkScalar centerX = 50; 311 SkScalar centerY = outerRadius; 312 SkScalar startAngles[] = { 1.5f * SK_ScalarPI , 1.501f * SK_ScalarPI }; 313 SkScalar sweepAngle = 10.0f / outerRadius; 314 315 for (size_t i = 0; i < SK_ARRAY_COUNT(startAngles); ++i) { 316 SkPath path; 317 SkScalar endAngle = startAngles[i] + sweepAngle; 318 path.moveTo(centerX + innerRadius * sk_float_cos(startAngles[i]), 319 centerY + innerRadius * sk_float_sin(startAngles[i])); 320 path.lineTo(centerX + outerRadius * sk_float_cos(startAngles[i]), 321 centerY + outerRadius * sk_float_sin(startAngles[i])); 322 // A combination of tiny sweepAngle + large radius, we should draw a line. 323 html_canvas_arc(&path, centerX, outerRadius, outerRadius, 324 startAngles[i] * 180 / SK_ScalarPI, endAngle * 180 / SK_ScalarPI, 325 true, true); 326 path.lineTo(centerX + innerRadius * sk_float_cos(endAngle), 327 centerY + innerRadius * sk_float_sin(endAngle)); 328 html_canvas_arc(&path, centerX, outerRadius, innerRadius, 329 endAngle * 180 / SK_ScalarPI, startAngles[i] * 180 / SK_ScalarPI, 330 true, false); 331 canvas->drawPath(path, paint); 332 canvas->translate(20, 0); 333 } 334 } 335 336 private: 337 typedef skiagm::GM INHERITED; 338 }; 339 DEF_GM( return new TinyAngleBigRadiusArcsGM; ) 340