1 /*
2 * Copyright 2012 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 "include/core/SkPaint.h"
9 #include "include/core/SkPath.h"
10 #include "include/core/SkPathUtils.h"
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkStrokeRec.h"
15 #include "src/base/SkFloatBits.h"
16 #include "src/core/SkPathPriv.h"
17 #include "tests/Test.h"
18
19 #include <array>
20 #include <cstddef>
21 #include <cstdint>
22
equal(const SkRect & a,const SkRect & b)23 static bool equal(const SkRect& a, const SkRect& b) {
24 return SkScalarNearlyEqual(a.left(), b.left()) &&
25 SkScalarNearlyEqual(a.top(), b.top()) &&
26 SkScalarNearlyEqual(a.right(), b.right()) &&
27 SkScalarNearlyEqual(a.bottom(), b.bottom());
28 }
29
test_strokecubic(skiatest::Reporter * reporter)30 static void test_strokecubic(skiatest::Reporter* reporter) {
31 uint32_t hexCubicVals[] = {
32 0x424c1086, 0x44bcf0cb, // fX=51.0161362 fY=1511.52478
33 0x424c107c, 0x44bcf0cb, // fX=51.0160980 fY=1511.52478
34 0x424c10c2, 0x44bcf0cb, // fX=51.0163651 fY=1511.52478
35 0x424c1119, 0x44bcf0ca, // fX=51.0166969 fY=1511.52466
36 };
37 SkPoint cubicVals[] = {
38 {51.0161362f, 1511.52478f },
39 {51.0160980f, 1511.52478f },
40 {51.0163651f, 1511.52478f },
41 {51.0166969f, 1511.52466f },
42 };
43 SkPaint paint;
44
45 paint.setStyle(SkPaint::kStroke_Style);
46 paint.setStrokeWidth(0.394537568f);
47 SkPath path, fillPath;
48 path.moveTo(cubicVals[0]);
49 path.cubicTo(cubicVals[1], cubicVals[2], cubicVals[3]);
50 skpathutils::FillPathWithPaint(path, paint, &fillPath);
51 path.reset();
52 path.moveTo(SkBits2Float(hexCubicVals[0]), SkBits2Float(hexCubicVals[1]));
53 path.cubicTo(SkBits2Float(hexCubicVals[2]), SkBits2Float(hexCubicVals[3]),
54 SkBits2Float(hexCubicVals[4]), SkBits2Float(hexCubicVals[5]),
55 SkBits2Float(hexCubicVals[6]), SkBits2Float(hexCubicVals[7]));
56 skpathutils::FillPathWithPaint(path, paint, &fillPath);
57 }
58
test_strokerect(skiatest::Reporter * reporter)59 static void test_strokerect(skiatest::Reporter* reporter) {
60 const SkScalar width = SkIntToScalar(10);
61 SkPaint paint;
62
63 paint.setStyle(SkPaint::kStroke_Style);
64 paint.setStrokeWidth(width);
65
66 SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(100) };
67
68 SkRect outer(r);
69 outer.outset(width/2, width/2);
70
71 static const SkPaint::Join joins[] = {
72 SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
73 };
74
75 for (size_t i = 0; i < std::size(joins); ++i) {
76 paint.setStrokeJoin(joins[i]);
77
78 SkPath path, fillPath;
79 path.addRect(r);
80 skpathutils::FillPathWithPaint(path, paint, &fillPath);
81
82 REPORTER_ASSERT(reporter, equal(outer, fillPath.getBounds()));
83
84 bool isMiter = SkPaint::kMiter_Join == joins[i];
85 SkRect nested[2];
86 REPORTER_ASSERT(reporter, SkPathPriv::IsNestedFillRects(fillPath, nested) == isMiter);
87 if (isMiter) {
88 SkRect inner(r);
89 inner.inset(width/2, width/2);
90 REPORTER_ASSERT(reporter, equal(nested[0], outer));
91 REPORTER_ASSERT(reporter, equal(nested[1], inner));
92 }
93 }
94 }
95
test_strokerec_equality(skiatest::Reporter * reporter)96 static void test_strokerec_equality(skiatest::Reporter* reporter) {
97 {
98 SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
99 SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
100 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
101
102 // Test that style mismatch is detected.
103 s2.setHairlineStyle();
104 REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
105
106 s1.setHairlineStyle();
107 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
108
109 // ResScale is not part of equality.
110 s1.setResScale(2.1f);
111 s2.setResScale(1.2f);
112 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
113 s1.setFillStyle();
114 s2.setFillStyle();
115 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
116 s1.setStrokeStyle(1.0f, false);
117 s2.setStrokeStyle(1.0f, false);
118 s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
119 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
120 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
121 }
122
123 // Stroke parameters on fill or hairline style are not part of equality.
124 {
125 SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
126 SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
127 for (int i = 0; i < 2; ++i) {
128 s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
129 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
130 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
131 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
132 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
133 s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
134 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
135 s1.setHairlineStyle();
136 s2.setHairlineStyle();
137 }
138 }
139
140 // Stroke parameters on stroke style are part of equality.
141 {
142 SkStrokeRec s1(SkStrokeRec::kFill_InitStyle);
143 SkStrokeRec s2(SkStrokeRec::kFill_InitStyle);
144 s1.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
145 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.9f);
146 s1.setStrokeStyle(1.0f, false);
147
148 s2.setStrokeStyle(1.0f, true);
149 REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
150
151 s2.setStrokeStyle(2.1f, false);
152 REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
153
154 s2.setStrokeStyle(1.0f, false);
155 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
156
157 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kRound_Join, 2.1f);
158 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2)); // Miter limit not relevant to butt caps.
159 s2.setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 2.9f);
160 REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
161 s2.setStrokeParams(SkPaint::kRound_Cap, SkPaint::kRound_Join, 2.9f);
162 REPORTER_ASSERT(reporter, !s1.hasEqualEffect(s2));
163
164 // Sets fill.
165 s1.setStrokeStyle(0.0f, true);
166 s2.setStrokeStyle(0.0f, true);
167 REPORTER_ASSERT(reporter, s1.hasEqualEffect(s2));
168 }
169 }
170
171 // From skbug.com/6491. The large stroke width can cause numerical instabilities.
test_big_stroke(skiatest::Reporter * reporter)172 static void test_big_stroke(skiatest::Reporter* reporter) {
173 SkPaint paint;
174 paint.setStyle(SkPaint::kStrokeAndFill_Style);
175 paint.setStrokeWidth(1.49679073e+10f);
176
177 SkPath path;
178 path.moveTo(SkBits2Float(0x46380000), SkBits2Float(0xc6380000)); // 11776, -11776
179 path.lineTo(SkBits2Float(0x46a00000), SkBits2Float(0xc6a00000)); // 20480, -20480
180 path.lineTo(SkBits2Float(0x468c0000), SkBits2Float(0xc68c0000)); // 17920, -17920
181 path.lineTo(SkBits2Float(0x46100000), SkBits2Float(0xc6100000)); // 9216, -9216
182 path.lineTo(SkBits2Float(0x46380000), SkBits2Float(0xc6380000)); // 11776, -11776
183 path.close();
184
185 SkPath strokeAndFillPath;
186 skpathutils::FillPathWithPaint(path, paint, &strokeAndFillPath);
187 }
188
DEF_TEST(Stroke,reporter)189 DEF_TEST(Stroke, reporter) {
190 test_strokecubic(reporter);
191 test_strokerect(reporter);
192 test_strokerec_equality(reporter);
193 test_big_stroke(reporter);
194 }
195