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