Merge "Clip TouchFeedbackDrawable effect to receiver Outline"

This commit is contained in:
Chris Craik
2014-04-24 00:08:08 +00:00
committed by Android (Google) Code Review
21 changed files with 374 additions and 114 deletions

View File

@@ -287,7 +287,7 @@ static jint android_view_GLES20Canvas_saveLayerClip(JNIEnv* env, jobject clazz,
jlong rendererPtr, jlong paintPtr, jint saveFlags) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
const android::uirenderer::Rect& bounds(renderer->getClipBounds());
const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
return renderer->saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
paint, saveFlags);
}
@@ -302,7 +302,7 @@ static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject clazz,
static jint android_view_GLES20Canvas_saveLayerAlphaClip(JNIEnv* env, jobject clazz,
jlong rendererPtr, jint alpha, jint saveFlags) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
const android::uirenderer::Rect& bounds(renderer->getClipBounds());
const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
return renderer->saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
alpha, saveFlags);
}
@@ -356,7 +356,7 @@ static jboolean android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz,
static jboolean android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz,
jlong rendererPtr, jobject rect) {
OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
const android::uirenderer::Rect& bounds(renderer->getClipBounds());
const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
env->CallVoidMethod(rect, gRectClassInfo.set,
int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
@@ -870,8 +870,7 @@ static jlong android_view_GLES20Canvas_finishRecording(JNIEnv* env,
return reinterpret_cast<jlong>(renderer->finishRecording());
}
static jlong android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env,
jobject clazz) {
static jlong android_view_GLES20Canvas_createDisplayListRenderer(JNIEnv* env, jobject clazz) {
return reinterpret_cast<jlong>(new DisplayListRenderer);
}

View File

@@ -435,8 +435,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
// first. This will merge SRC_OVER (directly) onto the canvas.
if (!projected && rippleRestoreCount < 0) {
rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
bounds.right, bounds.bottom, null, 0);
canvas.clipRect(bounds);
bounds.right, bounds.bottom, null);
}
drewRipples |= ripple.draw(canvas, getRipplePaint());
@@ -454,7 +453,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
if (drewRipples && !projected && rippleRestoreCount >= 0) {
final PorterDuffXfermode xfermode = mState.getTintXfermode();
canvas.saveLayer(bounds.left, bounds.top,
bounds.right, bounds.bottom, getMaskingPaint(xfermode), 0);
bounds.right, bounds.bottom, getMaskingPaint(xfermode));
}
Drawable mask = null;
@@ -472,7 +471,7 @@ public class TouchFeedbackDrawable extends LayerDrawable {
if (mask != null && drewRipples) {
// TODO: This will also mask the lower layer, which is bad.
canvas.saveLayer(bounds.left, bounds.top, bounds.right,
bounds.bottom, getMaskingPaint(DST_IN), 0);
bounds.bottom, getMaskingPaint(DST_IN));
mask.draw(canvas);
}

View File

