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