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 15class AddArcGM : public skiagm::GM { 16public: 17 AddArcGM() : fRotate(0) {} 18 19protected: 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 60private: 61 SkScalar fRotate; 62 typedef skiagm::GM INHERITED; 63}; 64DEF_GM( return new AddArcGM; ) 65 66/////////////////////////////////////////////////// 67 68#define R 400 69 70class AddArcMeasGM : public skiagm::GM { 71public: 72 AddArcMeasGM() {} 73 74protected: 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 111private: 112 typedef skiagm::GM INHERITED; 113}; 114DEF_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// 121class StrokeCircleGM : public skiagm::GM { 122public: 123 StrokeCircleGM() : fRotate(0) {} 124 125protected: 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 160private: 161 SkScalar fRotate; 162 163 typedef skiagm::GM INHERITED; 164}; 165DEF_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. 171class FillCircleGM : public skiagm::GM { 172public: 173 FillCircleGM() : fRotate(0) {} 174 175protected: 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 213private: 214 SkScalar fRotate; 215 216 typedef skiagm::GM INHERITED; 217}; 218DEF_GM( return new FillCircleGM; ) 219 220////////////////////// 221 222static 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 233class ManyArcsGM : public skiagm::GM { 234public: 235 ManyArcsGM() {} 236 237protected: 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 286private: 287 typedef skiagm::GM INHERITED; 288}; 289DEF_GM( return new ManyArcsGM; ) 290 291// Lifted from https://bugs.chromium.org/p/chromium/issues/detail?id=640031 292class TinyAngleBigRadiusArcsGM : public skiagm::GM { 293public: 294 TinyAngleBigRadiusArcsGM() {} 295 296protected: 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 336private: 337 typedef skiagm::GM INHERITED; 338}; 339DEF_GM( return new TinyAngleBigRadiusArcsGM; ) 340