• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
GrOctoBounds(const SkRect & bounds,const SkRect & bounds45)30     GrOctoBounds(const SkRect& bounds, const SkRect& bounds45) {
31         this->set(bounds, bounds45);
32     }
33 
set(const SkRect & bounds,const SkRect & bounds45)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 
bounds()45     const SkRect& bounds() const { return fBounds; }
left()46     float left() const { return fBounds.left(); }
top()47     float top() const { return fBounds.top(); }
right()48     float right() const { return fBounds.right(); }
bottom()49     float bottom() const { return fBounds.bottom(); }
50 
51 
52     // The 45-degree bounding box resides in "| 1  -1 | * coords" space.
53     //                                        | 1   1 |
bounds45()54     const SkRect& bounds45() const { return fBounds45; }
left45()55     float left45() const { return fBounds45.left(); }
top45()56     float top45() const { return fBounds45.top(); }
right45()57     float right45() const { return fBounds45.right(); }
bottom45()58     float bottom45() const { return fBounds45.bottom(); }
59 
roundOut(SkIRect * out)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 
makeOffset(float dx,float dy)67     GrOctoBounds makeOffset(float dx, float dy) const {
68         GrOctoBounds offset;
69         offset.setOffset(*this, dx, dy);
70         return offset;
71     }
72 
setOffset(const GrOctoBounds & octoBounds,float dx,float dy)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 
outset(float radius)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.
Get_x45(float x,float y)99     constexpr static float Get_x45(float x, float y) { return x - y; }
Get_y45(float x,float y)100     constexpr static float Get_y45(float x, float y) { return x + y; }
Get_x(float x45,float y45)101     constexpr static float Get_x(float x45, float y45) { return (x45 + y45) * .5f; }
Get_y(float x45,float y45)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