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 { return mBounds; }
43 
transformedBounds()44     Rect transformedBounds() const {
45         Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
46         return transformedBounds;
47     }
48 
getTransform()49     const Matrix4& getTransform() const { return mTransform; }
50 
transform(const Matrix4 & transform)51     void transform(const Matrix4& transform) {
52         Matrix4 t;
53         t.loadMultiply(transform, mTransform);
54         mTransform = t;
55     }
56 
57 private:
58     Rect mBounds;
59     Matrix4 mTransform;
60 };
61 
62 class RectangleList {
63 public:
64     RectangleList();
65 
66     bool isEmpty() const;
67     int getTransformedRectanglesCount() const;
68     const TransformedRectangle& getTransformedRectangle(int i) const;
69 
70     void setEmpty();
71     void set(const Rect& bounds, const Matrix4& transform);
72     bool intersectWith(const Rect& bounds, const Matrix4& transform);
73     void transform(const Matrix4& transform);
74 
75     SkRegion convertToRegion(const SkRegion& clip) const;
76     Rect calculateBounds() const;
77 
78     enum { kMaxTransformedRectangles = 5 };
79 
80 private:
81     int mTransformedRectanglesCount;
82     TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
83 };
84 
85 enum class ClipMode {
86     Rectangle,
87     RectangleList,
88 
89     // region and path - intersected. if either is empty, don't use
90     Region
91 };
92 
93 struct ClipBase {
ClipBaseClipBase94     explicit ClipBase(ClipMode mode) : mode(mode) {}
ClipBaseClipBase95     explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {}
96     const ClipMode mode;
97     bool intersectWithRoot = false;
98     // Bounds of the clipping area, used to define the scissor, and define which
99     // portion of the stencil is updated/used
100     Rect rect;
101 
102     void dump() const;
103 };
104 
105 struct ClipRect : ClipBase {
ClipRectClipRect106     explicit ClipRect(const Rect& rect) : ClipBase(rect) {}
107 };
108 
109 struct ClipRectList : ClipBase {
ClipRectListClipRectList110     explicit ClipRectList(const RectangleList& rectList)
111             : ClipBase(ClipMode::RectangleList), rectList(rectList) {}
112     RectangleList rectList;
113 };
114 
115 struct ClipRegion : ClipBase {
ClipRegionClipRegion116     explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {}
ClipRegionClipRegion117     ClipRegion() : ClipBase(ClipMode::Region) {}
118     SkRegion region;
119 };
120 
121 class ClipArea {
122 public:
123     ClipArea();
124 
125     void setViewportDimensions(int width, int height);
126 
isEmpty()127     bool isEmpty() const { return mClipRect.isEmpty(); }
128 
129     void setEmpty();
130     void setClip(float left, float top, float right, float bottom);
131     void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
132     void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op);
133 
getClipRect()134     const Rect& getClipRect() const { return mClipRect; }
135 
getClipRegion()136     const SkRegion& getClipRegion() const { return mClipRegion; }
137 
getRectangleList()138     const RectangleList& getRectangleList() const { return mRectangleList; }
139 
isRegion()140     bool isRegion() const { return ClipMode::Region == mMode; }
141 
isSimple()142     bool isSimple() const { return mMode == ClipMode::Rectangle; }
143 
isRectangleList()144     bool isRectangleList() const { return mMode == ClipMode::RectangleList; }
145 
146     WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
147     WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
148             LinearAllocator& allocator, const ClipBase* recordedClip,
149             const Matrix4& recordedClipTransform);
150     void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
151 
152     static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
153 
154 private:
155     void enterRectangleMode();
156     void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
157 
158     void enterRectangleListMode();
159     void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
160                                                 SkRegion::Op op);
161 
162     void enterRegionModeFromRectangleMode();
163     void enterRegionModeFromRectangleListMode();
164     void enterRegionMode();
165     void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
166 
167     void clipRegion(const SkRegion& region, SkRegion::Op op);
168     void ensureClipRegion();
169     void onClipRegionUpdated();
170 
171     // Called by every state modifying public method.
onClipUpdated()172     void onClipUpdated() {
173         mPostViewportClipObserved = true;
174         mLastSerialization = nullptr;
175         mLastResolutionResult = nullptr;
176     }
177 
createViewportRegion()178     SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); }
179 
regionFromPath(const SkPath & path,SkRegion & pathAsRegion)180     void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
181         // TODO: this should not mask every path to the viewport - this makes it impossible to use
182         // paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
183         pathAsRegion.setPath(path, createViewportRegion());
184     }
185 
186     ClipMode mMode;
187     bool mPostViewportClipObserved = false;
188     bool mReplaceOpObserved = false;
189 
190     /**
191      * If mLastSerialization is non-null, it represents an already serialized copy
192      * of the current clip state. If null, it has not been computed.
193      */
194     const ClipBase* mLastSerialization = nullptr;
195 
196     /**
197      * This pair of pointers is a single entry cache of most recently seen
198      */
199     const ClipBase* mLastResolutionResult = nullptr;
200     const ClipBase* mLastResolutionClip = nullptr;
201     Matrix4 mLastResolutionTransform;
202 
203     Rect mViewportBounds;
204     Rect mClipRect;
205     SkRegion mClipRegion;
206     RectangleList mRectangleList;
207 };
208 
209 } /* namespace uirenderer */
210 } /* namespace android */
211 
212 #endif /* CLIPAREA_H_ */
213