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