Merge "Clip TouchFeedbackDrawable effect to receiver Outline"
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
26
tests/HwAccelerationTest/res/layout/projection_clipping.xml
Normal file
26
tests/HwAccelerationTest/res/layout/projection_clipping.xml
Normal 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>
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user