blob: 8e7efb4e35d67d3b90b05375cdaa8f3cb906bbaa [file] [log] [blame]
Rob Tsuk487a92c2015-01-06 13:22:54 -08001/*
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
23namespace android {
24namespace uirenderer {
25
26static 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
34static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
Chris Craik4d3e7042015-08-20 12:54:25 -070035 Vertex v = {x, y};
Rob Tsuk487a92c2015-01-06 13:22:54 -080036 transform.mapPoint(v.x, v.y);
37 transformedBounds.expandToCoverVertex(v.x, v.y);
38}
39
40Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
41 const float kMinFloat = std::numeric_limits<float>::lowest();
42 const float kMaxFloat = std::numeric_limits<float>::max();
43 Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
44 handlePoint(transformedBounds, transform, r.left, r.top);
45 handlePoint(transformedBounds, transform, r.right, r.top);
46 handlePoint(transformedBounds, transform, r.left, r.bottom);
47 handlePoint(transformedBounds, transform, r.right, r.bottom);
48 return transformedBounds;
49}
50
51/*
52 * TransformedRectangle
53 */
54
55TransformedRectangle::TransformedRectangle() {
56}
57
58TransformedRectangle::TransformedRectangle(const Rect& bounds,
59 const Matrix4& transform)
60 : mBounds(bounds)
61 , mTransform(transform) {
62}
63
64bool TransformedRectangle::canSimplyIntersectWith(
65 const TransformedRectangle& other) const {
66
67 return mTransform == other.mTransform;
68}
69
70bool TransformedRectangle::intersectWith(const TransformedRectangle& other) {
71 Rect translatedBounds(other.mBounds);
72 return intersect(mBounds, translatedBounds);
73}
74
75bool TransformedRectangle::isEmpty() const {
76 return mBounds.isEmpty();
77}
78
79/*
80 * RectangleList
81 */
82
83RectangleList::RectangleList()
84 : mTransformedRectanglesCount(0) {
85}
86
87bool RectangleList::isEmpty() const {
88 if (mTransformedRectanglesCount < 1) {
89 return true;
90 }
91
92 for (int i = 0; i < mTransformedRectanglesCount; i++) {
93 if (mTransformedRectangles[i].isEmpty()) {
94 return true;
95 }
96 }
97 return false;
98}
99
100int RectangleList::getTransformedRectanglesCount() const {
101 return mTransformedRectanglesCount;
102}
103
104const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
105 return mTransformedRectangles[i];
106}
107
108void RectangleList::setEmpty() {
109 mTransformedRectanglesCount = 0;
110}
111
112void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
113 mTransformedRectanglesCount = 1;
114 mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
115}
116
117bool RectangleList::intersectWith(const Rect& bounds,
118 const Matrix4& transform) {
119 TransformedRectangle newRectangle(bounds, transform);
120
121 // Try to find a rectangle with a compatible transformation
122 int index = 0;
123 for (; index < mTransformedRectanglesCount; index++) {
124 TransformedRectangle& tr(mTransformedRectangles[index]);
125 if (tr.canSimplyIntersectWith(newRectangle)) {
126 tr.intersectWith(newRectangle);
127 return true;
128 }
129 }
130
131 // Add it to the list if there is room
132 if (index < kMaxTransformedRectangles) {
133 mTransformedRectangles[index] = newRectangle;
134 mTransformedRectanglesCount += 1;
135 return true;
136 }
137
138 // This rectangle list is full
139 return false;
140}
141
142Rect RectangleList::calculateBounds() const {
143 Rect bounds;
144 for (int index = 0; index < mTransformedRectanglesCount; index++) {
145 const TransformedRectangle& tr(mTransformedRectangles[index]);
146 if (index == 0) {
147 bounds = tr.transformedBounds();
148 } else {
149 bounds.intersect(tr.transformedBounds());
150 }
151 }
152 return bounds;
153}
154
155static SkPath pathFromTransformedRectangle(const Rect& bounds,
156 const Matrix4& transform) {
157 SkPath rectPath;
158 SkPath rectPathTransformed;
159 rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
160 SkMatrix skTransform;
161 transform.copyTo(skTransform);
162 rectPath.transform(skTransform, &rectPathTransformed);
163 return rectPathTransformed;
164}
165
166SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
167 SkRegion rectangleListAsRegion;
168 for (int index = 0; index < mTransformedRectanglesCount; index++) {
169 const TransformedRectangle& tr(mTransformedRectangles[index]);
170 SkPath rectPathTransformed = pathFromTransformedRectangle(
171 tr.getBounds(), tr.getTransform());
172 if (index == 0) {
173 rectangleListAsRegion.setPath(rectPathTransformed, clip);
174 } else {
175 SkRegion rectRegion;
176 rectRegion.setPath(rectPathTransformed, clip);
177 rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
178 }
179 }
180 return rectangleListAsRegion;
181}
182
183/*
184 * ClipArea
185 */
186
187ClipArea::ClipArea()
Chris Craik4d3e7042015-08-20 12:54:25 -0700188 : mMode(Mode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800189}
190
191/*
192 * Interface
193 */
194
195void ClipArea::setViewportDimensions(int width, int height) {
196 mViewportBounds.set(0, 0, width, height);
197 mClipRect = mViewportBounds;
198}
199
200void ClipArea::setEmpty() {
Chris Craik4d3e7042015-08-20 12:54:25 -0700201 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800202 mClipRect.setEmpty();
203 mClipRegion.setEmpty();
204 mRectangleList.setEmpty();
205}
206
207void ClipArea::setClip(float left, float top, float right, float bottom) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700208 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800209 mClipRect.set(left, top, right, bottom);
210 mClipRegion.setEmpty();
211}
212
Chris Craik4d3e7042015-08-20 12:54:25 -0700213void ClipArea::clipRectWithTransform(float left, float top, float right,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800214 float bottom, const mat4* transform, SkRegion::Op op) {
215 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700216 clipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800217}
218
Chris Craik4d3e7042015-08-20 12:54:25 -0700219void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800220 SkRegion::Op op) {
221 switch (mMode) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700222 case Mode::Rectangle:
223 rectangleModeClipRectWithTransform(r, transform, op);
224 break;
225 case Mode::RectangleList:
226 rectangleListModeClipRectWithTransform(r, transform, op);
227 break;
228 case Mode::Region:
229 regionModeClipRectWithTransform(r, transform, op);
230 break;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800231 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800232}
233
Chris Craik4d3e7042015-08-20 12:54:25 -0700234void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800235 enterRegionMode();
236 mClipRegion.op(region, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400237 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800238}
239
Chris Craik4d3e7042015-08-20 12:54:25 -0700240void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800241 SkRegion::Op op) {
242 SkMatrix skTransform;
243 transform->copyTo(skTransform);
244 SkPath transformed;
245 path.transform(skTransform, &transformed);
246 SkRegion region;
247 regionFromPath(transformed, region);
Chris Craik4d3e7042015-08-20 12:54:25 -0700248 clipRegion(region, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800249}
250
251/*
252 * Rectangle mode
253 */
254
255void ClipArea::enterRectangleMode() {
256 // Entering rectangle mode discards any
257 // existing clipping information from the other modes.
258 // The only way this occurs is by a clip setting operation.
Chris Craik4d3e7042015-08-20 12:54:25 -0700259 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800260}
261
Chris Craik4d3e7042015-08-20 12:54:25 -0700262void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800263 const mat4* transform, SkRegion::Op op) {
264
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700265 if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
266 mClipRect = r;
267 transform->mapRect(mClipRect);
Chris Craik4d3e7042015-08-20 12:54:25 -0700268 return;
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700269 } else if (op != SkRegion::kIntersect_Op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800270 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700271 regionModeClipRectWithTransform(r, transform, op);
272 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800273 }
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 }
Chris Craik4d3e7042015-08-20 12:54:25 -0700282 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800283 }
284
285 enterRectangleListMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700286 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800287}
288
Chris Craik4d3e7042015-08-20 12:54:25 -0700289void ClipArea::rectangleModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800290 float right, float bottom, const mat4* transform, SkRegion::Op op) {
291 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700292 rectangleModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800293 mClipRect = mRectangleList.calculateBounds();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800294}
295
296/*
297 * RectangleList mode implementation
298 */
299
300void ClipArea::enterRectangleListMode() {
301 // Is is only legal to enter rectangle list mode from
302 // rectangle mode, since rectangle list mode cannot represent
303 // all clip areas that can be represented by a region.
Chris Craik4d3e7042015-08-20 12:54:25 -0700304 ALOG_ASSERT(mMode == Mode::Rectangle);
305 mMode = Mode::RectangleList;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800306 mRectangleList.set(mClipRect, Matrix4::identity());
307}
308
Chris Craik4d3e7042015-08-20 12:54:25 -0700309void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800310 const mat4* transform, SkRegion::Op op) {
311 if (op != SkRegion::kIntersect_Op
312 || !mRectangleList.intersectWith(r, *transform)) {
313 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700314 regionModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800315 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800316}
317
Chris Craik4d3e7042015-08-20 12:54:25 -0700318void ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800319 float right, float bottom, const mat4* transform, SkRegion::Op op) {
320 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700321 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800322}
323
324/*
325 * Region mode implementation
326 */
327
328void ClipArea::enterRegionMode() {
Tom Hudsone30b53c2015-03-30 15:59:02 -0400329 Mode oldMode = mMode;
Chris Craik4d3e7042015-08-20 12:54:25 -0700330 mMode = Mode::Region;
331 if (oldMode != Mode::Region) {
332 if (oldMode == Mode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800333 mClipRegion.setRect(mClipRect.left, mClipRect.top,
334 mClipRect.right, mClipRect.bottom);
335 } else {
336 mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
Tom Hudsone30b53c2015-03-30 15:59:02 -0400337 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800338 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800339 }
340}
341
Chris Craik4d3e7042015-08-20 12:54:25 -0700342void ClipArea::regionModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800343 const mat4* transform, SkRegion::Op op) {
344 SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
345 SkRegion transformedRectRegion;
346 regionFromPath(transformedRect, transformedRectRegion);
347 mClipRegion.op(transformedRectRegion, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400348 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800349}
350
Chris Craik4d3e7042015-08-20 12:54:25 -0700351void ClipArea::regionModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800352 float right, float bottom, const mat4* transform, SkRegion::Op op) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700353 regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800354}
355
Tom Hudsone30b53c2015-03-30 15:59:02 -0400356void ClipArea::onClipRegionUpdated() {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800357 if (!mClipRegion.isEmpty()) {
358 mClipRect.set(mClipRegion.getBounds());
359
360 if (mClipRegion.isRect()) {
361 mClipRegion.setEmpty();
Tom Hudsone30b53c2015-03-30 15:59:02 -0400362 enterRectangleMode();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800363 }
364 } else {
365 mClipRect.setEmpty();
366 }
367}
368
369} /* namespace uirenderer */
370} /* namespace android */