Bug Summary

File:root/firefox-clang/gfx/skia/skia/include/private/base/SkPoint_impl.h
Warning:line 433, column 21
The left operand of '==' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SkPath.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_SKIA -D SKIA_IMPLEMENTATION=1 -D SK_PDF_USE_HARFBUZZ_SUBSET=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/skia -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -I /root/firefox-clang/gfx/skia/skia -I /root/firefox-clang/gfx/harfbuzz/src -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pixman-1 -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-deprecated-declarations -Wno-overloaded-virtual -Wno-sign-compare -Wno-unreachable-code -Wno-unused-function -Wno-implicit-fallthrough -Wno-inconsistent-missing-override -Wno-macro-redefined -Wno-unused-private-field -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp

/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp

1/*
2 * Copyright 2006 The Android Open Source Project
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/SkPath.h"
9#include "include/core/SkPathBuilder.h"
10#include "include/core/SkPathTypes.h"
11#include "include/core/SkRRect.h"
12#include "include/core/SkSpan.h"
13#include "include/private/base/SkMalloc.h"
14#include "include/private/base/SkTArray.h"
15#include "include/private/base/SkTo.h"
16#include "src/base/SkFloatBits.h"
17#include "src/core/SkEdgeClipper.h"
18#include "src/core/SkGeometry.h"
19#include "src/core/SkPathEnums.h"
20#include "src/core/SkPathPriv.h"
21#include "src/core/SkPathRawShapes.h"
22#include "src/core/SkPointPriv.h"
23#include "src/core/SkSpanPriv.h"
24
25#include <algorithm>
26#include <cmath>
27#include <cstring>
28#include <limits.h>
29#include <utility>
30
31SkPath::SkPath(sk_sp<SkPathData> pd, SkPathFillType ft, bool isVolatile)
32 : fPathData(std::move(pd))
33 , fFillType(ft)
34 , fIsVolatile(isVolatile)
35{
36 SkASSERT(fPathData)static_cast<void>( __builtin_expect(static_cast<bool
>(fPathData), 1) ? static_cast<void>(0) : []{ do { if
(sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 36, "fPathData"); ; sk_abort_no_print(); } while (false); }
} while(false); }() )
;
37}
38
39SkPath::SkPath(SkPathFillType ft)
40 : fPathData(SkPathData::Empty())
41 , fFillType(ft)
42 , fIsVolatile(false)
43{}
44
45SkPath::SkPath(const SkPath&) = default;
46SkPath& SkPath::operator=(const SkPath&) = default;
47SkPath::SkPath(SkPath&&) = default;
48SkPath& SkPath::operator=(SkPath&&) = default;
49
50SkPath::~SkPath() {
51 SkDEBUGCODE(this->validate();)this->validate();
52}
53
54void SkPath::setConvexity(SkPathConvexity c) const {
55 fPathData->setConvexity(c);
56}
57
58SkPathConvexity SkPath::getConvexityOrUnknown() const {
59 return fPathData->getConvexityOrUnknown();
60}
61
62bool operator==(const SkPath& a, const SkPath& b) {
63 return &a == &b ||
64 (a.fFillType == b.fFillType && *a.fPathData == *b.fPathData);
65}
66
67void SkPath::swap(SkPath& that) {
68 if (this != &that) {
69 fPathData.swap(that.fPathData);
70 std::swap(fFillType, that.fFillType);
71 std::swap(fIsVolatile, that.fIsVolatile);
72 }
73}
74
75SkPath& SkPath::reset() {
76 *this = SkPath();
77 return *this;
78}
79
80const SkRect& SkPath::getBounds() const {
81 return fPathData->bounds();
82}
83
84uint32_t SkPath::getSegmentMasks() const {
85 return fPathData->segmentMask();
86}
87
88bool SkPath::isFinite() const {
89 return fPathData.get() != PeekErrorSingleton();
90}
91
92bool SkPath::isValid() const { return this->isFinite(); }
93
94uint32_t SkPath::getGenerationID() const { return fPathData->uniqueID(); }
95
96#ifdef SK_DEBUG
97void SkPath::validate() const {}
98#endif
99
100std::optional<SkPathOvalInfo> SkPath::getOvalInfo() const { return fPathData->asOval(); }
101std::optional<SkPathRRectInfo> SkPath::getRRectInfo() const { return fPathData->asRRect(); }
102
103SkSpan<const SkPoint> SkPath::points() const { return fPathData->points(); }
104SkSpan<const SkPathVerb> SkPath::verbs() const { return fPathData->verbs(); }
105SkSpan<const float> SkPath::conicWeights() const { return fPathData->conics(); }
106
107SkPath SkPath::Raw(SkSpan<const SkPoint> pts, SkSpan<const SkPathVerb> vbs,
108 SkSpan<const float> ws, SkPathFillType ft, bool isVolatile) {
109 return MakeNullCheck(SkPathData::Make(pts, vbs, ws), ft, isVolatile);
110}
111
112SkPath SkPath::Rect(const SkRect& r, SkPathFillType ft, SkPathDirection dir, unsigned startIndex) {
113 startIndex &= 3; // keep it legal
114 return MakeNullCheck(SkPathData::Rect(r, dir, startIndex), ft, false);
115}
116
117SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir, unsigned startIndex) {
118 startIndex &= 3; // keep it legal
119 return MakeNullCheck(SkPathData::Oval(r, dir, startIndex), SkPathFillType::kDefault, false);
120}
121
122SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir, unsigned startIndex) {
123 startIndex &= 7; // keep it legal
124 // To be backwards compatible with the old impl for building a rrect path, we
125 // first check to see if the rrect itself can be simplified...
126 const SkRect& bounds = rr.getBounds();
127 auto [asType, newIndex] = SkPathPriv::SimplifyRRect(rr, startIndex);
128 switch (asType) {
129 case SkPathPriv::RRectAsEnum::kRect:
130 return SkPath::Rect(bounds, SkPathFillType::kDefault, dir, newIndex);
131
132 case SkPathPriv::RRectAsEnum::kOval:
133 return SkPath::Oval(bounds, dir, newIndex);
134
135 case SkPathPriv::RRectAsEnum::kRRect:
136 // fall through
137 break;
138 }
139 return MakeNullCheck(SkPathData::RRect(rr, dir, newIndex), SkPathFillType::kDefault, false);
140}
141
142SkPath SkPath::Polygon(SkSpan<const SkPoint> pts, bool isClosed,
143 SkPathFillType ft, bool isVolatile) {
144 return MakeNullCheck(SkPathData::Polygon(pts, isClosed), ft, isVolatile);
145}
146
147std::optional<SkPath> SkPath::tryMakeTransform(const SkMatrix& matrix) const {
148 if (auto pdata = fPathData->makeTransform(matrix)) {
149 return SkPath(std::move(pdata), fFillType, fIsVolatile);
150 }
151 return {};
152}
153
154SkPath SkPath::makeTransform(const SkMatrix& matrix) const {
155 if (!this->isFinite()) {
156 return *this;
157 }
158 if (auto newpath = this->tryMakeTransform(matrix)) {
159 return *newpath;
160 }
161 return SkPath(sk_ref_sp(PeekErrorSingleton()), fFillType, false);
162}
163
164std::optional<SkPathRaw> SkPath::raw(SkResolveConvexity rc) const {
165 return fPathData->raw(fFillType, rc);
166}
167
168static inline bool check_edge_against_rect(const SkPoint& p0,
169 const SkPoint& p1,
170 const SkRect& rect,
171 SkPathDirection dir) {
172 const SkPoint* edgeBegin;
173 SkVector v;
174 if (SkPathDirection::kCW == dir) {
175 v = p1 - p0;
176 edgeBegin = &p0;
177 } else {
178 v = p0 - p1;
179 edgeBegin = &p1;
180 }
181 if (v.fX || v.fY) {
182 // check the cross product of v with the vec from edgeBegin to each rect corner
183 SkScalar yL = v.fY * (rect.fLeft - edgeBegin->fX);
184 SkScalar xT = v.fX * (rect.fTop - edgeBegin->fY);
185 SkScalar yR = v.fY * (rect.fRight - edgeBegin->fX);
186 SkScalar xB = v.fX * (rect.fBottom - edgeBegin->fY);
187 if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
188 return false;
189 }
190 }
191 return true;
192}
193
194bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
195 const SkPathConvexity convexity = this->getConvexity();
196 if (!SkPathConvexity_IsConvex(convexity)) {
1
Taking false branch
197 return false;
198 }
199
200 const auto direction = SkPathConvexity_ToDirection(convexity);
201 if (!direction) {
2
Taking false branch
202 return false;
203 }
204
205 SkPoint firstPt;
206 SkPoint prevPt;
207 int segmentCount = 0;
208 SkDEBUGCODE(int moveCnt = 0;)int moveCnt = 0;
209
210 for (auto [verb, pts, weight] : SkPathPriv::Iterate(*this)) {
211 if (verb
2.1
Verb is not equal to kClose
2.1
Verb is not equal to kClose
== SkPathVerb::kClose || (segmentCount
2.2
'segmentCount' is <= 0
2.2
'segmentCount' is <= 0
> 0 && verb == SkPathVerb::kMove)) {
212 // Closing the current contour; but since convexity is a precondition, it's the only
213 // contour that matters.
214 SkASSERT(moveCnt)static_cast<void>( __builtin_expect(static_cast<bool
>(moveCnt), 1) ? static_cast<void>(0) : []{ do { if (
sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 214, "moveCnt"); ; sk_abort_no_print(); } while (false); } }
while(false); }() )
;
215 segmentCount++;
216 break;
217 } else if (verb
2.3
Verb is not equal to kMove
2.3
Verb is not equal to kMove
== SkPathVerb::kMove) {
3
Taking false branch
218 // A move at the start of the contour (or multiple leading moves, in which case we
219 // keep the last one before a non-move verb).
220 SkASSERT(!segmentCount)static_cast<void>( __builtin_expect(static_cast<bool
>(!segmentCount), 1) ? static_cast<void>(0) : []{ do
{ if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 220, "!segmentCount"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
221 SkDEBUGCODE(++moveCnt)++moveCnt;
222 firstPt = prevPt = pts[0];
223 } else {
224 int pointCount = SkPathPriv::PtsInVerb((unsigned) verb);
225 SkASSERT(pointCount > 0)static_cast<void>( __builtin_expect(static_cast<bool
>(pointCount > 0), 1) ? static_cast<void>(0) : []
{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 225, "pointCount > 0"); ; sk_abort_no_print(); } while (
false); } } while(false); }() )
;
4
'?' condition is true
226
227 if (!SkPathPriv::AllPointsEq({pts, (size_t)pointCount + 1})) {
5
Assuming the condition is true
6
Taking true branch
228 SkASSERT(moveCnt)static_cast<void>( __builtin_expect(static_cast<bool
>(moveCnt), 1) ? static_cast<void>(0) : []{ do { if (
sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 228, "moveCnt"); ; sk_abort_no_print(); } while (false); } }
while(false); }() )
;
7
'?' condition is false
229 int nextPt = pointCount;
230 segmentCount++;
231
232 if (prevPt == pts[nextPt]) {
8
Calling 'operator=='
233 // A pre-condition to getting here is that the path is convex, so if a
234 // verb's start and end points are the same, it means it's the only
235 // verb in the contour (and the only contour). While it's possible for
236 // such a single verb to be a convex curve, we do not have any non-zero
237 // length edges to conservatively test against without splitting or
238 // evaluating the curve. For simplicity, just reject the rectangle.
239 return false;
240 } else if (SkPathVerb::kConic == verb) {
241 SkConic orig;
242 orig.set(pts, *weight);
243 SkPoint quadPts[5];
244 int count = orig.chopIntoQuadsPOW2(quadPts, 1);
245 SkASSERT_RELEASE(2 == count)static_cast<void>( __builtin_expect(static_cast<bool
>(2 == count), 1) ? static_cast<void>(0) : []{ do { if
(sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 245, "2 == count"); ; sk_abort_no_print(); } while (false);
} } while(false); }() )
;
246
247 if (!check_edge_against_rect(quadPts[0], quadPts[2], rect, *direction)) {
248 return false;
249 }
250 if (!check_edge_against_rect(quadPts[2], quadPts[4], rect, *direction)) {
251 return false;
252 }
253 } else {
254 if (!check_edge_against_rect(prevPt, pts[nextPt], rect, *direction)) {
255 return false;
256 }
257 }
258 prevPt = pts[nextPt];
259 }
260 }
261 }
262
263 if (segmentCount) {
264 return check_edge_against_rect(prevPt, firstPt, rect, *direction);
265 }
266 return false;
267}
268
269bool SkPath::isLastContourClosed() const {
270 SkSpan<const SkPathVerb> verbs = this->verbs();
271 return !verbs.empty() && verbs.back() == SkPathVerb::kClose;
272}
273
274bool SkPath::isLine(SkPoint line[2]) const {
275 SkSpan<const SkPathVerb> verbs = this->verbs();
276 if (verbs.size() == 2 && verbs[1] == SkPathVerb::kLine) {
277 SkASSERT(verbs[0] == SkPathVerb::kMove)static_cast<void>( __builtin_expect(static_cast<bool
>(verbs[0] == SkPathVerb::kMove), 1) ? static_cast<void
>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 277, "verbs[0] == SkPathVerb::kMove"); ; sk_abort_no_print(
); } while (false); } } while(false); }() )
;
278 SkSpan<const SkPoint> pts = this->points();
279 SkASSERT(pts.size() == 2)static_cast<void>( __builtin_expect(static_cast<bool
>(pts.size() == 2), 1) ? static_cast<void>(0) : []{ do
{ if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 279, "pts.size() == 2"); ; sk_abort_no_print(); } while (false
); } } while(false); }() )
;
280 if (line) {
281 line[0] = pts[0];
282 line[1] = pts[1];
283 }
284 return true;
285 }
286 return false;
287}
288
289bool SkPath::isEmpty() const {
290 SkDEBUGCODE(this->validate();)this->validate();
291 return this->verbs().empty();
292}
293
294bool SkPath::isConvex() const {
295 return SkPathConvexity_IsConvex(this->getConvexity());
296}
297
298bool SkPath::isRect(SkRect* rect, bool* isClosed, SkPathDirection* direction) const {
299 SkDEBUGCODE(this->validate();)this->validate();
300 SkSpan<const SkPoint> pts = this->points();
301 SkSpan<const SkPathVerb> vbs = this->verbs();
302 if (auto rc = SkPathPriv::IsRectContour(pts, vbs, this->getSegmentMasks(), false)) {
303 if (rect) {
304 *rect = rc->fRect;
305 }
306 if (isClosed) {
307 *isClosed = rc->fIsClosed;
308 }
309 if (direction) {
310 *direction = rc->fDirection;
311 }
312 return true;
313 }
314 return false;
315}
316
317bool SkPath::isOval(SkRect* bounds) const {
318 if (auto info = this->getOvalInfo()) {
319 if (bounds) {
320 *bounds = info->fBounds;
321 }
322 return true;
323 }
324 return false;
325}
326
327bool SkPath::isRRect(SkRRect* rrect) const {
328 if (auto info = this->getRRectInfo()) {
329 if (rrect) {
330 *rrect = info->fRRect;
331 }
332 return true;
333 }
334 return false;
335}
336
337#ifdef SK_LEGACY_PATH_ACCESSORS
338size_t SkPath::getPoints(SkSpan<SkPoint> dst) const {
339 SkDEBUGCODE(this->validate();)this->validate();
340 SkSpan<const SkPoint> src = this->points();
341
342 const size_t n = std::min(dst.size(), src.size());
343 sk_careful_memcpy(dst.data(), src.data(), n * sizeof(SkPoint));
344 return src.size();
345}
346
347SkPoint SkPath::getPoint(int index) const {
348 SkSpan<const SkPoint> pts = this->points();
349 if ((unsigned)index < (unsigned)pts.size()) {
350 return pts[index];
351 }
352 return SkPoint::Make(0, 0);
353}
354
355size_t SkPath::getVerbs(SkSpan<uint8_t> dst) const {
356 SkDEBUGCODE(this->validate();)this->validate();
357 SkSpan<const SkPathVerb> src = this->verbs();
358
359 const size_t n = std::min(dst.size(), src.size());
360 sk_careful_memcpy(dst.data(), src.data(), n);
361 return src.size();
362}
363#endif
364
365size_t SkPath::approximateBytesUsed() const {
366 return sizeof(SkPath)
367 + this->points().size_bytes()
368 + this->verbs().size_bytes()
369 + this->conicWeights().size_bytes();
370}
371
372std::optional<SkPoint> SkPath::getLastPt() const {
373 SkDEBUGCODE(this->validate();)this->validate();
374 SkSpan<const SkPoint> pts = this->points();
375 if (!pts.empty()) {
376 return pts.back();
377 }
378 return {};
379}
380
381SkPathConvexity SkPath::getConvexity() const {
382// Enable once we fix all the bugs
383// SkDEBUGCODE(this->isConvexityAccurate());
384 SkPathConvexity convexity = this->getConvexityOrUnknown();
385 if (convexity == SkPathConvexity::kUnknown) {
386 convexity = this->computeConvexity();
387 }
388 SkASSERT(convexity != SkPathConvexity::kUnknown)static_cast<void>( __builtin_expect(static_cast<bool
>(convexity != SkPathConvexity::kUnknown), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 388, "convexity != SkPathConvexity::kUnknown"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
389 return convexity;
390}
391
392SkPathIter SkPath::iter() const {
393 return { this->points(), this->verbs(), this->conicWeights() };
394}
395
396///////////////////////////////////////////////////////////////////////////////
397
398SkPath::Iter::Iter() {
399#ifdef SK_DEBUG
400 fPts = nullptr;
401 fConicWeights = nullptr;
402 fMoveTo.fX = fMoveTo.fY = fLastPt.fX = fLastPt.fY = 0;
403 fForceClose = fCloseLine = false;
404#endif
405 // need to init enough to make next() harmlessly return kDone_Verb
406 fVerbs = nullptr;
407 fVerbStop = nullptr;
408 fNeedClose = false;
409}
410
411SkPath::Iter::Iter(const SkPath& path, bool forceClose) {
412 this->setPath(path, forceClose);
413}
414
415void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
416 fPts = path.points().data();
417
418 const SkSpan<const SkPathVerb> vbs = path.verbs();
419 fVerbs = vbs.begin();
420 fVerbStop = vbs.end();
421 // For empty paths we receive an empty span, which should yield a nullptr fVerbsStop.
422 SkASSERT(!!fVerbs == !!fVerbStop)static_cast<void>( __builtin_expect(static_cast<bool
>(!!fVerbs == !!fVerbStop), 1) ? static_cast<void>(0
) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 422, "!!fVerbs == !!fVerbStop"); ; sk_abort_no_print(); } while
(false); } } while(false); }() )
;
423
424 fConicWeights = path.conicWeights().data();
425 if (fConicWeights) {
426 fConicWeights -= 1; // begin one behind
427 }
428 fLastPt.fX = fLastPt.fY = 0;
429 fMoveTo.fX = fMoveTo.fY = 0;
430 fForceClose = SkToU8(forceClose);
431 fNeedClose = false;
432}
433
434bool SkPath::Iter::isClosedContour() const {
435 if (fVerbs == nullptr || fVerbs == fVerbStop) {
436 return false;
437 }
438 if (fForceClose) {
439 return true;
440 }
441
442 const SkPathVerb* verbs = fVerbs;
443 const SkPathVerb* stop = fVerbStop;
444
445 if (SkPathVerb::kMove == *verbs) {
446 verbs += 1; // skip the initial moveto
447 }
448
449 while (verbs < stop) {
450 // verbs points one beyond the current verb, decrement first.
451 SkPathVerb v = *verbs++;
452 if (SkPathVerb::kMove == v) {
453 break;
454 }
455 if (SkPathVerb::kClose == v) {
456 return true;
457 }
458 }
459 return false;
460}
461
462SkPathVerb SkPath::Iter::autoClose(SkPoint pts[2]) {
463 SkASSERT(pts)static_cast<void>( __builtin_expect(static_cast<bool
>(pts), 1) ? static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp", 463
, "pts"); ; sk_abort_no_print(); } while (false); } } while(false
); }() )
;
464 if (fLastPt != fMoveTo) {
465 // A special case: if both points are NaN, SkPoint::operation== returns
466 // false, but the iterator expects that they are treated as the same.
467 // (consider SkPoint is a 2-dimension float point).
468 if (SkIsNaN(fLastPt.fX) || SkIsNaN(fLastPt.fY) ||
469 SkIsNaN(fMoveTo.fX) || SkIsNaN(fMoveTo.fY)) {
470 return SkPathVerb::kClose;
471 }
472
473 pts[0] = fLastPt;
474 pts[1] = fMoveTo;
475 fLastPt = fMoveTo;
476 fCloseLine = true;
477 return SkPathVerb::kLine;
478 }
479 pts[0] = fMoveTo;
480 return SkPathVerb::kClose;
481}
482
483SkPath::Verb SkPath::Iter::next(SkPoint ptsParam[4]) {
484 SkASSERT(ptsParam)static_cast<void>( __builtin_expect(static_cast<bool
>(ptsParam), 1) ? static_cast<void>(0) : []{ do { if
(sk_abort_is_enabled()) { do { SkDebugf("%s:%d" ": fatal error: \""
"check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 484, "ptsParam"); ; sk_abort_no_print(); } while (false); }
} while(false); }() )
;
485 SkASSERT(!!fVerbs == !!fVerbStop)static_cast<void>( __builtin_expect(static_cast<bool
>(!!fVerbs == !!fVerbStop), 1) ? static_cast<void>(0
) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf("%s:%d"
": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 485, "!!fVerbs == !!fVerbStop"); ; sk_abort_no_print(); } while
(false); } } while(false); }() )
;
486
487 if (fVerbs == fVerbStop) {
488 // Close the curve if requested and if there is some curve to close
489 if (fNeedClose) {
490 if (SkPathVerb::kLine == this->autoClose(ptsParam)) {
491 return kLine_Verb;
492 }
493 fNeedClose = false;
494 return kClose_Verb;
495 }
496 return kDone_Verb;
497 }
498
499 SkPathVerb verb = *fVerbs++;
500 const SkPoint* SK_RESTRICT__restrict__ srcPts = fPts;
501 SkPoint* SK_RESTRICT__restrict__ pts = ptsParam;
502
503 switch (verb) {
504 case SkPathVerb::kMove:
505 if (fNeedClose) {
506 fVerbs--; // move back one verb
507 verb = this->autoClose(pts);
508 if (verb == SkPathVerb::kClose) {
509 fNeedClose = false;
510 }
511 return (Verb)verb;
512 }
513 if (fVerbs == fVerbStop) { // might be a trailing moveto
514 return kDone_Verb;
515 }
516 fMoveTo = *srcPts;
517 pts[0] = *srcPts;
518 srcPts += 1;
519 fLastPt = fMoveTo;
520 fNeedClose = fForceClose;
521 break;
522 case SkPathVerb::kLine:
523 pts[0] = fLastPt;
524 pts[1] = srcPts[0];
525 fLastPt = srcPts[0];
526 fCloseLine = false;
527 srcPts += 1;
528 break;
529 case SkPathVerb::kConic:
530 fConicWeights += 1;
531 [[fallthrough]];
532 case SkPathVerb::kQuad:
533 pts[0] = fLastPt;
534 memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
535 fLastPt = srcPts[1];
536 srcPts += 2;
537 break;
538 case SkPathVerb::kCubic:
539 pts[0] = fLastPt;
540 memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
541 fLastPt = srcPts[2];
542 srcPts += 3;
543 break;
544 case SkPathVerb::kClose:
545 verb = this->autoClose(pts);
546 if (verb == SkPathVerb::kLine) {
547 fVerbs--; // move back one verb
548 } else {
549 fNeedClose = false;
550 }
551 fLastPt = fMoveTo;
552 break;
553 }
554 fPts = srcPts;
555 return (Verb)verb;
556}
557
558static inline uint8_t SkPathIterPointsPerVerb(SkPathVerb verb) {
559 static const uint8_t gCounts[] = { 1, 2, 3, 3, 4, 0 };
560 unsigned index = static_cast<unsigned>(verb);
561 SkASSERT(index < std::size(gCounts))static_cast<void>( __builtin_expect(static_cast<bool
>(index < std::size(gCounts)), 1) ? static_cast<void
>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 561, "index < std::size(gCounts)"); ; sk_abort_no_print(
); } while (false); } } while(false); }() )
;
562 return gCounts[index];
563}
564
565std::optional<SkPath::IterRec> SkPath::Iter::next() {
566 auto legacyVerb = this->next(fStorage.data());
567 if (legacyVerb == kDone_Verb) {
568 return {};
569 }
570
571 SkPathVerb verb = static_cast<SkPathVerb>(legacyVerb);
572 return {{
573 verb,
574 {fStorage.data(), SkPathIterPointsPerVerb(verb)},
575 verb == SkPathVerb::kConic ? *fConicWeights : 1,
576 }};
577}
578
579void SkPath::RawIter::setPath(const SkPath& path) {
580 SkPathPriv::Iterate iterate(path);
581 fIter = iterate.begin();
582 fEnd = iterate.end();
583}
584
585SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
586 if (!(fIter != fEnd)) {
587 return kDone_Verb;
588 }
589 auto [verb, iterPts, weights] = *fIter;
590 int numPts;
591 switch (verb) {
592 case SkPathVerb::kMove: numPts = 1; break;
593 case SkPathVerb::kLine: numPts = 2; break;
594 case SkPathVerb::kQuad: numPts = 3; break;
595 case SkPathVerb::kConic:
596 numPts = 3;
597 fConicWeight = *weights;
598 break;
599 case SkPathVerb::kCubic: numPts = 4; break;
600 case SkPathVerb::kClose: numPts = 0; break;
601 }
602 memcpy(pts, iterPts, sizeof(SkPoint) * numPts);
603 ++fIter;
604 return (Verb) verb;
605}
606
607std::optional<SkPath::IterRec> SkPath::RawIter::next() {
608 if (fIter == fEnd) {
609 return {};
610 }
611
612 auto [verb, iterPts, weights] = *fIter++;
613 return {{
614 verb,
615 {iterPts, SkPathIterPointsPerVerb(verb)},
616 verb == SkPathVerb::kConic ? *weights : 1
617 }};
618}
619
620///////////////////////////////////////////////////////////////////////////////
621
622SkPath SkPath::makeFillType(SkPathFillType ft) const {
623 SkPath copy = *this;
624 copy.setFillType(ft);
625 return copy;
626}
627
628SkPath SkPath::makeToggleInverseFillType() const {
629 return this->makeFillType(SkPathFillType_ToggleInverse(fFillType));
630}
631
632SkPath SkPath::makeIsVolatile(bool v) const {
633 SkPath copy = *this;
634 copy.fIsVolatile = v;
635 return copy;
636}
637
638SkPathConvexity SkPath::computeConvexity() const {
639 if (auto c = this->getConvexityOrUnknown(); c != SkPathConvexity::kUnknown) {
640 return c;
641 }
642
643 SkPathConvexity convexity = SkPathConvexity::kConcave;
644
645 if (this->isFinite()) {
646 convexity = SkPathPriv::ComputeConvexity(this->points(),
647 this->verbs(),
648 this->conicWeights());
649 }
650
651 SkASSERT(convexity != SkPathConvexity::kUnknown)static_cast<void>( __builtin_expect(static_cast<bool
>(convexity != SkPathConvexity::kUnknown), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 651, "convexity != SkPathConvexity::kUnknown"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
652 this->setConvexity(convexity);
653 return convexity;
654}
655
656bool SkPath::contains(SkPoint p) const {
657 const auto raw = SkPathPriv::Raw(*this, SkResolveConvexity::kNo);
658 return raw.has_value() && SkPathPriv::Contains(*raw, p);
659}
660
661int SkPath::ConvertConicToQuads(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2,
662 SkScalar w, SkPoint pts[], int pow2) {
663 const SkConic conic(p0, p1, p2, w);
664 return conic.chopIntoQuadsPOW2(pts, pow2);
665}
666
667///////////////////////////////////////////////////////////////////////////////////////////////////
668
669SkRect SkPath::computeTightBounds() const {
670 // If we're only lines, then our (quick) bounds is also tight.
671 if (this->getSegmentMasks() == SkPath::kLine_SegmentMask) {
672 return this->getBounds();
673 }
674
675 return SkPathPriv::ComputeTightBounds(this->points(),
676 this->verbs(),
677 this->conicWeights());
678}
679
680bool SkPath::IsLineDegenerate(const SkPoint& p1, const SkPoint& p2, bool exact) {
681 return exact ? p1 == p2 : SkPointPriv::EqualsWithinTolerance(p1, p2);
682}
683
684bool SkPath::IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
685 const SkPoint& p3, bool exact) {
686 return exact ? p1 == p2 && p2 == p3 : SkPointPriv::EqualsWithinTolerance(p1, p2) &&
687 SkPointPriv::EqualsWithinTolerance(p2, p3);
688}
689
690bool SkPath::IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
691 const SkPoint& p3, const SkPoint& p4, bool exact) {
692 return exact ? p1 == p2 && p2 == p3 && p3 == p4 :
693 SkPointPriv::EqualsWithinTolerance(p1, p2) &&
694 SkPointPriv::EqualsWithinTolerance(p2, p3) &&
695 SkPointPriv::EqualsWithinTolerance(p3, p4);
696}
697
698SkPath SkPath::RRect(const SkRRect& rr, SkPathDirection dir) {
699 // legacy start indices: 6 (CW) and 7 (CCW)
700 return RRect(rr, dir, dir == SkPathDirection::kCW ? 6 : 7);
701}
702
703SkPath SkPath::Oval(const SkRect& r, SkPathDirection dir) {
704 // legacy start index: 1
705 return Oval(r, dir, 1);
706}
707
708SkPath SkPath::Circle(SkScalar x, SkScalar y, SkScalar r, SkPathDirection dir) {
709 if (r >= 0) {
710 return Oval(SkRect::MakeLTRB(x - r, y - r, x + r, y + r), dir);
711 } else {
712 return SkPath();
713 }
714}
715
716SkPath SkPath::RRect(const SkRect& r, SkScalar rx, SkScalar ry, SkPathDirection dir) {
717 return RRect(SkRRect::MakeRectXY(r, rx, ry), dir);
718}
719
720SkPathFirstDirection SkPathPriv::ComputeFirstDirection(const SkPath& path) {
721 auto convexity = path.getConvexityOrUnknown();
722 if (SkPathConvexity_IsConvex(convexity)) {
723 // Note, this can return kUnknown. That is valid. If we've determined that the
724 // path is convex, then we've already tried to compute its first-direction. If
725 // that failed, then kUnknown is the right answer.
726 return SkPathConvexity_ToFirstDirection(convexity);
727 }
728
729 // Note, this can compute a 'first' direction, even for non-convex shapes.
730 if (auto raw = SkPathPriv::Raw(path, SkResolveConvexity::kNo)) {
731 return ComputeFirstDirection(*raw);
732 } else {
733 return SkPathFirstDirection::kUnknown;
734 }
735}
736
737/*
738 * This returns a singleton instance which SkPath uses to signify that its pathdata is in error:
739 * either because the inputs were invalid (e.g. bad verbs), or its coordintes were non-finite
740 * (either from the client, or after a makeTransform() call).
741 */
742SkPathData* SkPath::PeekErrorSingleton() {
743 static SkPathData* gErrorSingleton = SkPathData::MakeNoCheck({}, {}, {}, {}, {}).release();
744
745 // Make sure MakeNoCheck() didn't alias us to the standard Empty instance. We want our
746 // pointer to be distinct from that one.
747 SkASSERT(gErrorSingleton != SkPathData::Empty().get())static_cast<void>( __builtin_expect(static_cast<bool
>(gErrorSingleton != SkPathData::Empty().get()), 1) ? static_cast
<void>(0) : []{ do { if (sk_abort_is_enabled()) { do { SkDebugf
("%s:%d" ": fatal error: \"" "check(%s)" "\"\n", "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp"
, 747, "gErrorSingleton != SkPathData::Empty().get()"); ; sk_abort_no_print
(); } while (false); } } while(false); }() )
;
748
749 return gErrorSingleton;
750}
751
752SkPath SkPath::MakeNullCheck(sk_sp<SkPathData> pdata, SkPathFillType ft, bool isVolatile) {
753 if (!pdata) {
754 pdata = sk_ref_sp(PeekErrorSingleton());
755 }
756 return SkPath(std::move(pdata), ft, isVolatile);
757}
758
759///////////////////////////////////////////////////////////////////////////////////////////////////
760
761struct SkHalfPlane {
762 SkScalar fA, fB, fC;
763
764 SkScalar eval(SkScalar x, SkScalar y) const {
765 return fA * x + fB * y + fC;
766 }
767 SkScalar operator()(SkScalar x, SkScalar y) const { return this->eval(x, y); }
768
769 bool normalize() {
770 double a = fA;
771 double b = fB;
772 double c = fC;
773 double dmag = sqrt(a * a + b * b);
774 // length of initial plane normal is zero
775 if (dmag == 0) {
776 fA = fB = 0;
777 fC = SK_Scalar11.0f;
778 return true;
779 }
780 double dscale = sk_ieee_double_divide(1.0, dmag);
781 a *= dscale;
782 b *= dscale;
783 c *= dscale;
784 // check if we're not finite, or normal is zero-length
785 if (!SkIsFinite(a, b, c) ||
786 (a == 0 && b == 0)) {
787 fA = fB = 0;
788 fC = SK_Scalar11.0f;
789 return false;
790 }
791 fA = a;
792 fB = b;
793 fC = c;
794 return true;
795 }
796
797 enum Result {
798 kAllNegative,
799 kAllPositive,
800 kMixed
801 };
802 Result test(const SkRect& bounds) const {
803 // check whether the diagonal aligned with the normal crosses the plane
804 SkPoint diagMin, diagMax;
805 if (fA >= 0) {
806 diagMin.fX = bounds.fLeft;
807 diagMax.fX = bounds.fRight;
808 } else {
809 diagMin.fX = bounds.fRight;
810 diagMax.fX = bounds.fLeft;
811 }
812 if (fB >= 0) {
813 diagMin.fY = bounds.fTop;
814 diagMax.fY = bounds.fBottom;
815 } else {
816 diagMin.fY = bounds.fBottom;
817 diagMax.fY = bounds.fTop;
818 }
819 SkScalar test = this->eval(diagMin.fX, diagMin.fY);
820 SkScalar sign = test*this->eval(diagMax.fX, diagMax.fY);
821 if (sign > 0) {
822 // the path is either all on one side of the half-plane or the other
823 if (test < 0) {
824 return kAllNegative;
825 } else {
826 return kAllPositive;
827 }
828 }
829 return kMixed;
830 }
831};
832
833// assumes plane is pre-normalized
834static std::optional<SkPath> clip(const SkPath& path, const SkHalfPlane& plane) {
835 SkMatrix mx;
836 SkPoint p0 = { -plane.fA*plane.fC, -plane.fB*plane.fC };
837 mx.setAll( plane.fB, plane.fA, p0.fX,
838 -plane.fA, plane.fB, p0.fY,
839 0, 0, 1);
840 auto inv = mx.invert();
841 if (!inv) {
842 return {};
843 }
844
845 auto rotated = path.tryMakeTransform(*inv);
846 if (!rotated) {
847 return {};
848 }
849 auto raw = SkPathPriv::Raw(*rotated, SkResolveConvexity::kNo);
850 if (!raw) {
851 SkASSERT(false)static_cast<void>( __builtin_expect(static_cast<bool
>(false), 1) ? static_cast<void>(0) : []{ do { if (sk_abort_is_enabled
()) { do { SkDebugf("%s:%d" ": fatal error: \"" "check(%s)" "\"\n"
, "/root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp", 851
, "false"); ; sk_abort_no_print(); } while (false); } } while
(false); }() )
; // if rotated was valid, so should the raw
852 return {};
853 }
854
855 SkScalar big = SK_ScalarMax3.402823466e+38f;
856 SkRect clip = {-big, 0, big, big };
857
858 struct Rec {
859 SkPathBuilder fResult;
860 SkPoint fPrev = {0,0};
861 } rec;
862
863 SkEdgeClipper::ClipPath(*raw, clip, false,
864 [](SkEdgeClipper* clipper, bool newCtr, void* ctx) {
865 Rec* rec = (Rec*)ctx;
866
867 bool addLineTo = false;
868 SkPoint pts[4];
869 while (auto verb = clipper->next(pts)) {
870 if (newCtr) {
871 rec->fResult.moveTo(pts[0]);
872 rec->fPrev = pts[0];
873 newCtr = false;
874 }
875
876 if (addLineTo || pts[0] != rec->fPrev) {
877 rec->fResult.lineTo(pts[0]);
878 }
879
880 switch (*verb) {
881 case SkPathVerb::kLine:
882 rec->fResult.lineTo(pts[1]);
883 rec->fPrev = pts[1];
884 break;
885 case SkPathVerb::kQuad:
886 rec->fResult.quadTo(pts[1], pts[2]);
887 rec->fPrev = pts[2];
888 break;
889 case SkPathVerb::kCubic:
890 rec->fResult.cubicTo(pts[1], pts[2], pts[3]);
891 rec->fPrev = pts[3];
892 break;
893 default: break;
894 }
895 addLineTo = true;
896 }
897 }, &rec);
898
899 rec.fResult.setFillType(path.getFillType());
900 SkPath result = rec.fResult.detach(&mx);
901 if (!result.isFinite()) {
902 return {};
903 }
904 return result;
905}
906
907// true means we have written to clippedPath
908bool SkPathPriv::PerspectiveClip(const SkPath& path, const SkMatrix& matrix, SkPath* clippedPath) {
909 if (!matrix.hasPerspective()) {
910 return false;
911 }
912
913 SkHalfPlane plane {
914 matrix[SkMatrix::kMPersp0],
915 matrix[SkMatrix::kMPersp1],
916 matrix[SkMatrix::kMPersp2] - kW0PlaneDistance
917 };
918 if (plane.normalize()) {
919 switch (plane.test(path.getBounds())) {
920 case SkHalfPlane::kAllPositive:
921 return false;
922 case SkHalfPlane::kMixed: {
923 if (auto result = clip(path, plane)) {
924 *clippedPath = *result;
925 } else {
926 *clippedPath = SkPath(); // clipped out (or failed)
927 }
928 return true;
929 }
930 default: break; // handled outside of the switch
931 }
932 }
933 // clipped out (or failed)
934 *clippedPath = SkPath();
935 return true;
936}
937
938std::optional<SkPathRectInfo> SkPathPriv::IsSimpleRect(const SkPath& path, bool isSimpleFill) {
939 if (path.getSegmentMasks() != SkPath::kLine_SegmentMask) {
940 return {};
941 }
942 SkPoint rectPts[5];
943 int rectPtCnt = 0;
944 bool needsClose = !isSimpleFill;
945 for (auto [v, verbPts, w] : SkPathPriv::Iterate(path)) {
946 switch (v) {
947 case SkPathVerb::kMove:
948 if (0 != rectPtCnt) {
949 return {};
950 }
951 rectPts[0] = verbPts[0];
952 ++rectPtCnt;
953 break;
954 case SkPathVerb::kLine:
955 if (5 == rectPtCnt) {
956 return {};
957 }
958 rectPts[rectPtCnt] = verbPts[1];
959 ++rectPtCnt;
960 break;
961 case SkPathVerb::kClose:
962 if (4 == rectPtCnt) {
963 rectPts[4] = rectPts[0];
964 rectPtCnt = 5;
965 }
966 needsClose = false;
967 break;
968 case SkPathVerb::kQuad:
969 case SkPathVerb::kConic:
970 case SkPathVerb::kCubic:
971 return {};
972 }
973 }
974 if (needsClose) {
975 return {};
976 }
977 if (rectPtCnt < 5) {
978 return {};
979 }
980 if (rectPts[0] != rectPts[4]) {
981 return {};
982 }
983 // Check for two cases of rectangles: pts 0 and 3 form a vertical edge or a horizontal edge (
984 // and pts 1 and 2 the opposite vertical or horizontal edge).
985 bool vec03IsVertical;
986 if (rectPts[0].fX == rectPts[3].fX && rectPts[1].fX == rectPts[2].fX &&
987 rectPts[0].fY == rectPts[1].fY && rectPts[3].fY == rectPts[2].fY) {
988 // Make sure it has non-zero width and height
989 if (rectPts[0].fX == rectPts[1].fX || rectPts[0].fY == rectPts[3].fY) {
990 return {};
991 }
992 vec03IsVertical = true;
993 } else if (rectPts[0].fY == rectPts[3].fY && rectPts[1].fY == rectPts[2].fY &&
994 rectPts[0].fX == rectPts[1].fX && rectPts[3].fX == rectPts[2].fX) {
995 // Make sure it has non-zero width and height
996 if (rectPts[0].fY == rectPts[1].fY || rectPts[0].fX == rectPts[3].fX) {
997 return {};
998 }
999 vec03IsVertical = false;
1000 } else {
1001 return {};
1002 }
1003
1004 SkPathRectInfo info;
1005
1006 // Set sortFlags so that it has the low bit set if pt index 0 is on right edge and second bit
1007 // set if it is on the bottom edge.
1008 unsigned sortFlags =
1009 ((rectPts[0].fX < rectPts[2].fX) ? 0b00 : 0b01) |
1010 ((rectPts[0].fY < rectPts[2].fY) ? 0b00 : 0b10);
1011 switch (sortFlags) {
1012 case 0b00:
1013 info.fRect.setLTRB(rectPts[0].fX, rectPts[0].fY, rectPts[2].fX, rectPts[2].fY);
1014 info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
1015 info.fStartIndex = 0;
1016 break;
1017 case 0b01:
1018 info.fRect.setLTRB(rectPts[2].fX, rectPts[0].fY, rectPts[0].fX, rectPts[2].fY);
1019 info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
1020 info.fStartIndex = 1;
1021 break;
1022 case 0b10:
1023 info.fRect.setLTRB(rectPts[0].fX, rectPts[2].fY, rectPts[2].fX, rectPts[0].fY);
1024 info.fDirection = vec03IsVertical ? SkPathDirection::kCCW : SkPathDirection::kCW;
1025 info.fStartIndex = 3;
1026 break;
1027 case 0b11:
1028 info.fRect.setLTRB(rectPts[2].fX, rectPts[2].fY, rectPts[0].fX, rectPts[0].fY);
1029 info.fDirection = vec03IsVertical ? SkPathDirection::kCW : SkPathDirection::kCCW;
1030 info.fStartIndex = 2;
1031 break;
1032 }
1033 return info;
1034}
1035
1036//////////////////////////////////////////////////////////////////////////////////////////////////
1037
1038SkPathEdgeIter::SkPathEdgeIter(const SkPathRaw& raw) {
1039 fMoveToPtr = fPts = raw.fPoints.data();
1040 fVerbs = raw.fVerbs.data();
1041 fVerbsStop = fVerbs + raw.fVerbs.size();
1042 fConicWeights = raw.fConics.data();
1043 if (fConicWeights) {
1044 fConicWeights -= 1; // begin one behind
1045 }
1046
1047 fNeedsCloseLine = false;
1048 fNextIsNewContour = false;
1049 SkDEBUGCODE(fIsConic = false;)fIsConic = false;
1050}
1051
1052SkPathEdgeIter::SkPathEdgeIter(const SkPath& path)
1053 : SkPathEdgeIter(SkPathPriv::Raw(path, SkResolveConvexity::kNo).value_or(SkPathRaw::Empty()))
1054{}

