Merge "Implement clipRect with a transform, clipRegion & clipPath Bug #7146141"
This commit is contained in:
@@ -172,17 +172,6 @@ public abstract class HardwareRenderer {
|
||||
*/
|
||||
public static final String DEBUG_SHOW_OVERDRAW_PROPERTY = "debug.hwui.show_overdraw";
|
||||
|
||||
/**
|
||||
* Turn on to allow region clipping (see
|
||||
* {@link android.graphics.Canvas#clipPath(android.graphics.Path)} and
|
||||
* {@link android.graphics.Canvas#clipRegion(android.graphics.Region)}.
|
||||
*
|
||||
* When this option is turned on a stencil buffer is always required.
|
||||
* If this option is off a stencil buffer is only created when the overdraw
|
||||
* debugging mode is turned on.
|
||||
*/
|
||||
private static final boolean REGION_CLIPPING_ENABLED = false;
|
||||
|
||||
/**
|
||||
* A process can set this flag to false to prevent the use of hardware
|
||||
* rendering.
|
||||
@@ -885,15 +874,6 @@ public abstract class HardwareRenderer {
|
||||
if (value != mShowOverdraw) {
|
||||
changed = true;
|
||||
mShowOverdraw = value;
|
||||
|
||||
if (!REGION_CLIPPING_ENABLED) {
|
||||
if (surface != null && isEnabled()) {
|
||||
if (validate()) {
|
||||
sEglConfig = loadEglConfig();
|
||||
invalidate(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nLoadProperties()) {
|
||||
@@ -1764,9 +1744,8 @@ public abstract class HardwareRenderer {
|
||||
|
||||
@Override
|
||||
int[] getConfig(boolean dirtyRegions) {
|
||||
//noinspection PointlessBooleanExpression
|
||||
final int stencilSize = mShowOverdraw || REGION_CLIPPING_ENABLED ?
|
||||
GLES20Canvas.getStencilSize() : 0;
|
||||
//noinspection PointlessBooleanExpression,ConstantConditions
|
||||
final int stencilSize = GLES20Canvas.getStencilSize();
|
||||
final int swapBehavior = dirtyRegions ? EGL14.EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
|
||||
|
||||
return new int[] {
|
||||
|
||||
@@ -270,9 +270,7 @@ public:
|
||||
GammaFontRenderer* fontRenderer;
|
||||
|
||||
Dither dither;
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
Stencil stencil;
|
||||
#endif
|
||||
|
||||
// Debug methods
|
||||
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
|
||||
|
||||
@@ -41,6 +41,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
|
||||
renderer = NULL;
|
||||
displayList = NULL;
|
||||
fbo = 0;
|
||||
stencil = 0;
|
||||
debugDrawUpdate = false;
|
||||
Caches::getInstance().resourceCache.incrementRefcount(this);
|
||||
}
|
||||
@@ -53,9 +54,22 @@ Layer::~Layer() {
|
||||
deleteTexture();
|
||||
}
|
||||
|
||||
void Layer::removeFbo() {
|
||||
void Layer::removeFbo(bool flush) {
|
||||
if (stencil) {
|
||||
// TODO: recycle & cache instead of simply deleting
|
||||
GLuint previousFbo;
|
||||
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
|
||||
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
|
||||
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
|
||||
|
||||
glDeleteRenderbuffers(1, &stencil);
|
||||
stencil = 0;
|
||||
}
|
||||
|
||||
if (fbo) {
|
||||
LayerRenderer::flushLayer(this);
|
||||
if (flush) LayerRenderer::flushLayer(this);
|
||||
// If put fails the cache will delete the FBO
|
||||
Caches::getInstance().fboCache.put(fbo);
|
||||
fbo = 0;
|
||||
}
|
||||
@@ -75,7 +89,5 @@ void Layer::setColorFilter(SkiaColorFilter* filter) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
@@ -48,7 +48,12 @@ struct Layer {
|
||||
Layer(const uint32_t layerWidth, const uint32_t layerHeight);
|
||||
~Layer();
|
||||
|
||||
void removeFbo();
|
||||
/**
|
||||
* Calling this method will remove (either by recycling or
|
||||
* destroying) the associated FBO, if present, and any render
|
||||
* buffer (stencil for instance.)
|
||||
*/
|
||||
void removeFbo(bool flush = true);
|
||||
|
||||
/**
|
||||
* Sets this layer's region to a rectangle. Computes the appropriate
|
||||
@@ -134,6 +139,14 @@ struct Layer {
|
||||
return fbo;
|
||||
}
|
||||
|
||||
inline void setStencilRenderBuffer(GLuint renderBuffer) {
|
||||
this->stencil = renderBuffer;
|
||||
}
|
||||
|
||||
inline GLuint getStencilRenderBuffer() {
|
||||
return stencil;
|
||||
}
|
||||
|
||||
inline GLuint getTexture() {
|
||||
return texture.id;
|
||||
}
|
||||
@@ -212,10 +225,6 @@ struct Layer {
|
||||
texture.id = 0;
|
||||
}
|
||||
|
||||
inline void deleteFbo() {
|
||||
if (fbo) glDeleteFramebuffers(1, &fbo);
|
||||
}
|
||||
|
||||
inline void allocateTexture(GLenum format, GLenum storage) {
|
||||
#if DEBUG_LAYERS
|
||||
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
|
||||
@@ -274,6 +283,12 @@ private:
|
||||
*/
|
||||
GLuint fbo;
|
||||
|
||||
/**
|
||||
* Name of the render buffer used as the stencil buffer. If the
|
||||
* name is 0, this layer does not have a stencil buffer.
|
||||
*/
|
||||
GLuint stencil;
|
||||
|
||||
/**
|
||||
* Indicates whether this layer has been used already.
|
||||
*/
|
||||
|
||||
@@ -100,13 +100,21 @@ bool LayerRenderer::suppressErrorChecks() {
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Dirty region tracking
|
||||
// Layer support
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool LayerRenderer::hasLayer() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void LayerRenderer::ensureStencilBuffer() {
|
||||
attachStencilBufferToLayer(mLayer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Dirty region tracking
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Region* LayerRenderer::getRegion() {
|
||||
if (getSnapshot()->flags & Snapshot::kFlagFboTarget) {
|
||||
return OpenGLRenderer::getRegion();
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
static void flushLayer(Layer* layer);
|
||||
|
||||
protected:
|
||||
virtual void ensureStencilBuffer();
|
||||
virtual bool hasLayer();
|
||||
virtual Region* getRegion();
|
||||
virtual GLint getTargetFbo();
|
||||
|
||||
@@ -30,6 +30,16 @@
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Defines
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const float EPSILON = 0.0000001f;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Matrix
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Matrix4::loadIdentity() {
|
||||
data[kScaleX] = 1.0f;
|
||||
data[kSkewY] = 0.0f;
|
||||
@@ -51,44 +61,91 @@ void Matrix4::loadIdentity() {
|
||||
data[kTranslateZ] = 0.0f;
|
||||
data[kPerspective2] = 1.0f;
|
||||
|
||||
mIsIdentity = true;
|
||||
mSimpleMatrix = true;
|
||||
mType = kTypeIdentity | kTypeRectToRect;
|
||||
}
|
||||
|
||||
static bool isZero(float f) {
|
||||
return fabs(f) <= EPSILON;
|
||||
}
|
||||
|
||||
uint32_t Matrix4::getType() const {
|
||||
if (mType & kTypeUnknown) {
|
||||
mType = kTypeIdentity;
|
||||
|
||||
if (data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f ||
|
||||
data[kPerspective2] != 1.0f) {
|
||||
mType |= kTypePerspective;
|
||||
}
|
||||
|
||||
if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
|
||||
mType |= kTypeTranslate;
|
||||
}
|
||||
|
||||
float m00 = data[kScaleX];
|
||||
float m01 = data[kSkewX];
|
||||
float m10 = data[kSkewY];
|
||||
float m11 = data[kScaleY];
|
||||
|
||||
if (m01 != 0.0f || m10 != 0.0f) {
|
||||
mType |= kTypeAffine;
|
||||
}
|
||||
|
||||
if (m00 != 1.0f || m11 != 1.0f) {
|
||||
mType |= kTypeScale;
|
||||
}
|
||||
|
||||
// The following section determines whether the matrix will preserve
|
||||
// rectangles. For instance, a rectangle transformed by a pure
|
||||
// translation matrix will result in a rectangle. A rectangle
|
||||
// transformed by a 45 degrees rotation matrix is not a rectangle.
|
||||
// If the matrix has a perspective component then we already know
|
||||
// it doesn't preserve rectangles.
|
||||
if (!(mType & kTypePerspective)) {
|
||||
if ((isZero(m00) && isZero(m11) && !isZero(m01) && !isZero(m10)) ||
|
||||
(isZero(m01) && isZero(m10) && !isZero(m00) && !isZero(m11))) {
|
||||
mType |= kTypeRectToRect;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mType;
|
||||
}
|
||||
|
||||
uint32_t Matrix4::getGeometryType() const {
|
||||
return getType() & sGeometryMask;
|
||||
}
|
||||
|
||||
bool Matrix4::rectToRect() const {
|
||||
return getType() & kTypeRectToRect;
|
||||
}
|
||||
|
||||
bool Matrix4::changesBounds() const {
|
||||
return !(data[0] == 1.0f && data[1] == 0.0f && data[2] == 0.0f && data[4] == 0.0f &&
|
||||
data[5] == 1.0f && data[6] == 0.0f && data[8] == 0.0f && data[9] == 0.0f &&
|
||||
data[10] == 1.0f);
|
||||
return getType() & (kTypeScale | kTypeAffine | kTypePerspective);
|
||||
}
|
||||
|
||||
bool Matrix4::isPureTranslate() const {
|
||||
return mSimpleMatrix && data[kScaleX] == 1.0f && data[kScaleY] == 1.0f;
|
||||
return getGeometryType() == kTypeTranslate;
|
||||
}
|
||||
|
||||
bool Matrix4::isSimple() const {
|
||||
return mSimpleMatrix;
|
||||
return getGeometryType() <= (kTypeScale | kTypeTranslate);
|
||||
}
|
||||
|
||||
bool Matrix4::isIdentity() const {
|
||||
return mIsIdentity;
|
||||
return getGeometryType() == kTypeIdentity;
|
||||
}
|
||||
|
||||
bool Matrix4::isPerspective() const {
|
||||
return data[kPerspective0] != 0.0f || data[kPerspective1] != 0.0f ||
|
||||
data[kPerspective2] != 1.0f;
|
||||
return getType() & kTypePerspective;
|
||||
}
|
||||
|
||||
void Matrix4::load(const float* v) {
|
||||
memcpy(data, v, sizeof(data));
|
||||
// TODO: Do something smarter here
|
||||
mSimpleMatrix = false;
|
||||
mIsIdentity = false;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::load(const Matrix4& v) {
|
||||
memcpy(data, v.data, sizeof(data));
|
||||
mSimpleMatrix = v.mSimpleMatrix;
|
||||
mIsIdentity = v.mIsIdentity;
|
||||
mType = v.getType();
|
||||
}
|
||||
|
||||
void Matrix4::load(const SkMatrix& v) {
|
||||
@@ -108,8 +165,14 @@ void Matrix4::load(const SkMatrix& v) {
|
||||
|
||||
data[kScaleZ] = 1.0f;
|
||||
|
||||
mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
|
||||
mIsIdentity = v.isIdentity();
|
||||
// NOTE: The flags are compatible between SkMatrix and this class.
|
||||
// However, SkMatrix::getType() does not return the flag
|
||||
// kRectStaysRect. The return value is masked with 0xF
|
||||
// so we need the extra rectStaysRect() check
|
||||
mType = v.getType();
|
||||
if (v.rectStaysRect()) {
|
||||
mType |= kTypeRectToRect;
|
||||
}
|
||||
}
|
||||
|
||||
void Matrix4::copyTo(SkMatrix& v) const {
|
||||
@@ -158,8 +221,7 @@ void Matrix4::loadInverse(const Matrix4& v) {
|
||||
data[kPerspective2] = (v.data[kScaleX] * v.data[kScaleY] -
|
||||
v.data[kSkewX] * v.data[kSkewY]) * scale;
|
||||
|
||||
mSimpleMatrix = v.mSimpleMatrix;
|
||||
mIsIdentity = v.mIsIdentity;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::copyTo(float* v) const {
|
||||
@@ -178,7 +240,7 @@ void Matrix4::multiply(float v) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
data[i] *= v;
|
||||
}
|
||||
mIsIdentity = false;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::loadTranslate(float x, float y, float z) {
|
||||
@@ -188,7 +250,7 @@ void Matrix4::loadTranslate(float x, float y, float z) {
|
||||
data[kTranslateY] = y;
|
||||
data[kTranslateZ] = z;
|
||||
|
||||
mIsIdentity = false;
|
||||
mType = kTypeTranslate | kTypeRectToRect;
|
||||
}
|
||||
|
||||
void Matrix4::loadScale(float sx, float sy, float sz) {
|
||||
@@ -198,7 +260,7 @@ void Matrix4::loadScale(float sx, float sy, float sz) {
|
||||
data[kScaleY] = sy;
|
||||
data[kScaleZ] = sz;
|
||||
|
||||
mIsIdentity = false;
|
||||
mType = kTypeScale | kTypeRectToRect;
|
||||
}
|
||||
|
||||
void Matrix4::loadSkew(float sx, float sy) {
|
||||
@@ -216,8 +278,23 @@ void Matrix4::loadSkew(float sx, float sy) {
|
||||
data[kPerspective1] = 0.0f;
|
||||
data[kPerspective2] = 1.0f;
|
||||
|
||||
mSimpleMatrix = false;
|
||||
mIsIdentity = false;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::loadRotate(float angle) {
|
||||
angle *= float(M_PI / 180.0f);
|
||||
float c = cosf(angle);
|
||||
float s = sinf(angle);
|
||||
|
||||
loadIdentity();
|
||||
|
||||
data[kScaleX] = c;
|
||||
data[kSkewX] = -s;
|
||||
|
||||
data[kSkewY] = s;
|
||||
data[kScaleY] = c;
|
||||
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::loadRotate(float angle, float x, float y, float z) {
|
||||
@@ -257,8 +334,7 @@ void Matrix4::loadRotate(float angle, float x, float y, float z) {
|
||||
data[6] = yz * nc + xs;
|
||||
data[kScaleZ] = z * z * nc + c;
|
||||
|
||||
mSimpleMatrix = false;
|
||||
mIsIdentity = false;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
|
||||
@@ -282,8 +358,7 @@ void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
|
||||
set(i, 3, w);
|
||||
}
|
||||
|
||||
mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
|
||||
mIsIdentity = false;
|
||||
mType = kTypeUnknown;
|
||||
}
|
||||
|
||||
void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
|
||||
@@ -296,13 +371,13 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float
|
||||
data[kTranslateY] = -(top + bottom) / (top - bottom);
|
||||
data[kTranslateZ] = -(far + near) / (far - near);
|
||||
|
||||
mIsIdentity = false;
|
||||
mType = kTypeTranslate | kTypeScale | kTypeRectToRect;
|
||||
}
|
||||
|
||||
#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
|
||||
|
||||
void Matrix4::mapPoint(float& x, float& y) const {
|
||||
if (mSimpleMatrix) {
|
||||
if (isSimple()) {
|
||||
MUL_ADD_STORE(x, data[kScaleX], data[kTranslateX]);
|
||||
MUL_ADD_STORE(y, data[kScaleY], data[kTranslateY]);
|
||||
return;
|
||||
@@ -318,7 +393,7 @@ void Matrix4::mapPoint(float& x, float& y) const {
|
||||
}
|
||||
|
||||
void Matrix4::mapRect(Rect& r) const {
|
||||
if (mSimpleMatrix) {
|
||||
if (isSimple()) {
|
||||
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
|
||||
MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
|
||||
MUL_ADD_STORE(r.top, data[kScaleY], data[kTranslateY]);
|
||||
@@ -376,7 +451,7 @@ void Matrix4::mapRect(Rect& r) const {
|
||||
}
|
||||
|
||||
void Matrix4::dump() const {
|
||||
ALOGD("Matrix4[simple=%d", mSimpleMatrix);
|
||||
ALOGD("Matrix4[simple=%d, type=0x%x", isSimple(), getType());
|
||||
ALOGD(" %f %f %f %f", data[kScaleX], data[kSkewX], data[8], data[kTranslateX]);
|
||||
ALOGD(" %f %f %f %f", data[kSkewY], data[kScaleY], data[9], data[kTranslateY]);
|
||||
ALOGD(" %f %f %f %f", data[2], data[6], data[kScaleZ], data[kTranslateZ]);
|
||||
|
||||
@@ -48,6 +48,21 @@ public:
|
||||
kPerspective2 = 15
|
||||
};
|
||||
|
||||
// NOTE: The flags from kTypeIdentity to kTypePerspective
|
||||
// must be kept in sync with the type flags found
|
||||
// in SkMatrix
|
||||
enum Type {
|
||||
kTypeIdentity = 0,
|
||||
kTypeTranslate = 0x1,
|
||||
kTypeScale = 0x2,
|
||||
kTypeAffine = 0x4,
|
||||
kTypePerspective = 0x8,
|
||||
kTypeRectToRect = 0x10,
|
||||
kTypeUnknown = 0x20,
|
||||
};
|
||||
|
||||
static const int sGeometryMask = 0xf;
|
||||
|
||||
Matrix4() {
|
||||
loadIdentity();
|
||||
}
|
||||
@@ -75,11 +90,14 @@ public:
|
||||
void loadTranslate(float x, float y, float z);
|
||||
void loadScale(float sx, float sy, float sz);
|
||||
void loadSkew(float sx, float sy);
|
||||
void loadRotate(float angle);
|
||||
void loadRotate(float angle, float x, float y, float z);
|
||||
void loadMultiply(const Matrix4& u, const Matrix4& v);
|
||||
|
||||
void loadOrtho(float left, float right, float bottom, float top, float near, float far);
|
||||
|
||||
uint32_t getType() const;
|
||||
|
||||
void multiply(const Matrix4& v) {
|
||||
Matrix4 u;
|
||||
u.loadMultiply(*this, v);
|
||||
@@ -112,10 +130,14 @@ public:
|
||||
multiply(u);
|
||||
}
|
||||
|
||||
bool isPureTranslate() const;
|
||||
/**
|
||||
* If the matrix is identity or translate and/or scale.
|
||||
*/
|
||||
bool isSimple() const;
|
||||
bool isPureTranslate() const;
|
||||
bool isIdentity() const;
|
||||
bool isPerspective() const;
|
||||
bool rectToRect() const;
|
||||
|
||||
bool changesBounds() const;
|
||||
|
||||
@@ -131,8 +153,7 @@ public:
|
||||
void dump() const;
|
||||
|
||||
private:
|
||||
bool mSimpleMatrix;
|
||||
bool mIsIdentity;
|
||||
mutable uint32_t mType;
|
||||
|
||||
inline float get(int i, int j) const {
|
||||
return data[i * 4 + j];
|
||||
@@ -141,6 +162,9 @@ private:
|
||||
inline void set(int i, int j, float v) {
|
||||
data[i * 4 + j] = v;
|
||||
}
|
||||
|
||||
uint32_t getGeometryType() const;
|
||||
|
||||
}; // class Matrix4
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -837,6 +837,8 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
||||
return;
|
||||
}
|
||||
|
||||
Layer* layer = current->layer;
|
||||
const Rect& rect = layer->layer;
|
||||
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
|
||||
|
||||
if (fboLayer) {
|
||||
@@ -844,6 +846,9 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
||||
|
||||
// Detach the texture from the FBO
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
|
||||
|
||||
layer->removeFbo(false);
|
||||
|
||||
// Unbind current FBO and restore previous one
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
|
||||
debugOverdraw(true, false);
|
||||
@@ -851,9 +856,6 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
||||
startTiling(previous);
|
||||
}
|
||||
|
||||
Layer* layer = current->layer;
|
||||
const Rect& rect = layer->layer;
|
||||
|
||||
if (!fboLayer && layer->getAlpha() < 255) {
|
||||
drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
|
||||
layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true);
|
||||
@@ -881,17 +883,6 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
||||
composeLayerRect(layer, rect, true);
|
||||
}
|
||||
|
||||
if (fboLayer) {
|
||||
// Note: No need to use glDiscardFramebufferEXT() since we never
|
||||
// create/compose layers that are not on screen with this
|
||||
// code path
|
||||
// See LayerRenderer::destroyLayer(Layer*)
|
||||
|
||||
// Put the FBO name back in the cache, if it doesn't fit, it will be destroyed
|
||||
mCaches.fboCache.put(current->fbo);
|
||||
layer->setFbo(0);
|
||||
}
|
||||
|
||||
dirtyClip();
|
||||
|
||||
// Failing to add the layer to the cache should happen only if the layer is too large
|
||||
@@ -1001,10 +992,14 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
|
||||
const float texY = 1.0f / float(layer->getHeight());
|
||||
const float height = rect.getHeight();
|
||||
|
||||
setupDraw();
|
||||
|
||||
// We must get (and therefore bind) the region mesh buffer
|
||||
// after we setup drawing in case we need to mess with the
|
||||
// stencil buffer in setupDraw()
|
||||
TextureVertex* mesh = mCaches.getRegionMesh();
|
||||
GLsizei numQuads = 0;
|
||||
|
||||
setupDraw();
|
||||
setupDrawWithTexture();
|
||||
setupDrawColor(alpha, alpha, alpha, alpha);
|
||||
setupDrawColorFilter();
|
||||
@@ -1089,6 +1084,25 @@ void OpenGLRenderer::drawRegionRects(const Region& region) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
|
||||
SkXfermode::Mode mode, bool dirty) {
|
||||
int count = 0;
|
||||
Vector<float> rects;
|
||||
|
||||
SkRegion::Iterator it(region);
|
||||
while (!it.done()) {
|
||||
const SkIRect& r = it.rect();
|
||||
rects.push(r.fLeft);
|
||||
rects.push(r.fTop);
|
||||
rects.push(r.fRight);
|
||||
rects.push(r.fBottom);
|
||||
count++;
|
||||
it.next();
|
||||
}
|
||||
|
||||
drawColorRects(rects.array(), count, color, mode, true, dirty);
|
||||
}
|
||||
|
||||
void OpenGLRenderer::dirtyLayer(const float left, const float top,
|
||||
const float right, const float bottom, const mat4 transform) {
|
||||
if (hasLayer()) {
|
||||
@@ -1219,6 +1233,65 @@ void OpenGLRenderer::setScissorFromClip() {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::ensureStencilBuffer() {
|
||||
// Thanks to the mismatch between EGL and OpenGL ES FBO we
|
||||
// cannot attach a stencil buffer to fbo0 dynamically. Let's
|
||||
// just hope we have one when hasLayer() returns false.
|
||||
if (hasLayer()) {
|
||||
attachStencilBufferToLayer(mSnapshot->layer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
|
||||
// The layer's FBO is already bound when we reach this stage
|
||||
if (!layer->getStencilRenderBuffer()) {
|
||||
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
|
||||
GLuint buffer;
|
||||
glGenRenderbuffers(1, &buffer);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
||||
layer->getWidth(), layer->getHeight());
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
|
||||
layer->setStencilRenderBuffer(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLRenderer::setStencilFromClip() {
|
||||
if (!mCaches.debugOverdraw) {
|
||||
if (!mSnapshot->clipRegion->isEmpty()) {
|
||||
// NOTE: The order here is important, we must set dirtyClip to false
|
||||
// before any draw call to avoid calling back into this method
|
||||
mDirtyClip = false;
|
||||
|
||||
ensureStencilBuffer();
|
||||
|
||||
mCaches.stencil.enableWrite();
|
||||
|
||||
// Clear the stencil but first make sure we restrict drawing
|
||||
// to the region's bounds
|
||||
bool resetScissor = mCaches.enableScissor();
|
||||
if (resetScissor) {
|
||||
// The scissor was not set so we now need to update it
|
||||
setScissorFromClip();
|
||||
}
|
||||
mCaches.stencil.clear();
|
||||
if (resetScissor) mCaches.disableScissor();
|
||||
|
||||
// NOTE: We could use the region contour path to generate a smaller mesh
|
||||
// Since we are using the stencil we could use the red book path
|
||||
// drawing technique. It might increase bandwidth usage though.
|
||||
|
||||
// The last parameter is important: we are not drawing in the color buffer
|
||||
// so we don't want to dirty the current layer, if any
|
||||
drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false);
|
||||
|
||||
mCaches.stencil.enableTest();
|
||||
} else {
|
||||
mCaches.stencil.disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Rect& OpenGLRenderer::getClipBounds() {
|
||||
return mSnapshot->getLocalClip();
|
||||
}
|
||||
@@ -1284,40 +1357,60 @@ bool OpenGLRenderer::quickReject(float left, float top, float right, float botto
|
||||
return rejected;
|
||||
}
|
||||
|
||||
void OpenGLRenderer::debugClip() {
|
||||
#if DEBUG_CLIP_REGIONS
|
||||
if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) {
|
||||
drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
|
||||
bool clipped = mSnapshot->clip(left, top, right, bottom, op);
|
||||
if (CC_LIKELY(mSnapshot->transform->rectToRect())) {
|
||||
bool clipped = mSnapshot->clip(left, top, right, bottom, op);
|
||||
if (clipped) {
|
||||
dirtyClip();
|
||||
}
|
||||
return !mSnapshot->clipRect->isEmpty();
|
||||
}
|
||||
|
||||
SkPath path;
|
||||
path.addRect(left, top, right, bottom);
|
||||
|
||||
return clipPath(&path, op);
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
|
||||
SkMatrix transform;
|
||||
mSnapshot->transform->copyTo(transform);
|
||||
|
||||
SkPath transformed;
|
||||
path->transform(transform, &transformed);
|
||||
|
||||
SkRegion clip;
|
||||
if (!mSnapshot->clipRegion->isEmpty()) {
|
||||
clip.setRegion(*mSnapshot->clipRegion);
|
||||
} else {
|
||||
Rect* bounds = mSnapshot->clipRect;
|
||||
clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
|
||||
}
|
||||
|
||||
SkRegion region;
|
||||
region.setPath(transformed, clip);
|
||||
|
||||
bool clipped = mSnapshot->clipRegionTransformed(region, op);
|
||||
if (clipped) {
|
||||
dirtyClip();
|
||||
#if DEBUG_CLIP_REGIONS
|
||||
if (!isDeferred() && mSnapshot->clipRegion && !mSnapshot->clipRegion->isRect()) {
|
||||
int count = 0;
|
||||
Vector<float> rects;
|
||||
SkRegion::Iterator it(*mSnapshot->clipRegion);
|
||||
while (!it.done()) {
|
||||
const SkIRect& r = it.rect();
|
||||
rects.push(r.fLeft);
|
||||
rects.push(r.fTop);
|
||||
rects.push(r.fRight);
|
||||
rects.push(r.fBottom);
|
||||
count++;
|
||||
it.next();
|
||||
}
|
||||
|
||||
drawColorRects(rects.array(), count, 0x7f00ff00, SkXfermode::kSrcOver_Mode, true);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return !mSnapshot->clipRect->isEmpty();
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
|
||||
const SkRect& bounds = path->getBounds();
|
||||
return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
|
||||
}
|
||||
|
||||
bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) {
|
||||
const SkIRect& bounds = region->getBounds();
|
||||
return clipRect(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, op);
|
||||
bool clipped = mSnapshot->clipRegionTransformed(*region, op);
|
||||
if (clipped) {
|
||||
dirtyClip();
|
||||
}
|
||||
return !mSnapshot->clipRect->isEmpty();
|
||||
}
|
||||
|
||||
Rect* OpenGLRenderer::getClipRect() {
|
||||
@@ -1332,8 +1425,11 @@ void OpenGLRenderer::setupDraw(bool clear) {
|
||||
// TODO: It would be best if we could do this before quickReject()
|
||||
// changes the scissor test state
|
||||
if (clear) clearLayerRegions();
|
||||
// Make sure setScissor & setStencil happen at the beginning of
|
||||
// this method
|
||||
if (mDirtyClip) {
|
||||
setScissorFromClip();
|
||||
setStencilFromClip();
|
||||
}
|
||||
mDescription.reset();
|
||||
mSetShaderColor = false;
|
||||
@@ -3085,7 +3181,7 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint
|
||||
}
|
||||
|
||||
status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
|
||||
SkXfermode::Mode mode, bool ignoreTransform) {
|
||||
SkXfermode::Mode mode, bool ignoreTransform, bool dirty) {
|
||||
|
||||
float left = FLT_MAX;
|
||||
float top = FLT_MAX;
|
||||
@@ -3103,7 +3199,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
|
||||
float r = rects[index + 2];
|
||||
float b = rects[index + 3];
|
||||
|
||||
if (!quickRejectNoScissor(left, top, right, bottom)) {
|
||||
if (ignoreTransform || !quickRejectNoScissor(left, top, right, bottom)) {
|
||||
Vertex::set(vertex++, l, b);
|
||||
Vertex::set(vertex++, l, t);
|
||||
Vertex::set(vertex++, r, t);
|
||||
@@ -3136,7 +3232,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
|
||||
setupDrawColorFilterUniforms();
|
||||
setupDrawVertices((GLvoid*) &mesh[0].position[0]);
|
||||
|
||||
if (hasLayer()) {
|
||||
if (dirty && hasLayer()) {
|
||||
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
|
||||
}
|
||||
|
||||
|
||||
@@ -286,6 +286,19 @@ protected:
|
||||
*/
|
||||
void resumeAfterLayer();
|
||||
|
||||
/**
|
||||
* This method is called whenever a stencil buffer is required. Subclasses
|
||||
* should override this method and call attachStencilBufferToLayer() on the
|
||||
* appropriate layer(s).
|
||||
*/
|
||||
virtual void ensureStencilBuffer();
|
||||
|
||||
/**
|
||||
* Obtains a stencil render buffer (allocating it if necessary) and
|
||||
* attaches it to the specified layer.
|
||||
*/
|
||||
void attachStencilBufferToLayer(Layer* layer);
|
||||
|
||||
/**
|
||||
* Compose the layer defined in the current snapshot with the layer
|
||||
* defined by the previous snapshot.
|
||||
@@ -422,6 +435,12 @@ private:
|
||||
*/
|
||||
void setScissorFromClip();
|
||||
|
||||
/**
|
||||
* Sets the clipping region using the stencil buffer. The clip region
|
||||
* is defined by the current snapshot's clipRegion member.
|
||||
*/
|
||||
void setStencilFromClip();
|
||||
|
||||
/**
|
||||
* Performs a quick reject but does not affect the scissor. Returns
|
||||
* the transformed rect to test and the current clip.
|
||||
@@ -524,9 +543,10 @@ private:
|
||||
* @param color The rectangles' ARGB color, defined as a packed 32 bits word
|
||||
* @param mode The Skia xfermode to use
|
||||
* @param ignoreTransform True if the current transform should be ignored
|
||||
* @param dirty True if calling this method should dirty the current layer
|
||||
*/
|
||||
status_t drawColorRects(const float* rects, int count, int color,
|
||||
SkXfermode::Mode mode, bool ignoreTransform = false);
|
||||
SkXfermode::Mode mode, bool ignoreTransform = false, bool dirty = true);
|
||||
|
||||
/**
|
||||
* Draws the shape represented by the specified path texture.
|
||||
@@ -774,6 +794,19 @@ private:
|
||||
*/
|
||||
void drawRegionRects(const Region& region);
|
||||
|
||||
/**
|
||||
* Renders the specified region as a series of rectangles. The region
|
||||
* must be in screen-space coordinates.
|
||||
*/
|
||||
void drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode,
|
||||
bool dirty = false);
|
||||
|
||||
/**
|
||||
* Draws the current clip region if any. Only when DEBUG_CLIP_REGIONS
|
||||
* is turned on.
|
||||
*/
|
||||
void debugClip();
|
||||
|
||||
void debugOverdraw(bool enable, bool clear);
|
||||
void renderOverdraw();
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
|
||||
transform = &mTransformRoot;
|
||||
clipRect = &mClipRectRoot;
|
||||
region = NULL;
|
||||
clipRegion = NULL;
|
||||
clipRegion = &mClipRegionRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,12 +39,10 @@ Snapshot::Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0),
|
||||
* the previous snapshot.
|
||||
*/
|
||||
Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
|
||||
flags(0), previous(s), layer(NULL), fbo(s->fbo),
|
||||
flags(0), previous(s), layer(s->layer), fbo(s->fbo),
|
||||
invisible(s->invisible), empty(false),
|
||||
viewport(s->viewport), height(s->height), alpha(s->alpha) {
|
||||
|
||||
clipRegion = NULL;
|
||||
|
||||
if (saveFlags & SkCanvas::kMatrix_SaveFlag) {
|
||||
mTransformRoot.load(*s->transform);
|
||||
transform = &mTransformRoot;
|
||||
@@ -55,17 +53,13 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
|
||||
if (saveFlags & SkCanvas::kClip_SaveFlag) {
|
||||
mClipRectRoot.set(*s->clipRect);
|
||||
clipRect = &mClipRectRoot;
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
if (s->clipRegion) {
|
||||
if (!s->clipRegion->isEmpty()) {
|
||||
mClipRegionRoot.op(*s->clipRegion, SkRegion::kUnion_Op);
|
||||
clipRegion = &mClipRegionRoot;
|
||||
}
|
||||
#endif
|
||||
clipRegion = &mClipRegionRoot;
|
||||
} else {
|
||||
clipRect = s->clipRect;
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
clipRegion = s->clipRegion;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (s->flags & Snapshot::kFlagFboTarget) {
|
||||
@@ -81,41 +75,38 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags):
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Snapshot::ensureClipRegion() {
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
if (!clipRegion) {
|
||||
clipRegion = &mClipRegionRoot;
|
||||
if (clipRegion->isEmpty()) {
|
||||
clipRegion->setRect(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Snapshot::copyClipRectFromRegion() {
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
if (!clipRegion->isEmpty()) {
|
||||
const SkIRect& bounds = clipRegion->getBounds();
|
||||
clipRect->set(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom);
|
||||
|
||||
if (clipRegion->isRect()) {
|
||||
clipRegion->setEmpty();
|
||||
clipRegion = NULL;
|
||||
}
|
||||
} else {
|
||||
clipRect->setEmpty();
|
||||
clipRegion = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Snapshot::clipRegionOp(float left, float top, float right, float bottom, SkRegion::Op op) {
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
SkIRect tmp;
|
||||
tmp.set(left, top, right, bottom);
|
||||
clipRegion->op(tmp, op);
|
||||
copyClipRectFromRegion();
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Snapshot::clipRegionTransformed(const SkRegion& region, SkRegion::Op op) {
|
||||
ensureClipRegion();
|
||||
clipRegion->op(region, op);
|
||||
copyClipRectFromRegion();
|
||||
flags |= Snapshot::kFlagClipSet;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Snapshot::clip(float left, float top, float right, float bottom, SkRegion::Op op) {
|
||||
@@ -129,7 +120,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
|
||||
|
||||
switch (op) {
|
||||
case SkRegion::kIntersect_Op: {
|
||||
if (CC_UNLIKELY(clipRegion)) {
|
||||
if (CC_UNLIKELY(!clipRegion->isEmpty())) {
|
||||
ensureClipRegion();
|
||||
clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kIntersect_Op);
|
||||
} else {
|
||||
@@ -142,7 +133,7 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
|
||||
break;
|
||||
}
|
||||
case SkRegion::kUnion_Op: {
|
||||
if (CC_UNLIKELY(clipRegion)) {
|
||||
if (CC_UNLIKELY(!clipRegion->isEmpty())) {
|
||||
ensureClipRegion();
|
||||
clipped = clipRegionOp(r.left, r.top, r.right, r.bottom, SkRegion::kUnion_Op);
|
||||
} else {
|
||||
@@ -171,12 +162,9 @@ bool Snapshot::clipTransformed(const Rect& r, SkRegion::Op op) {
|
||||
|
||||
void Snapshot::setClip(float left, float top, float right, float bottom) {
|
||||
clipRect->set(left, top, right, bottom);
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
if (clipRegion) {
|
||||
if (!clipRegion->isEmpty()) {
|
||||
clipRegion->setEmpty();
|
||||
clipRegion = NULL;
|
||||
}
|
||||
#endif
|
||||
flags |= Snapshot::kFlagClipSet;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,6 +93,12 @@ public:
|
||||
*/
|
||||
bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op);
|
||||
|
||||
/**
|
||||
* Modifies the current clip with the specified region and operation.
|
||||
* The specified region is considered already transformed.
|
||||
*/
|
||||
bool clipRegionTransformed(const SkRegion& region, SkRegion::Op op);
|
||||
|
||||
/**
|
||||
* Sets the current clip.
|
||||
*/
|
||||
@@ -136,7 +142,7 @@ public:
|
||||
sp<Snapshot> previous;
|
||||
|
||||
/**
|
||||
* Only set when the flag kFlagIsLayer is set.
|
||||
* A pointer to the currently active layer.
|
||||
*
|
||||
* This snapshot does not own the layer, this pointer must not be freed.
|
||||
*/
|
||||
@@ -200,8 +206,6 @@ public:
|
||||
*
|
||||
* This is a reference to a region owned by this snapshot or another
|
||||
* snapshot. This pointer must not be freed. See ::mClipRegionRoot.
|
||||
*
|
||||
* This field is used only if STENCIL_BUFFER_SIZE is > 0.
|
||||
*/
|
||||
SkRegion* clipRegion;
|
||||
|
||||
@@ -234,9 +238,7 @@ private:
|
||||
Rect mClipRectRoot;
|
||||
Rect mLocalClip;
|
||||
|
||||
#if STENCIL_BUFFER_SIZE
|
||||
SkRegion mClipRegionRoot;
|
||||
#endif
|
||||
|
||||
}; // class Snapshot
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ void Stencil::clear() {
|
||||
void Stencil::enableTest() {
|
||||
if (mState != kTest) {
|
||||
enable();
|
||||
glStencilFunc(GL_EQUAL, 0x1, 0x1);
|
||||
glStencilFunc(GL_EQUAL, 0xff, 0xff);
|
||||
// We only want to test, let's keep everything
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
|
||||
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
|
||||
@@ -48,7 +48,7 @@ void Stencil::enableTest() {
|
||||
void Stencil::enableWrite() {
|
||||
if (mState != kWrite) {
|
||||
enable();
|
||||
glStencilFunc(GL_ALWAYS, 0x1, 0x1);
|
||||
glStencilFunc(GL_ALWAYS, 0xff, 0xff);
|
||||
// The test always passes so the first two values are meaningless
|
||||
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
|
||||
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
|
||||
|
||||
@@ -148,7 +148,16 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
|
||||
<activity
|
||||
android:name="ClipRegion2Activity"
|
||||
android:label="_ClipRegion2">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name="DisplayListLayersActivity"
|
||||
android:label="__DisplayListLayers">
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.
|
||||
*/
|
||||
|
||||
package com.android.test.hwui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Region;
|
||||
import android.os.Bundle;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
public class ClipRegion2Activity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
final RegionView group = new RegionView(this);
|
||||
|
||||
final TextView text = new TextView(this);
|
||||
text.setText(buildText());
|
||||
group.addView(text);
|
||||
|
||||
setContentView(group);
|
||||
}
|
||||
|
||||
private static CharSequence buildText() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
buffer.append(LOREM_IPSUM);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static class RegionView extends FrameLayout {
|
||||
private final Region mRegion = new Region();
|
||||
private float mClipPosition = 0.0f;
|
||||
|
||||
public RegionView(Context c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
public float getClipPosition() {
|
||||
return mClipPosition;
|
||||
}
|
||||
|
||||
public void setClipPosition(float clipPosition) {
|
||||
mClipPosition = clipPosition;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
|
||||
canvas.save();
|
||||
|
||||
mRegion.setEmpty();
|
||||
mRegion.op(0, 0, getWidth(), getHeight(),
|
||||
Region.Op.REPLACE);
|
||||
mRegion.op(getWidth() / 4, getHeight() / 4, 3 * getWidth() / 4, 3 * getHeight() / 4,
|
||||
Region.Op.DIFFERENCE);
|
||||
|
||||
canvas.clipRegion(mRegion);
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula.";
|
||||
}
|
||||
@@ -16,38 +16,77 @@
|
||||
|
||||
package com.android.test.hwui;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Region;
|
||||
import android.graphics.Path;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
public class ClipRegionActivity extends Activity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
final RegionView view = new RegionView(this);
|
||||
setContentView(view);
|
||||
|
||||
final RegionView group = new RegionView(this);
|
||||
|
||||
final TextView text = new TextView(this);
|
||||
text.setText(buildText());
|
||||
group.addView(text);
|
||||
|
||||
setContentView(group);
|
||||
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(group, "clipPosition", 0.0f, 1.0f);
|
||||
animator.setDuration(3000);
|
||||
animator.setRepeatCount(ValueAnimator.INFINITE);
|
||||
animator.setRepeatMode(ValueAnimator.REVERSE);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
public static class RegionView extends View {
|
||||
private static CharSequence buildText() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
buffer.append(LOREM_IPSUM);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
public static class RegionView extends FrameLayout {
|
||||
private final Path mClipPath = new Path();
|
||||
private float mClipPosition = 0.0f;
|
||||
|
||||
public RegionView(Context c) {
|
||||
super(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
canvas.save();
|
||||
canvas.clipRect(100.0f, 100.0f, getWidth() - 100.0f, getHeight() - 100.0f,
|
||||
Region.Op.DIFFERENCE);
|
||||
canvas.drawARGB(128, 255, 0, 0);
|
||||
canvas.restore();
|
||||
public float getClipPosition() {
|
||||
return mClipPosition;
|
||||
}
|
||||
|
||||
public void setClipPosition(float clipPosition) {
|
||||
mClipPosition = clipPosition;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispatchDraw(Canvas canvas) {
|
||||
|
||||
canvas.save();
|
||||
|
||||
mClipPath.reset();
|
||||
mClipPath.addCircle(mClipPosition * getWidth(), getHeight() / 2.0f,
|
||||
getWidth() / 4.0f, Path.Direction.CW);
|
||||
|
||||
canvas.clipPath(mClipPath);
|
||||
super.dispatchDraw(canvas);
|
||||
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
private static final String LOREM_IPSUM = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sagittis molestie aliquam. Donec metus metus, laoreet nec sagittis vitae, ultricies sit amet eros. Suspendisse sed massa sit amet felis consectetur gravida. In vitae erat mi, in egestas nisl. Phasellus quis ipsum massa, at scelerisque arcu. Nam lectus est, pellentesque eget lacinia non, congue vitae augue. Aliquam erat volutpat. Pellentesque bibendum tincidunt viverra. Aliquam erat volutpat. Maecenas pretium vulputate placerat. Nulla varius elementum rutrum. Aenean mollis blandit imperdiet. Pellentesque interdum fringilla ligula.";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user