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