replicant-frameworks_native/services/surfaceflinger/LayerBlur.cpp
Ramakant Singh 2bb57a8bc3 sf : Apply user defined panel orientation to blur layer
Blur layer need to capture screen shot to create to blur
effect. Hence need to take care any user defined orientation
as well.

Change-Id: I1c0478810d12661edf2586293bbed342a5b4dc7e
2015-11-16 14:00:25 -08:00

419 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());
Transform::orientation_flags rotation = Transform::ROT_0;
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);
if (hw->isPanelInverseMounted())
rotation = Transform::ROT_180;
mFlinger->renderScreenImplLocked(
hw,
Rect(0,0,width,height),
width, height,
0, getDrawingState().z-1,
false,
false,
rotation);
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