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
This commit is contained in:
@ -149,43 +149,44 @@ class GLES20Canvas extends Canvas {
|
||||
|
||||
@Override
|
||||
public boolean clipRect(float left, float top, float right, float bottom) {
|
||||
return nClipRect(mRenderer, left, top, right, bottom);
|
||||
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
|
||||
}
|
||||
|
||||
private native boolean nClipRect(int renderer, float left, float top, float right, float bottom);
|
||||
private native boolean nClipRect(int renderer, float left, float top,
|
||||
float right, float bottom, int op);
|
||||
|
||||
@Override
|
||||
public boolean clipRect(float left, float top, float right, float bottom, Region.Op op) {
|
||||
throw new UnsupportedOperationException();
|
||||
return nClipRect(mRenderer, left, top, right, bottom, op.nativeInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clipRect(int left, int top, int right, int bottom) {
|
||||
return nClipRect(mRenderer, left, top, right, bottom);
|
||||
return nClipRect(mRenderer, left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
|
||||
}
|
||||
|
||||
private native boolean nClipRect(int renderer, int left, int top, int right, int bottom);
|
||||
private native boolean nClipRect(int renderer, int left, int top, int right, int bottom, int op);
|
||||
|
||||
@Override
|
||||
public boolean clipRect(Rect rect) {
|
||||
return clipRect(rect.left, rect.top, rect.right, rect.bottom);
|
||||
return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
|
||||
Region.Op.INTERSECT.nativeInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clipRect(Rect rect, Region.Op op) {
|
||||
// TODO: Implement
|
||||
throw new UnsupportedOperationException();
|
||||
return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clipRect(RectF rect) {
|
||||
return clipRect(rect.left, rect.top, rect.right, rect.bottom);
|
||||
return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom,
|
||||
Region.Op.INTERSECT.nativeInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean clipRect(RectF rect, Region.Op op) {
|
||||
// TODO: Implement
|
||||
throw new UnsupportedOperationException();
|
||||
return nClipRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, op.nativeInt);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -347,7 +348,6 @@ class GLES20Canvas extends Canvas {
|
||||
|
||||
@Override
|
||||
public void setDrawFilter(DrawFilter filter) {
|
||||
// Don't crash, but ignore the draw filter
|
||||
// TODO: Implement PaintDrawFilter
|
||||
mFilter = filter;
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <SkCanvas.h>
|
||||
#include <SkMatrix.h>
|
||||
#include <SkPaint.h>
|
||||
#include <SkRegion.h>
|
||||
#include <SkXfermode.h>
|
||||
|
||||
#include <OpenGLRenderer.h>
|
||||
@ -120,13 +121,15 @@ static bool android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject canvas,
|
||||
}
|
||||
|
||||
static bool android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject canvas,
|
||||
OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom) {
|
||||
return renderer->clipRect(left, top, right, bottom);
|
||||
OpenGLRenderer* renderer, jfloat left, jfloat top, jfloat right, jfloat bottom,
|
||||
SkRegion::Op op) {
|
||||
return renderer->clipRect(left, top, right, bottom, op);
|
||||
}
|
||||
|
||||
static bool android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject canvas,
|
||||
OpenGLRenderer* renderer, jint left, jint top, jint right, jint bottom) {
|
||||
return renderer->clipRect(float(left), float(top), float(right), float(bottom));
|
||||
OpenGLRenderer* renderer, jint left, jint top, jint right, jint bottom,
|
||||
SkRegion::Op op) {
|
||||
return renderer->clipRect(float(left), float(top), float(right), float(bottom), op);
|
||||
}
|
||||
|
||||
static bool android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject canvas,
|
||||
@ -257,8 +260,8 @@ static JNINativeMethod gMethods[] = {
|
||||
{ "nSaveLayerAlpha", "(IFFFFII)I", (void*) android_view_GLES20Canvas_saveLayerAlpha },
|
||||
|
||||
{ "nQuickReject", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_quickReject },
|
||||
{ "nClipRect", "(IFFFF)Z", (void*) android_view_GLES20Canvas_clipRectF },
|
||||
{ "nClipRect", "(IIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
|
||||
{ "nClipRect", "(IFFFFI)Z", (void*) android_view_GLES20Canvas_clipRectF },
|
||||
{ "nClipRect", "(IIIIII)Z", (void*) android_view_GLES20Canvas_clipRect },
|
||||
|
||||
{ "nTranslate", "(IFF)V", (void*) android_view_GLES20Canvas_translate },
|
||||
{ "nRotate", "(IF)V", (void*) android_view_GLES20Canvas_rotate },
|
||||
|
@ -33,7 +33,11 @@ public class Region implements Parcelable {
|
||||
Op(int nativeInt) {
|
||||
this.nativeInt = nativeInt;
|
||||
}
|
||||
final int nativeInt;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final int nativeInt;
|
||||
}
|
||||
|
||||
/** Create an empty region
|
||||
|
@ -93,6 +93,25 @@ void Matrix4::copyTo(SkMatrix& v) const {
|
||||
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));
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ public:
|
||||
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);
|
||||
|
@ -244,7 +244,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
|
||||
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 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
|
||||
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 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
|
||||
|
||||
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::getMatrix(SkMatrix* matrix) {
|
||||
void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
|
||||
mat4 m(*matrix);
|
||||
mSnapshot->transform.multiply(m);
|
||||
mSnapshot->flags |= Snapshot::kFlagDirtyTransform;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -392,38 +386,26 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
|
||||
}
|
||||
|
||||
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 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot
|
||||
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);
|
||||
}
|
||||
|
@ -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 @@ public:
|
||||
|
||||
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);
|
||||
|
@ -129,6 +129,25 @@ struct Rect {
|
||||
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);
|
||||
}
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include <SkRegion.h>
|
||||
|
||||
#include "Layer.h"
|
||||
#include "Matrix.h"
|
||||
#include "Rect.h"
|
||||
@ -40,7 +42,7 @@ namespace uirenderer {
|
||||
*/
|
||||
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 @@ public:
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,39 +69,49 @@ public:
|
||||
* snapshot is restored so must the clip.
|
||||
*/
|
||||
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 @@ public:
|
||||
*/
|
||||
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 @@ public:
|
||||
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 @@ public:
|
||||
mat4 orthoMatrix;
|
||||
|
||||
private:
|
||||
// Clipping rectangle mapped with the transform
|
||||
Rect mappedClip;
|
||||
Rect localClip;
|
||||
|
||||
}; // class Snapshot
|
||||
|
||||
}; // namespace uirenderer
|
||||
|
Reference in New Issue
Block a user