xref: /aosp_15_r20/external/skia/src/pathops/SkPathOpsTightBounds.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "include/core/SkPath.h"
8 #include "include/core/SkPathTypes.h"
9 #include "include/core/SkPoint.h"
10 #include "include/core/SkRect.h"
11 #include "include/core/SkScalar.h"
12 #include "include/pathops/SkPathOps.h"
13 #include "src/base/SkArenaAlloc.h"
14 #include "src/core/SkPathPriv.h"
15 #include "src/pathops/SkOpContour.h"
16 #include "src/pathops/SkOpEdgeBuilder.h"
17 #include "src/pathops/SkPathOpsBounds.h"
18 #include "src/pathops/SkPathOpsCommon.h"
19 #include "src/pathops/SkPathOpsTypes.h"
20 
21 #include <algorithm>
22 
TightBounds(const SkPath & path,SkRect * result)23 bool TightBounds(const SkPath& path, SkRect* result) {
24     SkRect moveBounds = { SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin };
25     bool wellBehaved = true;
26     for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
27         switch (verb) {
28             case SkPathVerb::kMove:
29                 moveBounds.fLeft = std::min(moveBounds.fLeft, pts[0].fX);
30                 moveBounds.fTop = std::min(moveBounds.fTop, pts[0].fY);
31                 moveBounds.fRight = std::max(moveBounds.fRight, pts[0].fX);
32                 moveBounds.fBottom = std::max(moveBounds.fBottom, pts[0].fY);
33                 break;
34             case SkPathVerb::kQuad:
35             case SkPathVerb::kConic:
36                 if (!wellBehaved) {
37                     break;
38                 }
39                 wellBehaved &= between(pts[0].fX, pts[1].fX, pts[2].fX);
40                 wellBehaved &= between(pts[0].fY, pts[1].fY, pts[2].fY);
41                 break;
42             case SkPathVerb::kCubic:
43                 if (!wellBehaved) {
44                     break;
45                 }
46                 wellBehaved &= between(pts[0].fX, pts[1].fX, pts[3].fX);
47                 wellBehaved &= between(pts[0].fY, pts[1].fY, pts[3].fY);
48                 wellBehaved &= between(pts[0].fX, pts[2].fX, pts[3].fX);
49                 wellBehaved &= between(pts[0].fY, pts[2].fY, pts[3].fY);
50                 break;
51             default:
52                 break;
53         }
54     }
55     if (wellBehaved) {
56         *result = path.getBounds();
57         return true;
58     }
59     SkSTArenaAlloc<4096> allocator;  // FIXME: constant-ize, tune
60     SkOpContour contour;
61     SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
62     SkOpGlobalState globalState(contourList, &allocator  SkDEBUGPARAMS(false)
63             SkDEBUGPARAMS(nullptr));
64     // turn path into list of segments
65     SkOpEdgeBuilder builder(path, contourList, &globalState);
66     if (!builder.finish()) {
67         return false;
68     }
69     if (!SortContourList(&contourList, false, false)) {
70         *result = moveBounds;
71         return true;
72     }
73     SkOpContour* current = contourList;
74     SkPathOpsBounds bounds = current->bounds();
75     while ((current = current->next())) {
76         bounds.add(current->bounds());
77     }
78     *result = bounds;
79     if (!moveBounds.isEmpty()) {
80         result->join(moveBounds);
81     }
82     return true;
83 }
84