@@ -29,7 +29,10 @@
namespace android {
namespace uirenderer {
DisplayListData::DisplayListData() : projectionReceiveIndex(-1), functorCount(0), hasDrawOps(false) {
DisplayListData::DisplayListData()
: projectionReceiveIndex(-1)
, functorCount(0)
, hasDrawOps(false) {
}
DisplayListData::~DisplayListData() {

View File

@@ -318,12 +318,19 @@ private:
class SaveLayerOp : public StateOp {
public:
SaveLayerOp(float left, float top, float right, float bottom, int alpha, int flags)
: mArea(left, top, right, bottom), mPaint(&mCachedPaint), mFlags(flags) {
: mArea(left, top, right, bottom)
, mPaint(&mCachedPaint)
, mFlags(flags)
, mConvexMask(NULL) {
mCachedPaint.setAlpha(alpha);
}
SaveLayerOp(float left, float top, float right, float bottom, const SkPaint* paint, int flags)
: mArea(left, top, right, bottom), mPaint(paint), mFlags(flags) {}
: mArea(left, top, right, bottom)
, mPaint(paint)
, mFlags(flags)
, mConvexMask(NULL)
{}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
@@ -338,7 +345,8 @@ public:
}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mPaint, mFlags);
renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom,
mPaint, mFlags, mConvexMask);
}
virtual void output(int level, uint32_t logFlags) const {
@@ -350,6 +358,11 @@ public:
int getFlags() { return mFlags; }
// Called to make SaveLayerOp clip to the provided mask when drawing back/restored
void setMask(const SkPath* convexMask) {
mConvexMask = convexMask;
}
private:
bool isSaveLayerAlpha() const {
SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
@@ -361,6 +374,10 @@ private:
const SkPaint* mPaint;
SkPaint mCachedPaint;
int mFlags;
// Convex path, points at data in RenderNode, valid for the duration of the frame only
// Only used for masking the SaveLayer which wraps projected RenderNodes
const SkPath* mConvexMask;
};
class TranslateOp : public StateOp {

View File

@@ -184,20 +184,15 @@ status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList,
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
// TODO: To be safe, the display list should be ref-counted in the
// resources cache, but we rely on the caller (UI toolkit) to
// do the right thing for now
if (displayList->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size();
}
DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList,
flags, *currentTransform());
addDrawOp(op);
mDisplayListData->addChild(op);
if (displayList->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size() - 1;
}
return DrawGlInfo::kStatusDone;
}

View File

@@ -48,6 +48,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
hasDrawnSinceUpdate = false;
forceFilter = false;
deferredList = NULL;
convexMask = NULL;
caches.resourceCache.incrementRefcount(this);
}

View File

@@ -227,6 +227,14 @@ public:
ANDROID_API void setColorFilter(SkColorFilter* filter);
inline void setConvexMask(const SkPath* convexMask) {
this->convexMask = convexMask;
}
inline const SkPath* getConvexMask() {
return convexMask;
}
void bindStencilRenderBuffer() const;
void bindTexture() const;
@@ -378,6 +386,13 @@ private:
*/
DeferredDisplayList* deferredList;
/**
* This convex path should be used to mask the layer's draw to the screen.
*
* Data not owned/managed by layer object.
*/
const SkPath* convexMask;
}; // struct Layer
}; // namespace uirenderer

View File

