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 */
27class GrOctoBounds {
28public:
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
110private:
111 SkRect fBounds;
112 SkRect fBounds45;
113};
114
115#endif
116