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