@@ -704,11 +704,11 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot&
///////////////////////////////////////////////////////////////////////////////
int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags) {
const SkPaint* paint, int flags, const SkPath* convexMask) {
const int count = saveSnapshot(flags);
if (!currentSnapshot()->isIgnored()) {
createLayer(left, top, right, bottom, paint, flags);
createLayer(left, top, right, bottom, paint, flags, convexMask);
}
return count;
@@ -782,7 +782,6 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
return count;
}
/**
* Layers are viewed by Skia are slightly different than layers in image editing
* programs (for instance.) When a layer is created, previously created layers
@@ -835,7 +834,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
* something actually gets drawn are the layers regions cleared.
*/
bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags) {
const SkPaint* paint, int flags, const SkPath* convexMask) {
LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top);
LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
@@ -865,6 +864,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
layer->setBlend(true);
layer->setDirty(false);
layer->setConvexMask(convexMask); // note: the mask must be cleared before returning to the cache
// Save the layer in the snapshot
mSnapshot->flags |= Snapshot::kFlagIsLayer;
@@ -1013,6 +1013,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
dirtyClip();
// Failing to add the layer to the cache should happen only if the layer is too large
layer->setConvexMask(NULL);
if (!mCaches.layerCache.put(layer)) {
LAYER_LOGD("Deleting layer");
Caches::getInstance().resourceCache.decrementRefcount(layer);
@@ -1122,6 +1123,38 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap)
#define DRAW_DOUBLE_STENCIL(DRAW_COMMAND) DRAW_DOUBLE_STENCIL_IF(true, DRAW_COMMAND)
void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
if (CC_UNLIKELY(layer->region.isEmpty())) return; // nothing to draw
if (layer->getConvexMask()) {
save(SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag);
// clip to the area of the layer the mask can be larger
clipRect(rect.left, rect.top, rect.right, rect.bottom, SkRegion::kIntersect_Op);
SkPaint paint;
paint.setAntiAlias(true);
paint.setColor(SkColorSetARGB(int(getLayerAlpha(layer) * 255), 0, 0, 0));
SkiaShader* oldShader = mDrawModifiers.mShader;
// create LayerShader to map SaveLayer content into subsequent draw
SkMatrix shaderMatrix;
shaderMatrix.setTranslate(rect.left, rect.bottom);
shaderMatrix.preScale(1, -1);
SkiaLayerShader layerShader(layer, &shaderMatrix);
mDrawModifiers.mShader = &layerShader;
// Since the drawing primitive is defined in local drawing space,
// we don't need to modify the draw matrix
const SkPath* maskPath = layer->getConvexMask();
DRAW_DOUBLE_STENCIL(drawConvexPath(*maskPath, &paint));
mDrawModifiers.mShader = oldShader;
restore();
return;
}
if (layer->region.isRect()) {
layer->setRegionAsRect();
@@ -1131,88 +1164,87 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
return;
}
if (CC_LIKELY(!layer->region.isEmpty())) {
size_t count;
const android::Rect* rects;
Region safeRegion;
if (CC_LIKELY(hasRectToRectTransform())) {
rects = layer->region.getArray(&count);
} else {
safeRegion = Region::createTJunctionFreeRegion(layer->region);
rects = safeRegion.getArray(&count);
}
// standard Region based draw
size_t count;
const android::Rect* rects;
Region safeRegion;
if (CC_LIKELY(hasRectToRectTransform())) {
rects = layer->region.getArray(&count);
} else {
safeRegion = Region::createTJunctionFreeRegion(layer->region);
rects = safeRegion.getArray(&count);
}
const float alpha = getLayerAlpha(layer);
const float texX = 1.0f / float(layer->getWidth());
const float texY = 1.0f / float(layer->getHeight());
const float height = rect.getHeight();
const float alpha = getLayerAlpha(layer);
const float texX = 1.0f / float(layer->getWidth());
const float texY = 1.0f / float(layer->getHeight());
const float height = rect.getHeight();
setupDraw();
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();
uint32_t numQuads = 0;
// 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();
uint32_t numQuads = 0;
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
setupDrawColorFilter(layer->getColorFilter());
setupDrawBlending(layer);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(layer->getColorFilter());
setupDrawTexture(layer->getTexture());
if (currentTransform()->isPureTranslate()) {
const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
setupDrawColorFilter(layer->getColorFilter());
setupDrawBlending(layer);
setupDrawProgram();
setupDrawDirtyRegionsDisabled();
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms(layer->getColorFilter());
setupDrawTexture(layer->getTexture());
if (currentTransform()->isPureTranslate()) {
const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f);
const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f);
layer->setFilter(GL_NEAREST);
setupDrawModelView(kModelViewMode_Translate, false,
x, y, x + rect.getWidth(), y + rect.getHeight(), true);
} else {
layer->setFilter(GL_LINEAR);
setupDrawModelView(kModelViewMode_Translate, false,
rect.left, rect.top, rect.right, rect.bottom);
}
setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
layer->setFilter(GL_NEAREST);
setupDrawModelView(kModelViewMode_Translate, false,
x, y, x + rect.getWidth(), y + rect.getHeight(), true);
} else {
layer->setFilter(GL_LINEAR);
setupDrawModelView(kModelViewMode_Translate, false,
rect.left, rect.top, rect.right, rect.bottom);
}
setupDrawMeshIndices(&mesh[0].x, &mesh[0].u);
for (size_t i = 0; i < count; i++) {
const android::Rect* r = &rects[i];
for (size_t i = 0; i < count; i++) {
const android::Rect* r = &rects[i];
const float u1 = r->left * texX;
const float v1 = (height - r->top) * texY;
const float u2 = r->right * texX;
const float v2 = (height - r->bottom) * texY;
const float u1 = r->left * texX;
const float v1 = (height - r->top) * texY;
const float u2 = r->right * texX;
const float v2 = (height - r->bottom) * texY;
// TODO: Reject quads outside of the clip
TextureVertex::set(mesh++, r->left, r->top, u1, v1);
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
// TODO: Reject quads outside of the clip
TextureVertex::set(mesh++, r->left, r->top, u1, v1);
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
numQuads++;
numQuads++;
if (numQuads >= gMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
numQuads = 0;
mesh = mCaches.getRegionMesh();
}
}
if (numQuads > 0) {
if (numQuads >= gMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
numQuads = 0;
mesh = mCaches.getRegionMesh();
}
}
if (numQuads > 0) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
}
#if DEBUG_LAYERS_AS_REGIONS
drawRegionRectsDebug(layer->region);
drawRegionRectsDebug(layer->region);
#endif
layer->region.clear();
}
layer->region.clear();
}
#if DEBUG_LAYERS_AS_REGIONS
@@ -2926,7 +2958,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
if (layer->isTextureLayer()) {
transform = &layer->getTransform();
if (!transform->isIdentity()) {
save(0);
save(SkCanvas::kMatrix_SaveFlag);
concatMatrix(*transform);
}
}

