Merge "Allow layers with a stencil buffer to be resized on the fly Bug #7146141"
This commit is contained in:
@@ -54,6 +54,51 @@ Layer::~Layer() {
|
|||||||
deleteTexture();
|
deleteTexture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
|
||||||
|
return uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
|
||||||
|
return uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Layer::resize(const uint32_t width, const uint32_t height) {
|
||||||
|
uint32_t desiredWidth = computeIdealWidth(width);
|
||||||
|
uint32_t desiredHeight = computeIdealWidth(height);
|
||||||
|
|
||||||
|
if (desiredWidth <= getWidth() && desiredHeight <= getHeight()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t oldWidth = getWidth();
|
||||||
|
uint32_t oldHeight = getHeight();
|
||||||
|
|
||||||
|
setSize(desiredWidth, desiredHeight);
|
||||||
|
|
||||||
|
if (fbo) {
|
||||||
|
Caches::getInstance().activeTexture(0);
|
||||||
|
bindTexture();
|
||||||
|
allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
setSize(oldWidth, oldHeight);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stencil) {
|
||||||
|
bindStencilRenderBuffer();
|
||||||
|
allocateStencilRenderBuffer();
|
||||||
|
|
||||||
|
if (glGetError() != GL_NO_ERROR) {
|
||||||
|
setSize(oldWidth, oldHeight);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void Layer::removeFbo(bool flush) {
|
void Layer::removeFbo(bool flush) {
|
||||||
if (stencil) {
|
if (stencil) {
|
||||||
// TODO: recycle & cache instead of simply deleting
|
// TODO: recycle & cache instead of simply deleting
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ struct Layer {
|
|||||||
Layer(const uint32_t layerWidth, const uint32_t layerHeight);
|
Layer(const uint32_t layerWidth, const uint32_t layerHeight);
|
||||||
~Layer();
|
~Layer();
|
||||||
|
|
||||||
|
static uint32_t computeIdealWidth(uint32_t layerWidth);
|
||||||
|
static uint32_t computeIdealHeight(uint32_t layerHeight);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling this method will remove (either by recycling or
|
* Calling this method will remove (either by recycling or
|
||||||
* destroying) the associated FBO, if present, and any render
|
* destroying) the associated FBO, if present, and any render
|
||||||
@@ -91,6 +94,17 @@ struct Layer {
|
|||||||
return texture.height;
|
return texture.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize the layer and its texture if needed.
|
||||||
|
*
|
||||||
|
* @param width The new width of the layer
|
||||||
|
* @param height The new height of the layer
|
||||||
|
*
|
||||||
|
* @return True if the layer was resized or nothing happened, false if
|
||||||
|
* a failure occurred during the resizing operation
|
||||||
|
*/
|
||||||
|
bool resize(const uint32_t width, const uint32_t height);
|
||||||
|
|
||||||
void setSize(uint32_t width, uint32_t height) {
|
void setSize(uint32_t width, uint32_t height) {
|
||||||
texture.width = width;
|
texture.width = width;
|
||||||
texture.height = height;
|
texture.height = height;
|
||||||
@@ -203,6 +217,12 @@ struct Layer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void bindStencilRenderBuffer() {
|
||||||
|
if (stencil) {
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline void generateTexture() {
|
inline void generateTexture() {
|
||||||
if (!texture.id) {
|
if (!texture.id) {
|
||||||
glGenTextures(1, &texture.id);
|
glGenTextures(1, &texture.id);
|
||||||
@@ -229,7 +249,16 @@ struct Layer {
|
|||||||
#if DEBUG_LAYERS
|
#if DEBUG_LAYERS
|
||||||
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
|
ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
|
||||||
#endif
|
#endif
|
||||||
glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL);
|
if (texture.id) {
|
||||||
|
glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0,
|
||||||
|
format, storage, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void allocateStencilRenderBuffer() {
|
||||||
|
if (stencil) {
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, getWidth(), getHeight());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline mat4& getTexTransform() {
|
inline mat4& getTexTransform() {
|
||||||
|
|||||||
@@ -136,31 +136,6 @@ void LayerCache::dump() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t height) {
|
|
||||||
// TODO: We should be smarter and see if we have a texture of the appropriate
|
|
||||||
// size already in the cache, and reuse it instead of creating a new one
|
|
||||||
|
|
||||||
LayerEntry entry(width, height);
|
|
||||||
if (entry.mWidth <= layer->getWidth() && entry.mHeight <= layer->getHeight()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t oldWidth = layer->getWidth();
|
|
||||||
uint32_t oldHeight = layer->getHeight();
|
|
||||||
|
|
||||||
Caches::getInstance().activeTexture(0);
|
|
||||||
layer->bindTexture();
|
|
||||||
layer->setSize(entry.mWidth, entry.mHeight);
|
|
||||||
layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
|
|
||||||
|
|
||||||
if (glGetError() != GL_NO_ERROR) {
|
|
||||||
layer->setSize(oldWidth, oldHeight);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LayerCache::put(Layer* layer) {
|
bool LayerCache::put(Layer* layer) {
|
||||||
if (!layer->isCacheable()) return false;
|
if (!layer->isCacheable()) return false;
|
||||||
|
|
||||||
|
|||||||
@@ -72,17 +72,6 @@ public:
|
|||||||
* Clears the cache. This causes all layers to be deleted.
|
* Clears the cache. This causes all layers to be deleted.
|
||||||
*/
|
*/
|
||||||
void clear();
|
void clear();
|
||||||
/**
|
|
||||||
* Resize the specified layer if needed.
|
|
||||||
*
|
|
||||||
* @param layer The layer to resize
|
|
||||||
* @param width The new width of the layer
|
|
||||||
* @param height The new height of the layer
|
|
||||||
*
|
|
||||||
* @return True if the layer was resized or nothing happened, false if
|
|
||||||
* a failure occurred during the resizing operation
|
|
||||||
*/
|
|
||||||
bool resize(Layer* layer, const uint32_t width, const uint32_t height);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the maximum size of the cache in bytes.
|
* Sets the maximum size of the cache in bytes.
|
||||||
@@ -108,8 +97,8 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) {
|
LayerEntry(const uint32_t layerWidth, const uint32_t layerHeight): mLayer(NULL) {
|
||||||
mWidth = uint32_t(ceilf(layerWidth / float(LAYER_SIZE)) * LAYER_SIZE);
|
mWidth = Layer::computeIdealWidth(layerWidth);
|
||||||
mHeight = uint32_t(ceilf(layerHeight / float(LAYER_SIZE)) * LAYER_SIZE);
|
mHeight = Layer::computeIdealHeight(layerHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
LayerEntry(Layer* layer):
|
LayerEntry(Layer* layer):
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
|
|||||||
if (layer) {
|
if (layer) {
|
||||||
LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height);
|
LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height);
|
||||||
|
|
||||||
if (Caches::getInstance().layerCache.resize(layer, width, height)) {
|
if (layer->resize(width, height)) {
|
||||||
layer->layer.set(0.0f, 0.0f, width, height);
|
layer->layer.set(0.0f, 0.0f, width, height);
|
||||||
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
|
layer->texCoords.set(0.0f, height / float(layer->getHeight()),
|
||||||
width / float(layer->getWidth()), 0.0f);
|
width / float(layer->getWidth()), 0.0f);
|
||||||
|
|||||||
@@ -1248,11 +1248,12 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
|
|||||||
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
|
// TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
|
||||||
GLuint buffer;
|
GLuint buffer;
|
||||||
glGenRenderbuffers(1, &buffer);
|
glGenRenderbuffers(1, &buffer);
|
||||||
glBindRenderbuffer(GL_RENDERBUFFER, buffer);
|
|
||||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8,
|
|
||||||
layer->getWidth(), layer->getHeight());
|
|
||||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
|
|
||||||
layer->setStencilRenderBuffer(buffer);
|
layer->setStencilRenderBuffer(buffer);
|
||||||
|
layer->bindStencilRenderBuffer();
|
||||||
|
layer->allocateStencilRenderBuffer();
|
||||||
|
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,12 +49,31 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Grow layer" />
|
android:text="Grow layer" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:onClick="enableClip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Circle clip" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:onClick="disableClip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="No clip" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ListView
|
<view class="com.android.test.hwui.ViewLayersActivity5$ClipFrameLayout"
|
||||||
android:id="@+id/list1"
|
android:id="@+id/container"
|
||||||
android:layout_width="0dip"
|
android:layout_width="0dip"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_weight="1" />
|
android:layout_weight="1">
|
||||||
|
|
||||||
|
<ListView
|
||||||
|
android:id="@+id/list1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
</view>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -19,14 +19,18 @@ package com.android.test.hwui;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ListView;
|
import android.widget.ListView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@@ -38,29 +42,74 @@ public class ViewLayersActivity5 extends Activity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
setContentView(R.layout.view_layers_5);
|
setContentView(R.layout.view_layers_5);
|
||||||
|
|
||||||
mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
|
|
||||||
|
|
||||||
setupList(R.id.list1);
|
setupList(R.id.list1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ClipFrameLayout extends FrameLayout {
|
||||||
|
private final Path mClipPath = new Path();
|
||||||
|
private boolean mClipEnabled;
|
||||||
|
|
||||||
|
public ClipFrameLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClipFrameLayout(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClipEnabled() {
|
||||||
|
return mClipEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClipEnabled(boolean clipEnabled) {
|
||||||
|
mClipEnabled = clipEnabled;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void dispatchDraw(Canvas canvas) {
|
||||||
|
if (mClipEnabled) {
|
||||||
|
mClipPath.reset();
|
||||||
|
mClipPath.addCircle(getWidth() / 2.0f, getHeight() / 2.0f,
|
||||||
|
Math.min(getWidth(), getHeight()) / 3.0f, Path.Direction.CW);
|
||||||
|
|
||||||
|
canvas.clipPath(mClipPath);
|
||||||
|
}
|
||||||
|
super.dispatchDraw(canvas);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
mPaint.setColorFilter(new PorterDuffColorFilter(0xff00ff00, PorterDuff.Mode.MULTIPLY));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void enableClip(View v) {
|
||||||
|
((ClipFrameLayout) findViewById(R.id.container)).setClipEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disableClip(View v) {
|
||||||
|
((ClipFrameLayout) findViewById(R.id.container)).setClipEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
public void enableLayer(View v) {
|
public void enableLayer(View v) {
|
||||||
findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
|
findViewById(R.id.container).setLayerType(View.LAYER_TYPE_HARDWARE, mPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disableLayer(View v) {
|
public void disableLayer(View v) {
|
||||||
findViewById(R.id.list1).setLayerType(View.LAYER_TYPE_NONE, null);
|
findViewById(R.id.container).setLayerType(View.LAYER_TYPE_NONE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void growLayer(View v) {
|
public void growLayer(View v) {
|
||||||
findViewById(R.id.list1).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
findViewById(R.id.container).getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
findViewById(R.id.list1).requestLayout();
|
findViewById(R.id.container).requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shrinkLayer(View v) {
|
public void shrinkLayer(View v) {
|
||||||
findViewById(R.id.list1).getLayoutParams().height = 300;
|
findViewById(R.id.container).getLayoutParams().height = 300;
|
||||||
findViewById(R.id.list1).requestLayout();
|
findViewById(R.id.container).requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupList(int listId) {
|
private void setupList(int listId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user