blob: a7a11801cfe23e92dc51ad4d84019b4c03aac8b3 [file] [log] [blame]
/*
* 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.
*/
#ifndef CLIPAREA_H
#define CLIPAREA_H
#include "Matrix.h"
#include "Rect.h"
#include "utils/Pair.h"
#include <SkRegion.h>
namespace android {
namespace uirenderer {
class LinearAllocator;
Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform);
class TransformedRectangle {
public:
TransformedRectangle();
TransformedRectangle(const Rect& bounds, const Matrix4& transform);
bool canSimplyIntersectWith(const TransformedRectangle& other) const;
void intersectWith(const TransformedRectangle& other);
bool isEmpty() const;
const Rect& getBounds() const { return mBounds; }
Rect transformedBounds() const {
Rect transformedBounds(transformAndCalculateBounds(mBounds, mTransform));
return transformedBounds;
}
const Matrix4& getTransform() const { return mTransform; }
void transform(const Matrix4& transform) {
Matrix4 t;
t.loadMultiply(transform, mTransform);
mTransform = t;
}
private:
Rect mBounds;
Matrix4 mTransform;
};
class RectangleList {
public:
RectangleList();
bool isEmpty() const;
int getTransformedRectanglesCount() const;
const TransformedRectangle& getTransformedRectangle(int i) const;
void setEmpty();
void set(const Rect& bounds, const Matrix4& transform);
bool intersectWith(const Rect& bounds, const Matrix4& transform);
void transform(const Matrix4& transform);
SkRegion convertToRegion(const SkRegion& clip) const;
Rect calculateBounds() const;
enum { kMaxTransformedRectangles = 5 };
private:
int mTransformedRectanglesCount;
TransformedRectangle mTransformedRectangles[kMaxTransformedRectangles];
};
enum class ClipMode {
Rectangle,
RectangleList,
// region and path - intersected. if either is empty, don't use
Region
};
struct ClipBase {
explicit ClipBase(ClipMode mode) : mode(mode) {}
explicit ClipBase(const Rect& rect) : mode(ClipMode::Rectangle), rect(rect) {}
const ClipMode mode;
bool intersectWithRoot = false;
// Bounds of the clipping area, used to define the scissor, and define which
// portion of the stencil is updated/used
Rect rect;
void dump() const;
};
struct ClipRect : ClipBase {
explicit ClipRect(const Rect& rect) : ClipBase(rect) {}
};
struct ClipRectList : ClipBase {
explicit ClipRectList(const RectangleList& rectList)
: ClipBase(ClipMode::RectangleList), rectList(rectList) {}
RectangleList rectList;
};
struct ClipRegion : ClipBase {
explicit ClipRegion(const SkRegion& region) : ClipBase(ClipMode::Region), region(region) {}
ClipRegion() : ClipBase(ClipMode::Region) {}
SkRegion region;
};
class ClipArea {
public:
ClipArea();
void setViewportDimensions(int width, int height);
bool isEmpty() const { return mClipRect.isEmpty(); }
void setEmpty();
void setClip(float left, float top, float right, float bottom);
void clipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
void clipPathWithTransform(const SkPath& path, const mat4* transform, SkRegion::Op op);
const Rect& getClipRect() const { return mClipRect; }
const SkRegion& getClipRegion() const { return mClipRegion; }
const RectangleList& getRectangleList() const { return mRectangleList; }
bool isRegion() const { return ClipMode::Region == mMode; }
bool isSimple() const { return mMode == ClipMode::Rectangle; }
bool isRectangleList() const { return mMode == ClipMode::RectangleList; }
WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(
LinearAllocator& allocator, const ClipBase* recordedClip,
const Matrix4& recordedClipTransform);
void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
static void applyTransformToRegion(const Matrix4& transform, SkRegion* region);
private:
void enterRectangleMode();
void rectangleModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
void enterRectangleListMode();
void rectangleListModeClipRectWithTransform(const Rect& r, const mat4* transform,
SkRegion::Op op);
void enterRegionModeFromRectangleMode();
void enterRegionModeFromRectangleListMode();
void enterRegionMode();
void regionModeClipRectWithTransform(const Rect& r, const mat4* transform, SkRegion::Op op);
void clipRegion(const SkRegion& region, SkRegion::Op op);
void ensureClipRegion();
void onClipRegionUpdated();
// Called by every state modifying public method.
void onClipUpdated() {
mPostViewportClipObserved = true;
mLastSerialization = nullptr;
mLastResolutionResult = nullptr;
}
SkRegion createViewportRegion() { return SkRegion(mViewportBounds.toSkIRect()); }
void regionFromPath(const SkPath& path, SkRegion& pathAsRegion) {
// TODO: this should not mask every path to the viewport - this makes it impossible to use
// paths to clip to larger areas (which is valid e.g. with SkRegion::kReplace_Op)
pathAsRegion.setPath(path, createViewportRegion());
}
ClipMode mMode;
bool mPostViewportClipObserved = false;
bool mReplaceOpObserved = false;
/**
* If mLastSerialization is non-null, it represents an already serialized copy
* of the current clip state. If null, it has not been computed.
*/
const ClipBase* mLastSerialization = nullptr;
/**
* This pair of pointers is a single entry cache of most recently seen
*/
const ClipBase* mLastResolutionResult = nullptr;
const ClipBase* mLastResolutionClip = nullptr;
Matrix4 mLastResolutionTransform;
Rect mViewportBounds;
Rect mClipRect;
SkRegion mClipRegion;
RectangleList mRectangleList;
};
} /* namespace uirenderer */
} /* namespace android */
#endif /* CLIPAREA_H_ */