Bug Summary

File:root/firefox-clang/gfx/skia/skia/src/core/SkPath.cpp
Warning:line 499, column 23
Dereference of null pointer

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
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)) {
197 return false;
198 }
199
200 const auto direction = SkPathConvexity_ToDirection(convexity);
201 if (!direction) {
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 == SkPathVerb::kClose || (segmentCount > 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 == SkPathVerb::kMove) {
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); }() )
;
226
227 if (!SkPathPriv::AllPointsEq({pts, (size_t)pointCount + 1})) {
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); }() )
;
229 int nextPt = pointCount;
230 segmentCount++;
231
232 if (prevPt == pts[nextPt]) {
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); }() )
;
2
Assuming the condition is true
3
'?' condition is true
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); }() )
;
4
Assuming field 'fVerbs' is null
5
Assuming field 'fVerbStop' is non-null
6
'?' condition is false
486
487 if (fVerbs
6.1
Field 'fVerbs' is not equal to field 'fVerbStop'
== fVerbStop) {
7
Taking false branch
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++;
8
Dereference of null pointer
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());
1
Calling 'Iter::next'
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{}