blob: 35fe06dc5e21b2ef05054158d5ced6e4c77801bc [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
Chris Craike4db79d2015-12-22 16:32:23 -080018#include "utils/LinearAllocator.h"
19
Rob Tsuk487a92c2015-01-06 13:22:54 -080020#include <SkPath.h>
21#include <limits>
Chris Craike4db79d2015-12-22 16:32:23 -080022#include <type_traits>
Rob Tsuk487a92c2015-01-06 13:22:54 -080023
24namespace android {
25namespace uirenderer {
26
Rob Tsuk487a92c2015-01-06 13:22:54 -080027static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) {
Chris Craik4d3e7042015-08-20 12:54:25 -070028 Vertex v = {x, y};
Rob Tsuk487a92c2015-01-06 13:22:54 -080029 transform.mapPoint(v.x, v.y);
Chris Craik15c3f192015-12-03 12:16:56 -080030 transformedBounds.expandToCover(v.x, v.y);
Rob Tsuk487a92c2015-01-06 13:22:54 -080031}
32
33Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
34 const float kMinFloat = std::numeric_limits<float>::lowest();
35 const float kMaxFloat = std::numeric_limits<float>::max();
36 Rect transformedBounds = { kMaxFloat, kMaxFloat, kMinFloat, kMinFloat };
37 handlePoint(transformedBounds, transform, r.left, r.top);
38 handlePoint(transformedBounds, transform, r.right, r.top);
39 handlePoint(transformedBounds, transform, r.left, r.bottom);
40 handlePoint(transformedBounds, transform, r.right, r.bottom);
41 return transformedBounds;
42}
43
Chris Craik02806282016-03-11 19:16:21 -080044void ClipBase::dump() const {
45 ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
46}
47
Rob Tsuk487a92c2015-01-06 13:22:54 -080048/*
49 * TransformedRectangle
50 */
51
52TransformedRectangle::TransformedRectangle() {
53}
54
55TransformedRectangle::TransformedRectangle(const Rect& bounds,
56 const Matrix4& transform)
57 : mBounds(bounds)
58 , mTransform(transform) {
59}
60
61bool TransformedRectangle::canSimplyIntersectWith(
62 const TransformedRectangle& other) const {
63
64 return mTransform == other.mTransform;
65}
66
Chris Craikac02eb92015-10-05 12:23:46 -070067void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
68 mBounds.doIntersect(other.mBounds);
Rob Tsuk487a92c2015-01-06 13:22:54 -080069}
70
71bool TransformedRectangle::isEmpty() const {
72 return mBounds.isEmpty();
73}
74
75/*
76 * RectangleList
77 */
78
79RectangleList::RectangleList()
80 : mTransformedRectanglesCount(0) {
81}
82
83bool RectangleList::isEmpty() const {
84 if (mTransformedRectanglesCount < 1) {
85 return true;
86 }
87
88 for (int i = 0; i < mTransformedRectanglesCount; i++) {
89 if (mTransformedRectangles[i].isEmpty()) {
90 return true;
91 }
92 }
93 return false;
94}
95
96int RectangleList::getTransformedRectanglesCount() const {
97 return mTransformedRectanglesCount;
98}
99
100const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
101 return mTransformedRectangles[i];
102}
103
104void RectangleList::setEmpty() {
105 mTransformedRectanglesCount = 0;
106}
107
108void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
109 mTransformedRectanglesCount = 1;
110 mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
111}
112
113bool RectangleList::intersectWith(const Rect& bounds,
114 const Matrix4& transform) {
115 TransformedRectangle newRectangle(bounds, transform);
116
117 // Try to find a rectangle with a compatible transformation
118 int index = 0;
119 for (; index < mTransformedRectanglesCount; index++) {
120 TransformedRectangle& tr(mTransformedRectangles[index]);
121 if (tr.canSimplyIntersectWith(newRectangle)) {
122 tr.intersectWith(newRectangle);
123 return true;
124 }
125 }
126
127 // Add it to the list if there is room
128 if (index < kMaxTransformedRectangles) {
129 mTransformedRectangles[index] = newRectangle;
130 mTransformedRectanglesCount += 1;
131 return true;
132 }
133
134 // This rectangle list is full
135 return false;
136}
137
138Rect RectangleList::calculateBounds() const {
139 Rect bounds;
140 for (int index = 0; index < mTransformedRectanglesCount; index++) {
141 const TransformedRectangle& tr(mTransformedRectangles[index]);
142 if (index == 0) {
143 bounds = tr.transformedBounds();
144 } else {
Chris Craikac02eb92015-10-05 12:23:46 -0700145 bounds.doIntersect(tr.transformedBounds());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800146 }
147 }
148 return bounds;
149}
150
151static SkPath pathFromTransformedRectangle(const Rect& bounds,
152 const Matrix4& transform) {
153 SkPath rectPath;
154 SkPath rectPathTransformed;
155 rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
156 SkMatrix skTransform;
157 transform.copyTo(skTransform);
158 rectPath.transform(skTransform, &rectPathTransformed);
159 return rectPathTransformed;
160}
161
162SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
163 SkRegion rectangleListAsRegion;
164 for (int index = 0; index < mTransformedRectanglesCount; index++) {
165 const TransformedRectangle& tr(mTransformedRectangles[index]);
166 SkPath rectPathTransformed = pathFromTransformedRectangle(
167 tr.getBounds(), tr.getTransform());
168 if (index == 0) {
169 rectangleListAsRegion.setPath(rectPathTransformed, clip);
170 } else {
171 SkRegion rectRegion;
172 rectRegion.setPath(rectPathTransformed, clip);
173 rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
174 }
175 }
176 return rectangleListAsRegion;
177}
178
Chris Craike4db79d2015-12-22 16:32:23 -0800179void RectangleList::transform(const Matrix4& transform) {
180 for (int index = 0; index < mTransformedRectanglesCount; index++) {
181 mTransformedRectangles[index].transform(transform);
182 }
183}
184
Rob Tsuk487a92c2015-01-06 13:22:54 -0800185/*
186 * ClipArea
187 */
188
189ClipArea::ClipArea()
Chris Craike4db79d2015-12-22 16:32:23 -0800190 : mMode(ClipMode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800191}
192
193/*
194 * Interface
195 */
196
197void ClipArea::setViewportDimensions(int width, int height) {
Chris Craike4db79d2015-12-22 16:32:23 -0800198 mPostViewportClipObserved = false;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800199 mViewportBounds.set(0, 0, width, height);
200 mClipRect = mViewportBounds;
201}
202
203void ClipArea::setEmpty() {
Chris Craike4db79d2015-12-22 16:32:23 -0800204 onClipUpdated();
205 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800206 mClipRect.setEmpty();
207 mClipRegion.setEmpty();
208 mRectangleList.setEmpty();
209}
210
211void ClipArea::setClip(float left, float top, float right, float bottom) {
Chris Craike4db79d2015-12-22 16:32:23 -0800212 onClipUpdated();
213 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800214 mClipRect.set(left, top, right, bottom);
215 mClipRegion.setEmpty();
216}
217
Chris Craik4d3e7042015-08-20 12:54:25 -0700218void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800219 SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700220 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800221 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800222 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800223 switch (mMode) {
Chris Craike4db79d2015-12-22 16:32:23 -0800224 case ClipMode::Rectangle:
Chris Craik4d3e7042015-08-20 12:54:25 -0700225 rectangleModeClipRectWithTransform(r, transform, op);
226 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800227 case ClipMode::RectangleList:
Chris Craik4d3e7042015-08-20 12:54:25 -0700228 rectangleListModeClipRectWithTransform(r, transform, op);
229 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800230 case ClipMode::Region:
Chris Craik4d3e7042015-08-20 12:54:25 -0700231 regionModeClipRectWithTransform(r, transform, op);
232 break;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800233 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800234}
235
Chris Craik4d3e7042015-08-20 12:54:25 -0700236void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700237 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800238 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800239 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800240 enterRegionMode();
241 mClipRegion.op(region, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400242 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800243}
244
Chris Craik4d3e7042015-08-20 12:54:25 -0700245void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800246 SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700247 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800248 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800249 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800250 SkMatrix skTransform;
251 transform->copyTo(skTransform);
252 SkPath transformed;
253 path.transform(skTransform, &transformed);
254 SkRegion region;
255 regionFromPath(transformed, region);
Chris Craik4d3e7042015-08-20 12:54:25 -0700256 clipRegion(region, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800257}
258
259/*
260 * Rectangle mode
261 */
262
263void ClipArea::enterRectangleMode() {
264 // Entering rectangle mode discards any
265 // existing clipping information from the other modes.
266 // The only way this occurs is by a clip setting operation.
Chris Craike4db79d2015-12-22 16:32:23 -0800267 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800268}
269
Chris Craik4d3e7042015-08-20 12:54:25 -0700270void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800271 const mat4* transform, SkRegion::Op op) {
272
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700273 if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
274 mClipRect = r;
275 transform->mapRect(mClipRect);
Chris Craik4d3e7042015-08-20 12:54:25 -0700276 return;
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700277 } else if (op != SkRegion::kIntersect_Op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800278 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700279 regionModeClipRectWithTransform(r, transform, op);
280 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800281 }
282
283 if (transform->rectToRect()) {
284 Rect transformed(r);
285 transform->mapRect(transformed);
Chris Craikac02eb92015-10-05 12:23:46 -0700286 mClipRect.doIntersect(transformed);
Chris Craik4d3e7042015-08-20 12:54:25 -0700287 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800288 }
289
290 enterRectangleListMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700291 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800292}
293
Rob Tsuk487a92c2015-01-06 13:22:54 -0800294/*
295 * RectangleList mode implementation
296 */
297
298void ClipArea::enterRectangleListMode() {
299 // Is is only legal to enter rectangle list mode from
300 // rectangle mode, since rectangle list mode cannot represent
301 // all clip areas that can be represented by a region.
Chris Craike4db79d2015-12-22 16:32:23 -0800302 ALOG_ASSERT(mMode == ClipMode::Rectangle);
303 mMode = ClipMode::RectangleList;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800304 mRectangleList.set(mClipRect, Matrix4::identity());
305}
306
Chris Craik4d3e7042015-08-20 12:54:25 -0700307void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800308 const mat4* transform, SkRegion::Op op) {
309 if (op != SkRegion::kIntersect_Op
310 || !mRectangleList.intersectWith(r, *transform)) {
311 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700312 regionModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800313 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800314}
315
Rob Tsuk487a92c2015-01-06 13:22:54 -0800316/*
317 * Region mode implementation
318 */
319
320void ClipArea::enterRegionMode() {
Chris Craike4db79d2015-12-22 16:32:23 -0800321 ClipMode oldMode = mMode;
322 mMode = ClipMode::Region;
323 if (oldMode != ClipMode::Region) {
324 if (oldMode == ClipMode::Rectangle) {
325 mClipRegion.setRect(mClipRect.toSkIRect());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800326 } else {
327 mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
Tom Hudsone30b53c2015-03-30 15:59:02 -0400328 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800329 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800330 }
331}
332
Chris Craik4d3e7042015-08-20 12:54:25 -0700333void ClipArea::regionModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800334 const mat4* transform, SkRegion::Op op) {
335 SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
336 SkRegion transformedRectRegion;
337 regionFromPath(transformedRect, transformedRectRegion);
338 mClipRegion.op(transformedRectRegion, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400339 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800340}
341
Tom Hudsone30b53c2015-03-30 15:59:02 -0400342void ClipArea::onClipRegionUpdated() {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800343 if (!mClipRegion.isEmpty()) {
344 mClipRect.set(mClipRegion.getBounds());
345
346 if (mClipRegion.isRect()) {
347 mClipRegion.setEmpty();
Tom Hudsone30b53c2015-03-30 15:59:02 -0400348 enterRectangleMode();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800349 }
350 } else {
351 mClipRect.setEmpty();
352 }
353}
354
Chris Craike4db79d2015-12-22 16:32:23 -0800355/**
356 * Clip serialization
357 */
358
359const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
360 if (!mPostViewportClipObserved) {
361 // Only initial clip-to-viewport observed, so no serialization of clip necessary
362 return nullptr;
363 }
364
365 static_assert(std::is_trivially_destructible<Rect>::value,
366 "expect Rect to be trivially destructible");
367 static_assert(std::is_trivially_destructible<RectangleList>::value,
368 "expect RectangleList to be trivially destructible");
369
370 if (mLastSerialization == nullptr) {
Chris Craik4f4c6082016-02-09 16:32:32 -0800371 ClipBase* serialization = nullptr;
Chris Craike4db79d2015-12-22 16:32:23 -0800372 switch (mMode) {
373 case ClipMode::Rectangle:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800374 serialization = allocator.create<ClipRect>(mClipRect);
Chris Craike4db79d2015-12-22 16:32:23 -0800375 break;
376 case ClipMode::RectangleList:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800377 serialization = allocator.create<ClipRectList>(mRectangleList);
378 serialization->rect = mRectangleList.calculateBounds();
Chris Craike4db79d2015-12-22 16:32:23 -0800379 break;
380 case ClipMode::Region:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800381 serialization = allocator.create<ClipRegion>(mClipRegion);
382 serialization->rect.set(mClipRegion.getBounds());
Chris Craike4db79d2015-12-22 16:32:23 -0800383 break;
384 }
Chris Craik04d46eb2016-04-07 13:51:07 -0700385 serialization->intersectWithRoot = mReplaceOpObserved;
Chris Craik69aeabe2016-03-03 12:58:39 -0800386 // TODO: this is only done for draw time, should eventually avoid for record time
387 serialization->rect.snapToPixelBoundaries();
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800388 mLastSerialization = serialization;
Chris Craike4db79d2015-12-22 16:32:23 -0800389 }
390 return mLastSerialization;
391}
392
Chris Craike4db79d2015-12-22 16:32:23 -0800393inline static const RectangleList& getRectList(const ClipBase* scb) {
394 return reinterpret_cast<const ClipRectList*>(scb)->rectList;
395}
396
397inline static const SkRegion& getRegion(const ClipBase* scb) {
398 return reinterpret_cast<const ClipRegion*>(scb)->region;
399}
400
401// Conservative check for too many rectangles to fit in rectangle list.
402// For simplicity, doesn't account for rect merging
403static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
404 int currentRectCount = clipArea.isRectangleList()
405 ? clipArea.getRectangleList().getTransformedRectanglesCount()
406 : 1;
407 int recordedRectCount = (scb->mode == ClipMode::RectangleList)
408 ? getRectList(scb).getTransformedRectanglesCount()
409 : 1;
410 return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
411}
412
Chris Craik261725f2016-02-29 12:52:33 -0800413static const ClipRect sEmptyClipRect(Rect(0, 0));
414
Chris Craike4db79d2015-12-22 16:32:23 -0800415const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
416 const ClipBase* recordedClip, const Matrix4& recordedClipTransform) {
Chris Craik261725f2016-02-29 12:52:33 -0800417
Chris Craike4db79d2015-12-22 16:32:23 -0800418 // if no recordedClip passed, just serialize current state
419 if (!recordedClip) return serializeClip(allocator);
420
Chris Craik261725f2016-02-29 12:52:33 -0800421 // if either is empty, clip is empty
422 if (CC_UNLIKELY(recordedClip->rect.isEmpty())|| mClipRect.isEmpty()) return &sEmptyClipRect;
423
Chris Craike4db79d2015-12-22 16:32:23 -0800424 if (!mLastResolutionResult
425 || recordedClip != mLastResolutionClip
426 || recordedClipTransform != mLastResolutionTransform) {
427 mLastResolutionClip = recordedClip;
428 mLastResolutionTransform = recordedClipTransform;
429
430 if (CC_LIKELY(mMode == ClipMode::Rectangle
431 && recordedClip->mode == ClipMode::Rectangle
432 && recordedClipTransform.rectToRect())) {
433 // common case - result is a single rectangle
Chris Craik69aeabe2016-03-03 12:58:39 -0800434 auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
Chris Craike4db79d2015-12-22 16:32:23 -0800435 recordedClipTransform.mapRect(rectClip->rect);
436 rectClip->rect.doIntersect(mClipRect);
Chris Craik69aeabe2016-03-03 12:58:39 -0800437 rectClip->rect.snapToPixelBoundaries();
Chris Craike4db79d2015-12-22 16:32:23 -0800438 mLastResolutionResult = rectClip;
439 } else if (CC_UNLIKELY(mMode == ClipMode::Region
440 || recordedClip->mode == ClipMode::Region
441 || cannotFitInRectangleList(*this, recordedClip))) {
442 // region case
443 SkRegion other;
444 switch (recordedClip->mode) {
445 case ClipMode::Rectangle:
446 if (CC_LIKELY(recordedClipTransform.rectToRect())) {
447 // simple transform, skip creating SkPath
Chris Craik69aeabe2016-03-03 12:58:39 -0800448 Rect resultClip(recordedClip->rect);
Chris Craike4db79d2015-12-22 16:32:23 -0800449 recordedClipTransform.mapRect(resultClip);
450 other.setRect(resultClip.toSkIRect());
451 } else {
Chris Craik69aeabe2016-03-03 12:58:39 -0800452 SkPath transformedRect = pathFromTransformedRectangle(recordedClip->rect,
Chris Craike4db79d2015-12-22 16:32:23 -0800453 recordedClipTransform);
454 other.setPath(transformedRect, createViewportRegion());
455 }
456 break;
457 case ClipMode::RectangleList: {
458 RectangleList transformedList(getRectList(recordedClip));
459 transformedList.transform(recordedClipTransform);
460 other = transformedList.convertToRegion(createViewportRegion());
461 break;
462 }
463 case ClipMode::Region:
464 other = getRegion(recordedClip);
465
466 // TODO: handle non-translate transforms properly!
467 other.translate(recordedClipTransform.getTranslateX(),
468 recordedClipTransform.getTranslateY());
469 }
470
471 ClipRegion* regionClip = allocator.create<ClipRegion>();
472 switch (mMode) {
473 case ClipMode::Rectangle:
474 regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
475 break;
476 case ClipMode::RectangleList:
477 regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
478 other, SkRegion::kIntersect_Op);
479 break;
480 case ClipMode::Region:
481 regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
482 break;
483 }
Chris Craik69aeabe2016-03-03 12:58:39 -0800484 // Don't need to snap, since region's in int bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800485 regionClip->rect.set(regionClip->region.getBounds());
486 mLastResolutionResult = regionClip;
487 } else {
488 auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
489 auto&& rectList = rectListClip->rectList;
490 if (mMode == ClipMode::Rectangle) {
491 rectList.set(mClipRect, Matrix4::identity());
492 }
493
494 if (recordedClip->mode == ClipMode::Rectangle) {
Chris Craik69aeabe2016-03-03 12:58:39 -0800495 rectList.intersectWith(recordedClip->rect, recordedClipTransform);
Chris Craike4db79d2015-12-22 16:32:23 -0800496 } else {
497 const RectangleList& other = getRectList(recordedClip);
498 for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
499 auto&& tr = other.getTransformedRectangle(i);
500 Matrix4 totalTransform(recordedClipTransform);
501 totalTransform.multiply(tr.getTransform());
502 rectList.intersectWith(tr.getBounds(), totalTransform);
503 }
504 }
505 rectListClip->rect = rectList.calculateBounds();
Chris Craik69aeabe2016-03-03 12:58:39 -0800506 rectListClip->rect.snapToPixelBoundaries();
Chris Craike4db79d2015-12-22 16:32:23 -0800507 mLastResolutionResult = rectListClip;
508 }
509 }
510 return mLastResolutionResult;
511}
512
513void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
514 if (!clip) return; // nothing to do
515
516 if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
Chris Craik69aeabe2016-03-03 12:58:39 -0800517 clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
Chris Craike4db79d2015-12-22 16:32:23 -0800518 } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
519 auto&& rectList = getRectList(clip);
520 for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
521 auto&& tr = rectList.getTransformedRectangle(i);
522 Matrix4 totalTransform(transform);
523 totalTransform.multiply(tr.getTransform());
524 clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
525 }
526 } else {
527 SkRegion region(getRegion(clip));
528 // TODO: handle non-translate transforms properly!
529 region.translate(transform.getTranslateX(), transform.getTranslateY());
530 clipRegion(region, SkRegion::kIntersect_Op);
531 }
532}
533
Rob Tsuk487a92c2015-01-06 13:22:54 -0800534} /* namespace uirenderer */
535} /* namespace android */