blob: e368537f0b4f6553e09f8de08a7e21efe75dc7d5 [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
44/*
45 * TransformedRectangle
46 */
47
48TransformedRectangle::TransformedRectangle() {
49}
50
51TransformedRectangle::TransformedRectangle(const Rect& bounds,
52 const Matrix4& transform)
53 : mBounds(bounds)
54 , mTransform(transform) {
55}
56
57bool TransformedRectangle::canSimplyIntersectWith(
58 const TransformedRectangle& other) const {
59
60 return mTransform == other.mTransform;
61}
62
Chris Craikac02eb92015-10-05 12:23:46 -070063void TransformedRectangle::intersectWith(const TransformedRectangle& other) {
64 mBounds.doIntersect(other.mBounds);
Rob Tsuk487a92c2015-01-06 13:22:54 -080065}
66
67bool TransformedRectangle::isEmpty() const {
68 return mBounds.isEmpty();
69}
70
71/*
72 * RectangleList
73 */
74
75RectangleList::RectangleList()
76 : mTransformedRectanglesCount(0) {
77}
78
79bool RectangleList::isEmpty() const {
80 if (mTransformedRectanglesCount < 1) {
81 return true;
82 }
83
84 for (int i = 0; i < mTransformedRectanglesCount; i++) {
85 if (mTransformedRectangles[i].isEmpty()) {
86 return true;
87 }
88 }
89 return false;
90}
91
92int RectangleList::getTransformedRectanglesCount() const {
93 return mTransformedRectanglesCount;
94}
95
96const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const {
97 return mTransformedRectangles[i];
98}
99
100void RectangleList::setEmpty() {
101 mTransformedRectanglesCount = 0;
102}
103
104void RectangleList::set(const Rect& bounds, const Matrix4& transform) {
105 mTransformedRectanglesCount = 1;
106 mTransformedRectangles[0] = TransformedRectangle(bounds, transform);
107}
108
109bool RectangleList::intersectWith(const Rect& bounds,
110 const Matrix4& transform) {
111 TransformedRectangle newRectangle(bounds, transform);
112
113 // Try to find a rectangle with a compatible transformation
114 int index = 0;
115 for (; index < mTransformedRectanglesCount; index++) {
116 TransformedRectangle& tr(mTransformedRectangles[index]);
117 if (tr.canSimplyIntersectWith(newRectangle)) {
118 tr.intersectWith(newRectangle);
119 return true;
120 }
121 }
122
123 // Add it to the list if there is room
124 if (index < kMaxTransformedRectangles) {
125 mTransformedRectangles[index] = newRectangle;
126 mTransformedRectanglesCount += 1;
127 return true;
128 }
129
130 // This rectangle list is full
131 return false;
132}
133
134Rect RectangleList::calculateBounds() const {
135 Rect bounds;
136 for (int index = 0; index < mTransformedRectanglesCount; index++) {
137 const TransformedRectangle& tr(mTransformedRectangles[index]);
138 if (index == 0) {
139 bounds = tr.transformedBounds();
140 } else {
Chris Craikac02eb92015-10-05 12:23:46 -0700141 bounds.doIntersect(tr.transformedBounds());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800142 }
143 }
144 return bounds;
145}
146
147static SkPath pathFromTransformedRectangle(const Rect& bounds,
148 const Matrix4& transform) {
149 SkPath rectPath;
150 SkPath rectPathTransformed;
151 rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom);
152 SkMatrix skTransform;
153 transform.copyTo(skTransform);
154 rectPath.transform(skTransform, &rectPathTransformed);
155 return rectPathTransformed;
156}
157
158SkRegion RectangleList::convertToRegion(const SkRegion& clip) const {
159 SkRegion rectangleListAsRegion;
160 for (int index = 0; index < mTransformedRectanglesCount; index++) {
161 const TransformedRectangle& tr(mTransformedRectangles[index]);
162 SkPath rectPathTransformed = pathFromTransformedRectangle(
163 tr.getBounds(), tr.getTransform());
164 if (index == 0) {
165 rectangleListAsRegion.setPath(rectPathTransformed, clip);
166 } else {
167 SkRegion rectRegion;
168 rectRegion.setPath(rectPathTransformed, clip);
169 rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op);
170 }
171 }
172 return rectangleListAsRegion;
173}
174
Chris Craike4db79d2015-12-22 16:32:23 -0800175void RectangleList::transform(const Matrix4& transform) {
176 for (int index = 0; index < mTransformedRectanglesCount; index++) {
177 mTransformedRectangles[index].transform(transform);
178 }
179}
180
Rob Tsuk487a92c2015-01-06 13:22:54 -0800181/*
182 * ClipArea
183 */
184
185ClipArea::ClipArea()
Chris Craike4db79d2015-12-22 16:32:23 -0800186 : mMode(ClipMode::Rectangle) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800187}
188
189/*
190 * Interface
191 */
192
193void ClipArea::setViewportDimensions(int width, int height) {
Chris Craike4db79d2015-12-22 16:32:23 -0800194 mPostViewportClipObserved = false;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800195 mViewportBounds.set(0, 0, width, height);
196 mClipRect = mViewportBounds;
197}
198
199void ClipArea::setEmpty() {
Chris Craike4db79d2015-12-22 16:32:23 -0800200 onClipUpdated();
201 mMode = ClipMode::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 Craike4db79d2015-12-22 16:32:23 -0800208 onClipUpdated();
209 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800210 mClipRect.set(left, top, right, bottom);
211 mClipRegion.setEmpty();
212}
213
Chris Craik4d3e7042015-08-20 12:54:25 -0700214void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800215 SkRegion::Op op) {
Chris Craik7fc1b032016-02-03 19:45:06 -0800216 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800217 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800218 switch (mMode) {
Chris Craike4db79d2015-12-22 16:32:23 -0800219 case ClipMode::Rectangle:
Chris Craik4d3e7042015-08-20 12:54:25 -0700220 rectangleModeClipRectWithTransform(r, transform, op);
221 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800222 case ClipMode::RectangleList:
Chris Craik4d3e7042015-08-20 12:54:25 -0700223 rectangleListModeClipRectWithTransform(r, transform, op);
224 break;
Chris Craike4db79d2015-12-22 16:32:23 -0800225 case ClipMode::Region:
Chris Craik4d3e7042015-08-20 12:54:25 -0700226 regionModeClipRectWithTransform(r, transform, op);
227 break;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800228 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800229}
230
Chris Craik4d3e7042015-08-20 12:54:25 -0700231void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
Chris Craik7fc1b032016-02-03 19:45:06 -0800232 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800233 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800234 enterRegionMode();
235 mClipRegion.op(region, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400236 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800237}
238
Chris Craik4d3e7042015-08-20 12:54:25 -0700239void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800240 SkRegion::Op op) {
Chris Craik7fc1b032016-02-03 19:45:06 -0800241 if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
Chris Craike4db79d2015-12-22 16:32:23 -0800242 onClipUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800243 SkMatrix skTransform;
244 transform->copyTo(skTransform);
245 SkPath transformed;
246 path.transform(skTransform, &transformed);
247 SkRegion region;
248 regionFromPath(transformed, region);
Chris Craik4d3e7042015-08-20 12:54:25 -0700249 clipRegion(region, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800250}
251
252/*
253 * Rectangle mode
254 */
255
256void 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.
Chris Craike4db79d2015-12-22 16:32:23 -0800260 mMode = ClipMode::Rectangle;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800261}
262
Chris Craik4d3e7042015-08-20 12:54:25 -0700263void ClipArea::rectangleModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800264 const mat4* transform, SkRegion::Op op) {
265
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700266 if (op == SkRegion::kReplace_Op && transform->rectToRect()) {
267 mClipRect = r;
268 transform->mapRect(mClipRect);
Chris Craik4d3e7042015-08-20 12:54:25 -0700269 return;
Chris Craik8ce8f3f2015-07-16 13:07:45 -0700270 } else if (op != SkRegion::kIntersect_Op) {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800271 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700272 regionModeClipRectWithTransform(r, transform, op);
273 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800274 }
275
276 if (transform->rectToRect()) {
277 Rect transformed(r);
278 transform->mapRect(transformed);
Chris Craikac02eb92015-10-05 12:23:46 -0700279 mClipRect.doIntersect(transformed);
Chris Craik4d3e7042015-08-20 12:54:25 -0700280 return;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800281 }
282
283 enterRectangleListMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700284 rectangleListModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800285}
286
Rob Tsuk487a92c2015-01-06 13:22:54 -0800287/*
288 * RectangleList mode implementation
289 */
290
291void ClipArea::enterRectangleListMode() {
292 // Is is only legal to enter rectangle list mode from
293 // rectangle mode, since rectangle list mode cannot represent
294 // all clip areas that can be represented by a region.
Chris Craike4db79d2015-12-22 16:32:23 -0800295 ALOG_ASSERT(mMode == ClipMode::Rectangle);
296 mMode = ClipMode::RectangleList;
Rob Tsuk487a92c2015-01-06 13:22:54 -0800297 mRectangleList.set(mClipRect, Matrix4::identity());
298}
299
Chris Craik4d3e7042015-08-20 12:54:25 -0700300void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800301 const mat4* transform, SkRegion::Op op) {
302 if (op != SkRegion::kIntersect_Op
303 || !mRectangleList.intersectWith(r, *transform)) {
304 enterRegionMode();
Chris Craik4d3e7042015-08-20 12:54:25 -0700305 regionModeClipRectWithTransform(r, transform, op);
Rob Tsuk487a92c2015-01-06 13:22:54 -0800306 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800307}
308
Rob Tsuk487a92c2015-01-06 13:22:54 -0800309/*
310 * Region mode implementation
311 */
312
313void ClipArea::enterRegionMode() {
Chris Craike4db79d2015-12-22 16:32:23 -0800314 ClipMode oldMode = mMode;
315 mMode = ClipMode::Region;
316 if (oldMode != ClipMode::Region) {
317 if (oldMode == ClipMode::Rectangle) {
318 mClipRegion.setRect(mClipRect.toSkIRect());
Rob Tsuk487a92c2015-01-06 13:22:54 -0800319 } else {
320 mClipRegion = mRectangleList.convertToRegion(createViewportRegion());
Tom Hudsone30b53c2015-03-30 15:59:02 -0400321 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800322 }
Rob Tsuk487a92c2015-01-06 13:22:54 -0800323 }
324}
325
Chris Craik4d3e7042015-08-20 12:54:25 -0700326void ClipArea::regionModeClipRectWithTransform(const Rect& r,
Rob Tsuk487a92c2015-01-06 13:22:54 -0800327 const mat4* transform, SkRegion::Op op) {
328 SkPath transformedRect = pathFromTransformedRectangle(r, *transform);
329 SkRegion transformedRectRegion;
330 regionFromPath(transformedRect, transformedRectRegion);
331 mClipRegion.op(transformedRectRegion, op);
Tom Hudsone30b53c2015-03-30 15:59:02 -0400332 onClipRegionUpdated();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800333}
334
Tom Hudsone30b53c2015-03-30 15:59:02 -0400335void ClipArea::onClipRegionUpdated() {
Rob Tsuk487a92c2015-01-06 13:22:54 -0800336 if (!mClipRegion.isEmpty()) {
337 mClipRect.set(mClipRegion.getBounds());
338
339 if (mClipRegion.isRect()) {
340 mClipRegion.setEmpty();
Tom Hudsone30b53c2015-03-30 15:59:02 -0400341 enterRectangleMode();
Rob Tsuk487a92c2015-01-06 13:22:54 -0800342 }
343 } else {
344 mClipRect.setEmpty();
345 }
346}
347
Chris Craike4db79d2015-12-22 16:32:23 -0800348/**
349 * Clip serialization
350 */
351
352const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) {
353 if (!mPostViewportClipObserved) {
354 // Only initial clip-to-viewport observed, so no serialization of clip necessary
355 return nullptr;
356 }
357
358 static_assert(std::is_trivially_destructible<Rect>::value,
359 "expect Rect to be trivially destructible");
360 static_assert(std::is_trivially_destructible<RectangleList>::value,
361 "expect RectangleList to be trivially destructible");
362
363 if (mLastSerialization == nullptr) {
Chris Craik4f4c6082016-02-09 16:32:32 -0800364 ClipBase* serialization = nullptr;
Chris Craike4db79d2015-12-22 16:32:23 -0800365 switch (mMode) {
366 case ClipMode::Rectangle:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800367 serialization = allocator.create<ClipRect>(mClipRect);
Chris Craike4db79d2015-12-22 16:32:23 -0800368 break;
369 case ClipMode::RectangleList:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800370 serialization = allocator.create<ClipRectList>(mRectangleList);
371 serialization->rect = mRectangleList.calculateBounds();
Chris Craike4db79d2015-12-22 16:32:23 -0800372 break;
373 case ClipMode::Region:
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800374 serialization = allocator.create<ClipRegion>(mClipRegion);
375 serialization->rect.set(mClipRegion.getBounds());
Chris Craike4db79d2015-12-22 16:32:23 -0800376 break;
377 }
Chris Craikb4f4f3e2016-02-08 17:27:04 -0800378 mLastSerialization = serialization;
Chris Craike4db79d2015-12-22 16:32:23 -0800379 }
380 return mLastSerialization;
381}
382
383inline static const Rect& getRect(const ClipBase* scb) {
384 return reinterpret_cast<const ClipRect*>(scb)->rect;
385}
386
387inline static const RectangleList& getRectList(const ClipBase* scb) {
388 return reinterpret_cast<const ClipRectList*>(scb)->rectList;
389}
390
391inline static const SkRegion& getRegion(const ClipBase* scb) {
392 return reinterpret_cast<const ClipRegion*>(scb)->region;
393}
394
395// Conservative check for too many rectangles to fit in rectangle list.
396// For simplicity, doesn't account for rect merging
397static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) {
398 int currentRectCount = clipArea.isRectangleList()
399 ? clipArea.getRectangleList().getTransformedRectanglesCount()
400 : 1;
401 int recordedRectCount = (scb->mode == ClipMode::RectangleList)
402 ? getRectList(scb).getTransformedRectanglesCount()
403 : 1;
404 return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles;
405}
406
407const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator,
408 const ClipBase* recordedClip, const Matrix4& recordedClipTransform) {
409 // if no recordedClip passed, just serialize current state
410 if (!recordedClip) return serializeClip(allocator);
411
412 if (!mLastResolutionResult
413 || recordedClip != mLastResolutionClip
414 || recordedClipTransform != mLastResolutionTransform) {
415 mLastResolutionClip = recordedClip;
416 mLastResolutionTransform = recordedClipTransform;
417
418 if (CC_LIKELY(mMode == ClipMode::Rectangle
419 && recordedClip->mode == ClipMode::Rectangle
420 && recordedClipTransform.rectToRect())) {
421 // common case - result is a single rectangle
422 auto rectClip = allocator.create<ClipRect>(getRect(recordedClip));
423 recordedClipTransform.mapRect(rectClip->rect);
424 rectClip->rect.doIntersect(mClipRect);
425 mLastResolutionResult = rectClip;
426 } else if (CC_UNLIKELY(mMode == ClipMode::Region
427 || recordedClip->mode == ClipMode::Region
428 || cannotFitInRectangleList(*this, recordedClip))) {
429 // region case
430 SkRegion other;
431 switch (recordedClip->mode) {
432 case ClipMode::Rectangle:
433 if (CC_LIKELY(recordedClipTransform.rectToRect())) {
434 // simple transform, skip creating SkPath
435 Rect resultClip(getRect(recordedClip));
436 recordedClipTransform.mapRect(resultClip);
437 other.setRect(resultClip.toSkIRect());
438 } else {
439 SkPath transformedRect = pathFromTransformedRectangle(getRect(recordedClip),
440 recordedClipTransform);
441 other.setPath(transformedRect, createViewportRegion());
442 }
443 break;
444 case ClipMode::RectangleList: {
445 RectangleList transformedList(getRectList(recordedClip));
446 transformedList.transform(recordedClipTransform);
447 other = transformedList.convertToRegion(createViewportRegion());
448 break;
449 }
450 case ClipMode::Region:
451 other = getRegion(recordedClip);
452
453 // TODO: handle non-translate transforms properly!
454 other.translate(recordedClipTransform.getTranslateX(),
455 recordedClipTransform.getTranslateY());
456 }
457
458 ClipRegion* regionClip = allocator.create<ClipRegion>();
459 switch (mMode) {
460 case ClipMode::Rectangle:
461 regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op);
462 break;
463 case ClipMode::RectangleList:
464 regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()),
465 other, SkRegion::kIntersect_Op);
466 break;
467 case ClipMode::Region:
468 regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op);
469 break;
470 }
471 regionClip->rect.set(regionClip->region.getBounds());
472 mLastResolutionResult = regionClip;
473 } else {
474 auto rectListClip = allocator.create<ClipRectList>(mRectangleList);
475 auto&& rectList = rectListClip->rectList;
476 if (mMode == ClipMode::Rectangle) {
477 rectList.set(mClipRect, Matrix4::identity());
478 }
479
480 if (recordedClip->mode == ClipMode::Rectangle) {
481 rectList.intersectWith(getRect(recordedClip), recordedClipTransform);
482 } else {
483 const RectangleList& other = getRectList(recordedClip);
484 for (int i = 0; i < other.getTransformedRectanglesCount(); i++) {
485 auto&& tr = other.getTransformedRectangle(i);
486 Matrix4 totalTransform(recordedClipTransform);
487 totalTransform.multiply(tr.getTransform());
488 rectList.intersectWith(tr.getBounds(), totalTransform);
489 }
490 }
491 rectListClip->rect = rectList.calculateBounds();
492 mLastResolutionResult = rectListClip;
493 }
494 }
495 return mLastResolutionResult;
496}
497
498void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) {
499 if (!clip) return; // nothing to do
500
501 if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) {
502 clipRectWithTransform(getRect(clip), &transform, SkRegion::kIntersect_Op);
503 } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
504 auto&& rectList = getRectList(clip);
505 for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) {
506 auto&& tr = rectList.getTransformedRectangle(i);
507 Matrix4 totalTransform(transform);
508 totalTransform.multiply(tr.getTransform());
509 clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op);
510 }
511 } else {
512 SkRegion region(getRegion(clip));
513 // TODO: handle non-translate transforms properly!
514 region.translate(transform.getTranslateX(), transform.getTranslateY());
515 clipRegion(region, SkRegion::kIntersect_Op);
516 }
517}
518
Rob Tsuk487a92c2015-01-06 13:22:54 -0800519} /* namespace uirenderer */
520} /* namespace android */