xref: /aosp_15_r20/external/skia/tests/StrokeTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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