diff options
Diffstat (limited to 'libs/hwui/ClipArea.cpp')
-rw-r--r-- | libs/hwui/ClipArea.cpp | 534 |
1 files changed, 0 insertions, 534 deletions
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp deleted file mode 100644 index 27d93cfa0391..000000000000 --- a/libs/hwui/ClipArea.cpp +++ /dev/null @@ -1,534 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "ClipArea.h" - -#include "utils/LinearAllocator.h" - -#include <SkPath.h> -#include <limits> -#include <type_traits> - -namespace android { -namespace uirenderer { - -static void handlePoint(Rect& transformedBounds, const Matrix4& transform, float x, float y) { - Vertex v = {x, y}; - transform.mapPoint(v.x, v.y); - transformedBounds.expandToCover(v.x, v.y); -} - -Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) { - const float kMinFloat = std::numeric_limits<float>::lowest(); - const float kMaxFloat = std::numeric_limits<float>::max(); - Rect transformedBounds = {kMaxFloat, kMaxFloat, kMinFloat, kMinFloat}; - handlePoint(transformedBounds, transform, r.left, r.top); - handlePoint(transformedBounds, transform, r.right, r.top); - handlePoint(transformedBounds, transform, r.left, r.bottom); - handlePoint(transformedBounds, transform, r.right, r.bottom); - return transformedBounds; -} - -void ClipBase::dump() const { - ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect)); -} - -/* - * TransformedRectangle - */ - -TransformedRectangle::TransformedRectangle() {} - -TransformedRectangle::TransformedRectangle(const Rect& bounds, const Matrix4& transform) - : mBounds(bounds), mTransform(transform) {} - -bool TransformedRectangle::canSimplyIntersectWith(const TransformedRectangle& other) const { - return mTransform == other.mTransform; -} - -void TransformedRectangle::intersectWith(const TransformedRectangle& other) { - mBounds.doIntersect(other.mBounds); -} - -bool TransformedRectangle::isEmpty() const { - return mBounds.isEmpty(); -} - -/* - * RectangleList - */ - -RectangleList::RectangleList() : mTransformedRectanglesCount(0) {} - -bool RectangleList::isEmpty() const { - if (mTransformedRectanglesCount < 1) { - return true; - } - - for (int i = 0; i < mTransformedRectanglesCount; i++) { - if (mTransformedRectangles[i].isEmpty()) { - return true; - } - } - return false; -} - -int RectangleList::getTransformedRectanglesCount() const { - return mTransformedRectanglesCount; -} - -const TransformedRectangle& RectangleList::getTransformedRectangle(int i) const { - return mTransformedRectangles[i]; -} - -void RectangleList::setEmpty() { - mTransformedRectanglesCount = 0; -} - -void RectangleList::set(const Rect& bounds, const Matrix4& transform) { - mTransformedRectanglesCount = 1; - mTransformedRectangles[0] = TransformedRectangle(bounds, transform); -} - -bool RectangleList::intersectWith(const Rect& bounds, const Matrix4& transform) { - TransformedRectangle newRectangle(bounds, transform); - - // Try to find a rectangle with a compatible transformation - int index = 0; - for (; index < mTransformedRectanglesCount; index++) { - TransformedRectangle& tr(mTransformedRectangles[index]); - if (tr.canSimplyIntersectWith(newRectangle)) { - tr.intersectWith(newRectangle); - return true; - } - } - - // Add it to the list if there is room - if (index < kMaxTransformedRectangles) { - mTransformedRectangles[index] = newRectangle; - mTransformedRectanglesCount += 1; - return true; - } - - // This rectangle list is full - return false; -} - -Rect RectangleList::calculateBounds() const { - Rect bounds; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - if (index == 0) { - bounds = tr.transformedBounds(); - } else { - bounds.doIntersect(tr.transformedBounds()); - } - } - return bounds; -} - -static SkPath pathFromTransformedRectangle(const Rect& bounds, const Matrix4& transform) { - SkPath rectPath; - SkPath rectPathTransformed; - rectPath.addRect(bounds.left, bounds.top, bounds.right, bounds.bottom); - SkMatrix skTransform; - transform.copyTo(skTransform); - rectPath.transform(skTransform, &rectPathTransformed); - return rectPathTransformed; -} - -SkRegion RectangleList::convertToRegion(const SkRegion& clip) const { - SkRegion rectangleListAsRegion; - for (int index = 0; index < mTransformedRectanglesCount; index++) { - const TransformedRectangle& tr(mTransformedRectangles[index]); - SkPath rectPathTransformed = - pathFromTransformedRectangle(tr.getBounds(), tr.getTransform()); - if (index == 0) { - rectangleListAsRegion.setPath(rectPathTransformed, clip); - } else { - SkRegion rectRegion; - rectRegion.setPath(rectPathTransformed, clip); - rectangleListAsRegion.op(rectRegion, SkRegion::kIntersect_Op); - } - } - return rectangleListAsRegion; -} - -void RectangleList::transform(const Matrix4& transform) { - for (int index = 0; index < mTransformedRectanglesCount; index++) { - mTransformedRectangles[index].transform(transform); - } -} - -/* - * ClipArea - */ - -ClipArea::ClipArea() : mMode(ClipMode::Rectangle) {} - -/* - * Interface - */ - -void ClipArea::setViewportDimensions(int width, int height) { - mPostViewportClipObserved = false; - mViewportBounds.set(0, 0, width, height); - mClipRect = mViewportBounds; -} - -void ClipArea::setEmpty() { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.setEmpty(); - mClipRegion.setEmpty(); - mRectangleList.setEmpty(); -} - -void ClipArea::setClip(float left, float top, float right, float bottom) { - onClipUpdated(); - mMode = ClipMode::Rectangle; - mClipRect.set(left, top, right, bottom); - mClipRegion.setEmpty(); -} - -void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - switch (mMode) { - case ClipMode::Rectangle: - rectangleModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::RectangleList: - rectangleListModeClipRectWithTransform(r, transform, op); - break; - case ClipMode::Region: - regionModeClipRectWithTransform(r, transform, op); - break; - } -} - -void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op) { - if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true; - if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op; - onClipUpdated(); - SkMatrix skTransform; - transform->copyTo(skTransform); - SkPath transformed; - path.transform(skTransform, &transformed); - SkRegion region; - regionFromPath(transformed, region); - enterRegionMode(); - mClipRegion.op(region, op); - onClipRegionUpdated(); -} - -/* - * Rectangle mode - */ - -void ClipArea::enterRectangleMode() { - // Entering rectangle mode discards any - // existing clipping information from the other modes. - // The only way this occurs is by a clip setting operation. - mMode = ClipMode::Rectangle; -} - -void ClipArea::rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op == SkRegion::kReplace_Op && transform->rectToRect()) { - mClipRect = r; - transform->mapRect(mClipRect); - return; - } else if (op != SkRegion::kIntersect_Op) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - return; - } - - if (transform->rectToRect()) { - Rect transformed(r); - transform->mapRect(transformed); - mClipRect.doIntersect(transformed); - return; - } - - enterRectangleListMode(); - rectangleListModeClipRectWithTransform(r, transform, op); -} - -/* - * RectangleList mode implementation - */ - -void ClipArea::enterRectangleListMode() { - // Is is only legal to enter rectangle list mode from - // rectangle mode, since rectangle list mode cannot represent - // all clip areas that can be represented by a region. - ALOG_ASSERT(mMode == ClipMode::Rectangle); - mMode = ClipMode::RectangleList; - mRectangleList.set(mClipRect, Matrix4::identity()); -} - -void ClipArea::rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - if (op != SkRegion::kIntersect_Op || !mRectangleList.intersectWith(r, *transform)) { - enterRegionMode(); - regionModeClipRectWithTransform(r, transform, op); - } -} - -/* - * Region mode implementation - */ - -void ClipArea::enterRegionMode() { - ClipMode oldMode = mMode; - mMode = ClipMode::Region; - if (oldMode != ClipMode::Region) { - if (oldMode == ClipMode::Rectangle) { - mClipRegion.setRect(mClipRect.toSkIRect()); - } else { - mClipRegion = mRectangleList.convertToRegion(createViewportRegion()); - onClipRegionUpdated(); - } - } -} - -void ClipArea::regionModeClipRectWithTransform(const Rect& r, const mat4* transform, - SkRegion::Op op) { - SkPath transformedRect = pathFromTransformedRectangle(r, *transform); - SkRegion transformedRectRegion; - regionFromPath(transformedRect, transformedRectRegion); - mClipRegion.op(transformedRectRegion, op); - onClipRegionUpdated(); -} - -void ClipArea::onClipRegionUpdated() { - if (!mClipRegion.isEmpty()) { - mClipRect.set(mClipRegion.getBounds()); - - if (mClipRegion.isRect()) { - mClipRegion.setEmpty(); - enterRectangleMode(); - } - } else { - mClipRect.setEmpty(); - } -} - -/** - * Clip serialization - */ - -const ClipBase* ClipArea::serializeClip(LinearAllocator& allocator) { - if (!mPostViewportClipObserved) { - // Only initial clip-to-viewport observed, so no serialization of clip necessary - return nullptr; - } - - static_assert(std::is_trivially_destructible<Rect>::value, - "expect Rect to be trivially destructible"); - static_assert(std::is_trivially_destructible<RectangleList>::value, - "expect RectangleList to be trivially destructible"); - - if (mLastSerialization == nullptr) { - ClipBase* serialization = nullptr; - switch (mMode) { - case ClipMode::Rectangle: - serialization = allocator.create<ClipRect>(mClipRect); - break; - case ClipMode::RectangleList: - serialization = allocator.create<ClipRectList>(mRectangleList); - serialization->rect = mRectangleList.calculateBounds(); - break; - case ClipMode::Region: - serialization = allocator.create<ClipRegion>(mClipRegion); - serialization->rect.set(mClipRegion.getBounds()); - break; - } - serialization->intersectWithRoot = mReplaceOpObserved; - // TODO: this is only done for draw time, should eventually avoid for record time - serialization->rect.snapToPixelBoundaries(); - mLastSerialization = serialization; - } - return mLastSerialization; -} - -inline static const RectangleList& getRectList(const ClipBase* scb) { - return reinterpret_cast<const ClipRectList*>(scb)->rectList; -} - -inline static const SkRegion& getRegion(const ClipBase* scb) { - return reinterpret_cast<const ClipRegion*>(scb)->region; -} - -// Conservative check for too many rectangles to fit in rectangle list. -// For simplicity, doesn't account for rect merging -static bool cannotFitInRectangleList(const ClipArea& clipArea, const ClipBase* scb) { - int currentRectCount = clipArea.isRectangleList() - ? clipArea.getRectangleList().getTransformedRectanglesCount() - : 1; - int recordedRectCount = (scb->mode == ClipMode::RectangleList) - ? getRectList(scb).getTransformedRectanglesCount() - : 1; - return currentRectCount + recordedRectCount > RectangleList::kMaxTransformedRectangles; -} - -static const ClipRect sEmptyClipRect(Rect(0, 0)); - -const ClipBase* ClipArea::serializeIntersectedClip(LinearAllocator& allocator, - const ClipBase* recordedClip, - const Matrix4& recordedClipTransform) { - // if no recordedClip passed, just serialize current state - if (!recordedClip) return serializeClip(allocator); - - // if either is empty, clip is empty - if (CC_UNLIKELY(recordedClip->rect.isEmpty()) || mClipRect.isEmpty()) return &sEmptyClipRect; - - if (!mLastResolutionResult || recordedClip != mLastResolutionClip || - recordedClipTransform != mLastResolutionTransform) { - mLastResolutionClip = recordedClip; - mLastResolutionTransform = recordedClipTransform; - - if (CC_LIKELY(mMode == ClipMode::Rectangle && recordedClip->mode == ClipMode::Rectangle && - recordedClipTransform.rectToRect())) { - // common case - result is a single rectangle - auto rectClip = allocator.create<ClipRect>(recordedClip->rect); - recordedClipTransform.mapRect(rectClip->rect); - rectClip->rect.doIntersect(mClipRect); - rectClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectClip; - } else if (CC_UNLIKELY(mMode == ClipMode::Region || - recordedClip->mode == ClipMode::Region || - cannotFitInRectangleList(*this, recordedClip))) { - // region case - SkRegion other; - switch (recordedClip->mode) { - case ClipMode::Rectangle: - if (CC_LIKELY(recordedClipTransform.rectToRect())) { - // simple transform, skip creating SkPath - Rect resultClip(recordedClip->rect); - recordedClipTransform.mapRect(resultClip); - other.setRect(resultClip.toSkIRect()); - } else { - SkPath transformedRect = pathFromTransformedRectangle( - recordedClip->rect, recordedClipTransform); - other.setPath(transformedRect, createViewportRegion()); - } - break; - case ClipMode::RectangleList: { - RectangleList transformedList(getRectList(recordedClip)); - transformedList.transform(recordedClipTransform); - other = transformedList.convertToRegion(createViewportRegion()); - break; - } - case ClipMode::Region: - other = getRegion(recordedClip); - applyTransformToRegion(recordedClipTransform, &other); - } - - ClipRegion* regionClip = allocator.create<ClipRegion>(); - switch (mMode) { - case ClipMode::Rectangle: - regionClip->region.op(mClipRect.toSkIRect(), other, SkRegion::kIntersect_Op); - break; - case ClipMode::RectangleList: - regionClip->region.op(mRectangleList.convertToRegion(createViewportRegion()), - other, SkRegion::kIntersect_Op); - break; - case ClipMode::Region: - regionClip->region.op(mClipRegion, other, SkRegion::kIntersect_Op); - break; - } - // Don't need to snap, since region's in int bounds - regionClip->rect.set(regionClip->region.getBounds()); - mLastResolutionResult = regionClip; - } else { - auto rectListClip = allocator.create<ClipRectList>(mRectangleList); - auto&& rectList = rectListClip->rectList; - if (mMode == ClipMode::Rectangle) { - rectList.set(mClipRect, Matrix4::identity()); - } - - if (recordedClip->mode == ClipMode::Rectangle) { - rectList.intersectWith(recordedClip->rect, recordedClipTransform); - } else { - const RectangleList& other = getRectList(recordedClip); - for (int i = 0; i < other.getTransformedRectanglesCount(); i++) { - auto&& tr = other.getTransformedRectangle(i); - Matrix4 totalTransform(recordedClipTransform); - totalTransform.multiply(tr.getTransform()); - rectList.intersectWith(tr.getBounds(), totalTransform); - } - } - rectListClip->rect = rectList.calculateBounds(); - rectListClip->rect.snapToPixelBoundaries(); - mLastResolutionResult = rectListClip; - } - } - return mLastResolutionResult; -} - -void ClipArea::applyClip(const ClipBase* clip, const Matrix4& transform) { - if (!clip) return; // nothing to do - - if (CC_LIKELY(clip->mode == ClipMode::Rectangle)) { - clipRectWithTransform(clip->rect, &transform, SkRegion::kIntersect_Op); - } else if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) { - auto&& rectList = getRectList(clip); - for (int i = 0; i < rectList.getTransformedRectanglesCount(); i++) { - auto&& tr = rectList.getTransformedRectangle(i); - Matrix4 totalTransform(transform); - totalTransform.multiply(tr.getTransform()); - clipRectWithTransform(tr.getBounds(), &totalTransform, SkRegion::kIntersect_Op); - } - } else { - SkRegion region(getRegion(clip)); - applyTransformToRegion(transform, ®ion); - clipRegion(region, SkRegion::kIntersect_Op); - } -} - -void ClipArea::applyTransformToRegion(const Matrix4& transform, SkRegion* region) { - if (transform.rectToRect() && !transform.isPureTranslate()) { - // handle matrices with scale manually by mapping each rect - SkRegion other; - SkRegion::Iterator it(*region); - while (!it.done()) { - Rect rect(it.rect()); - transform.mapRect(rect); - rect.snapGeometryToPixelBoundaries(true); - other.op(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kUnion_Op); - it.next(); - } - region->swap(other); - } else { - // TODO: handle non-translate transforms properly! - region->translate(transform.getTranslateX(), transform.getTranslateY()); - } -} - -} /* namespace uirenderer */ -} /* namespace android */ |