Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
trickycubicstrokes.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 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"
13#include "include/core/SkPath.h"
15#include "include/core/SkRect.h"
16#include "include/core/SkSize.h"
21#include "src/base/SkRandom.h"
22#include "src/core/SkGeometry.h"
27
28static constexpr float kStrokeWidth = 30;
29static constexpr int kCellSize = 200;
30static constexpr int kNumCols = 5;
31static constexpr int kNumRows = 5;
32static constexpr int kTestWidth = kNumCols * kCellSize;
33static constexpr int kTestHeight = kNumRows * kCellSize;
34
35enum class CellFillMode {
38};
39
46
47// This is a compilation of cubics that have given strokers grief. Feel free to add more.
48static const TrickyCubic kTrickyCubics[] = {
49 {{{122, 737}, {348, 553}, {403, 761}, {400, 760}}, 4, CellFillMode::kStretch},
50 {{{244, 520}, {244, 518}, {1141, 634}, {394, 688}}, 4, CellFillMode::kStretch},
51 {{{550, 194}, {138, 130}, {1035, 246}, {288, 300}}, 4, CellFillMode::kStretch},
52 {{{226, 733}, {556, 779}, {-43, 471}, {348, 683}}, 4, CellFillMode::kStretch},
53 {{{268, 204}, {492, 304}, {352, 23}, {433, 412}}, 4, CellFillMode::kStretch},
54 {{{172, 480}, {396, 580}, {256, 299}, {338, 677}}, 4, CellFillMode::kStretch},
55 {{{731, 340}, {318, 252}, {1026, -64}, {367, 265}}, 4, CellFillMode::kStretch},
56 {{{475, 708}, {62, 620}, {770, 304}, {220, 659}}, 4, CellFillMode::kStretch},
57 {{{0, 0}, {128, 128}, {128, 0}, {0, 128}}, 4, CellFillMode::kCenter}, // Perfect cusp
58 {{{0,.01f}, {128,127.999f}, {128,.01f}, {0,127.99f}}, 4, CellFillMode::kCenter}, // Near-cusp
59 {{{0,-.01f}, {128,128.001f}, {128,-.01f}, {0,128.001f}}, 4, CellFillMode::kCenter}, // Near-cusp
60 {{{0,0}, {0,-10}, {0,-10}, {0,10}}, 4, CellFillMode::kCenter, 1.098283f}, // Flat line with 180
61 {{{10,0}, {0,0}, {20,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with 2 180s
62 {{{39,-39}, {40,-40}, {40,-40}, {0,0}}, 4, CellFillMode::kStretch}, // Flat diagonal with 180
63 {{{39,-39}, {40,-40}, {37,-39}, {0,0}}, 4, CellFillMode::kStretch}, // Near-flat diagonal
64 {{{40, 40}, {0, 0}, {200, 200}, {0, 0}}, 4, CellFillMode::kStretch}, // Diag w/ an internal 180
65 {{{0,0}, {1e-2f,0}, {-1e-2f,0}, {0,0}}, 4, CellFillMode::kCenter}, // Circle
66 {{{400.75f,100.05f}, {400.75f,100.05f}, {100.05f,300.95f}, {100.05f,300.95f}}, 4,
67 CellFillMode::kStretch}, // Flat line with no turns
68 {{{0.5f,0}, {0,0}, {20,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with 2 180s
69 {{{10,0}, {0,0}, {10,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with a 180
70 {{{1,1}, {2,1}, {1,1}, {1, std::numeric_limits<float>::quiet_NaN()}}, 3,
71 CellFillMode::kStretch}, // Flat QUAD with a cusp
72 {{{1,1}, {100,1}, {25,1}, {.3f, std::numeric_limits<float>::quiet_NaN()}}, 3,
73 CellFillMode::kStretch}, // Flat CONIC with a cusp
74 {{{1,1}, {100,1}, {25,1}, {1.5f, std::numeric_limits<float>::quiet_NaN()}}, 3,
75 CellFillMode::kStretch}, // Flat CONIC with a cusp
76};
77
78static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
79 if (0 == depth) {
80 SkRect bounds;
81 bounds.fLeft = std::min(std::min(P[0].x(), P[1].x()), std::min(P[2].x(), P[3].x()));
82 bounds.fTop = std::min(std::min(P[0].y(), P[1].y()), std::min(P[2].y(), P[3].y()));
83 bounds.fRight = std::max(std::max(P[0].x(), P[1].x()), std::max(P[2].x(), P[3].x()));
84 bounds.fBottom = std::max(std::max(P[0].y(), P[1].y()), std::max(P[2].y(), P[3].y()));
85 return bounds;
86 }
87
88 SkPoint chopped[7];
89 SkChopCubicAt(P, chopped, .5f);
90 SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
91 bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
92 return bounds;
93}
94
95static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
96 SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
97 return (b - a) * T + a;
98}
99
100enum class FillMode {
101 kCenter,
102 kScale
103};
104
105static void draw_test(SkCanvas* canvas, SkPaint::Cap cap, SkPaint::Join join) {
106 SkRandom rand;
107
108 canvas->clear(SK_ColorBLACK);
109
110 SkPaint strokePaint;
111 strokePaint.setAntiAlias(true);
112 strokePaint.setStrokeWidth(kStrokeWidth);
113 strokePaint.setStyle(SkPaint::kStroke_Style);
114 strokePaint.setStrokeCap(cap);
115 strokePaint.setStrokeJoin(join);
116
117 for (size_t i = 0; i < std::size(kTrickyCubics); ++i) {
118 auto [originalPts, numPts, fillMode, scale] = kTrickyCubics[i];
119
120 SkASSERT(numPts <= 4);
121 SkPoint p[4];
122 memcpy(p, originalPts, sizeof(SkPoint) * numPts);
123 for (int j = 0; j < numPts; ++j) {
124 p[j] *= scale;
125 }
126 float w = originalPts[3].fX;
127
128 auto cellRect = SkRect::MakeXYWH((i % kNumCols) * kCellSize, (i / kNumCols) * kCellSize,
130
131 SkRect strokeBounds;
132 if (numPts == 4) {
133 strokeBounds = calc_tight_cubic_bounds(p);
134 } else {
135 SkASSERT(numPts == 3);
136 SkPoint asCubic[4] = {p[0], lerp(p[0], p[1], 2/3.f), lerp(p[1], p[2], 1/3.f), p[2]};
137 strokeBounds = calc_tight_cubic_bounds(asCubic);
138 }
139 strokeBounds.outset(kStrokeWidth, kStrokeWidth);
140
141 SkMatrix matrix;
142 if (fillMode == CellFillMode::kStretch) {
143 matrix = SkMatrix::RectToRect(strokeBounds, cellRect, SkMatrix::kCenter_ScaleToFit);
144 } else {
145 matrix.setTranslate(cellRect.x() + kStrokeWidth +
146 (cellRect.width() - strokeBounds.width()) / 2,
147 cellRect.y() + kStrokeWidth +
148 (cellRect.height() - strokeBounds.height()) / 2);
149 }
150
151 SkAutoCanvasRestore acr(canvas, true);
152 canvas->concat(matrix);
153 strokePaint.setStrokeWidth(kStrokeWidth / matrix.getMaxScale());
154 strokePaint.setColor(rand.nextU() | 0xff808080);
155 SkPath path = SkPath().moveTo(p[0]);
156 if (numPts == 4) {
157 path.cubicTo(p[1], p[2], p[3]);
158 } else if (w == 1) {
159 SkASSERT(numPts == 3);
160 path.quadTo(p[1], p[2]);
161 } else {
162 SkASSERT(numPts == 3);
163 path.conicTo(p[1], p[2], w);
164 }
165 canvas->drawPath(path, strokePaint);
166 }
167}
168
169DEF_SIMPLE_GM(trickycubicstrokes, canvas, kTestWidth, kTestHeight) {
171}
172
173DEF_SIMPLE_GM(trickycubicstrokes_roundcaps, canvas, kTestWidth, kTestHeight) {
175}
#define SkASSERT(cond)
Definition SkAssert.h:116
constexpr SkColor SK_ColorBLACK
Definition SkColor.h:103
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
void clear(SkColor color)
Definition SkCanvas.h:1199
void drawPath(const SkPath &path, const SkPaint &paint)
void concat(const SkMatrix &matrix)
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
@ kCenter_ScaleToFit
scales and aligns to center
Definition SkMatrix.h:139
@ kRound_Cap
adds circle
Definition SkPaint.h:335
@ kButt_Cap
no stroke extension
Definition SkPaint.h:334
void setStyle(Style style)
Definition SkPaint.cpp:105
void setColor(SkColor color)
Definition SkPaint.cpp:119
void setAntiAlias(bool aa)
Definition SkPaint.h:170
void setStrokeCap(Cap cap)
Definition SkPaint.cpp:179
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
void setStrokeJoin(Join join)
Definition SkPaint.cpp:189
@ kRound_Join
adds circle
Definition SkPaint.h:360
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
SkPath & moveTo(SkScalar x, SkScalar y)
Definition SkPath.cpp:678
uint32_t nextU()
Definition SkRandom.h:42
static bool b
struct MyStruct a[10]
#define DEF_SIMPLE_GM(NAME, CANVAS, W, H)
Definition gm.h:50
double y
double x
SkScalar w
#define T
const Scalar scale
void outset(float dx, float dy)
Definition SkRect.h:1077
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
constexpr float height() const
Definition SkRect.h:769
constexpr float width() const
Definition SkRect.h:762
CellFillMode fFillMode
static const TrickyCubic kTrickyCubics[]
static constexpr int kCellSize
static constexpr float kStrokeWidth
static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5)
static constexpr int kTestHeight
static constexpr int kNumCols
static constexpr int kNumRows
static void draw_test(SkCanvas *canvas, SkPaint::Cap cap, SkPaint::Join join)
static constexpr int kTestWidth
static SkPoint lerp(const SkPoint &a, const SkPoint &b, float T)