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
25namespace android {
26namespace uirenderer {
27
28class LinearAllocator;
29
30Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
31
32class TransformedRectangle {
33public:
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
42    const Rect& getBounds() const {
43        return mBounds;
44    }
45
46    Rect transformedBounds() const {
47        Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
48        return transformedBounds;
49    }
50
51    const Matrix4& getTransform() const {
52        return mTransform;
53    }
54
55    void transform(const Matrix4& transform) {
56        Matrix4 t;
57        t.loadMultiply(transform, mTransform);
58        mTransform = t;
59    }
60
61private:
62    Rect mBounds;
63    Matrix4 mTransform;
64};
65
66class RectangleList {
67public:
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
86private:
87    int mTransformedRectanglesCount;
88    TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
89};
90
91enum class ClipMode {
92    Rectangle,
93    RectangleList,
94
95    // region and path - intersected. if either is empty, don't use
96    Region
97};
98
99struct ClipBase {
100    ClipBase(ClipMode mode)
101            : mode(mode) {}
102    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
114struct ClipRect : ClipBase {
115    ClipRect(const Rect& rect)
116            : ClipBase(rect) {}
117};
118
119struct ClipRectList : ClipBase {
120    ClipRectList(const RectangleList& rectList)
121            : ClipBase(ClipMode::RectangleList)
122            , rectList(rectList) {}
123    RectangleList rectList;
124};
125
126struct ClipRegion : ClipBase {
127    ClipRegion(const SkRegion& region)
128            : ClipBase(ClipMode::Region)
129            , region(region) {}
130    ClipRegion()
131            : ClipBase(ClipMode::Region) {}
132    SkRegion region;
133};
134
135class ClipArea {
136public:
137    ClipArea();
138
139    void setViewportDimensions(int width, int height);
140
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
153    const Rect& getClipRect() const {
154        return mClipRect;
155    }
156
157    const SkRegion& getClipRegion() const {
158        return mClipRegion;
159    }
160
161    const RectangleList& getRectangleList() const {
162        return mRectangleList;
163    }
164
165    bool isRegion() const {
166        return ClipMode::Region == mMode;
167    }
168
169    bool isSimple() const {
170        return mMode == ClipMode::Rectangle;
171    }
172
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
182private:
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.
200    void onClipUpdated() {
201        mPostViewportClipObserved = true;
202        mLastSerialization = nullptr;
203        mLastResolutionResult = nullptr;
204    }
205
206    SkRegion createViewportRegion() {
207        return SkRegion(mViewportBounds.toSkIRect());
208    }
209
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