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/SkPaint.h" 12#include "include/core/SkPathBuilder.h" 13#include "include/core/SkPathEffect.h" 14#include "include/core/SkRect.h" 15#include "include/core/SkScalar.h" 16#include "include/core/SkTypes.h" 17#include "include/effects/SkDashPathEffect.h" 18#include "include/private/SkFloatBits.h" 19#include "include/private/SkTArray.h" 20 21#include <functional> 22 23#include "include/effects/SkStrokeAndFillPathEffect.h" 24static void set_strokeandfill(SkPaint* paint) { 25 SkASSERT(paint->getPathEffect() == nullptr); 26 paint->setPathEffect(SkStrokeAndFillPathEffect::Make()); 27 paint->setStroke(true); 28} 29 30constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f}; 31constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f}; 32constexpr SkScalar kDiameter = 40.f; 33constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter}; 34constexpr int kW = 1000; 35constexpr int kH = 1000; 36constexpr SkScalar kPad = 20.f; 37 38void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) { 39 // Draws grid of arcs with different start/sweep angles in red and their complement arcs in 40 // blue. 41 auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) { 42 SkPaint p0; 43 p0.setColor(SK_ColorRED); 44 p0.setAntiAlias(aa); 45 // Set a reasonable stroke width that configureStyle can override. 46 p0.setStrokeWidth(15.f); 47 SkPaint p1 = p0; 48 p1.setColor(SK_ColorBLUE); 49 // Use alpha so we see magenta on overlap between arc and its complement. 50 p0.setAlpha(100); 51 p1.setAlpha(100); 52 configureStyle(&p0); 53 configureStyle(&p1); 54 55 canvas->save(); 56 canvas->translate(kPad + x, kPad + y); 57 for (auto start : kStarts) { 58 canvas->save(); 59 for (auto sweep : kSweeps) { 60 canvas->drawArc(kRect, start, sweep, useCenter, p0); 61 canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1); 62 canvas->translate(kRect.width() + kPad, 0.f); 63 } 64 canvas->restore(); 65 canvas->translate(0, kRect.height() + kPad); 66 } 67 canvas->restore(); 68 }; 69 // Draw a grids for combo of enabling/disabling aa and using center. 70 constexpr SkScalar kGridW = kW / 2.f; 71 constexpr SkScalar kGridH = kH / 2.f; 72 drawGrid(0.f , 0.f , false, false); 73 drawGrid(kGridW, 0.f , true , false); 74 drawGrid(0.f , kGridH, false, true ); 75 drawGrid(kGridW, kGridH, true , true ); 76 // Draw separators between the grids. 77 SkPaint linePaint; 78 linePaint.setAntiAlias(true); 79 linePaint.setColor(SK_ColorBLACK); 80 canvas->drawLine(kGridW, 0.f , kGridW, SkIntToScalar(kH), linePaint); 81 canvas->drawLine(0.f , kGridH, SkIntToScalar(kW), kGridH, linePaint); 82} 83 84#define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH) 85 86DEF_ARC_GM(fill) { 87 auto setFill = [] (SkPaint*p) { p->setStroke(false); }; 88 draw_arcs(canvas, setFill); 89} 90 91DEF_ARC_GM(hairline) { 92 auto setHairline = [] (SkPaint* p) { 93 p->setStroke(true); 94 p->setStrokeWidth(0.f); 95 }; 96 draw_arcs(canvas, setHairline); 97} 98 99DEF_ARC_GM(stroke_butt) { 100 auto setStroke = [](SkPaint* p) { 101 p->setStroke(true); 102 p->setStrokeCap(SkPaint::kButt_Cap); 103 }; 104 draw_arcs(canvas, setStroke); 105} 106 107DEF_ARC_GM(stroke_square) { 108 auto setStroke = [] (SkPaint* p) { 109 p->setStroke(true); 110 p->setStrokeCap(SkPaint::kSquare_Cap); 111 }; 112 draw_arcs(canvas, setStroke); 113} 114 115DEF_ARC_GM(stroke_round) { 116 auto setStroke = [] (SkPaint* p) { 117 p->setStroke(true); 118 p->setStrokeCap(SkPaint::kRound_Cap); 119 }; 120 draw_arcs(canvas, setStroke); 121} 122 123DEF_ARC_GM(stroke_and_fill_butt) { 124 auto setStroke = [] (SkPaint* p) { 125 set_strokeandfill(p); 126 p->setStrokeCap(SkPaint::kButt_Cap); 127 }; 128 draw_arcs(canvas, setStroke); 129} 130 131DEF_ARC_GM(stroke_and_fill_square) { 132 auto setStroke = [] (SkPaint* p) { 133 set_strokeandfill(p); 134 p->setStrokeCap(SkPaint::kSquare_Cap); 135 }; 136 draw_arcs(canvas, setStroke); 137} 138 139DEF_ARC_GM(stroke_and_fill_round) { 140 auto setStroke = [] (SkPaint* p) { 141 set_strokeandfill(p); 142 p->setStrokeCap(SkPaint::kRound_Cap); 143 }; 144 draw_arcs(canvas, setStroke); 145} 146 147DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) { 148 constexpr SkScalar kS = 50; 149 struct Arc { 150 SkRect fOval; 151 SkScalar fStart; 152 SkScalar fSweep; 153 }; 154 const Arc noDrawArcs[] = { 155 // no sweep 156 {SkRect::MakeWH(kS, kS), 0, 0}, 157 // empty rect in x 158 {SkRect::MakeWH(-kS, kS), 0, 90}, 159 // empty rect in y 160 {SkRect::MakeWH(kS, -kS), 0, 90}, 161 // empty rect in x and y 162 {SkRect::MakeWH( 0, 0), 0, 90}, 163 }; 164 const Arc arcs[] = { 165 // large start 166 {SkRect::MakeWH(kS, kS), 810.f, 90.f}, 167 // large negative start 168 {SkRect::MakeWH(kS, kS), -810.f, 90.f}, 169 // exactly 360 sweep 170 {SkRect::MakeWH(kS, kS), 0.f, 360.f}, 171 // exactly -360 sweep 172 {SkRect::MakeWH(kS, kS), 0.f, -360.f}, 173 // exactly 540 sweep 174 {SkRect::MakeWH(kS, kS), 0.f, 540.f}, 175 // exactly -540 sweep 176 {SkRect::MakeWH(kS, kS), 0.f, -540.f}, 177 // generic large sweep and large start 178 {SkRect::MakeWH(kS, kS), 1125.f, 990.f}, 179 }; 180 SkTArray<SkPaint> paints; 181 // fill 182 paints.push_back(); 183 // stroke 184 paints.push_back().setStroke(true); 185 paints.back().setStrokeWidth(kS / 6.f); 186 // hairline 187 paints.push_back().setStroke(true); 188 paints.back().setStrokeWidth(0.f); 189 // stroke and fill 190 paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style); 191 paints.back().setStrokeWidth(kS / 6.f); 192 // dash effect 193 paints.push_back().setStroke(true); 194 paints.back().setStrokeWidth(kS / 6.f); 195 constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15}; 196 paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f)); 197 198 canvas->translate(kPad, kPad); 199 // This loop should draw nothing. 200 for (auto arc : noDrawArcs) { 201 for (auto paint : paints) { 202 paint.setAntiAlias(true); 203 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint); 204 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint); 205 } 206 } 207 208 SkPaint linePaint; 209 linePaint.setAntiAlias(true); 210 linePaint.setColor(SK_ColorRED); 211 SkScalar midX = SK_ARRAY_COUNT(arcs) * (kS + kPad) - kPad/2.f; 212 SkScalar height = paints.count() * (kS + kPad); 213 canvas->drawLine(midX, -kPad, midX, height, linePaint); 214 215 for (auto paint : paints) { 216 paint.setAntiAlias(true); 217 canvas->save(); 218 for (auto arc : arcs) { 219 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint); 220 canvas->translate(kS + kPad, 0.f); 221 } 222 for (auto arc : arcs) { 223 canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint); 224 canvas->translate(kS + kPad, 0.f); 225 } 226 canvas->restore(); 227 canvas->translate(0, kS + kPad); 228 } 229} 230 231DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) { 232 SkPathBuilder path; 233 path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20 234 path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c)); // 34.1421f, 34.1421f 235 path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318), // 20, 48.2843f 236 SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f 237 SkBits2Float(0x3f3504f3)); // 0.707107f 238 path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f 239 SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c)); // 5.85787f, 34.1421f 240 path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20 241 path.close(); 242 SkPaint p0; 243 p0.setColor(SK_ColorRED); 244 p0.setStrokeWidth(15.f); 245 p0.setStroke(true); 246 p0.setAlpha(100); 247 canvas->translate(20, 0); 248 canvas->drawPath(path.detach(), p0); 249 250 canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0); 251} 252 253DEF_SIMPLE_GM(crbug_888453, canvas, 480, 150) { 254 // Two GPU path renderers were using a too-large tolerance when chopping connics to quads. 255 // This manifested as not-very-round circular arcs at certain radii. All the arcs being drawn 256 // here should look like circles. 257 SkPaint fill; 258 fill.setAntiAlias(true); 259 SkPaint hairline = fill; 260 hairline.setStroke(true); 261 SkPaint stroke = hairline; 262 stroke.setStrokeWidth(2.0f); 263 int x = 4; 264 int y0 = 25, y1 = 75, y2 = 125; 265 for (int r = 2; r <= 20; ++r) { 266 canvas->drawArc(SkRect::MakeXYWH(x - r, y0 - r, 2 * r, 2 * r), 0, 360, false, fill); 267 canvas->drawArc(SkRect::MakeXYWH(x - r, y1 - r, 2 * r, 2 * r), 0, 360, false, hairline); 268 canvas->drawArc(SkRect::MakeXYWH(x - r, y2 - r, 2 * r, 2 * r), 0, 360, false, stroke); 269 x += 2 * r + 4; 270 } 271} 272 273DEF_SIMPLE_GM(circular_arc_stroke_matrix, canvas, 820, 1090) { 274 static constexpr SkScalar kRadius = 40.f; 275 static constexpr SkScalar kStrokeWidth = 5.f; 276 static constexpr SkScalar kStart = 89.f; 277 static constexpr SkScalar kSweep = 180.f/SK_ScalarPI; // one radian 278 279 SkTArray<SkMatrix> matrices; 280 matrices.push_back().setRotate(kRadius, kRadius, 45.f); 281 matrices.push_back(SkMatrix::I()); 282 matrices.push_back().setAll(-1, 0, 2*kRadius, 283 0, 1, 0, 284 0, 0, 1); 285 matrices.push_back().setAll( 1, 0, 0, 286 0, -1, 2*kRadius, 287 0, 0, 1); 288 matrices.push_back().setAll( 1, 0, 0, 289 0, -1, 2*kRadius, 290 0, 0, 1); 291 matrices.push_back().setAll( 0, -1, 2*kRadius, 292 -1, 0, 2*kRadius, 293 0, 0, 1); 294 matrices.push_back().setAll( 0, -1, 2*kRadius, 295 1, 0, 0, 296 0, 0, 1); 297 matrices.push_back().setAll( 0, 1, 0, 298 1, 0, 0, 299 0, 0, 1); 300 matrices.push_back().setAll( 0, 1, 0, 301 -1, 0, 2*kRadius, 302 0, 0, 1); 303 int baseMatrixCnt = matrices.count(); 304 305 306 SkMatrix tinyCW; 307 tinyCW.setRotate(0.001f, kRadius, kRadius); 308 for (int i = 0; i < baseMatrixCnt; ++i) { 309 matrices.push_back().setConcat(matrices[i], tinyCW); 310 } 311 SkMatrix tinyCCW; 312 tinyCCW.setRotate(-0.001f, kRadius, kRadius); 313 for (int i = 0; i < baseMatrixCnt; ++i) { 314 matrices.push_back().setConcat(matrices[i], tinyCCW); 315 } 316 SkMatrix cw45; 317 cw45.setRotate(45.f, kRadius, kRadius); 318 for (int i = 0; i < baseMatrixCnt; ++i) { 319 matrices.push_back().setConcat(matrices[i], cw45); 320 } 321 322 int x = 0; 323 int y = 0; 324 static constexpr SkScalar kPad = 2*kStrokeWidth; 325 canvas->translate(kPad, kPad); 326 auto bounds = SkRect::MakeWH(2*kRadius, 2*kRadius); 327 for (auto cap : {SkPaint::kRound_Cap, SkPaint::kButt_Cap, SkPaint::kSquare_Cap}) { 328 for (const auto& m : matrices) { 329 SkPaint paint; 330 paint.setStrokeCap(cap); 331 paint.setAntiAlias(true); 332 paint.setStroke(true); 333 paint.setStrokeWidth(kStrokeWidth); 334 canvas->save(); 335 canvas->translate(x * (2*kRadius + kPad), y * (2*kRadius + kPad)); 336 canvas->concat(m); 337 paint.setColor(SK_ColorRED); 338 paint.setAlpha(0x80); 339 canvas->drawArc(bounds, kStart, kSweep, false, paint); 340 paint.setColor(SK_ColorBLUE); 341 paint.setAlpha(0x80); 342 canvas->drawArc(bounds, kStart, kSweep - 360.f, false, paint); 343 canvas->restore(); 344 ++x; 345 if (x == baseMatrixCnt) { 346 x = 0; 347 ++y; 348 } 349 } 350 } 351} 352