Improve clip support (add intersect, union and replace.)
This change also modifies the way the clip is stored. The clip is now
always stored in screen-space coordinates.
Change-Id: I96375784d82dfe975bc6477a159e6866e7052487
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 877d3bb..b459202 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -93,6 +93,25 @@
v.set(SkMatrix::kMPersp2, data[15]);
}
+void Matrix4::loadInverse(const Matrix4& v) {
+ double scale = 1.0 /
+ (v.data[0] * ((double) v.data[5] * v.data[15] - (double) v.data[13] * v.data[7]) +
+ v.data[4] * ((double) v.data[13] * v.data[3] - (double) v.data[1] * v.data[15]) +
+ v.data[12] * ((double) v.data[1] * v.data[7] - (double) v.data[5] * v.data[3]));
+
+ data[0] = (v.data[5] * v.data[15] - v.data[13] * v.data[7]) * scale;
+ data[4] = (v.data[12] * v.data[7] - v.data[4] * v.data[15]) * scale;
+ data[12] = (v.data[4] * v.data[13] - v.data[12] * v.data[5]) * scale;
+
+ data[1] = (v.data[13] * v.data[3] - v.data[1] * v.data[15]) * scale;
+ data[5] = (v.data[0] * v.data[15] - v.data[12] * v.data[3]) * scale;
+ data[13] = (v.data[12] * v.data[1] - v.data[0] * v.data[13]) * scale;
+
+ data[3] = (v.data[1] * v.data[7] - v.data[5] * v.data[3]) * scale;
+ data[7] = (v.data[4] * v.data[3] - v.data[0] * v.data[7]) * scale;
+ data[15] = (v.data[0] * v.data[5] - v.data[4] * v.data[1]) * scale;
+}
+
void Matrix4::copyTo(float* v) const {
memcpy(v, data, sizeof(data));
}
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index ba5be03..b8a4da7 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -54,6 +54,8 @@
void load(const Matrix4& v);
void load(const SkMatrix& v);
+ void loadInverse(const Matrix4& v);
+
void loadTranslate(float x, float y, float z);
void loadScale(float sx, float sy, float sz);
void loadRotate(float angle, float x, float y, float z);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index decfecf..d950ffa 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -244,7 +244,7 @@
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
// Restore the clip from the previous snapshot
- const Rect& clip = previous->getMappedClip();
+ const Rect& clip = previous->clipRect;
glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
Layer* layer = current->layer;
@@ -339,12 +339,11 @@
saveSnapshot();
// TODO: This doesn't preserve other transformations (check Skia first)
mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
- mSnapshot->setClip(left, top, right, bottom);
+ mSnapshot->setClip(0.0f, 0.0f, right - left, bottom - top);
mSnapshot->height = bottom - top;
setScissorFromClip();
- mSnapshot->flags = Snapshot::kFlagDirtyTransform | Snapshot::kFlagDirtyOrtho |
- Snapshot::kFlagClipSet;
+ mSnapshot->flags = Snapshot::kFlagDirtyOrtho | Snapshot::kFlagClipSet;
mSnapshot->orthoMatrix.load(mOrthoMatrix);
// Change the ortho projection
@@ -359,22 +358,18 @@
void OpenGLRenderer::translate(float dx, float dy) {
mSnapshot->transform.translate(dx, dy, 0.0f);
- mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
}
void OpenGLRenderer::rotate(float degrees) {
mSnapshot->transform.rotate(degrees, 0.0f, 0.0f, 1.0f);
- mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
}
void OpenGLRenderer::scale(float sx, float sy) {
mSnapshot->transform.scale(sx, sy, 1.0f);
- mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
}
void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
mSnapshot->transform.load(*matrix);
- mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
}
void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
@@ -384,7 +379,6 @@
void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
mat4 m(*matrix);
mSnapshot->transform.multiply(m);
- mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
}
///////////////////////////////////////////////////////////////////////////////
@@ -392,38 +386,26 @@
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::setScissorFromClip() {
- const Rect& clip = mSnapshot->getMappedClip();
+ const Rect& clip = mSnapshot->clipRect;
glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
}
const Rect& OpenGLRenderer::getClipBounds() {
- return mSnapshot->clipRect;
+ return mSnapshot->getLocalClip();
}
bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
- /*
- * The documentation of quickReject() indicates that the specified rect
- * is transformed before being compared to the clip rect. However, the
- * clip rect is not stored transformed in the snapshot and can thus be
- * compared directly
- *
- * The following code can be used instead to performed a mapped comparison:
- *
- * mSnapshot->transform.mapRect(r);
- * const Rect& clip = mSnapshot->getMappedClip();
- * return !clip.intersects(r);
- */
Rect r(left, top, right, bottom);
+ mSnapshot->transform.mapRect(r);
return !mSnapshot->clipRect.intersects(r);
}
-bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom) {
- bool clipped = mSnapshot->clip(left, top, right, bottom);
+bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ bool clipped = mSnapshot->clip(left, top, right, bottom, op);
if (clipped) {
- mSnapshot->flags |= Snapshot::kFlagClipSet;
setScissorFromClip();
}
- return clipped;
+ return !mSnapshot->clipRect.isEmpty();
}
///////////////////////////////////////////////////////////////////////////////
@@ -504,7 +486,7 @@
}
void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
- const Rect& clip = mSnapshot->getMappedClip();
+ const Rect& clip = mSnapshot->clipRect;
drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
}
@@ -584,22 +566,20 @@
mModelView.loadTranslate(left, top, 0.0f);
mModelView.scale(right - left, bottom - top, 1.0f);
- // TODO: Pick the program matching the current shader
- sp<DrawColorProgram> program = mDrawColorProgram;
- if (!useProgram(program)) {
+ if (!useProgram(mDrawColorProgram)) {
const GLvoid* p = &gDrawColorVertices[0].position[0];
- glVertexAttribPointer(program->position, 2, GL_FLOAT, GL_FALSE,
+ glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
gDrawColorVertexStride, p);
}
if (!ignoreTransform) {
- program->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+ mDrawColorProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
} else {
mat4 identity;
- program->set(mOrthoMatrix, mModelView, identity);
+ mDrawColorProgram->set(mOrthoMatrix, mModelView, identity);
}
- glUniform4f(program->color, r, g, b, a);
+ glUniform4f(mDrawColorProgram->color, r, g, b, a);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
}
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 2a96432..5667229 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -23,6 +23,7 @@
#include <SkBitmap.h>
#include <SkMatrix.h>
#include <SkPaint.h>
+#include <SkRegion.h>
#include <SkShader.h>
#include <SkXfermode.h>
@@ -88,7 +89,7 @@
const Rect& getClipBounds();
bool quickReject(float left, float top, float right, float bottom);
- bool clipRect(float left, float top, float right, float bottom);
+ bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index ad57550..7be0c340 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -129,6 +129,25 @@
return intersect(r.left, r.top, r.right, r.bottom);
}
+ bool unionWith(const Rect& r) {
+ if (r.left < r.right && r.top < r.bottom) {
+ if (left < right && top < bottom) {
+ if (left > r.left) left = r.left;
+ if (top > r.top) top = r.top;
+ if (right < r.right) right = r.right;
+ if (bottom < r.bottom) bottom = r.bottom;
+ return true;
+ } else {
+ left = r.left;
+ top = r.top;
+ right = r.right;
+ bottom = r.bottom;
+ return true;
+ }
+ }
+ return false;
+ }
+
void dump() const {
LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
}
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 32fee32..96dfab9 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -22,6 +22,8 @@
#include <utils/RefBase.h>
+#include <SkRegion.h>
+
#include "Layer.h"
#include "Matrix.h"
#include "Rect.h"
@@ -40,7 +42,7 @@
*/
class Snapshot: public LightRefBase<Snapshot> {
public:
- Snapshot(): layer(NULL), fbo(0) { }
+ Snapshot(): flags(0x0), previous(NULL), layer(NULL), fbo(0) { }
/**
* Copies the specified snapshot. Only the transform and clip rectangle
@@ -52,12 +54,10 @@
height(s->height),
transform(s->transform),
clipRect(s->clipRect),
- flags(kFlagDirtyTransform),
+ flags(0x0),
previous(s),
layer(NULL),
fbo(s->fbo) {
- mappedClip.set(s->clipRect);
- transform.mapRect(mappedClip);
}
/**
@@ -70,38 +70,48 @@
*/
kFlagClipSet = 0x1,
/**
- * Indicates that the snapshot holds new transform
- * information.
- */
- kFlagDirtyTransform = 0x2,
- /**
* Indicates that this snapshot was created when saving
* a new layer.
*/
- kFlagIsLayer = 0x4,
+ kFlagIsLayer = 0x2,
/**
* Indicates that this snapshot has changed the ortho matrix.
*/
- kFlagDirtyOrtho = 0x8,
+ kFlagDirtyOrtho = 0x4,
};
/**
- * Returns the current clip region mapped by the current transform.
- */
- const Rect& getMappedClip() {
- return mappedClip;
- }
-
- /**
* Intersects the current clip with the new clip rectangle.
*/
- bool clip(float left, float top, float right, float bottom) {
- bool clipped = clipRect.intersect(left, top, right, bottom);
- if (flags & kFlagDirtyTransform) {
- flags &= ~kFlagDirtyTransform;
- mappedClip.set(clipRect);
- transform.mapRect(mappedClip);
+ bool clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+ bool clipped = false;
+
+ Rect r(left, top, right, bottom);
+ transform.mapRect(r);
+
+ switch (op) {
+ case SkRegion::kDifference_Op:
+ break;
+ case SkRegion::kIntersect_Op:
+ clipped = clipRect.intersect(r);
+ break;
+ case SkRegion::kUnion_Op:
+ clipped = clipRect.unionWith(r);
+ break;
+ case SkRegion::kXOR_Op:
+ break;
+ case SkRegion::kReverseDifference_Op:
+ break;
+ case SkRegion::kReplace_Op:
+ clipRect.set(r);
+ clipped = true;
+ break;
}
+
+ if (clipped) {
+ flags |= Snapshot::kFlagClipSet;
+ }
+
return clipped;
}
@@ -110,11 +120,15 @@
*/
void setClip(float left, float top, float right, float bottom) {
clipRect.set(left, top, right, bottom);
- if (flags & kFlagDirtyTransform) {
- flags &= ~kFlagDirtyTransform;
- mappedClip.set(clipRect);
- transform.mapRect(mappedClip);
- }
+ flags |= Snapshot::kFlagClipSet;
+ }
+
+ const Rect& getLocalClip() {
+ mat4 inverse;
+ inverse.loadInverse(transform);
+ localClip.set(clipRect);
+ inverse.mapRect(localClip);
+ return localClip;
}
/**
@@ -129,7 +143,8 @@
mat4 transform;
/**
- * Current clip region.
+ * Current clip region. The clip is stored in canvas-space coordinates,
+ * (screen-space coordinates in the regular case.)
*/
Rect clipRect;
@@ -155,8 +170,8 @@
mat4 orthoMatrix;
private:
- // Clipping rectangle mapped with the transform
- Rect mappedClip;
+ Rect localClip;
+
}; // class Snapshot
}; // namespace uirenderer