1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2018 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrContextOptions.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrDirectContext.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkGeometry.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrCaps.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDirectContextPriv.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrDrawingManager.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/ganesh/GrRecordingContextPriv.h"
27*c8dee2aaSAndroid Build Coastguard Worker
28*c8dee2aaSAndroid Build Coastguard Worker static constexpr float kStrokeWidth = 30;
29*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kCellSize = 200;
30*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kNumCols = 5;
31*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kNumRows = 5;
32*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kTestWidth = kNumCols * kCellSize;
33*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kTestHeight = kNumRows * kCellSize;
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker enum class CellFillMode {
36*c8dee2aaSAndroid Build Coastguard Worker kStretch,
37*c8dee2aaSAndroid Build Coastguard Worker kCenter
38*c8dee2aaSAndroid Build Coastguard Worker };
39*c8dee2aaSAndroid Build Coastguard Worker
40*c8dee2aaSAndroid Build Coastguard Worker struct TrickyCubic {
41*c8dee2aaSAndroid Build Coastguard Worker SkPoint fPoints[4];
42*c8dee2aaSAndroid Build Coastguard Worker int fNumPts;
43*c8dee2aaSAndroid Build Coastguard Worker CellFillMode fFillMode;
44*c8dee2aaSAndroid Build Coastguard Worker float fScale = 1;
45*c8dee2aaSAndroid Build Coastguard Worker };
46*c8dee2aaSAndroid Build Coastguard Worker
47*c8dee2aaSAndroid Build Coastguard Worker // This is a compilation of cubics that have given strokers grief. Feel free to add more.
48*c8dee2aaSAndroid Build Coastguard Worker static const TrickyCubic kTrickyCubics[] = {
49*c8dee2aaSAndroid Build Coastguard Worker {{{122, 737}, {348, 553}, {403, 761}, {400, 760}}, 4, CellFillMode::kStretch},
50*c8dee2aaSAndroid Build Coastguard Worker {{{244, 520}, {244, 518}, {1141, 634}, {394, 688}}, 4, CellFillMode::kStretch},
51*c8dee2aaSAndroid Build Coastguard Worker {{{550, 194}, {138, 130}, {1035, 246}, {288, 300}}, 4, CellFillMode::kStretch},
52*c8dee2aaSAndroid Build Coastguard Worker {{{226, 733}, {556, 779}, {-43, 471}, {348, 683}}, 4, CellFillMode::kStretch},
53*c8dee2aaSAndroid Build Coastguard Worker {{{268, 204}, {492, 304}, {352, 23}, {433, 412}}, 4, CellFillMode::kStretch},
54*c8dee2aaSAndroid Build Coastguard Worker {{{172, 480}, {396, 580}, {256, 299}, {338, 677}}, 4, CellFillMode::kStretch},
55*c8dee2aaSAndroid Build Coastguard Worker {{{731, 340}, {318, 252}, {1026, -64}, {367, 265}}, 4, CellFillMode::kStretch},
56*c8dee2aaSAndroid Build Coastguard Worker {{{475, 708}, {62, 620}, {770, 304}, {220, 659}}, 4, CellFillMode::kStretch},
57*c8dee2aaSAndroid Build Coastguard Worker {{{0, 0}, {128, 128}, {128, 0}, {0, 128}}, 4, CellFillMode::kCenter}, // Perfect cusp
58*c8dee2aaSAndroid Build Coastguard Worker {{{0,.01f}, {128,127.999f}, {128,.01f}, {0,127.99f}}, 4, CellFillMode::kCenter}, // Near-cusp
59*c8dee2aaSAndroid Build Coastguard Worker {{{0,-.01f}, {128,128.001f}, {128,-.01f}, {0,128.001f}}, 4, CellFillMode::kCenter}, // Near-cusp
60*c8dee2aaSAndroid Build Coastguard Worker {{{0,0}, {0,-10}, {0,-10}, {0,10}}, 4, CellFillMode::kCenter, 1.098283f}, // Flat line with 180
61*c8dee2aaSAndroid Build Coastguard Worker {{{10,0}, {0,0}, {20,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with 2 180s
62*c8dee2aaSAndroid Build Coastguard Worker {{{39,-39}, {40,-40}, {40,-40}, {0,0}}, 4, CellFillMode::kStretch}, // Flat diagonal with 180
63*c8dee2aaSAndroid Build Coastguard Worker {{{39,-39}, {40,-40}, {37,-39}, {0,0}}, 4, CellFillMode::kStretch}, // Near-flat diagonal
64*c8dee2aaSAndroid Build Coastguard Worker {{{40, 40}, {0, 0}, {200, 200}, {0, 0}}, 4, CellFillMode::kStretch}, // Diag w/ an internal 180
65*c8dee2aaSAndroid Build Coastguard Worker {{{0,0}, {1e-2f,0}, {-1e-2f,0}, {0,0}}, 4, CellFillMode::kCenter}, // Circle
66*c8dee2aaSAndroid Build Coastguard Worker {{{400.75f,100.05f}, {400.75f,100.05f}, {100.05f,300.95f}, {100.05f,300.95f}}, 4,
67*c8dee2aaSAndroid Build Coastguard Worker CellFillMode::kStretch}, // Flat line with no turns
68*c8dee2aaSAndroid Build Coastguard Worker {{{0.5f,0}, {0,0}, {20,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with 2 180s
69*c8dee2aaSAndroid Build Coastguard Worker {{{10,0}, {0,0}, {10,0}, {10,0}}, 4, CellFillMode::kStretch}, // Flat line with a 180
70*c8dee2aaSAndroid Build Coastguard Worker {{{1,1}, {2,1}, {1,1}, {1, std::numeric_limits<float>::quiet_NaN()}}, 3,
71*c8dee2aaSAndroid Build Coastguard Worker CellFillMode::kStretch}, // Flat QUAD with a cusp
72*c8dee2aaSAndroid Build Coastguard Worker {{{1,1}, {100,1}, {25,1}, {.3f, std::numeric_limits<float>::quiet_NaN()}}, 3,
73*c8dee2aaSAndroid Build Coastguard Worker CellFillMode::kStretch}, // Flat CONIC with a cusp
74*c8dee2aaSAndroid Build Coastguard Worker {{{1,1}, {100,1}, {25,1}, {1.5f, std::numeric_limits<float>::quiet_NaN()}}, 3,
75*c8dee2aaSAndroid Build Coastguard Worker CellFillMode::kStretch}, // Flat CONIC with a cusp
76*c8dee2aaSAndroid Build Coastguard Worker };
77*c8dee2aaSAndroid Build Coastguard Worker
calc_tight_cubic_bounds(const SkPoint P[4],int depth=5)78*c8dee2aaSAndroid Build Coastguard Worker static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
79*c8dee2aaSAndroid Build Coastguard Worker if (0 == depth) {
80*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds;
81*c8dee2aaSAndroid Build Coastguard Worker bounds.fLeft = std::min(std::min(P[0].x(), P[1].x()), std::min(P[2].x(), P[3].x()));
82*c8dee2aaSAndroid Build Coastguard Worker bounds.fTop = std::min(std::min(P[0].y(), P[1].y()), std::min(P[2].y(), P[3].y()));
83*c8dee2aaSAndroid Build Coastguard Worker bounds.fRight = std::max(std::max(P[0].x(), P[1].x()), std::max(P[2].x(), P[3].x()));
84*c8dee2aaSAndroid Build Coastguard Worker bounds.fBottom = std::max(std::max(P[0].y(), P[1].y()), std::max(P[2].y(), P[3].y()));
85*c8dee2aaSAndroid Build Coastguard Worker return bounds;
86*c8dee2aaSAndroid Build Coastguard Worker }
87*c8dee2aaSAndroid Build Coastguard Worker
88*c8dee2aaSAndroid Build Coastguard Worker SkPoint chopped[7];
89*c8dee2aaSAndroid Build Coastguard Worker SkChopCubicAt(P, chopped, .5f);
90*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
91*c8dee2aaSAndroid Build Coastguard Worker bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
92*c8dee2aaSAndroid Build Coastguard Worker return bounds;
93*c8dee2aaSAndroid Build Coastguard Worker }
94*c8dee2aaSAndroid Build Coastguard Worker
lerp(const SkPoint & a,const SkPoint & b,float T)95*c8dee2aaSAndroid Build Coastguard Worker static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
96*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
97*c8dee2aaSAndroid Build Coastguard Worker return (b - a) * T + a;
98*c8dee2aaSAndroid Build Coastguard Worker }
99*c8dee2aaSAndroid Build Coastguard Worker
100*c8dee2aaSAndroid Build Coastguard Worker enum class FillMode {
101*c8dee2aaSAndroid Build Coastguard Worker kCenter,
102*c8dee2aaSAndroid Build Coastguard Worker kScale
103*c8dee2aaSAndroid Build Coastguard Worker };
104*c8dee2aaSAndroid Build Coastguard Worker
draw_test(SkCanvas * canvas,SkPaint::Cap cap,SkPaint::Join join)105*c8dee2aaSAndroid Build Coastguard Worker static void draw_test(SkCanvas* canvas, SkPaint::Cap cap, SkPaint::Join join) {
106*c8dee2aaSAndroid Build Coastguard Worker SkRandom rand;
107*c8dee2aaSAndroid Build Coastguard Worker
108*c8dee2aaSAndroid Build Coastguard Worker canvas->clear(SK_ColorBLACK);
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker SkPaint strokePaint;
111*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setAntiAlias(true);
112*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeWidth(kStrokeWidth);
113*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStyle(SkPaint::kStroke_Style);
114*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeCap(cap);
115*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeJoin(join);
116*c8dee2aaSAndroid Build Coastguard Worker
117*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < std::size(kTrickyCubics); ++i) {
118*c8dee2aaSAndroid Build Coastguard Worker auto [originalPts, numPts, fillMode, scale] = kTrickyCubics[i];
119*c8dee2aaSAndroid Build Coastguard Worker
120*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(numPts <= 4);
121*c8dee2aaSAndroid Build Coastguard Worker SkPoint p[4];
122*c8dee2aaSAndroid Build Coastguard Worker memcpy(p, originalPts, sizeof(SkPoint) * numPts);
123*c8dee2aaSAndroid Build Coastguard Worker for (int j = 0; j < numPts; ++j) {
124*c8dee2aaSAndroid Build Coastguard Worker p[j] *= scale;
125*c8dee2aaSAndroid Build Coastguard Worker }
126*c8dee2aaSAndroid Build Coastguard Worker float w = originalPts[3].fX;
127*c8dee2aaSAndroid Build Coastguard Worker
128*c8dee2aaSAndroid Build Coastguard Worker auto cellRect = SkRect::MakeXYWH((i % kNumCols) * kCellSize, (i / kNumCols) * kCellSize,
129*c8dee2aaSAndroid Build Coastguard Worker kCellSize, kCellSize);
130*c8dee2aaSAndroid Build Coastguard Worker
131*c8dee2aaSAndroid Build Coastguard Worker SkRect strokeBounds;
132*c8dee2aaSAndroid Build Coastguard Worker if (numPts == 4) {
133*c8dee2aaSAndroid Build Coastguard Worker strokeBounds = calc_tight_cubic_bounds(p);
134*c8dee2aaSAndroid Build Coastguard Worker } else {
135*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(numPts == 3);
136*c8dee2aaSAndroid Build Coastguard Worker SkPoint asCubic[4] = {p[0], lerp(p[0], p[1], 2/3.f), lerp(p[1], p[2], 1/3.f), p[2]};
137*c8dee2aaSAndroid Build Coastguard Worker strokeBounds = calc_tight_cubic_bounds(asCubic);
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker strokeBounds.outset(kStrokeWidth, kStrokeWidth);
140*c8dee2aaSAndroid Build Coastguard Worker
141*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix;
142*c8dee2aaSAndroid Build Coastguard Worker if (fillMode == CellFillMode::kStretch) {
143*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::RectToRect(strokeBounds, cellRect, SkMatrix::kCenter_ScaleToFit);
144*c8dee2aaSAndroid Build Coastguard Worker } else {
145*c8dee2aaSAndroid Build Coastguard Worker matrix.setTranslate(cellRect.x() + kStrokeWidth +
146*c8dee2aaSAndroid Build Coastguard Worker (cellRect.width() - strokeBounds.width()) / 2,
147*c8dee2aaSAndroid Build Coastguard Worker cellRect.y() + kStrokeWidth +
148*c8dee2aaSAndroid Build Coastguard Worker (cellRect.height() - strokeBounds.height()) / 2);
149*c8dee2aaSAndroid Build Coastguard Worker }
150*c8dee2aaSAndroid Build Coastguard Worker
151*c8dee2aaSAndroid Build Coastguard Worker SkAutoCanvasRestore acr(canvas, true);
152*c8dee2aaSAndroid Build Coastguard Worker canvas->concat(matrix);
153*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setStrokeWidth(kStrokeWidth / matrix.getMaxScale());
154*c8dee2aaSAndroid Build Coastguard Worker strokePaint.setColor(rand.nextU() | 0xff808080);
155*c8dee2aaSAndroid Build Coastguard Worker SkPath path = SkPath().moveTo(p[0]);
156*c8dee2aaSAndroid Build Coastguard Worker if (numPts == 4) {
157*c8dee2aaSAndroid Build Coastguard Worker path.cubicTo(p[1], p[2], p[3]);
158*c8dee2aaSAndroid Build Coastguard Worker } else if (w == 1) {
159*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(numPts == 3);
160*c8dee2aaSAndroid Build Coastguard Worker path.quadTo(p[1], p[2]);
161*c8dee2aaSAndroid Build Coastguard Worker } else {
162*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(numPts == 3);
163*c8dee2aaSAndroid Build Coastguard Worker path.conicTo(p[1], p[2], w);
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker canvas->drawPath(path, strokePaint);
166*c8dee2aaSAndroid Build Coastguard Worker }
167*c8dee2aaSAndroid Build Coastguard Worker }
168*c8dee2aaSAndroid Build Coastguard Worker
DEF_SIMPLE_GM(trickycubicstrokes,canvas,kTestWidth,kTestHeight)169*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(trickycubicstrokes, canvas, kTestWidth, kTestHeight) {
170*c8dee2aaSAndroid Build Coastguard Worker draw_test(canvas, SkPaint::kButt_Cap, SkPaint::kMiter_Join);
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker
DEF_SIMPLE_GM(trickycubicstrokes_roundcaps,canvas,kTestWidth,kTestHeight)173*c8dee2aaSAndroid Build Coastguard Worker DEF_SIMPLE_GM(trickycubicstrokes_roundcaps, canvas, kTestWidth, kTestHeight) {
174*c8dee2aaSAndroid Build Coastguard Worker draw_test(canvas, SkPaint::kRound_Cap, SkPaint::kRound_Join);
175*c8dee2aaSAndroid Build Coastguard Worker }
176