View File

@@ -161,7 +161,14 @@ public:
ANDROID_API void flushLayerUpdates();
ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
const SkPaint* paint, int flags) {
return saveLayer(left, top, right, bottom, paint, flags, NULL);
}
// Specialized saveLayer implementation, which will pass the convexMask to an FBO layer, if
// created, which will in turn clip to that mask when drawn back/restored.
int saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags, const SkPath* convexMask);
int saveLayerDeferred(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
@@ -523,11 +530,12 @@ private:
* @param alpha The translucency of the layer
* @param mode The blending mode of the layer
* @param flags The layer save flags
* @param mask A mask to use when drawing the layer back, may be empty
*
* @return True if the layer was successfully created, false otherwise
*/
bool createLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
const SkPaint* paint, int flags, const SkPath* convexMask);
/**
* Creates a new layer stored in the specified snapshot as an FBO.

View File

@@ -30,6 +30,8 @@ namespace uirenderer {
#define RECT_STRING "%7.2f %7.2f %7.2f %7.2f"
#define RECT_ARGS(r) \
(r).left, (r).top, (r).right, (r).bottom
#define SK_RECT_ARGS(r) \
(r).left(), (r).top(), (r).right(), (r).bottom()
///////////////////////////////////////////////////////////////////////////////
// Structs

View File

@@ -77,7 +77,7 @@ void RenderNode::setStagingDisplayList(DisplayListData* data) {
*/
void RenderNode::output(uint32_t level) {
ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
mName.string(), isRenderable());
getName(), isRenderable());
ALOGD("%*s%s %d", level * 2, "", "Save",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
@@ -87,7 +87,7 @@ void RenderNode::output(uint32_t level) {
mDisplayListData->displayListOps[i]->output(level, flags);
}
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, getName());
}
void RenderNode::prepareTree(TreeInfo& info) {
@@ -263,12 +263,13 @@ void RenderNode::computeOrdering() {
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
DrawDisplayListOp* childOp = mDisplayListData->children()[i];
childOp->mDisplayList->computeOrderingImpl(childOp,
&mProjectedNodes, &mat4::identity());
properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
}
}
void RenderNode::computeOrderingImpl(
DrawDisplayListOp* opState,
const SkPath* outlineOfProjectionSurface,
Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
mProjectedNodes.clear();
@@ -296,6 +297,7 @@ void RenderNode::computeOrderingImpl(
DrawDisplayListOp* childOp = mDisplayListData->children()[i];
RenderNode* child = childOp->mDisplayList;
const SkPath* projectionOutline = NULL;
Vector<DrawDisplayListOp*>* projectionChildren = NULL;
const mat4* projectionTransform = NULL;
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
@@ -304,6 +306,7 @@ void RenderNode::computeOrderingImpl(
// Note that if a direct descendent is projecting backwards, we pass it's
// grandparent projection collection, since it shouldn't project onto it's
// parent, where it will already be drawing.
projectionOutline = properties().getOutline().getPath();
projectionChildren = &mProjectedNodes;
projectionTransform = &mat4::identity();
} else {
@@ -311,10 +314,12 @@ void RenderNode::computeOrderingImpl(
applyViewPropertyTransforms(localTransformFromProjectionSurface);
haveAppliedPropertiesToProjection = true;
}
projectionOutline = outlineOfProjectionSurface;
projectionChildren = compositedChildrenOfProjectionSurface;
projectionTransform = &localTransformFromProjectionSurface;
}
child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
child->computeOrderingImpl(childOp,
projectionOutline, projectionChildren, projectionTransform);
}
}
}
@@ -354,7 +359,7 @@ public:
: mReplayStruct(replayStruct), mLevel(level) {}
inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
properties().getReplayStruct().mRenderer.eventMark(operation->name());
mReplayStruct.mRenderer.eventMark(operation->name());
#endif
operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
}
@@ -364,8 +369,6 @@ public:
}
inline void endMark() {
mReplayStruct.mRenderer.endMark();
DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
mReplayStruct.mDrawGlStatus);
}
inline int level() { return mLevel; }
inline int replayFlags() { return mReplayStruct.mReplayFlags; }
@@ -470,6 +473,10 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair
endIndex = size;
shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
}
DISPLAY_LIST_LOGD("%*s%d %s 3d children:", (handler.level() + 1) * 2, "",
endIndex - drawIndex, mode == kNegativeZChildren ? "negative" : "positive");
float lastCasterZ = 0.0f;
while (shadowIndex < endIndex || drawIndex < endIndex) {
if (shadowIndex < endIndex) {
@@ -506,6 +513,42 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair
template <class T>
void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
bool maskProjecteesWithPath = projectionReceiverOutline != NULL
&& !projectionReceiverOutline->isRect(NULL);
int restoreTo = renderer.getSaveCount();
// If the projection reciever has an outline, we mask each of the projected rendernodes to it
// Either with clipRect, or special saveLayer masking
LinearAllocator& alloc = handler.allocator();
if (projectionReceiverOutline != NULL) {
const SkRect& outlineBounds = projectionReceiverOutline->getBounds();
if (projectionReceiverOutline->isRect(NULL)) {
// mask to the rect outline simply with clipRect
handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
ClipRectOp* clipOp = new (alloc) ClipRectOp(
outlineBounds.left(), outlineBounds.top(),
outlineBounds.right(), outlineBounds.bottom(), SkRegion::kIntersect_Op);
handler(clipOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
} else {
// wrap the projected RenderNodes with a SaveLayer that will mask to the outline
SaveLayerOp* op = new (alloc) SaveLayerOp(
outlineBounds.left(), outlineBounds.top(),
outlineBounds.right(), outlineBounds.bottom(),
255, SkCanvas::kARGB_ClipLayer_SaveFlag);
op->setMask(projectionReceiverOutline);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
/* TODO: add optimizations here to take advantage of placement/size of projected
* children (which may shrink saveLayer area significantly). This is dependent on
* passing actual drawing/dirtying bounds of projected content down to native.
*/
}
}
// draw projected nodes
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
DrawDisplayListOp* childOp = mProjectedNodes[i];
@@ -517,6 +560,11 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
childOp->mSkipInOrderDraw = true;
renderer.restoreToCount(restoreTo);
}
if (projectionReceiverOutline != NULL) {
handler(new (alloc) RestoreToCountOp(restoreTo),
PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
}
/**
@@ -532,17 +580,17 @@ template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
const int level = handler.level();
if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
return;
}
handler.startMark(mName.string());
handler.startMark(getName());
#if DEBUG_DISPLAY_LIST
Rect* clipRect = renderer.getClipRect();
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
clipRect->right, clipRect->bottom);
const Rect& clipRect = renderer.getLocalClipBounds();
DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), localClipBounds: %.0f, %.0f, %.0f, %.0f",
level * 2, "", this, getName(),
clipRect.left, clipRect.top, clipRect.right, clipRect.bottom);
#endif
LinearAllocator& alloc = handler.allocator();
@@ -590,6 +638,7 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
PROPERTY_SAVECOUNT, properties().getClipToBounds());
renderer.setOverrideLayerAlpha(1.0f);
DISPLAY_LIST_LOGD("%*sDone (%p, %s)", level * 2, "", this, getName());
handler.endMark();
}

View File

@@ -172,6 +172,7 @@ private:
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
void computeOrderingImpl(DrawDisplayListOp* opState,
const SkPath* outlineOfProjectionSurface,
Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface);

View File

@@ -167,7 +167,7 @@ public:
virtual void concatMatrix(const SkMatrix* matrix) = 0;
// clip
virtual const Rect& getClipBounds() const = 0;
virtual const Rect& getLocalClipBounds() const = 0;
virtual bool quickRejectConservative(float left, float top,
float right, float bottom) const = 0;
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0;

View File

@@ -73,7 +73,7 @@ SkiaShader::SkiaShader(): mCaches(NULL) {
}
SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
SkShader::TileMode tileY, const SkMatrix* matrix, bool blend):
mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
mCaches(NULL) {
setMatrix(matrix);
@@ -100,6 +100,49 @@ void SkiaShader::computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelVi
screenSpace.multiply(modelView);
}
///////////////////////////////////////////////////////////////////////////////
// Layer shader
///////////////////////////////////////////////////////////////////////////////
SkiaLayerShader::SkiaLayerShader(Layer* layer, const SkMatrix* matrix):
SkiaShader(kBitmap, NULL, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
matrix, layer->isBlend()), mLayer(layer) {
updateLocalMatrix(matrix);
}
SkiaShader* SkiaLayerShader::copy() {
SkiaLayerShader* copy = new SkiaLayerShader();
copy->copyFrom(*this);
copy->mLayer = mLayer;
return copy;
}
void SkiaLayerShader::describe(ProgramDescription& description, const Extensions& extensions) {
description.hasBitmap = true;
}
void SkiaLayerShader::setupProgram(Program* program, const mat4& modelView,
const Snapshot& snapshot, GLuint* textureUnit) {
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
const float width = mLayer->getWidth();
const float height = mLayer->getHeight();
mat4 textureTransform;
computeScreenSpaceMatrix(textureTransform, modelView);
// Uniforms
mLayer->bindTexture();
mLayer->setWrap(GL_CLAMP_TO_EDGE);
mLayer->setFilter(GL_LINEAR);
glUniform1i(program->getUniform("bitmapSampler"), textureSlot);
glUniformMatrix4fv(program->getUniform("textureTransform"), 1,
GL_FALSE, &textureTransform.data[0]);
glUniform2f(program->getUniform("textureDimension"), 1.0f / width, 1.0f / height);
}
///////////////////////////////////////////////////////////////////////////////
// Bitmap shader
///////////////////////////////////////////////////////////////////////////////

View File

@@ -58,7 +58,7 @@ public:
};
ANDROID_API SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend);
SkShader::TileMode tileY, const SkMatrix* matrix, bool blend);
virtual ~SkiaShader();
virtual SkiaShader* copy() = 0;
@@ -88,7 +88,7 @@ public:
return mGenerationId;
}
void setMatrix(SkMatrix* matrix) {
void setMatrix(const SkMatrix* matrix) {
updateLocalMatrix(matrix);
mGenerationId++;
}
@@ -133,6 +133,24 @@ private:
// Implementations
///////////////////////////////////////////////////////////////////////////////
/**
* A shader that draws a layer.
*/
struct SkiaLayerShader: public SkiaShader {
SkiaLayerShader(Layer* layer, const SkMatrix* matrix);
SkiaShader* copy();
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
GLuint* textureUnit);
private:
SkiaLayerShader() {
}
Layer* mLayer;
}; // struct SkiaLayerShader
/**
* A shader that draws a bitmap.
*/

View File

@@ -108,7 +108,12 @@ public:
* Returns the current clip in local coordinates. The clip rect is
* transformed by the inverse transform matrix.
*/
ANDROID_API const Rect& getLocalClip();
const Rect& getLocalClip();
/**
* Returns the current clip in render target coordinates.
*/
const Rect& getRenderTargetClip() { return *clipRect; }
/**
* Resets the clip to the specified rect.
@@ -238,7 +243,7 @@ private:
mat4 mTransformRoot;
Rect mClipRectRoot;
Rect mLocalClip;
Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this
SkRegion mClipRegionRoot;

View File

@@ -75,7 +75,8 @@ public:
void concatMatrix(const Matrix4& matrix); // internal only convenience method
// Clip
const Rect& getClipBounds() const { return mSnapshot->getLocalClip(); }
virtual const Rect& getLocalClipBounds() const { return mSnapshot->getLocalClip(); }
virtual bool quickRejectConservative(float left, float top, float right, float bottom) const;
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
@@ -83,6 +84,8 @@ public:
virtual bool clipRegion(const SkRegion* region, SkRegion::Op op);
protected:
const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); }
int getWidth() { return mWidth; }
int getHeight() { return mHeight; }

View File

@@ -867,5 +867,16 @@
</intent-filter>
</activity>
<activity
android:name=".ProjectionClippingActivity"
android:label="Reordering/Projection Clipping">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android" >
<solid android:color="#eee" />
<corners android:radius="30dp" />
</shape>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:translationX="50dp"
android:translationY="50dp"
android:translationZ="30dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/round_rect_background">
<View
android:id="@+id/clickable1"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="?android:attr/selectableItemBackground"/>
<View
android:id="@+id/clickable2"
android:translationX="50dp"
android:translationY="10dp"
android:layout_width="150dp"
android:layout_height="100dp"
android:background="?android:attr/selectableItemBackground"/>
</FrameLayout>
</LinearLayout>

View File

@@ -0,0 +1,27 @@
package com.android.test.hwui;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.RenderNode;
import android.view.View;
public class ProjectionClippingActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.projection_clipping);
View.OnClickListener listener = new View.OnClickListener() {
@Override
public void onClick(View v) {
// woo! nothing!
}
};
findViewById(R.id.clickable1).setOnClickListener(listener);
findViewById(R.id.clickable2).setOnClickListener(listener);
}
}