blob: 27d93cfa0391597c8ea0d9f60c70d4bda6b8a794 [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();
John Reck1bcacfd2017-11-03 10:12:19 -070036 Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat};
Rob Tsuk487a92c2015-01-06 13:22:54 -080037 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
John Reck1bcacfd2017-11-03 10:12:19 -070052TransformedRectangle::TransformedRectangle() {}
Rob Tsuk487a92c2015-01-06 13:22:54 -080053
John Reck1bcacfd2017-11-03 10:12:19 -070054TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform)
55 : mBounds(bounds), mTransform(transform) {}
Rob Tsuk487a92c2015-01-06 13:22:54 -080056
John Reck1bcacfd2017-11-03 10:12:19 -070057bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const {
Rob Tsuk487a92c2015-01-06 13:22:54 -080058 return mTransform == other.mTransform;
59}
60
Chris Craikac02eb92015-10-05 12:23:46 -070061void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
62 mBounds.doIntersect(other.mBounds);
Rob Tsuk487a92c2015-01-06 13:22:54 -080063}
64
65bool TransformedRectangle::isEmpty() const {
66 return mBounds.isEmpty();
67}
68
69/*
70 * RectangleList
71 */
72
John Reck1bcacfd2017-11-03 10:12:19 -070073RectangleList::RectangleList() : mTransformedRectanglesCount(0) {}
Rob Tsuk487a92c2015-01-06 13:22:54 -080074
75bool RectangleList::isEmpty() const {
76 if (mTransformedRectanglesCount < 1) {
77 return true;
78 }
79
80 for (int i = 0; i < mTransformedRectanglesCount; i++) {
81 if (mTransformedRectangles[i].isEmpty()) {
82 return true;
83 }
84 }
85 return false;
86}
87
88int RectangleList::getTransformedRectanglesCount() const {
89 return mTransformedRectanglesCount;
90}
91
92const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
93 return mTransformedRectangles[i];
94}
95
96void RectangleList::setEmpty() {
97 mTransformedRectanglesCount = 0;
98}
99
100void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
101 mTransformedRectanglesCount = 1;
102 mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
103}
104
John Reck1bcacfd2017-11-03 10:12:19 -0700105bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800106 TransformedRectangle newRectangle(bounds, transform);
107
108 // Try to find a rectangle with a compatible transformation
109 int index = 0;
110 for (; index < mTransformedRectanglesCount; index++) {
111 TransformedRectangle& tr(mTransformedRectangles[index]);
112 if (tr.canSimplyIntersectWith(newRectangle)) {
113 tr.intersectWith(newRectangle);
114 return true;
115 }
116 }
117
118 // Add it to the list if there is room
119 if (index < kMaxTransformedRectangles) {
120 mTransformedRectangles[index] = newRectangle;
121 mTransformedRectanglesCount += 1;
122 return true;
123 }
124
125 // This rectangle list is full
126 return false;
127}
128
129Rect RectangleList::calculateBounds() const {
130 Rect bounds;
131 for (int index = 0; index < mTransformedRectanglesCount; index++) {
132 const TransformedRectangle& tr(mTransformedRectangles[index]);
133 if (index == 0) {
134 bounds = tr.transformedBounds();
135 } else {
Chris Craikac02eb92015-10-05 12:23:46 -0700136 bounds.doIntersect(tr.transformedBounds());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800137 }
138 }
139 return bounds;
140}
141
John Reck1bcacfd2017-11-03 10:12:19 -0700142static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800143 SkPath rectPath;
144 SkPath rectPathTransformed;
145 rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
146 SkMatrix skTransform;
147 transform.copyTo(skTransform);
148 rectPath.transform(skTransform, &rectPathTransformed);
149 return rectPathTransformed;
150}
151
152SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
153 SkRegion rectangleListAsRegion;
154 for (int index = 0; index < mTransformedRectanglesCount; index++) {
155 const TransformedRectangle& tr(mTransformedRectangles[index]);
John Reck1bcacfd2017-11-03 10:12:19 -0700156 SkPath rectPathTransformed =
157 pathFromTransformedRectangle(tr.getBounds(), tr.getTransform());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800158 if (index == 0) {
159 rectangleListAsRegion.setPath(rectPathTransformed, clip);
160 } else {
161 SkRegion rectRegion;
162 rectRegion.setPath(rectPathTransformed, clip);
163 rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
164 }
165 }
166 return rectangleListAsRegion;
167}
168
Chris Craike4db79d2015-12-22 16:32:23 -0800169void RectangleList::transform(const Matrix4& transform) {
170 for (int index = 0; index < mTransformedRectanglesCount; index++) {
171 mTransformedRectangles[index].transform(transform);
172 }
173}
174
Rob Tsuk487a92c2015-01-06 13:22:54 -0800175/*
176 * ClipArea
177 */
178
John Reck1bcacfd2017-11-03 10:12:19 -0700179ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {}
Rob Tsuk487a92c2015-01-06 13:22:54 -0800180
181/*
182 * Interface
183 */
184
185void ClipArea::setViewportDimensions(int width, int height) {
Chris Craike4db79d2015-12-22 16:32:23 -0800186 mPostViewportClipObserved = false;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800187 mViewportBounds.set(0, 0, width, height);
188 mClipRect = mViewportBounds;
189}
190
191void ClipArea::setEmpty() {
Chris Craike4db79d2015-12-22 16:32:23 -0800192 onClipUpdated();
193 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800194 mClipRect.setEmpty();
195 mClipRegion.setEmpty();
196 mRectangleList.setEmpty();
197}
198
199void ClipArea::setClip(float left, float top, float right, float bottom) {
Chris Craike4db79d2015-12-22 16:32:23 -0800200 onClipUpdated();
201 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800202 mClipRect.set(left, top, right, bottom);
203 mClipRegion.setEmpty();
204}
205
John Reck1bcacfd2017-11-03 10:12:19 -0700206void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700207 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800208 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800209 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800210 switch (mMode) {
John Reck1bcacfd2017-11-03 10:12:19 -0700211 case ClipMode::Rectangle:
212 rectangleModeClipRectWithTransform(r, transform, op);
213 break;
214 case ClipMode::RectangleList:
215 rectangleListModeClipRectWithTransform(r, transform, op);
216 break;
217 case ClipMode::Region:
218 regionModeClipRectWithTransform(r, transform, op);
219 break;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800220 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800221}
222
Chris Craik4d3e7042015-08-20 12:54:25 -0700223void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700224 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800225 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800226 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800227 enterRegionMode();
228 mClipRegion.op(region, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400229 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800230}
231
John Reck1bcacfd2017-11-03 10:12:19 -0700232void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) {
Chris Craik04d46eb2016-04-07 13:51:07 -0700233 if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
Chris Craik7fc1b032016-02-03 19:45:06 -0800234 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800235 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800236 SkMatrix skTransform;
237 transform->copyTo(skTransform);
238 SkPath transformed;
239 path.transform(skTransform, &transformed);
240 SkRegion region;
241 regionFromPath(transformed, region);
Chris Craikbf27b992016-04-13 15:57:46 -0700242 enterRegionMode();
243 mClipRegion.op(region, op);
244 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800245}
246
247/*
248 * Rectangle mode
249 */
250
251void ClipArea::enterRectangleMode() {
252 // Entering rectangle mode discards any
253 // existing clipping information from the other modes.
254 // The only way this occurs is by a clip setting operation.
Chris Craike4db79d2015-12-22 16:32:23 -0800255 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800256}
257
John Reck1bcacfd2017-11-03 10:12:19 -0700258void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform,
259 SkRegion::Op op) {
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700260 if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
261 mClipRect = r;
262 transform->mapRect(mClipRect);
Chris Craik4d3e7042015-08-20 12:54:25 -0700263 return;
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700264 } else if (op != SkRegion::kIntersect_Op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800265 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700266 regionModeClipRectWithTransform(r, transform, op);
267 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800268 }
269
270 if (transform->rectToRect()) {
271 Rect transformed(r);
272 transform->mapRect(transformed);
Chris Craikac02eb92015-10-05 12:23:46 -0700273 mClipRect.doIntersect(transformed);
Chris Craik4d3e7042015-08-20 12:54:25 -0700274 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800275 }
276
277 enterRectangleListMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700278 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800279}
280
Rob Tsuk487a92c2015-01-06 13:22:54 -0800281/*
282 * RectangleList mode implementation
283 */
284
285void ClipArea::enterRectangleListMode() {
286 // Is is only legal to enter rectangle list mode from
287 // rectangle mode, since rectangle list mode cannot represent
288 // all clip areas that can be represented by a region.
Chris Craike4db79d2015-12-22 16:32:23 -0800289 ALOG_ASSERT(mMode == ClipMode::Rectangle);
290 mMode = ClipMode::RectangleList;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800291 mRectangleList.set(mClipRect, Matrix4::identity());
292}
293
John Reck1bcacfd2017-11-03 10:12:19 -0700294void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
295 SkRegion::Op op) {
296 if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800297 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700298 regionModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800299 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800300}
301
Rob Tsuk487a92c2015-01-06 13:22:54 -0800302/*
303 * Region mode implementation
304 */
305
306void ClipArea::enterRegionMode() {
Chris Craike4db79d2015-12-22 16:32:23 -0800307 ClipMode oldMode = mMode;
308 mMode = ClipMode::Region;
309 if (oldMode != ClipMode::Region) {
310 if (oldMode == ClipMode::Rectangle) {
311 mClipRegion.setRect(mClipRect.toSkIRect());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800312 } else {
313 mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
Tom Hudsone30b53c2015-03-30 15:59:02 -0400314 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800315 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800316 }
317}
318
John Reck1bcacfd2017-11-03 10:12:19 -0700319void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform,
320 SkRegion::Op op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800321 SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
322 SkRegion transformedRectRegion;
323 regionFromPath(transformedRect, transformedRectRegion);
324 mClipRegion.op(transformedRectRegion, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400325 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800326}
327
Tom Hudsone30b53c2015-03-30 15:59:02 -0400328void ClipArea::onClipRegionUpdated() {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800329 if (!mClipRegion.isEmpty()) {
330 mClipRect.set(mClipRegion.getBounds());
331
332 if (mClipRegion.isRect()) {
333 mClipRegion.setEmpty();
Tom Hudsone30b53c2015-03-30 15:59:02 -0400334 enterRectangleMode();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800335 }
336 } else {
337 mClipRect.setEmpty();
338 }
339}
340
Chris Craike4db79d2015-12-22 16:32:23 -0800341/**
342 * Clip serialization
343 */
344
345const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
346 if (!mPostViewportClipObserved) {
347 // Only initial clip-to-viewport observed, so no serialization of clip necessary
348 return nullptr;
349 }
350
351 static_assert(std::is_trivially_destructible<Rect>::value,
John Reck1bcacfd2017-11-03 10:12:19 -0700352 "expect Rect to be trivially destructible");
Chris Craike4db79d2015-12-22 16:32:23 -0800353 static_assert(std::is_trivially_destructible<RectangleList>::value,
John Reck1bcacfd2017-11-03 10:12:19 -0700354 "expect RectangleList to be trivially destructible");
Chris Craike4db79d2015-12-22 16:32:23 -0800355
356 if (mLastSerialization == nullptr) {
Chris Craik4f4c6082016-02-09 16:32:32 -0800357 ClipBase* serialization = nullptr;
Chris Craike4db79d2015-12-22 16:32:23 -0800358 switch (mMode) {
John Reck1bcacfd2017-11-03 10:12:19 -0700359 case ClipMode::Rectangle:
360 serialization = allocator.create<ClipRect>(mClipRect);
361 break;
362 case ClipMode::RectangleList:
363 serialization = allocator.create<ClipRectList>(mRectangleList);
364 serialization->rect = mRectangleList.calculateBounds();
365 break;
366 case ClipMode::Region:
367 serialization = allocator.create<ClipRegion>(mClipRegion);
368 serialization->rect.set(mClipRegion.getBounds());
369 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800370 }
Chris Craik04d46eb2016-04-07 13:51:07 -0700371 serialization->intersectWithRoot = mReplaceOpObserved;
Chris Craik69aeabe2016-03-03 12:58:39 -0800372 // TODO: this is only done for draw time, should eventually avoid for record time
373 serialization->rect.snapToPixelBoundaries();
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800374 mLastSerialization = serialization;
Chris Craike4db79d2015-12-22 16:32:23 -0800375 }
376 return mLastSerialization;
377}
378
Chris Craike4db79d2015-12-22 16:32:23 -0800379inline static const RectangleList& getRectList(const ClipBase* scb) {
380 return reinterpret_cast<const ClipRectList*>(scb)->rectList;
381}
382
383inline static const SkRegion& getRegion(const ClipBase* scb) {
384 return reinterpret_cast<const ClipRegion*>(scb)->region;
385}
386
387// Conservative check for too many rectangles to fit in rectangle list.
388// For simplicity, doesn't account for rect merging
389static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
390 int currentRectCount = clipArea.isRectangleList()
John Reck1bcacfd2017-11-03 10:12:19 -0700391 ? clipArea.getRectangleList().getTransformedRectanglesCount()
392 : 1;
Chris Craike4db79d2015-12-22 16:32:23 -0800393 int recordedRectCount = (scb->mode == ClipMode::RectangleList)
John Reck1bcacfd2017-11-03 10:12:19 -0700394 ? getRectList(scb).getTransformedRectanglesCount()
395 : 1;
Chris Craike4db79d2015-12-22 16:32:23 -0800396 return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
397}
398
Chris Craik261725f2016-02-29 12:52:33 -0800399static const ClipRect sEmptyClipRect(Rect(0, 0));
400
Chris Craike4db79d2015-12-22 16:32:23 -0800401const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
John Reck1bcacfd2017-11-03 10:12:19 -0700402 const ClipBase* recordedClip,
403 const Matrix4& recordedClipTransform) {
Chris Craike4db79d2015-12-22 16:32:23 -0800404 // if no recordedClip passed, just serialize current state
405 if (!recordedClip) return serializeClip(allocator);
406
Chris Craik261725f2016-02-29 12:52:33 -0800407 // if either is empty, clip is empty
John Reck1bcacfd2017-11-03 10:12:19 -0700408 if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect;
Chris Craik261725f2016-02-29 12:52:33 -0800409
John Reck1bcacfd2017-11-03 10:12:19 -0700410 if (!mLastResolutionResult || recordedClip != mLastResolutionClip ||
411 recordedClipTransform != mLastResolutionTransform) {
Chris Craike4db79d2015-12-22 16:32:23 -0800412 mLastResolutionClip = recordedClip;
413 mLastResolutionTransform = recordedClipTransform;
414
John Reck1bcacfd2017-11-03 10:12:19 -0700415 if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle &&
416 recordedClipTransform.rectToRect())) {
Chris Craike4db79d2015-12-22 16:32:23 -0800417 // common case - result is a single rectangle
Chris Craik69aeabe2016-03-03 12:58:39 -0800418 auto rectClip = allocator.create<ClipRect>(recordedClip->rect);
Chris Craike4db79d2015-12-22 16:32:23 -0800419 recordedClipTransform.mapRect(rectClip->rect);
420 rectClip->rect.doIntersect(mClipRect);
Chris Craik69aeabe2016-03-03 12:58:39 -0800421 rectClip->rect.snapToPixelBoundaries();
Chris Craike4db79d2015-12-22 16:32:23 -0800422 mLastResolutionResult = rectClip;
John Reck1bcacfd2017-11-03 10:12:19 -0700423 } else if (CC_UNLIKELY(mMode == ClipMode::Region ||
424 recordedClip->mode == ClipMode::Region ||
425 cannotFitInRectangleList(*this, recordedClip))) {
Chris Craike4db79d2015-12-22 16:32:23 -0800426 // region case
427 SkRegion other;
428 switch (recordedClip->mode) {
John Reck1bcacfd2017-11-03 10:12:19 -0700429 case ClipMode::Rectangle:
430 if (CC_LIKELY(recordedClipTransform.rectToRect())) {
431 // simple transform, skip creating SkPath
432 Rect resultClip(recordedClip->rect);
433 recordedClipTransform.mapRect(resultClip);
434 other.setRect(resultClip.toSkIRect());
435 } else {
436 SkPath transformedRect = pathFromTransformedRectangle(
437 recordedClip->rect, recordedClipTransform);
438 other.setPath(transformedRect, createViewportRegion());
439 }
440 break;
441 case ClipMode::RectangleList: {
442 RectangleList transformedList(getRectList(recordedClip));
443 transformedList.transform(recordedClipTransform);
444 other = transformedList.convertToRegion(createViewportRegion());
445 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800446 }
John Reck1bcacfd2017-11-03 10:12:19 -0700447 case ClipMode::Region:
448 other = getRegion(recordedClip);
449 applyTransformToRegion(recordedClipTransform, &other);
Chris Craike4db79d2015-12-22 16:32:23 -0800450 }
451
452 ClipRegion* regionClip = allocator.create<ClipRegion>();
453 switch (mMode) {
John Reck1bcacfd2017-11-03 10:12:19 -0700454 case ClipMode::Rectangle:
455 regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
456 break;
457 case ClipMode::RectangleList:
458 regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
459 other, SkRegion::kIntersect_Op);
460 break;
461 case ClipMode::Region:
462 regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
463 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800464 }
Chris Craik69aeabe2016-03-03 12:58:39 -0800465 // Don't need to snap, since region's in int bounds
Chris Craike4db79d2015-12-22 16:32:23 -0800466 regionClip->rect.set(regionClip->region.getBounds());
467 mLastResolutionResult = regionClip;
468 } else {
469 auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
470 auto&& rectList = rectListClip->rectList;
471 if (mMode == ClipMode::Rectangle) {
472 rectList.set(mClipRect, Matrix4::identity());
473 }
474
475 if (recordedClip->mode == ClipMode::Rectangle) {
Chris Craik69aeabe2016-03-03 12:58:39 -0800476 rectList.intersectWith(recordedClip->rect, recordedClipTransform);
Chris Craike4db79d2015-12-22 16:32:23 -0800477 } else {
478 const RectangleList& other = getRectList(recordedClip);
479 for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
480 auto&& tr = other.getTransformedRectangle(i);
481 Matrix4 totalTransform(recordedClipTransform);
482 totalTransform.multiply(tr.getTransform());
483 rectList.intersectWith(tr.getBounds(), totalTransform);
484 }
485 }
486 rectListClip->rect = rectList.calculateBounds();
Chris Craik69aeabe2016-03-03 12:58:39 -0800487 rectListClip->rect.snapToPixelBoundaries();
Chris Craike4db79d2015-12-22 16:32:23 -0800488 mLastResolutionResult = rectListClip;
489 }
490 }
491 return mLastResolutionResult;
492}
493
494void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
John Reck1bcacfd2017-11-03 10:12:19 -0700495 if (!clip) return; // nothing to do
Chris Craike4db79d2015-12-22 16:32:23 -0800496
497 if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
Chris Craik69aeabe2016-03-03 12:58:39 -0800498 clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op);
Chris Craike4db79d2015-12-22 16:32:23 -0800499 } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
500 auto&& rectList = getRectList(clip);
501 for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
502 auto&& tr = rectList.getTransformedRectangle(i);
503 Matrix4 totalTransform(transform);
504 totalTransform.multiply(tr.getTransform());
505 clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
506 }
507 } else {
508 SkRegion region(getRegion(clip));
Chris Craik82457c52016-06-29 16:22:27 -0700509 applyTransformToRegion(transform, &region);
Chris Craike4db79d2015-12-22 16:32:23 -0800510 clipRegion(region, SkRegion::kIntersect_Op);
511 }
512}
513
Chris Craik82457c52016-06-29 16:22:27 -0700514void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) {
Chris Craik06584352016-07-14 14:24:16 -0700515 if (transform.rectToRect() && !transform.isPureTranslate()) {
Chris Craik82457c52016-06-29 16:22:27 -0700516 // handle matrices with scale manually by mapping each rect
517 SkRegion other;
518 SkRegion::Iterator it(*region);
519 while (!it.done()) {
520 Rect rect(it.rect());
521 transform.mapRect(rect);
Chris Craik06584352016-07-14 14:24:16 -0700522 rect.snapGeometryToPixelBoundaries(true);
Chris Craik82457c52016-06-29 16:22:27 -0700523 other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op);
524 it.next();
525 }
526 region->swap(other);
527 } else {
528 // TODO: handle non-translate transforms properly!
529 region->translate(transform.getTranslateX(), transform.getTranslateY());
530 }
531}
532
Rob Tsuk487a92c2015-01-06 13:22:54 -0800533} /* namespace uirenderer */
534} /* namespace android */