blob: a9d1e4284d2ea4f547f38a29f11ee446c1e367a6 [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
Rob Tsuk487a92c2015-01-06 13:22:54 -080026static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
Chris Craik4d3e7042015-08-20 12:54:25 -070027 Vertex v = {x, y};
Rob Tsuk487a92c2015-01-06 13:22:54 -080028 transform.mapPoint(v.x, v.y);
29 transformedBounds.expandToCoverVertex(v.x, v.y);
30}
31
32Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
33 const float kMinFloat = std::numeric_limits<float>::lowest();
34 const float kMaxFloat = std::numeric_limits<float>::max();
35 Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
36 handlePoint(transformedBounds, transform, r.left, r.top);
37 handlePoint(transformedBounds, transform, r.right, r.top);
38 handlePoint(transformedBounds, transform, r.left, r.bottom);
39 handlePoint(transformedBounds, transform, r.right, r.bottom);
40 return transformedBounds;
41}
42
43/*
44 * TransformedRectangle
45 */
46
47TransformedRectangle::TransformedRectangle() {
48}
49
50TransformedRectangle::TransformedRectangle(const Rect& bounds,
51 const Matrix4& transform)
52 : mBounds(bounds)
53 , mTransform(transform) {
54}
55
56bool TransformedRectangle::canSimplyIntersectWith(
57 const TransformedRectangle& other) const {
58
59 return mTransform == other.mTransform;
60}
61
Chris Craikac02eb92015-10-05 12:23:46 -070062void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
63 mBounds.doIntersect(other.mBounds);
Rob Tsuk487a92c2015-01-06 13:22:54 -080064}
65
66bool TransformedRectangle::isEmpty() const {
67 return mBounds.isEmpty();
68}
69
70/*
71 * RectangleList
72 */
73
74RectangleList::RectangleList()
75 : mTransformedRectanglesCount(0) {
76}
77
78bool RectangleList::isEmpty() const {
79 if (mTransformedRectanglesCount < 1) {
80 return true;
81 }
82
83 for (int i = 0; i < mTransformedRectanglesCount; i++) {
84 if (mTransformedRectangles[i].isEmpty()) {
85 return true;
86 }
87 }
88 return false;
89}
90
91int RectangleList::getTransformedRectanglesCount() const {
92 return mTransformedRectanglesCount;
93}
94
95const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
96 return mTransformedRectangles[i];
97}
98
99void RectangleList::setEmpty() {
100 mTransformedRectanglesCount = 0;
101}
102
103void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
104 mTransformedRectanglesCount = 1;
105 mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
106}
107
108bool RectangleList::intersectWith(const Rect& bounds,
109 const Matrix4& transform) {
110 TransformedRectangle newRectangle(bounds, transform);
111
112 // Try to find a rectangle with a compatible transformation
113 int index = 0;
114 for (; index < mTransformedRectanglesCount; index++) {
115 TransformedRectangle& tr(mTransformedRectangles[index]);
116 if (tr.canSimplyIntersectWith(newRectangle)) {
117 tr.intersectWith(newRectangle);
118 return true;
119 }
120 }
121
122 // Add it to the list if there is room
123 if (index < kMaxTransformedRectangles) {
124 mTransformedRectangles[index] = newRectangle;
125 mTransformedRectanglesCount += 1;
126 return true;
127 }
128
129 // This rectangle list is full
130 return false;
131}
132
133Rect RectangleList::calculateBounds() const {
134 Rect bounds;
135 for (int index = 0; index < mTransformedRectanglesCount; index++) {
136 const TransformedRectangle& tr(mTransformedRectangles[index]);
137 if (index == 0) {
138 bounds = tr.transformedBounds();
139 } else {
Chris Craikac02eb92015-10-05 12:23:46 -0700140 bounds.doIntersect(tr.transformedBounds());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800141 }
142 }
143 return bounds;
144}
145
146static SkPath pathFromTransformedRectangle(const Rect& bounds,
147 const Matrix4& transform) {
148 SkPath rectPath;
149 SkPath rectPathTransformed;
150 rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
151 SkMatrix skTransform;
152 transform.copyTo(skTransform);
153 rectPath.transform(skTransform, &rectPathTransformed);
154 return rectPathTransformed;
155}
156
157SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
158 SkRegion rectangleListAsRegion;
159 for (int index = 0; index < mTransformedRectanglesCount; index++) {
160 const TransformedRectangle& tr(mTransformedRectangles[index]);
161 SkPath rectPathTransformed = pathFromTransformedRectangle(
162 tr.getBounds(), tr.getTransform());
163 if (index == 0) {
164 rectangleListAsRegion.setPath(rectPathTransformed, clip);
165 } else {
166 SkRegion rectRegion;
167 rectRegion.setPath(rectPathTransformed, clip);
168 rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
169 }
170 }
171 return rectangleListAsRegion;
172}
173
174/*
175 * ClipArea
176 */
177
178ClipArea::ClipArea()
Chris Craik4d3e7042015-08-20 12:54:25 -0700179 : mMode(Mode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800180}
181
182/*
183 * Interface
184 */
185
186void ClipArea::setViewportDimensions(int width, int height) {
187 mViewportBounds.set(0, 0, width, height);
188 mClipRect = mViewportBounds;
189}
190
191void ClipArea::setEmpty() {
Chris Craik4d3e7042015-08-20 12:54:25 -0700192 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800193 mClipRect.setEmpty();
194 mClipRegion.setEmpty();
195 mRectangleList.setEmpty();
196}
197
198void ClipArea::setClip(float left, float top, float right, float bottom) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700199 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800200 mClipRect.set(left, top, right, bottom);
201 mClipRegion.setEmpty();
202}
203
Chris Craik4d3e7042015-08-20 12:54:25 -0700204void ClipArea::clipRectWithTransform(float left, float top, float right,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800205 float bottom, const mat4* transform, SkRegion::Op op) {
206 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700207 clipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800208}
209
Chris Craik4d3e7042015-08-20 12:54:25 -0700210void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800211 SkRegion::Op op) {
212 switch (mMode) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700213 case Mode::Rectangle:
214 rectangleModeClipRectWithTransform(r, transform, op);
215 break;
216 case Mode::RectangleList:
217 rectangleListModeClipRectWithTransform(r, transform, op);
218 break;
219 case Mode::Region:
220 regionModeClipRectWithTransform(r, transform, op);
221 break;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800222 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800223}
224
Chris Craik4d3e7042015-08-20 12:54:25 -0700225void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800226 enterRegionMode();
227 mClipRegion.op(region, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400228 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800229}
230
Chris Craik4d3e7042015-08-20 12:54:25 -0700231void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800232 SkRegion::Op op) {
233 SkMatrix skTransform;
234 transform->copyTo(skTransform);
235 SkPath transformed;
236 path.transform(skTransform, &transformed);
237 SkRegion region;
238 regionFromPath(transformed, region);
Chris Craik4d3e7042015-08-20 12:54:25 -0700239 clipRegion(region, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800240}
241
242/*
243 * Rectangle mode
244 */
245
246void ClipArea::enterRectangleMode() {
247 // Entering rectangle mode discards any
248 // existing clipping information from the other modes.
249 // The only way this occurs is by a clip setting operation.
Chris Craik4d3e7042015-08-20 12:54:25 -0700250 mMode = Mode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800251}
252
Chris Craik4d3e7042015-08-20 12:54:25 -0700253void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800254 const mat4* transform, SkRegion::Op op) {
255
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700256 if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
257 mClipRect = r;
258 transform->mapRect(mClipRect);
Chris Craik4d3e7042015-08-20 12:54:25 -0700259 return;
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700260 } else if (op != SkRegion::kIntersect_Op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800261 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700262 regionModeClipRectWithTransform(r, transform, op);
263 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800264 }
265
266 if (transform->rectToRect()) {
267 Rect transformed(r);
268 transform->mapRect(transformed);
Chris Craikac02eb92015-10-05 12:23:46 -0700269 mClipRect.doIntersect(transformed);
Chris Craik4d3e7042015-08-20 12:54:25 -0700270 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800271 }
272
273 enterRectangleListMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700274 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800275}
276
Chris Craik4d3e7042015-08-20 12:54:25 -0700277void ClipArea::rectangleModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800278 float right, float bottom, const mat4* transform, SkRegion::Op op) {
279 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700280 rectangleModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800281 mClipRect = mRectangleList.calculateBounds();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800282}
283
284/*
285 * RectangleList mode implementation
286 */
287
288void ClipArea::enterRectangleListMode() {
289 // Is is only legal to enter rectangle list mode from
290 // rectangle mode, since rectangle list mode cannot represent
291 // all clip areas that can be represented by a region.
Chris Craik4d3e7042015-08-20 12:54:25 -0700292 ALOG_ASSERT(mMode == Mode::Rectangle);
293 mMode = Mode::RectangleList;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800294 mRectangleList.set(mClipRect, Matrix4::identity());
295}
296
Chris Craik4d3e7042015-08-20 12:54:25 -0700297void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800298 const mat4* transform, SkRegion::Op op) {
299 if (op != SkRegion::kIntersect_Op
300 || !mRectangleList.intersectWith(r, *transform)) {
301 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700302 regionModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800303 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800304}
305
Chris Craik4d3e7042015-08-20 12:54:25 -0700306void ClipArea::rectangleListModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800307 float right, float bottom, const mat4* transform, SkRegion::Op op) {
308 Rect r(left, top, right, bottom);
Chris Craik4d3e7042015-08-20 12:54:25 -0700309 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800310}
311
312/*
313 * Region mode implementation
314 */
315
316void ClipArea::enterRegionMode() {
Tom Hudsone30b53c2015-03-30 15:59:02 -0400317 Mode oldMode = mMode;
Chris Craik4d3e7042015-08-20 12:54:25 -0700318 mMode = Mode::Region;
319 if (oldMode != Mode::Region) {
320 if (oldMode == Mode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800321 mClipRegion.setRect(mClipRect.left, mClipRect.top,
322 mClipRect.right, mClipRect.bottom);
323 } else {
324 mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
Tom Hudsone30b53c2015-03-30 15:59:02 -0400325 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800326 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800327 }
328}
329
Chris Craik4d3e7042015-08-20 12:54:25 -0700330void ClipArea::regionModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800331 const mat4* transform, SkRegion::Op op) {
332 SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
333 SkRegion transformedRectRegion;
334 regionFromPath(transformedRect, transformedRectRegion);
335 mClipRegion.op(transformedRectRegion, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400336 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800337}
338
Chris Craik4d3e7042015-08-20 12:54:25 -0700339void ClipArea::regionModeClipRectWithTransform(float left, float top,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800340 float right, float bottom, const mat4* transform, SkRegion::Op op) {
Chris Craik4d3e7042015-08-20 12:54:25 -0700341 regionModeClipRectWithTransform(Rect(left, top, right, bottom), transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800342}
343
Tom Hudsone30b53c2015-03-30 15:59:02 -0400344void ClipArea::onClipRegionUpdated() {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800345 if (!mClipRegion.isEmpty()) {
346 mClipRect.set(mClipRegion.getBounds());
347
348 if (mClipRegion.isRect()) {
349 mClipRegion.setEmpty();
Tom Hudsone30b53c2015-03-30 15:59:02 -0400350 enterRectangleMode();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800351 }
352 } else {
353 mClipRect.setEmpty();
354 }
355}
356
357} /* namespace uirenderer */
358} /* namespace android */