diff options
Diffstat (limited to 'services/surfaceflinger/LayerBlur.cpp')
-rw-r--r-- | services/surfaceflinger/LayerBlur.cpp | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp new file mode 100644 index 0000000..5c9e8ac --- /dev/null +++ b/services/surfaceflinger/LayerBlur.cpp @@ -0,0 +1,415 @@ +/* + * 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 |