blob: a46413c9c06fbe382fe3320f7b740415bcd50657 [file] [log] [blame]
Chris Dalton09a7bb22018-08-31 19:53:15 +08001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040010#include "include/core/SkColor.h"
11#include "include/core/SkMatrix.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050012#include "include/core/SkPaint.h"
13#include "include/core/SkPath.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040014#include "include/core/SkPoint.h"
15#include "include/core/SkRect.h"
16#include "include/core/SkSize.h"
17#include "include/core/SkString.h"
18#include "include/core/SkTypes.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050019#include "src/core/SkGeometry.h"
Chris Dalton09a7bb22018-08-31 19:53:15 +080020
Chris Dalton71e21262020-08-20 09:42:12 -060021static constexpr float kStrokeWidth = 30;
Chris Dalton09a7bb22018-08-31 19:53:15 +080022static constexpr int kCellSize = 200;
Chris Dalton71e21262020-08-20 09:42:12 -060023static constexpr int kNumCols = 4;
24static constexpr int kNumRows = 4;
Chris Dalton09a7bb22018-08-31 19:53:15 +080025
Chris Dalton71e21262020-08-20 09:42:12 -060026enum class CellFillMode {
27 kStretch,
28 kCenter
29};
30
31struct TrickyCubic {
32 SkPoint fPoints[4];
33 CellFillMode fFillMode;
34 float fScale = 1;
35};
36
37static const TrickyCubic kTrickyCubics[] = {
38 {{{122, 737}, {348, 553}, {403, 761}, {400, 760}}, CellFillMode::kStretch},
39 {{{244, 520}, {244, 518}, {1141, 634}, {394, 688}}, CellFillMode::kStretch},
40 {{{550, 194}, {138, 130}, {1035, 246}, {288, 300}}, CellFillMode::kStretch},
41 {{{226, 733}, {556, 779}, {-43, 471}, {348, 683}}, CellFillMode::kStretch},
42 {{{268, 204}, {492, 304}, {352, 23}, {433, 412}}, CellFillMode::kStretch},
43 {{{172, 480}, {396, 580}, {256, 299}, {338, 677}}, CellFillMode::kStretch},
44 {{{731, 340}, {318, 252}, {1026, -64}, {367, 265}}, CellFillMode::kStretch},
45 {{{475, 708}, {62, 620}, {770, 304}, {220, 659}}, CellFillMode::kStretch},
46 {{{0, 0}, {128, 128}, {128, 0}, {0, 128}}, CellFillMode::kCenter}, // Perfect cusp
47 {{{0,.01f}, {128,127.999f}, {128,.01f}, {0,127.99f}}, CellFillMode::kCenter}, // Near-cusp
48 {{{0,-.01f}, {128,128.001f}, {128,-.01f}, {0,128.001f}}, CellFillMode::kCenter}, // Near-cusp
49 {{{0,0}, {0,-10}, {0,-10}, {0,10}}, CellFillMode::kCenter, 1.098283f}, // Flat line with 180
50 {{{10,0}, {0,0}, {20,0}, {10,0}}, CellFillMode::kStretch}, // Flat line with 2 180s
51 {{{39,-39}, {40,-40}, {40,-40}, {0,0}}, CellFillMode::kStretch}, // Flat diagonal with 180
52 {{{40, 40}, {0, 0}, {200, 200}, {0, 0}}, CellFillMode::kStretch}, // Diag with an internal 180
53 {{{0,0}, {1e-2f,0}, {-1e-2f,0}, {0,0}}, CellFillMode::kCenter}, // Circle
Chris Dalton09a7bb22018-08-31 19:53:15 +080054};
55
56static SkRect calc_tight_cubic_bounds(const SkPoint P[4], int depth=5) {
57 if (0 == depth) {
58 SkRect bounds;
Brian Osman788b9162020-02-07 10:36:46 -050059 bounds.fLeft = std::min(std::min(P[0].x(), P[1].x()), std::min(P[2].x(), P[3].x()));
60 bounds.fTop = std::min(std::min(P[0].y(), P[1].y()), std::min(P[2].y(), P[3].y()));
61 bounds.fRight = std::max(std::max(P[0].x(), P[1].x()), std::max(P[2].x(), P[3].x()));
62 bounds.fBottom = std::max(std::max(P[0].y(), P[1].y()), std::max(P[2].y(), P[3].y()));
Chris Dalton09a7bb22018-08-31 19:53:15 +080063 return bounds;
64 }
65
66 SkPoint chopped[7];
67 SkChopCubicAt(P, chopped, .5f);
68 SkRect bounds = calc_tight_cubic_bounds(chopped, depth - 1);
69 bounds.join(calc_tight_cubic_bounds(chopped+3, depth - 1));
70 return bounds;
71}
72
Chris Dalton71e21262020-08-20 09:42:12 -060073enum class FillMode {
74 kCenter,
75 kScale
76};
77
Chris Dalton09a7bb22018-08-31 19:53:15 +080078// This is a compilation of cubics that have given strokers grief. Feel free to add more.
79class TrickyCubicStrokesGM : public skiagm::GM {
80public:
81 TrickyCubicStrokesGM() {}
82
83protected:
84
85 SkString onShortName() override {
86 return SkString("trickycubicstrokes");
87 }
88
89 SkISize onISize() override {
Chris Dalton71e21262020-08-20 09:42:12 -060090 return SkISize::Make(kNumCols * kCellSize, kNumRows * kCellSize);
Chris Dalton09a7bb22018-08-31 19:53:15 +080091 }
92
93 void onOnceBeforeDraw() override {
94 fStrokePaint.setAntiAlias(true);
95 fStrokePaint.setStrokeWidth(kStrokeWidth);
96 fStrokePaint.setColor(SK_ColorGREEN);
97 fStrokePaint.setStyle(SkPaint::kStroke_Style);
98 }
99
100 void onDraw(SkCanvas* canvas) override {
101 canvas->clear(SK_ColorBLACK);
102
Chris Dalton71e21262020-08-20 09:42:12 -0600103 for (size_t i = 0; i < SK_ARRAY_COUNT(kTrickyCubics); ++i) {
104 const auto& trickyCubic = kTrickyCubics[i];
105 SkPoint p[7];
106 memcpy(p, trickyCubic.fPoints, sizeof(SkPoint) * 4);
107 for (int j = 0; j < 4; ++j) {
108 p[j] *= trickyCubic.fScale;
109 }
110 this->drawStroke(canvas, p, i, trickyCubic.fFillMode);
Chris Dalton09a7bb22018-08-31 19:53:15 +0800111 }
112 }
113
Chris Dalton71e21262020-08-20 09:42:12 -0600114 void drawStroke(SkCanvas* canvas, const SkPoint p[4], int cellID, CellFillMode fillMode) {
115 auto cellRect = SkRect::MakeXYWH((cellID % kNumCols) * kCellSize,
116 (cellID / kNumCols) * kCellSize,
117 kCellSize, kCellSize);
118
119 SkRect strokeBounds = calc_tight_cubic_bounds(p);
Chris Dalton09a7bb22018-08-31 19:53:15 +0800120 strokeBounds.outset(kStrokeWidth, kStrokeWidth);
121
122 SkMatrix matrix;
Chris Dalton71e21262020-08-20 09:42:12 -0600123 if (fillMode == CellFillMode::kStretch) {
124 matrix.setRectToRect(strokeBounds, cellRect, SkMatrix::kCenter_ScaleToFit);
125 } else {
126 matrix.setTranslate(cellRect.x() + kStrokeWidth +
127 (cellRect.width() - strokeBounds.width()) / 2,
128 cellRect.y() + kStrokeWidth +
129 (cellRect.height() - strokeBounds.height()) / 2);
130 }
Chris Dalton09a7bb22018-08-31 19:53:15 +0800131
132 SkAutoCanvasRestore acr(canvas, true);
133 canvas->concat(matrix);
Chris Dalton71e21262020-08-20 09:42:12 -0600134 fStrokePaint.setStrokeWidth(kStrokeWidth / matrix.getMaxScale());
135 canvas->drawPath(SkPath().moveTo(p[0]).cubicTo(p[1], p[2], p[3]), fStrokePaint);
Chris Dalton09a7bb22018-08-31 19:53:15 +0800136 }
137
138private:
139 SkPaint fStrokePaint;
John Stiles7571f9e2020-09-02 22:42:33 -0400140 using INHERITED = GM;
Chris Dalton09a7bb22018-08-31 19:53:15 +0800141};
142
143DEF_GM( return new TrickyCubicStrokesGM; )