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