/* * 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 #include #include #include #include #include #include #include #include #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 position(mesh.getPositionArray()); 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 texCoords(mesh.getTexCoordArray()); 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 position(mesh.getPositionArray()); 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 texCoords(mesh.getTexCoordArray()); 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, 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& 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 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& 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& maskLayer, const sp& 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& 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& 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