1 | /* |
2 | * Copyright 2019 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 | #ifndef GrOctoBounds_DEFINED |
9 | #define GrOctoBounds_DEFINED |
10 | |
11 | #include "include/core/SkRect.h" |
12 | #include <functional> |
13 | |
14 | /** |
15 | * This class is composed of two bounding boxes: one in device space, and one in a 45-degree rotated |
16 | * space. |
17 | * |
18 | * The 45-degree bounding box resides in "| 1 -1 | * coords" space. |
19 | * | 1 1 | |
20 | * |
21 | * The intersection of these two boxes defines the bounding octagon of a shape. |
22 | * |
23 | * Furthermore, both bounding boxes are fully tightened. This means we can blindly find the |
24 | * intersections between each diagonal and its vertical and horizontal neighbors, and be left with |
25 | * 8 points that define a convex (possibly degenerate) octagon. |
26 | */ |
27 | class GrOctoBounds { |
28 | public: |
29 | GrOctoBounds() = default; |
30 | GrOctoBounds(const SkRect& bounds, const SkRect& bounds45) { |
31 | this->set(bounds, bounds45); |
32 | } |
33 | |
34 | void set(const SkRect& bounds, const SkRect& bounds45) { |
35 | fBounds = bounds; |
36 | fBounds45 = bounds45; |
37 | SkDEBUGCODE(this->validateBoundsAreTight()); |
38 | } |
39 | |
40 | bool operator==(const GrOctoBounds& that) const { |
41 | return fBounds == that.fBounds && fBounds45 == that.fBounds45; |
42 | } |
43 | bool operator!=(const GrOctoBounds& that) const { return !(*this == that); } |
44 | |
45 | const SkRect& bounds() const { return fBounds; } |
46 | float left() const { return fBounds.left(); } |
47 | float top() const { return fBounds.top(); } |
48 | float right() const { return fBounds.right(); } |
49 | float bottom() const { return fBounds.bottom(); } |
50 | |
51 | |
52 | // The 45-degree bounding box resides in "| 1 -1 | * coords" space. |
53 | // | 1 1 | |
54 | const SkRect& bounds45() const { return fBounds45; } |
55 | float left45() const { return fBounds45.left(); } |
56 | float top45() const { return fBounds45.top(); } |
57 | float right45() const { return fBounds45.right(); } |
58 | float bottom45() const { return fBounds45.bottom(); } |
59 | |
60 | void roundOut(SkIRect* out) const { |
61 | // The octagon is the intersection of fBounds and fBounds45 (see the comment at the start of |
62 | // the class). The octagon's bounding box is therefore just fBounds. And the integer |
63 | // bounding box can be found by simply rounding out fBounds. |
64 | fBounds.roundOut(out); |
65 | } |
66 | |
67 | GrOctoBounds makeOffset(float dx, float dy) const { |
68 | GrOctoBounds offset; |
69 | offset.setOffset(*this, dx, dy); |
70 | return offset; |
71 | } |
72 | |
73 | void setOffset(const GrOctoBounds& octoBounds, float dx, float dy) { |
74 | fBounds = octoBounds.fBounds.makeOffset(dx, dy); |
75 | fBounds45 = octoBounds.fBounds45.makeOffset(dx - dy, dx + dy); |
76 | SkDEBUGCODE(this->validateBoundsAreTight()); |
77 | } |
78 | |
79 | void outset(float radius) { |
80 | fBounds.outset(radius, radius); |
81 | fBounds45.outset(radius*SK_ScalarSqrt2, radius*SK_ScalarSqrt2); |
82 | SkDEBUGCODE(this->validateBoundsAreTight()); |
83 | } |
84 | |
85 | // Clips the octo bounds by a clip rect and ensures the resulting bounds are fully tightened. |
86 | // Returns false if the octagon and clipRect do not intersect at all. |
87 | // |
88 | // NOTE: Does not perform a trivial containment test before the clip routine. It is probably a |
89 | // good idea to not call this method if 'this->bounds()' are fully contained within 'clipRect'. |
90 | bool SK_WARN_UNUSED_RESULT clip(const SkIRect& clipRect); |
91 | |
92 | // The 45-degree bounding box resides in "| 1 -1 | * coords" space. |
93 | // | 1 1 | |
94 | // |
95 | // i.e., | x45 | = | x - y | |
96 | // | y45 | = | x + y | |
97 | // |
98 | // These methods transform points between device space and 45-degree space. |
99 | constexpr static float Get_x45(float x, float y) { return x - y; } |
100 | constexpr static float Get_y45(float x, float y) { return x + y; } |
101 | constexpr static float Get_x(float x45, float y45) { return (x45 + y45) * .5f; } |
102 | constexpr static float Get_y(float x45, float y45) { return (y45 - x45) * .5f; } |
103 | |
104 | #if defined(SK_DEBUG) || defined(GR_TEST_UTILS) |
105 | void validateBoundsAreTight() const; |
106 | void validateBoundsAreTight(const std::function<void( |
107 | bool cond, const char* file, int line, const char* code)>& validateFn) const; |
108 | #endif |
109 | |
110 | private: |
111 | SkRect fBounds; |
112 | SkRect fBounds45; |
113 | }; |
114 | |
115 | #endif |
116 | |