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:
Romain Guy
2010-07-16 14:12:24 -07:00
parent ebd6f94dbd
commit 079ba2c85b
9 changed files with 130 additions and 87 deletions

View File

@ -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;
}

View File

@ -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 },

View File

@ -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

View File

@ -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));
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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