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 
add(const SkPath & path,SkPathOp op)12 void 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 
reset()21 void 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. */
resolve(SkPath * result)29 bool 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