summaryrefslogtreecommitdiffstats
path: root/services/surfaceflinger/LayerBlur.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'services/surfaceflinger/LayerBlur.cpp')
-rw-r--r--services/surfaceflinger/LayerBlur.cpp418
1 files changed, 418 insertions, 0 deletions
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
new file mode 100644
index 0000000..021978d
--- /dev/null
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -0,0 +1,418 @@
+/*
+ * 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