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
8#include "SkMatrix.h"
9#include "SkPath.h"
10#include "SkPathOps.h"
11
12void SkOpBuilder::add(const SkPath& path, SkPathOp op) {
13    if (0 == fOps.count() && op != kUnion_SkPathOp) {
14        fPathRefs.push_back() = SkPath();
15        *fOps.append() = kUnion_SkPathOp;
16    }
17    fPathRefs.push_back() = path;
18    *fOps.append() = op;
19}
20
21void SkOpBuilder::reset() {
22    fPathRefs.reset();
23    fOps.reset();
24}
25
26/* OPTIMIZATION: Union doesn't need to be all-or-nothing. A run of three or more convex
27   paths with union ops could be locally resolved and still improve over doing the
28   ops one at a time. */
29bool SkOpBuilder::resolve(SkPath* result) {
30    SkPath original = *result;
31    int count = fOps.count();
32    bool allUnion = true;
33    SkPath::Direction firstDir;
34    for (int index = 0; index < count; ++index) {
35        SkPath* test = &fPathRefs[index];
36        if (kUnion_SkPathOp != fOps[index] || test->isInverseFillType()) {
37            allUnion = false;
38            break;
39        }
40        // If all paths are convex, track direction, reversing as needed.
41        if (test->isConvex()) {
42            SkPath::Direction dir;
43            if (!test->cheapComputeDirection(&dir)) {
44                allUnion = false;
45                break;
46            }
47            if (index == 0) {
48                firstDir = dir;
49            } else if (firstDir != dir) {
50                SkPath temp;
51                temp.reverseAddPath(*test);
52                *test = temp;
53            }
54            continue;
55        }
56        // If the path is not convex but its bounds do not intersect the others, simplify is enough.
57        const SkRect& testBounds = test->getBounds();
58        for (int inner = 0; inner < index; ++inner) {
59            // OPTIMIZE: check to see if the contour bounds do not intersect other contour bounds?
60            if (SkRect::Intersects(fPathRefs[inner].getBounds(), testBounds)) {
61                allUnion = false;
62                break;
63            }
64        }
65    }
66    if (!allUnion) {
67        *result = fPathRefs[0];
68        for (int index = 1; index < count; ++index) {
69            if (!Op(*result, fPathRefs[index], fOps[index], result)) {
70                reset();
71                *result = original;
72                return false;
73            }
74        }
75        reset();
76        return true;
77    }
78    SkPath sum;
79    for (int index = 0; index < count; ++index) {
80        if (!Simplify(fPathRefs[index], &fPathRefs[index])) {
81            reset();
82            *result = original;
83            return false;
84        }
85        sum.addPath(fPathRefs[index]);
86    }
87    reset();
88    sum.setFillType(SkPath::kEvenOdd_FillType);
89    bool success = Simplify(sum, result);
90    if (!success) {
91        *result = original;
92    }
93    return success;
94}
95