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 "src/pathops/SkOpEdgeBuilder.h" |
8 | #include "src/pathops/SkPathOpsCommon.h" |
9 | |
10 | bool TightBounds(const SkPath& path, SkRect* result) { |
11 | SkPath::RawIter iter(path); |
12 | SkRect moveBounds = { SK_ScalarMax, SK_ScalarMax, SK_ScalarMin, SK_ScalarMin }; |
13 | bool wellBehaved = true; |
14 | SkPath::Verb verb; |
15 | do { |
16 | SkPoint pts[4]; |
17 | verb = iter.next(pts); |
18 | switch (verb) { |
19 | case SkPath::kMove_Verb: |
20 | moveBounds.fLeft = std::min(moveBounds.fLeft, pts[0].fX); |
21 | moveBounds.fTop = std::min(moveBounds.fTop, pts[0].fY); |
22 | moveBounds.fRight = std::max(moveBounds.fRight, pts[0].fX); |
23 | moveBounds.fBottom = std::max(moveBounds.fBottom, pts[0].fY); |
24 | break; |
25 | case SkPath::kQuad_Verb: |
26 | case SkPath::kConic_Verb: |
27 | if (!wellBehaved) { |
28 | break; |
29 | } |
30 | wellBehaved &= between(pts[0].fX, pts[1].fX, pts[2].fX); |
31 | wellBehaved &= between(pts[0].fY, pts[1].fY, pts[2].fY); |
32 | break; |
33 | case SkPath::kCubic_Verb: |
34 | if (!wellBehaved) { |
35 | break; |
36 | } |
37 | wellBehaved &= between(pts[0].fX, pts[1].fX, pts[3].fX); |
38 | wellBehaved &= between(pts[0].fY, pts[1].fY, pts[3].fY); |
39 | wellBehaved &= between(pts[0].fX, pts[2].fX, pts[3].fX); |
40 | wellBehaved &= between(pts[0].fY, pts[2].fY, pts[3].fY); |
41 | break; |
42 | default: |
43 | break; |
44 | } |
45 | } while (verb != SkPath::kDone_Verb); |
46 | if (wellBehaved) { |
47 | *result = path.getBounds(); |
48 | return true; |
49 | } |
50 | SkSTArenaAlloc<4096> allocator; // FIXME: constant-ize, tune |
51 | SkOpContour contour; |
52 | SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour); |
53 | SkOpGlobalState globalState(contourList, &allocator SkDEBUGPARAMS(false) |
54 | SkDEBUGPARAMS(nullptr)); |
55 | // turn path into list of segments |
56 | SkOpEdgeBuilder builder(path, contourList, &globalState); |
57 | if (!builder.finish()) { |
58 | return false; |
59 | } |
60 | if (!SortContourList(&contourList, false, false)) { |
61 | *result = moveBounds; |
62 | return true; |
63 | } |
64 | SkOpContour* current = contourList; |
65 | SkPathOpsBounds bounds = current->bounds(); |
66 | while ((current = current->next())) { |
67 | bounds.add(current->bounds()); |
68 | } |
69 | *result = bounds; |
70 | if (!moveBounds.isEmpty()) { |
71 | result->join(moveBounds); |
72 | } |
73 | return true; |
74 | } |
75 | |