replicant-frameworks_native/services/surfaceflinger/LayerBlur.cpp
Byunghun Jeon 987034b563 SurfaceFlinger: Native changes to add blur effect
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
2015-11-08 01:07:13 -08:00

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