987034b563
Native changes to add blur-behind and blur mask effect Change-Id: I54faf82d750e8299de6d261f6a893ab26d08df84 SurfaceFlinger: Adding template for LayerBlur files Change-Id: I444009113b7bdd6c5284863fd1f56358e67d9fe6 SurfaceFlinger: Featurize libuiblur module for OSS build Change-Id: Ifdc176e699434125d17b111c044b8ba954cf717c
416 lines
13 KiB
C++
416 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
|
* Not a Contribution.
|
|
*
|
|
* Copyright (C) 2007 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
|
|
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
#include <time.h>
|
|
|
|
#include <utils/Errors.h>
|
|
#include <utils/Log.h>
|
|
#include <utils/Trace.h>
|
|
#include <GLES/gl.h>
|
|
#include <GLES2/gl2.h>
|
|
|
|
#include "LayerBlur.h"
|
|
#include "SurfaceFlinger.h"
|
|
#include "DisplayDevice.h"
|
|
#include "RenderEngine/RenderEngine.h"
|
|
|
|
namespace android {
|
|
// ---------------------------------------------------------------------------
|
|
|
|
// Automatically disables scissor test and restores it when destroyed
|
|
class ScopedScissorDisabler {
|
|
bool scissorEnabled;
|
|
public:
|
|
ScopedScissorDisabler(bool enabled) : scissorEnabled(enabled) {
|
|
if(scissorEnabled) {
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
}
|
|
~ScopedScissorDisabler() {
|
|
if(scissorEnabled) {
|
|
glEnable(GL_SCISSOR_TEST);
|
|
}
|
|
};
|
|
};
|
|
|
|
static void setupMeshPartial(Mesh& mesh, Rect rcDraw, Rect rcTexture, int texWidth, int texHeight, int viewportHeight) {
|
|
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
|
|
position[0] = vec2(rcDraw.left, rcDraw.top);
|
|
position[1] = vec2(rcDraw.left, rcDraw.bottom);
|
|
position[2] = vec2(rcDraw.right, rcDraw.bottom);
|
|
position[3] = vec2(rcDraw.right, rcDraw.top);
|
|
for(size_t i=0; i<4; ++i) {
|
|
position[i].y = viewportHeight - position[i].y;
|
|
}
|
|
|
|
Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
|
|
texCoords[0] = vec2(rcTexture.left/(float)texWidth, 1.0f - rcTexture.top/(float)texHeight);
|
|
texCoords[1] = vec2(rcTexture.left/(float)texWidth, 1.0f - rcTexture.bottom/(float)texHeight);
|
|
texCoords[2] = vec2(rcTexture.right/(float)texWidth, 1.0f - rcTexture.bottom/(float)texHeight);
|
|
texCoords[3] = vec2(rcTexture.right/(float)texWidth, 1.0f - rcTexture.top/(float)texHeight);
|
|
}
|
|
|
|
static void setupMesh(Mesh& mesh, int width, int height, int viewportHeight) {
|
|
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
|
|
position[0] = vec2(0, 0);
|
|
position[1] = vec2(0, height);
|
|
position[2] = vec2(width, height);
|
|
position[3] = vec2(width, 0);
|
|
for(size_t i=0; i<4; ++i) {
|
|
position[i].y = viewportHeight - position[i].y;
|
|
}
|
|
|
|
Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>());
|
|
texCoords[0] = vec2(0, 1.0f);
|
|
texCoords[1] = vec2(0, 0);
|
|
texCoords[2] = vec2(1.0f, 0);
|
|
texCoords[3] = vec2(1.0f, 1.0f);
|
|
}
|
|
|
|
|
|
LayerBlur::LayerBlur(SurfaceFlinger* flinger, const sp<Client>& client,
|
|
const String8& name, uint32_t w, uint32_t h, uint32_t flags)
|
|
: Layer(flinger, client, name, w, h, flags), mBlurMaskSampling(1), mBlurMaskAlphaThreshold(0.0f)
|
|
,mLastFrameSequence(0)
|
|
{
|
|
#ifdef UI_BLUR
|
|
mBlurToken = qtiblur::initBlurToken();
|
|
#endif
|
|
|
|
GLuint texnames[3];
|
|
mFlinger->getRenderEngine().genTextures(3, texnames);
|
|
mTextureCapture.init(Texture::TEXTURE_2D, texnames[0]);
|
|
mTextureBlur.init(Texture::TEXTURE_2D, texnames[1]);
|
|
mTextureMasking.init(Texture::TEXTURE_2D, texnames[2]);
|
|
}
|
|
|
|
LayerBlur::~LayerBlur() {
|
|
#ifdef UI_BLUR
|
|
qtiblur::releaseBlurToken(mBlurToken);
|
|
#endif
|
|
|
|
releaseFbo(mFboCapture);
|
|
releaseFbo(mFboMasking);
|
|
mFlinger->deleteTextureAsync(mTextureCapture.getTextureName());
|
|
mFlinger->deleteTextureAsync(mTextureBlur.getTextureName());
|
|
mFlinger->deleteTextureAsync(mTextureMasking.getTextureName());
|
|
}
|
|
|
|
void LayerBlur::onDraw(const sp<const DisplayDevice>& hw, const Region& /*clip*/,
|
|
bool useIdentityTransform)
|
|
{
|
|
clock_t t1 = clock();
|
|
const ScopedTrace traceTotal(ATRACE_TAG, "Blur.onDraw");
|
|
|
|
const Layer::State& s(getDrawingState());
|
|
|
|
if (s.alpha==0) {
|
|
return;
|
|
}
|
|
|
|
/////
|
|
// NOTE:
|
|
//
|
|
// Scissor test has been turned on by SurfaceFlinger for NON-primary display
|
|
// We need to turn off the scissor test during our fbo drawing
|
|
GLboolean isScissorEnabled = false;
|
|
glGetBooleanv(GL_SCISSOR_TEST, &isScissorEnabled);
|
|
ScopedScissorDisabler _(isScissorEnabled);
|
|
//
|
|
/////
|
|
|
|
|
|
int hwWidth = hw->getWidth();
|
|
int hwHeight = hw->getHeight();
|
|
|
|
RenderEngine& engine(mFlinger->getRenderEngine());
|
|
|
|
bool savedProjectionYSwap = engine.getProjectionYSwap();
|
|
Rect savedProjectionSourceCrop = engine.getProjectionSourceCrop();
|
|
Transform::orientation_flags savedProjectionRotation = engine.getProjectionRotation();
|
|
size_t savedViewportWidth = engine.getViewportWidth();
|
|
size_t savedViewportHeight = engine.getViewportHeight();
|
|
|
|
|
|
if (mLastFrameSequence != mFlinger->mActiveFrameSequence ||
|
|
mTextureBlur.getWidth() == 0 || mTextureBlur.getHeight() == 0) {
|
|
// full drawing needed.
|
|
|
|
|
|
// capture
|
|
if (!captureScreen(hw, mFboCapture, mTextureCapture, hwWidth, hwHeight)) {
|
|
return;
|
|
}
|
|
|
|
// blur
|
|
size_t outTexWidth = mTextureBlur.getWidth();
|
|
size_t outTexHeight = mTextureBlur.getHeight();
|
|
#ifdef UI_BLUR
|
|
if (!qtiblur::blur(mBlurToken,
|
|
s.blur,
|
|
mTextureCapture.getTextureName(),
|
|
mTextureCapture.getWidth(),
|
|
mTextureCapture.getHeight(),
|
|
mTextureBlur.getTextureName(),
|
|
&outTexWidth,
|
|
&outTexHeight)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// mTextureBlur now has "Blurred image"
|
|
mTextureBlur.setDimensions(outTexWidth, outTexHeight);
|
|
|
|
} else {
|
|
// We can just re-use mTextureBlur.
|
|
// SurfaceFlinger or other LayerBlur object called my draw() multiple times
|
|
// while making one frame.
|
|
//
|
|
// Fall through
|
|
}
|
|
|
|
// masking
|
|
bool masking = false;
|
|
sp<Layer> maskLayer = mBlurMaskLayer.promote();
|
|
if (maskLayer != 0) {
|
|
// The larger sampling, the faster drawing.
|
|
// The smaller sampling, the prettier out line.
|
|
int sampling = mBlurMaskSampling >= 1 ? mBlurMaskSampling : 1;
|
|
//ALOGV("maskLayer available, sampling:%d", sampling);
|
|
masking = drawMaskLayer(maskLayer, hw, mFboMasking, hwWidth, hwHeight, sampling, mTextureMasking);
|
|
}
|
|
|
|
|
|
// final draw
|
|
doDrawFinal(hw,
|
|
savedViewportWidth, savedViewportHeight,
|
|
savedProjectionSourceCrop,
|
|
savedProjectionYSwap,
|
|
savedProjectionRotation,
|
|
useIdentityTransform,
|
|
masking ? &mTextureMasking : 0
|
|
);
|
|
|
|
mLastFrameSequence = mFlinger->mActiveFrameSequence;
|
|
|
|
clock_t t2 = clock();
|
|
ALOGV("onDraw took %d ms", (int)(1000*(t2-t1)/CLOCKS_PER_SEC));
|
|
}
|
|
|
|
|
|
bool LayerBlur::captureScreen(const sp<const DisplayDevice>& hw, FBO& fbo, Texture& texture, int width, int height) {
|
|
ATRACE_CALL();
|
|
ensureFbo(fbo, width, height, texture.getTextureName());
|
|
if(fbo.fbo == 0) {
|
|
ALOGE("captureScreen(). fbo.fbo == 0");
|
|
return false;
|
|
}
|
|
|
|
GLint savedFramebuffer = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)fbo.fbo);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
texture.getTextureTarget(),
|
|
texture.getTextureName(), 0);
|
|
|
|
mFlinger->getRenderEngine().clearWithColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
mFlinger->renderScreenImplLocked(
|
|
hw,
|
|
Rect(0,0,width,height),
|
|
width, height,
|
|
0, getDrawingState().z-1,
|
|
false,
|
|
false,
|
|
Transform::ROT_0);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
|
|
|
|
texture.setDimensions(width, height);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LayerBlur::drawMaskLayer(sp<Layer>& maskLayer, const sp<const DisplayDevice>& hw,
|
|
FBO& fbo, int width, int height, int sampling, Texture& texture) {
|
|
// Draw maskLayer into fbo
|
|
ATRACE_CALL();
|
|
|
|
int maskWidth = width/sampling;
|
|
int maskHeight = height/sampling;
|
|
|
|
ensureFbo(fbo, maskWidth, maskHeight, texture.getTextureName());
|
|
if(fbo.fbo == 0) {
|
|
ALOGE("drawMaskLayer(). fbo.fbo == 0");
|
|
return false;
|
|
}
|
|
|
|
GLint savedFramebuffer = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, (GLuint)fbo.fbo);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
texture.getTextureTarget(),
|
|
texture.getTextureName(), 0);
|
|
|
|
mFlinger->getRenderEngine().setViewportAndProjection(
|
|
maskWidth, maskHeight,
|
|
Rect(0,0,width,height),
|
|
height,
|
|
false,
|
|
Transform::ROT_0
|
|
);
|
|
setupMesh(mMesh, width, height, height);
|
|
mFlinger->getRenderEngine().clearWithColor(0.0f, 0.0f, 0.0f, 0.0f); // alpha must be ZERO
|
|
maskLayer->draw(hw);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
|
|
|
|
texture.setDimensions(maskWidth, maskHeight);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* draw final texture into outer framebuffer
|
|
*/
|
|
void LayerBlur::doDrawFinal(const sp<const DisplayDevice>& hw,
|
|
int savedViewportWidth, int savedViewportHeight,
|
|
Rect savedProjectionSourceCrop,
|
|
bool savedProjectionYSwap,
|
|
Transform::orientation_flags savedRotation,
|
|
bool useIdentityTransform,
|
|
Texture* maskTexture
|
|
) {
|
|
ATRACE_CALL();
|
|
|
|
int hwWidth = hw->getWidth();
|
|
int hwHeight = hw->getHeight();
|
|
|
|
RenderEngine& engine(mFlinger->getRenderEngine());
|
|
const Layer::State& s(getDrawingState());
|
|
|
|
Transform trToDraw(useIdentityTransform ? hw->getTransform() : hw->getTransform() * s.transform);
|
|
Transform trToMapTexture(hw->getTransform() * s.transform);
|
|
|
|
Rect frameToDraw(trToDraw.transform(Rect(s.active.w, s.active.h)));
|
|
Rect frameToMapTexture(trToMapTexture.transform(Rect(s.active.w, s.active.h)));
|
|
|
|
engine.setViewportAndProjection(
|
|
savedViewportWidth, savedViewportHeight,
|
|
savedProjectionSourceCrop,
|
|
hwHeight,
|
|
savedProjectionYSwap,
|
|
savedRotation
|
|
);
|
|
|
|
|
|
const mat4 identity;
|
|
float textureMatrix[16];
|
|
memcpy(textureMatrix, identity.asArray(), sizeof(textureMatrix));
|
|
|
|
//mTextureBlur.setDimensions(hwWidth, hwHeight);
|
|
mTextureBlur.setFiltering(true);
|
|
mTextureBlur.setMatrix(textureMatrix);
|
|
|
|
if (maskTexture != 0) {
|
|
maskTexture->setFiltering(false);
|
|
maskTexture->setMatrix(textureMatrix);
|
|
}
|
|
|
|
setupMeshPartial(mMesh, frameToDraw, frameToMapTexture, hwWidth, hwHeight,
|
|
savedProjectionSourceCrop.height());
|
|
|
|
engine.setupLayerTexturing(mTextureBlur);
|
|
engine.setupLayerBlending(mPremultipliedAlpha, isOpaque(s), s.alpha);
|
|
if (maskTexture) {
|
|
engine.setupLayerMasking(*maskTexture, mBlurMaskAlphaThreshold);
|
|
}
|
|
engine.drawMesh(mMesh);
|
|
engine.disableLayerMasking();
|
|
engine.disableBlending();
|
|
engine.disableTexturing();
|
|
|
|
}
|
|
|
|
bool LayerBlur::isVisible() const {
|
|
const Layer::State& s(getDrawingState());
|
|
return !(s.flags & layer_state_t::eLayerHidden) && s.alpha;
|
|
}
|
|
|
|
bool LayerBlur::setBlurMaskLayer(sp<Layer>& maskLayer) {
|
|
if (maskLayer == mBlurMaskLayer) {
|
|
return false;
|
|
}
|
|
mBlurMaskLayer = maskLayer;
|
|
return true;
|
|
}
|
|
|
|
|
|
void LayerBlur::initFbo(FBO& fbobj, int width, int height, int textureName) {
|
|
GLuint fbo=0;
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height,
|
|
0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
GLint savedFramebuffer = 0;
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &savedFramebuffer);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
GL_TEXTURE_2D, textureName, 0);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, savedFramebuffer);
|
|
|
|
fbobj.fbo = fbo;
|
|
fbobj.width = width;
|
|
fbobj.height = height;
|
|
}
|
|
|
|
void LayerBlur::releaseFbo(FBO& fbo) {
|
|
if(fbo.fbo != 0) {
|
|
glDeleteFramebuffers(1, (GLuint*)&fbo.fbo);
|
|
}
|
|
fbo.fbo = 0;
|
|
fbo.width = 0;
|
|
fbo.height = 0;
|
|
}
|
|
|
|
void LayerBlur::ensureFbo(FBO& fbo, int width, int height, int textureName) {
|
|
if(fbo.fbo != 0) {
|
|
if(fbo.width != width || fbo.height != height) {
|
|
releaseFbo(fbo);
|
|
}
|
|
}
|
|
if(fbo.fbo == 0) {
|
|
initFbo(fbo, width, height, textureName);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
}; // namespace android
|