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 #include "ClipArea.h"
17 
18 #include <SkPath.h>
19 #include <limits>
20 
21 #include "Rect.h"
22 
23 namespace android {
24 namespace uirenderer {
25 
intersect(Rect & r,const Rect & r2)26 static bool intersect(Rect& r, const Rect& r2) {
27     bool hasIntersection = r.intersect(r2);
28     if (!hasIntersection) {
29         r.setEmpty();
30     }
31     return hasIntersection;
32 }
33 
handlePoint(Rect & transformedBounds,const Matrix4 & transform,float x,float y)34 static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
35     Vertex v;
36     v.x = x;
37     v.y = y;
38     transform.mapPoint(v.x, v.y);
39     transformedBounds.expandToCoverVertex(v.x, v.y);
40 }
41 
transformAndCalculateBounds(const Rect & r,const Matrix4 & transform)42 Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
43     const float kMinFloat = std::numeric_limits<float>::lowest();
44     const float kMaxFloat = std::numeric_limits<float>::max();
45     Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
46     handlePoint(transformedBounds, transform, r.left, r.top);
47     handlePoint(transformedBounds, transform, r.right, r.top);
48     handlePoint(transformedBounds, transform, r.left, r.bottom);
49     handlePoint(transformedBounds, transform, r.right, r.bottom);
50     return transformedBounds;
51 }
52 
53 /*
54  * TransformedRectangle
55  */
56 
TransformedRectangle()57 TransformedRectangle::TransformedRectangle() {
58 }
59 
TransformedRectangle(const Rect & bounds,const Matrix4 & transform)60 TransformedRectangle::TransformedRectangle(const Rect& bounds,
61         const Matrix4& transform)
62         : mBounds(bounds)
63         , mTransform(transform) {
64 }
65 
canSimplyIntersectWith(const TransformedRectangle & other) const66 bool TransformedRectangle::canSimplyIntersectWith(
67         const TransformedRectangle& other) const {
68 
69     return mTransform == other.mTransform;
70 }
71 
intersectWith(const TransformedRectangle & other)72 bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
73     Rect translatedBounds(other.mBounds);
74     return intersect(mBounds, translatedBounds);
75 }
76 
isEmpty() const77 bool TransformedRectangle::isEmpty() const {
78     return mBounds.isEmpty();
79 }
80 
81 /*
82  * RectangleList
83  */
84 
RectangleList()85 RectangleList::RectangleList()
86         : mTransformedRectanglesCount(0) {
87 }
88 
isEmpty() const89 bool RectangleList::isEmpty() const {
90     if (mTransformedRectanglesCount < 1) {
91         return true;
92     }
93 
94     for (int i = 0; i < mTransformedRectanglesCount; i++) {
95         if (mTransformedRectangles[i].isEmpty()) {
96             return true;
97         }
98     }
99     return false;
100 }
101 
getTransformedRectanglesCount() const102 int RectangleList::getTransformedRectanglesCount() const {
103     return mTransformedRectanglesCount;
104 }
105 
getTransformedRectangle(int i) const106 const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
107     return mTransformedRectangles[i];
108 }
109 
setEmpty()110 void RectangleList::setEmpty() {
111     mTransformedRectanglesCount = 0;
112 }
113 
set(const Rect & bounds,const Matrix4 & transform)114 void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
115     mTransformedRectanglesCount = 1;
116     mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
117 }
118 
intersectWith(const Rect & bounds,const Matrix4 & transform)119 bool RectangleList::intersectWith(const Rect& bounds,
120         const Matrix4& transform) {
121     TransformedRectangle newRectangle(bounds, transform);
122 
123     // Try to find a rectangle with a compatible transformation
124     int index = 0;
125     for (; index < mTransformedRectanglesCount; index++) {
126         TransformedRectangle& tr(mTransformedRectangles[index]);
127         if (tr.canSimplyIntersectWith(newRectangle)) {
128             tr.intersectWith(newRectangle);
129             return true;
130         }
131     }
132 
133     // Add it to the list if there is room
134     if (index < kMaxTransformedRectangles) {
135         mTransformedRectangles[index] = newRectangle;
136         mTransformedRectanglesCount += 1;
137         return true;
138     }
139 
140     // This rectangle list is full
141     return false;
142 }
143 
calculateBounds() const144 Rect RectangleList::calculateBounds() const {
145     Rect bounds;
146     for (int index = 0; index < mTransformedRectanglesCount; index++) {
147         const TransformedRectangle& tr(mTransformedRectangles[index]);
148         if (index == 0) {
149             bounds = tr.transformedBounds();
150         } else {
151             bounds.intersect(tr.transformedBounds());
152         }
153     }
154     return bounds;
155 }
156 
pathFromTransformedRectangle(const Rect & bounds,const Matrix4 & transform)157 static SkPath pathFromTransformedRectangle(const Rect& bounds,
158         const Matrix4& transform) {
159     SkPath rectPath;
160     SkPath rectPathTransformed;
161     rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
162     SkMatrix skTransform;
163     transform.copyTo(skTransform);
164     rectPath.transform(skTransform, &rectPathTransformed);
165     return rectPathTransformed;
166 }
167 
convertToRegion(const SkRegion & clip) const168 SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
169     SkRegion rectangleListAsRegion;
170     for (int index = 0; index < mTransformedRectanglesCount; index++) {
171         const TransformedRectangle& tr(mTransformedRectangles[index]);
172         SkPath rectPathTransformed = pathFromTransformedRectangle(
173                 tr.getBounds(), tr.getTransform());
174         if (index == 0) {
175             rectangleListAsRegion.setPath(rectPathTransformed, clip);
176         } else {
177             SkRegion rectRegion;
178             rectRegion.setPath(rectPathTransformed, clip);
179             rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
180         }
181     }
182     return rectangleListAsRegion;
183 }
184 
185 /*
186  * ClipArea
187  */
188 
ClipArea()189 ClipArea::ClipArea()
190         : mMode(kModeRectangle) {
191 }
192 
193 /*
194  * Interface
195  */
196 
setViewportDimensions(int width,int height)197 void ClipArea::setViewportDimensions(int width, int height) {
198     mViewportBounds.set(0, 0, width, height);
199     mClipRect = mViewportBounds;
200 }
201 
setEmpty()202 void ClipArea::setEmpty() {
203     mMode = kModeRectangle;
204     mClipRect.setEmpty();
205     mClipRegion.setEmpty();
206     mRectangleList.setEmpty();
207 }
208 
setClip(float left,float top,float right,float bottom)209 void ClipArea::setClip(float left, float top, float right, float bottom) {
210     mMode = kModeRectangle;
211     mClipRect.set(left, top, right, bottom);
212     mClipRegion.setEmpty();
213 }
214 
clipRectWithTransform(float left,float top,float right,float bottom,const mat4 * transform,SkRegion::Op op)215 bool ClipArea::clipRectWithTransform(float left, float top, float right,
216         float bottom, const mat4* transform, SkRegion::Op op) {
217     Rect r(left, top, right, bottom);
218     return clipRectWithTransform(r, transform, op);
219 }
220 
clipRectWithTransform(const Rect & r,const mat4 * transform,SkRegion::Op op)221 bool ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
222         SkRegion::Op op) {
223     switch (mMode) {
224     case kModeRectangle:
225         return rectangleModeClipRectWithTransform(r, transform, op);
226     case kModeRectangleList:
227         return rectangleListModeClipRectWithTransform(r, transform, op);
228     case kModeRegion:
229         return regionModeClipRectWithTransform(r, transform, op);
230     }
231     return false;
232 }
233 
clipRegion(const SkRegion & region,SkRegion::Op op)234 bool ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
235     enterRegionMode();
236     mClipRegion.op(region, op);
237     onClipRegionUpdated();
238     return true;
239 }
240 
clipPathWithTransform(const SkPath & path,const mat4 * transform,SkRegion::Op op)241 bool ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
242         SkRegion::Op op) {
243     SkMatrix skTransform;
244     transform->copyTo(skTransform);
245     SkPath transformed;
246     path.transform(skTransform, &transformed);
247     SkRegion region;
248     regionFromPath(transformed, region);
249     return clipRegion(region, op);
250 }
251 
252 /*
253  * Rectangle mode
254  */
255 
enterRectangleMode()256 void ClipArea::enterRectangleMode() {
257     // Entering rectangle mode discards any
258     // existing clipping information from the other modes.
259     // The only way this occurs is by a clip setting operation.
260     mMode = kModeRectangle;
261 }
262 
rectangleModeClipRectWithTransform(const Rect & r,const mat4 * transform,SkRegion::Op op)263 bool ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
264         const mat4* transform, SkRegion::Op op) {
265 
266     if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
267         mClipRect = r;
268         transform->mapRect(mClipRect);
269         return true;
270     } else if (op != SkRegion::kIntersect_Op) {
271         enterRegionMode();
272         return regionModeClipRectWithTransform(r, transform, op);
273     }
274 
275     if (transform->rectToRect()) {
276         Rect transformed(r);
277         transform->mapRect(transformed);
278         bool hasIntersection = mClipRect.intersect(transformed);
279         if (!hasIntersection) {
280             mClipRect.setEmpty();
281         }
282         return true;
283     }
284 
285     enterRectangleListMode();
286     return rectangleListModeClipRectWithTransform(r, transform, op);
287 }
288 
rectangleModeClipRectWithTransform(float left,float top,float right,float bottom,const mat4 * transform,SkRegion::Op op)289 bool ClipArea::rectangleModeClipRectWithTransform(float left, float top,
290         float right, float bottom, const mat4* transform, SkRegion::Op op) {
291     Rect r(left, top, right, bottom);
292     bool result = rectangleModeClipRectWithTransform(r, transform, op);
293     mClipRect = mRectangleList.calculateBounds();
294     return result;
295 }
296 
297 /*
298  * RectangleList mode implementation
299  */
300 
enterRectangleListMode()301 void ClipArea::enterRectangleListMode() {
302     // Is is only legal to enter rectangle list mode from
303     // rectangle mode, since rectangle list mode cannot represent
304     // all clip areas that can be represented by a region.
305     ALOG_ASSERT(mMode == kModeRectangle);
306     mMode = kModeRectangleList;
307     mRectangleList.set(mClipRect, Matrix4::identity());
308 }
309 
rectangleListModeClipRectWithTransform(const Rect & r,const mat4 * transform,SkRegion::Op op)310 bool ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
311         const mat4* transform, SkRegion::Op op) {
312     if (op != SkRegion::kIntersect_Op
313             || !mRectangleList.intersectWith(r, *transform)) {
314         enterRegionMode();
315         return regionModeClipRectWithTransform(r, transform, op);
316     }
317     return true;
318 }
319 
rectangleListModeClipRectWithTransform(float left,float top,float right,float bottom,const mat4 * transform,SkRegion::Op op)320 bool ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
321         float right, float bottom, const mat4* transform, SkRegion::Op op) {
322     Rect r(left, top, right, bottom);
323     return rectangleListModeClipRectWithTransform(r, transform, op);
324 }
325 
326 /*
327  * Region mode implementation
328  */
329 
enterRegionMode()330 void ClipArea::enterRegionMode() {
331     Mode oldMode = mMode;
332     mMode = kModeRegion;
333     if (oldMode != kModeRegion) {
334         if (oldMode == kModeRectangle) {
335             mClipRegion.setRect(mClipRect.left, mClipRect.top,
336                     mClipRect.right, mClipRect.bottom);
337         } else {
338             mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
339             onClipRegionUpdated();
340         }
341     }
342 }
343 
regionModeClipRectWithTransform(const Rect & r,const mat4 * transform,SkRegion::Op op)344 bool ClipArea::regionModeClipRectWithTransform(const Rect& r,
345         const mat4* transform, SkRegion::Op op) {
346     SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
347     SkRegion transformedRectRegion;
348     regionFromPath(transformedRect, transformedRectRegion);
349     mClipRegion.op(transformedRectRegion, op);
350     onClipRegionUpdated();
351     return true;
352 }
353 
regionModeClipRectWithTransform(float left,float top,float right,float bottom,const mat4 * transform,SkRegion::Op op)354 bool ClipArea::regionModeClipRectWithTransform(float left, float top,
355         float right, float bottom, const mat4* transform, SkRegion::Op op) {
356     return regionModeClipRectWithTransform(Rect(left, top, right, bottom),
357             transform, op);
358 }
359 
onClipRegionUpdated()360 void ClipArea::onClipRegionUpdated() {
361     if (!mClipRegion.isEmpty()) {
362         mClipRect.set(mClipRegion.getBounds());
363 
364         if (mClipRegion.isRect()) {
365             mClipRegion.setEmpty();
366             enterRectangleMode();
367         }
368     } else {
369         mClipRect.setEmpty();
370     }
371 }
372 
373 } /* namespace uirenderer */
374 } /* namespace android */
375