/root/firefox-clang/gfx/skia/skia/include/private/base/SkPoint_impl.h

1/*
2 * Copyright 2006 The Android Open Source Project
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#ifndef SkPoint_DEFINED
9#define SkPoint_DEFINED
10
11#include "include/private/base/SkAPI.h"
12#include "include/private/base/SkFloatingPoint.h"
13#include "include/private/base/SkSafe32.h"
14
15#include <cmath>
16#include <cstdint>
17
18struct SkIPoint;
19
20/** SkIVector provides an alternative name for SkIPoint. SkIVector and SkIPoint
21 can be used interchangeably for all purposes.
22*/
23typedef SkIPoint SkIVector;
24
25/** \struct SkIPoint
26 SkIPoint holds two 32-bit integer coordinates.
27*/
28struct SkIPoint {
29 int32_t fX; //!< x-axis value
30 int32_t fY; //!< y-axis value
31
32 /** Sets fX to x, fY to y.
33
34 @param x integer x-axis value of constructed SkIPoint
35 @param y integer y-axis value of constructed SkIPoint
36 @return SkIPoint (x, y)
37 */
38 static constexpr SkIPoint Make(int32_t x, int32_t y) {
39 return {x, y};
40 }
41
42 /** Returns x-axis value of SkIPoint.
43
44 @return fX
45 */
46 constexpr int32_t x() const { return fX; }
47
48 /** Returns y-axis value of SkIPoint.
49
50 @return fY
51 */
52 constexpr int32_t y() const { return fY; }
53
54 /** Returns true if fX and fY are both zero.
55
56 @return true if fX is zero and fY is zero
57 */
58 bool isZero() const { return (fX | fY) == 0; }
59
60 /** Sets fX to x and fY to y.
61
62 @param x new value for fX
63 @param y new value for fY
64 */
65 void set(int32_t x, int32_t y) {
66 fX = x;
67 fY = y;
68 }
69
70 /** Returns SkIPoint changing the signs of fX and fY.
71
72 @return SkIPoint as (-fX, -fY)
73 */
74 SkIPoint operator-() const {
75 return {-fX, -fY};
76 }
77
78 /** Offsets SkIPoint by ivector v. Sets SkIPoint to (fX + v.fX, fY + v.fY).
79
80 @param v ivector to add
81 */
82 void operator+=(const SkIVector& v) {
83 fX = Sk32_sat_add(fX, v.fX);
84 fY = Sk32_sat_add(fY, v.fY);
85 }
86
87 /** Subtracts ivector v from SkIPoint. Sets SkIPoint to: (fX - v.fX, fY - v.fY).
88
89 @param v ivector to subtract
90 */
91 void operator-=(const SkIVector& v) {
92 fX = Sk32_sat_sub(fX, v.fX);
93 fY = Sk32_sat_sub(fY, v.fY);
94 }
95
96 /** Returns true if SkIPoint is equivalent to SkIPoint constructed from (x, y).
97
98 @param x value compared with fX
99 @param y value compared with fY
100 @return true if SkIPoint equals (x, y)
101 */
102 bool equals(int32_t x, int32_t y) const {
103 return fX == x && fY == y;
104 }
105
106 /** Returns true if a is equivalent to b.
107
108 @param a SkIPoint to compare
109 @param b SkIPoint to compare
110 @return true if a.fX == b.fX and a.fY == b.fY
111 */
112 friend bool operator==(const SkIPoint& a, const SkIPoint& b) {
113 return a.fX == b.fX && a.fY == b.fY;
114 }
115
116 /** Returns true if a is not equivalent to b.
117
118 @param a SkIPoint to compare
119 @param b SkIPoint to compare
120 @return true if a.fX != b.fX or a.fY != b.fY
121 */
122 friend bool operator!=(const SkIPoint& a, const SkIPoint& b) {
123 return a.fX != b.fX || a.fY != b.fY;
124 }
125
126 /** Returns ivector from b to a; computed as (a.fX - b.fX, a.fY - b.fY).
127
128 Can also be used to subtract ivector from ivector, returning ivector.
129
130 @param a SkIPoint or ivector to subtract from
131 @param b ivector to subtract
132 @return ivector from b to a
133 */
134 friend SkIVector operator-(const SkIPoint& a, const SkIPoint& b) {
135 return { Sk32_sat_sub(a.fX, b.fX), Sk32_sat_sub(a.fY, b.fY) };
136 }
137
138 /** Returns SkIPoint resulting from SkIPoint a offset by ivector b, computed as:
139 (a.fX + b.fX, a.fY + b.fY).
140
141 Can also be used to offset SkIPoint b by ivector a, returning SkIPoint.
142 Can also be used to add ivector to ivector, returning ivector.
143
144 @param a SkIPoint or ivector to add to
145 @param b SkIPoint or ivector to add
146 @return SkIPoint equal to a offset by b
147 */
148 friend SkIPoint operator+(const SkIPoint& a, const SkIVector& b) {
149 return { Sk32_sat_add(a.fX, b.fX), Sk32_sat_add(a.fY, b.fY) };
150 }
151};
152
153struct SkPoint;
154
155/** SkVector provides an alternative name for SkPoint. SkVector and SkPoint can
156 be used interchangeably for all purposes.
157*/
158typedef SkPoint SkVector;
159
160/** \struct SkPoint
161 SkPoint holds two 32-bit floating point coordinates.
162*/
163struct SK_API SkPoint {
164 float fX; //!< x-axis value
165 float fY; //!< y-axis value
166
167 /** Sets fX to x, fY to y. Used both to set SkPoint and vector.
168
169 @param x float x-axis value of constructed SkPoint or vector
170 @param y float y-axis value of constructed SkPoint or vector
171 @return SkPoint (x, y)
172 */
173 static constexpr SkPoint Make(float x, float y) {
174 return {x, y};
175 }
176
177 /** Returns x-axis value of SkPoint or vector.
178
179 @return fX
180 */
181 constexpr float x() const { return fX; }
182
183 /** Returns y-axis value of SkPoint or vector.
184
185 @return fY
186 */
187 constexpr float y() const { return fY; }
188
189 /** Returns true if fX and fY are both zero.
190
191 @return true if fX is zero and fY is zero
192 */
193 bool isZero() const { return (0 == fX) & (0 == fY); }
194
195 /** Sets fX to x and fY to y.
196
197 @param x new value for fX
198 @param y new value for fY
199 */
200 void set(float x, float y) {
201 fX = x;
202 fY = y;
203 }
204
205 /** Sets fX to x and fY to y, promoting integers to float values.
206
207 Assigning a large integer value directly to fX or fY may cause a compiler
208 error, triggered by narrowing conversion of int to float. This safely
209 casts x and y to avoid the error.
210
211 @param x new value for fX
212 @param y new value for fY
213 */
214 void iset(int32_t x, int32_t y) {
215 fX = static_cast<float>(x);
216 fY = static_cast<float>(y);
217 }
218
219 /** Sets fX to p.fX and fY to p.fY, promoting integers to float values.
220
221 Assigning an SkIPoint containing a large integer value directly to fX or fY may
222 cause a compiler error, triggered by narrowing conversion of int to float.
223 This safely casts p.fX and p.fY to avoid the error.
224
225 @param p SkIPoint members promoted to float
226 */
227 void iset(const SkIPoint& p) {
228 fX = static_cast<float>(p.fX);
229 fY = static_cast<float>(p.fY);
230 }
231
232 /** Sets fX to absolute value of pt.fX; and fY to absolute value of pt.fY.
233
234 @param pt members providing magnitude for fX and fY
235 */
236 void setAbs(const SkPoint& pt) {
237 fX = std::abs(pt.fX);
238 fY = std::abs(pt.fY);
239 }
240
241 /** Adds offset to each SkPoint in points array with count entries.
242
243 @param points SkPoint array
244 @param count entries in array
245 @param offset vector added to points
246 */
247 static void Offset(SkPoint points[], int count, const SkVector& offset) {
248 Offset(points, count, offset.fX, offset.fY);
249 }
250
251 /** Adds offset (dx, dy) to each SkPoint in points array of length count.
252
253 @param points SkPoint array
254 @param count entries in array
255 @param dx added to fX in points
256 @param dy added to fY in points
257 */
258 static void Offset(SkPoint points[], int count, float dx, float dy) {
259 for (int i = 0; i < count; ++i) {
260 points[i].offset(dx, dy);
261 }
262 }
263
264 /** Adds offset (dx, dy) to SkPoint.
265
266 @param dx added to fX
267 @param dy added to fY
268 */
269 void offset(float dx, float dy) {
270 fX += dx;
271 fY += dy;
272 }
273
274 /** Returns the Euclidean distance from origin, computed as:
275
276 sqrt(fX * fX + fY * fY)
277
278 .
279
280 @return straight-line distance to origin
281 */
282 float length() const { return SkPoint::Length(fX, fY); }
283
284 /** Returns the Euclidean distance from origin, computed as:
285
286 sqrt(fX * fX + fY * fY)
287
288 .
289
290 @return straight-line distance to origin
291 */
292 float distanceToOrigin() const { return this->length(); }
293
294 /** Scales (fX, fY) so that length() returns one, while preserving ratio of fX to fY,
295 if possible. If prior length is nearly zero, sets vector to (0, 0) and returns
296 false; otherwise returns true.
297
298 @return true if former length is not zero or nearly zero
299
300 example: https://fiddle.skia.org/c/@Point_normalize_2
301 */
302 bool normalize();
303
304 /** Sets vector to (x, y) scaled so length() returns one, and so that
305 (fX, fY) is proportional to (x, y). If (x, y) length is nearly zero,
306 sets vector to (0, 0) and returns false; otherwise returns true.
307
308 @param x proportional value for fX
309 @param y proportional value for fY
310 @return true if (x, y) length is not zero or nearly zero
311
312 example: https://fiddle.skia.org/c/@Point_setNormalize
313 */
314 bool setNormalize(float x, float y);
315
316 /** Scales vector so that distanceToOrigin() returns length, if possible. If former
317 length is nearly zero, sets vector to (0, 0) and return false; otherwise returns
318 true.
319
320 @param length straight-line distance to origin
321 @return true if former length is not zero or nearly zero
322
323 example: https://fiddle.skia.org/c/@Point_setLength
324 */
325 bool setLength(float length);
326
327 /** Sets vector to (x, y) scaled to length, if possible. If former
328 length is nearly zero, sets vector to (0, 0) and return false; otherwise returns
329 true.
330
331 @param x proportional value for fX
332 @param y proportional value for fY
333 @param length straight-line distance to origin
334 @return true if (x, y) length is not zero or nearly zero
335
336 example: https://fiddle.skia.org/c/@Point_setLength_2
337 */
338 bool setLength(float x, float y, float length);
339
340 /** Sets dst to SkPoint times scale. dst may be SkPoint to modify SkPoint in place.
341
342 @param scale factor to multiply SkPoint by
343 @param dst storage for scaled SkPoint
344
345 example: https://fiddle.skia.org/c/@Point_scale
346 */
347 void scale(float scale, SkPoint* dst) const;
348
349 /** Scales SkPoint in place by scale.
350
351 @param value factor to multiply SkPoint by
352 */
353 void scale(float value) { this->scale(value, this); }
354
355 /** Changes the sign of fX and fY.
356 */
357 void negate() {
358 fX = -fX;
359 fY = -fY;
360 }
361
362 /** Returns SkPoint changing the signs of fX and fY.
363
364 @return SkPoint as (-fX, -fY)
365 */
366 SkPoint operator-() const {
367 return {-fX, -fY};
368 }
369
370 /** Adds vector v to SkPoint. Sets SkPoint to: (fX + v.fX, fY + v.fY).
371
372 @param v vector to add
373 */
374 void operator+=(const SkVector& v) {
375 fX += v.fX;
376 fY += v.fY;
377 }
378
379 /** Subtracts vector v from SkPoint. Sets SkPoint to: (fX - v.fX, fY - v.fY).
380
381 @param v vector to subtract
382 */
383 void operator-=(const SkVector& v) {
384 fX -= v.fX;
385 fY -= v.fY;
386 }
387
388 /** Returns SkPoint multiplied by scale.
389
390 @param scale float to multiply by
391 @return SkPoint as (fX * scale, fY * scale)
392 */
393 SkPoint operator*(float scale) const {
394 return {fX * scale, fY * scale};
395 }
396
397 /** Multiplies SkPoint by scale. Sets SkPoint to: (fX * scale, fY * scale).
398
399 @param scale float to multiply by
400 @return reference to SkPoint
401 */
402 SkPoint& operator*=(float scale) {
403 fX *= scale;
404 fY *= scale;
405 return *this;
406 }
407
408 /** Returns true if both fX and fY are measurable values.
409
410 @return true for values other than infinities and NaN
411 */
412 bool isFinite() const {
413 return SkIsFinite(fX, fY);
414 }
415
416 /** Returns true if SkPoint is equivalent to SkPoint constructed from (x, y).
417
418 @param x value compared with fX
419 @param y value compared with fY
420 @return true if SkPoint equals (x, y)
421 */
422 bool equals(float x, float y) const {
423 return fX == x && fY == y;
424 }
425
426 /** Returns true if a is equivalent to b.
427
428 @param a SkPoint to compare
429 @param b SkPoint to compare
430 @return true if a.fX == b.fX and a.fY == b.fY
431 */
432 friend bool operator==(const SkPoint& a, const SkPoint& b) {
433 return a.fX == b.fX && a.fY == b.fY;
9
The left operand of '==' is a garbage value
434 }
435
436 /** Returns true if a is not equivalent to b.
437
438 @param a SkPoint to compare
439 @param b SkPoint to compare
440 @return true if a.fX != b.fX or a.fY != b.fY
441 */
442 friend bool operator!=(const SkPoint& a, const SkPoint& b) {
443 return a.fX != b.fX || a.fY != b.fY;
444 }
445
446 /** Returns vector from b to a, computed as (a.fX - b.fX, a.fY - b.fY).
447
448 Can also be used to subtract vector from SkPoint, returning SkPoint.
449 Can also be used to subtract vector from vector, returning vector.
450
451 @param a SkPoint to subtract from
452 @param b SkPoint to subtract
453 @return vector from b to a
454 */
455 friend SkVector operator-(const SkPoint& a, const SkPoint& b) {
456 return {a.fX - b.fX, a.fY - b.fY};
457 }
458
459 /** Returns SkPoint resulting from SkPoint a offset by vector b, computed as:
460 (a.fX + b.fX, a.fY + b.fY).
461
462 Can also be used to offset SkPoint b by vector a, returning SkPoint.
463 Can also be used to add vector to vector, returning vector.
464
465 @param a SkPoint or vector to add to
466 @param b SkPoint or vector to add
467 @return SkPoint equal to a offset by b
468 */
469 friend SkPoint operator+(const SkPoint& a, const SkVector& b) {
470 return {a.fX + b.fX, a.fY + b.fY};
471 }
472
473 /** Returns the Euclidean distance from origin, computed as:
474
475 sqrt(x * x + y * y)
476
477 .
478
479 @param x component of length
480 @param y component of length
481 @return straight-line distance to origin
482
483 example: https://fiddle.skia.org/c/@Point_Length
484 */
485 static float Length(float x, float y);
486
487 /** Scales (vec->fX, vec->fY) so that length() returns one, while preserving ratio of vec->fX
488 to vec->fY, if possible. If original length is nearly zero, sets vec to (0, 0) and returns
489 zero; otherwise, returns length of vec before vec is scaled.
490
491 Returned prior length may be INFINITY if it can not be represented by float.
492
493 Note that normalize() is faster if prior length is not required.
494
495 @param vec normalized to unit length
496 @return original vec length
497
498 example: https://fiddle.skia.org/c/@Point_Normalize
499 */
500 static float Normalize(SkVector* vec);
501
502 /** Returns the Euclidean distance between a and b.
503
504 @param a line end point
505 @param b line end point
506 @return straight-line distance from a to b
507 */
508 static float Distance(const SkPoint& a, const SkPoint& b) {
509 return Length(a.fX - b.fX, a.fY - b.fY);
510 }
511
512 /** Returns the dot product of vector a and vector b.
513
514 @param a left side of dot product
515 @param b right side of dot product
516 @return product of input magnitudes and cosine of the angle between them
517 */
518 static float DotProduct(const SkVector& a, const SkVector& b) {
519 return a.fX * b.fX + a.fY * b.fY;
520 }
521
522 /** Returns the cross product of vector a and vector b.
523
524 a and b form three-dimensional vectors with z-axis value equal to zero. The
525 cross product is a three-dimensional vector with x-axis and y-axis values equal
526 to zero. The cross product z-axis component is returned.
527
528 @param a left side of cross product
529 @param b right side of cross product
530 @return area spanned by vectors signed by angle direction
531 */
532 static float CrossProduct(const SkVector& a, const SkVector& b) {
533 return a.fX * b.fY - a.fY * b.fX;
534 }
535
536 /** Returns the cross product of vector and vec.
537
538 Vector and vec form three-dimensional vectors with z-axis value equal to zero.
539 The cross product is a three-dimensional vector with x-axis and y-axis values
540 equal to zero. The cross product z-axis component is returned.
541
542 @param vec right side of cross product
543 @return area spanned by vectors signed by angle direction
544 */
545 float cross(const SkVector& vec) const {
546 return CrossProduct(*this, vec);
547 }
548
549 /** Returns the dot product of vector and vector vec.
550
551 @param vec right side of dot product
552 @return product of input magnitudes and cosine of the angle between them
553 */
554 float dot(const SkVector& vec) const {
555 return DotProduct(*this, vec);
556 }
557
558};
559
560#endif