1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #ifndef CLIPAREA_H
17 #define CLIPAREA_H
18 
19 #include "Matrix.h"
20 #include "Rect.h"
21 #include "utils/Pair.h"
22 
23 #include <SkRegion.h>
24 
25 namespace android {
26 namespace uirenderer {
27 
28 class LinearAllocator;
29 
30 Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
31 
32 class TransformedRectangle {
33 public:
34     TransformedRectangle();
35     TransformedRectangle(const Rect& bounds, const Matrix4& transform);
36 
37     bool canSimplyIntersectWith(const TransformedRectangle& other) const;
38     void intersectWith(const TransformedRectangle& other);
39 
40     bool isEmpty() const;
41 
getBounds()42     const Rect& getBounds() const {
43         return mBounds;
44     }
45 
transformedBounds()46     Rect transformedBounds() const {
47         Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
48         return transformedBounds;
49     }
50 
getTransform()51     const Matrix4& getTransform() const {
52         return mTransform;
53     }
54 
transform(const Matrix4 & transform)55     void transform(const Matrix4& transform) {
56         Matrix4 t;
57         t.loadMultiply(transform, mTransform);
58         mTransform = t;
59     }
60 
61 private:
62     Rect mBounds;
63     Matrix4 mTransform;
64 };
65 
66 class RectangleList {
67 public:
68     RectangleList();
69 
70     bool isEmpty() const;
71     int getTransformedRectanglesCount() const;
72     const TransformedRectangle& getTransformedRectangle(int i) const;
73 
74     void setEmpty();
75     void set(const Rect& bounds, const Matrix4& transform);
76     bool intersectWith(const Rect& bounds, const Matrix4& transform);
77     void transform(const Matrix4& transform);
78 
79     SkRegion convertToRegion(const SkRegion& clip) const;
80     Rect calculateBounds() const;
81 
82     enum {
83         kMaxTransformedRectangles = 5
84     };
85 
86 private:
87     int mTransformedRectanglesCount;
88     TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
89 };
90 
91 enum class ClipMode {
92     Rectangle,
93     RectangleList,
94 
95     // region and path - intersected. if either is empty, don't use
96     Region
97 };
98 
99 struct ClipBase {
ClipBaseClipBase100     ClipBase(ClipMode mode)
101             : mode(mode) {}
ClipBaseClipBase102     ClipBase(const Rect& rect)
103             : mode(ClipMode::Rectangle)
104             , rect(rect) {}
105     const ClipMode mode;
106     bool intersectWithRoot = false;
107     // Bounds of the clipping area, used to define the scissor, and define which
108     // portion of the stencil is updated/used
109     Rect rect;
110 
111     void dump() const;
112 };
113 
114 struct ClipRect : ClipBase {
ClipRectClipRect115     ClipRect(const Rect& rect)
116             : ClipBase(rect) {}
117 };
118 
119 struct ClipRectList : ClipBase {
ClipRectListClipRectList120     ClipRectList(const RectangleList& rectList)
121             : ClipBase(ClipMode::RectangleList)
122             , rectList(rectList) {}
123     RectangleList rectList;
124 };
125 
126 struct ClipRegion : ClipBase {
ClipRegionClipRegion127     ClipRegion(const SkRegion& region)
128             : ClipBase(ClipMode::Region)
129             , region(region) {}
ClipRegionClipRegion130     ClipRegion()
131             : ClipBase(ClipMode::Region) {}
132     SkRegion region;
133 };
134 
135 class ClipArea {
136 public:
137     ClipArea();
138 
139     void setViewportDimensions(int width, int height);
140 
isEmpty()141     bool isEmpty() const {
142         return mClipRect.isEmpty();
143     }
144 
145     void setEmpty();
146     void setClip(float left, float top, float right, float bottom);
147     void clipRectWithTransform(const Rect& r, const mat4* transform,
148             SkRegion::Op op);
149     void clipRegion(const SkRegion& region, SkRegion::Op op);
150     void clipPathWithTransform(const SkPath& path, const mat4* transform,
151             SkRegion::Op op);
152 
getClipRect()153     const Rect& getClipRect() const {
154         return mClipRect;
155     }
156 
getClipRegion()157     const SkRegion& getClipRegion() const {
158         return mClipRegion;
159     }
160 
getRectangleList()161     const RectangleList& getRectangleList() const {
162         return mRectangleList;
163     }
164 
isRegion()165     bool isRegion() const {
166         return ClipMode::Region == mMode;
167     }
168 
isSimple()169     bool isSimple() const {
170         return mMode == ClipMode::Rectangle;
171     }
172 
isRectangleList()173     bool isRectangleList() const {
174         return mMode == ClipMode::RectangleList;
175     }
176 
177     WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
178     WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator,
179             const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
180     void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
181 
182 private:
183     void enterRectangleMode();
184     void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
185 
186     void enterRectangleListMode();
187     void rectangleListModeClipRectWithTransform(const Rect& r,
188             const mat4* transform, SkRegion::Op op);
189 
190     void enterRegionModeFromRectangleMode();
191     void enterRegionModeFromRectangleListMode();
192     void enterRegionMode();
193     void regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
194             SkRegion::Op op);
195 
196     void ensureClipRegion();
197     void onClipRegionUpdated();
198 
199     // Called by every state modifying public method.
onClipUpdated()200     void onClipUpdated() {
201         mPostViewportClipObserved = true;
202         mLastSerialization = nullptr;
203         mLastResolutionResult = nullptr;
204     }
205 
createViewportRegion()206     SkRegion createViewportRegion() {
207         return SkRegion(mViewportBounds.toSkIRect());
208     }
209 
regionFromPath(const SkPath & path,SkRegion & pathAsRegion)210     void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
211         // TODO: this should not mask every path to the viewport - this makes it impossible to use
212         // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
213         pathAsRegion.setPath(path, createViewportRegion());
214     }
215 
216     ClipMode mMode;
217     bool mPostViewportClipObserved = false;
218     bool mReplaceOpObserved = false;
219 
220     /**
221      * If mLastSerialization is non-null, it represents an already serialized copy
222      * of the current clip state. If null, it has not been computed.
223      */
224     const ClipBase* mLastSerialization = nullptr;
225 
226     /**
227      * This pair of pointers is a single entry cache of most recently seen
228      */
229     const ClipBase* mLastResolutionResult = nullptr;
230     const ClipBase* mLastResolutionClip = nullptr;
231     Matrix4 mLastResolutionTransform;
232 
233     Rect mViewportBounds;
234     Rect mClipRect;
235     SkRegion mClipRegion;
236     RectangleList mRectangleList;
237 };
238 
239 } /* namespace uirenderer */
240 } /* namespace android */
241 
242 #endif /* CLIPAREA_H_ */
243