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