summaryrefslogtreecommitdiffstats
path: root/media/mca/filterfw/native/core/gl_frame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/mca/filterfw/native/core/gl_frame.cpp')
-rw-r--r--media/mca/filterfw/native/core/gl_frame.cpp467
1 files changed, 467 insertions, 0 deletions
diff --git a/media/mca/filterfw/native/core/gl_frame.cpp b/media/mca/filterfw/native/core/gl_frame.cpp
new file mode 100644
index 0000000..0f8b4a1
--- /dev/null
+++ b/media/mca/filterfw/native/core/gl_frame.cpp
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include "base/logging.h"
+
+#include "core/gl_env.h"
+#include "core/gl_frame.h"
+#include "core/shader_program.h"
+
+#include <vector>
+
+namespace android {
+namespace filterfw {
+
+static const int kIdentityShaderKey = 1;
+
+//
+// A GLFrame stores pixel data on the GPU. It uses two kinds of GL data
+// containers for this: Textures and Frame Buffer Objects (FBOs). Textures are
+// used whenever pixel data needs to be read into a shader or the host program,
+// and when pixel data is uploaded to a GLFrame. The FBO is used as a rendering
+// target for shaders.
+//
+
+GLFrame::GLFrame(GLEnv* gl_env)
+ : gl_env_(gl_env),
+ width_(0),
+ height_(0),
+ vp_x_(0),
+ vp_y_(0),
+ vp_width_(0),
+ vp_height_(0),
+ texture_id_(0),
+ fbo_id_(0),
+ texture_target_(GL_TEXTURE_2D),
+ texture_state_(kStateUninitialized),
+ fbo_state_(kStateUninitialized),
+ owns_texture_(false),
+ owns_fbo_(false) {
+ SetDefaultTexParameters();
+}
+
+bool GLFrame::Init(int width, int height) {
+ // Make sure we haven't been initialized already
+ if (width_ == 0 && height_ == 0) {
+ InitDimensions(width, height);
+ return true;
+ }
+ return false;
+}
+
+bool GLFrame::InitWithTexture(GLint texture_id, int width, int height) {
+ texture_id_ = texture_id;
+ texture_state_ = glIsTexture(texture_id) ? kStateComplete : kStateGenerated;
+ InitDimensions(width, height);
+ return true;
+}
+
+bool GLFrame::InitWithFbo(GLint fbo_id, int width, int height) {
+ fbo_id_ = fbo_id;
+ fbo_state_ = glIsFramebuffer(fbo_id) ? kStateComplete : kStateGenerated;
+ texture_state_ = kStateUnmanaged;
+ InitDimensions(width, height);
+ return true;
+}
+
+bool GLFrame::InitWithExternalTexture() {
+ texture_target_ = GL_TEXTURE_EXTERNAL_OES;
+ width_ = 0;
+ height_ = 0;
+ return GenerateTextureName();
+}
+
+void GLFrame::InitDimensions(int width, int height) {
+ width_ = width;
+ height_ = height;
+ vp_width_ = width;
+ vp_height_ = height;
+}
+
+GLFrame::~GLFrame() {
+ // Delete texture
+ if (owns_texture_) {
+ // Bind FBO so that texture is unbound from it during deletion
+ if (fbo_state_ == kStateComplete) {
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ }
+ glDeleteTextures(1, &texture_id_);
+ }
+
+ // Delete FBO
+ if (owns_fbo_) {
+ glDeleteFramebuffers(1, &fbo_id_);
+ }
+}
+
+bool GLFrame::GenerateMipMap() {
+ if (FocusTexture()) {
+ glGenerateMipmap(GL_TEXTURE_2D);
+ return !GLEnv::CheckGLError("Generating MipMap!");
+ }
+ return false;
+}
+
+bool GLFrame::SetTextureParameter(GLenum pname, GLint value) {
+ if (value != tex_params_[pname]) {
+ if (FocusTexture()) {
+ glTexParameteri(GL_TEXTURE_2D, pname, value);
+ if (!GLEnv::CheckGLError("Setting texture parameter!")) {
+ tex_params_[pname] = value;
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+ return false;
+}
+
+bool GLFrame::UpdateTexParameters() {
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_params_[GL_TEXTURE_MAG_FILTER]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_params_[GL_TEXTURE_MIN_FILTER]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_params_[GL_TEXTURE_WRAP_S]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_params_[GL_TEXTURE_WRAP_T]);
+ return !GLEnv::CheckGLError("Resetting texture parameters!");
+}
+
+bool GLFrame::TexParametersModifed() {
+ return tex_params_[GL_TEXTURE_MAG_FILTER] != GL_LINEAR
+ || tex_params_[GL_TEXTURE_MIN_FILTER] != GL_LINEAR
+ || tex_params_[GL_TEXTURE_WRAP_S] != GL_CLAMP_TO_EDGE
+ || tex_params_[GL_TEXTURE_WRAP_T] != GL_CLAMP_TO_EDGE;
+}
+
+void GLFrame::SetDefaultTexParameters() {
+ tex_params_[GL_TEXTURE_MAG_FILTER] = GL_LINEAR;
+ tex_params_[GL_TEXTURE_MIN_FILTER] = GL_LINEAR;
+ tex_params_[GL_TEXTURE_WRAP_S] = GL_CLAMP_TO_EDGE;
+ tex_params_[GL_TEXTURE_WRAP_T] = GL_CLAMP_TO_EDGE;
+}
+
+bool GLFrame::ResetTexParameters() {
+ if (TexParametersModifed()) {
+ if (BindTexture()) {
+ SetDefaultTexParameters();
+ return UpdateTexParameters();
+ }
+ return false;
+ }
+ return true;
+}
+
+bool GLFrame::CopyDataTo(uint8_t* buffer, int size) {
+ return (size >= Size())
+ ? CopyPixelsTo(buffer)
+ : false;
+}
+
+bool GLFrame::CopyPixelsTo(uint8_t* buffer) {
+ // Use one of the pixel reading methods below, ordered from most
+ // efficient to least efficient.
+ if (fbo_state_ == kStateComplete)
+ return ReadFboPixels(buffer);
+ else if (texture_state_ == kStateComplete)
+ return ReadTexturePixels(buffer);
+ else
+ return false;
+}
+
+bool GLFrame::WriteData(const uint8_t* data, int data_size) {
+ return (data_size == Size()) ? UploadTexturePixels(data) : false;
+}
+
+bool GLFrame::SetViewport(int x, int y, int width, int height) {
+ vp_x_ = x;
+ vp_y_ = y;
+ vp_width_ = width;
+ vp_height_ = height;
+ return true;
+}
+
+GLFrame* GLFrame::Clone() const {
+ GLFrame* target = new GLFrame(gl_env_);
+ target->Init(width_, height_);
+ target->CopyPixelsFrom(this);
+ return target;
+}
+
+bool GLFrame::CopyPixelsFrom(const GLFrame* frame) {
+ if (frame == this) {
+ return true;
+ } else if (frame && frame->width_ == width_ && frame->height_ == height_) {
+ std::vector<const GLFrame*> sources;
+ sources.push_back(frame);
+ GetIdentity()->Process(sources, this);
+ return true;
+ }
+ return false;
+}
+
+int GLFrame::Size() const {
+ return width_ * height_ * 4;
+}
+
+ShaderProgram* GLFrame::GetIdentity() const {
+ ShaderProgram* stored_shader = gl_env_->ShaderWithKey(kIdentityShaderKey);
+ if (!stored_shader) {
+ stored_shader = ShaderProgram::CreateIdentity(gl_env_);
+ gl_env_->AttachShader(kIdentityShaderKey, stored_shader);
+ }
+ return stored_shader;
+}
+
+bool GLFrame::BindFrameBuffer() const {
+ // Bind the FBO
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ if (GLEnv::CheckGLError("FBO Binding")) return false;
+
+ // Set viewport
+ glViewport(vp_x_, vp_y_, vp_width_, vp_height_);
+ if (GLEnv::CheckGLError("ViewPort Setup")) return false;
+
+ return true;
+}
+
+bool GLFrame::FocusFrameBuffer() {
+ // Create texture backing if necessary
+ if (texture_state_ == kStateUninitialized) {
+ if (!GenerateTextureName())
+ return false;
+ }
+
+ // Create and bind FBO to texture if necessary
+ if (fbo_state_ != kStateComplete) {
+ if (!GenerateFboName() || !AttachTextureToFbo())
+ return false;
+ }
+
+ // And bind it.
+ return BindFrameBuffer();
+}
+
+bool GLFrame::BindTexture() const {
+ glBindTexture(GL_TEXTURE_2D, texture_id_);
+ return !GLEnv::CheckGLError("Texture Binding");
+}
+
+GLuint GLFrame::GetTextureId() const {
+ return texture_id_;
+}
+
+// Returns the held FBO id. Only call this if the GLFrame holds an FBO. You
+// can check this by calling HoldsFbo().
+GLuint GLFrame::GetFboId() const {
+ return fbo_id_;
+}
+
+bool GLFrame::FocusTexture() {
+ // Make sure we have a texture
+ if (!GenerateTextureName())
+ return false;
+
+ // Bind the texture
+ if (!BindTexture())
+ return false;
+
+ return !GLEnv::CheckGLError("Texture Binding");
+}
+
+bool GLFrame::GenerateTextureName() {
+ if (texture_state_ == kStateUninitialized) {
+ // Make sure texture not in use already
+ if (glIsTexture(texture_id_)) {
+ ALOGE("GLFrame: Cannot generate texture id %d, as it is in use already!", texture_id_);
+ return false;
+ }
+
+ // Generate the texture
+ glGenTextures (1, &texture_id_);
+ if (GLEnv::CheckGLError("Texture Generation"))
+ return false;
+ texture_state_ = kStateGenerated;
+ owns_texture_ = true;
+ }
+
+ return true;
+}
+
+bool GLFrame::AllocateTexture() {
+ // Allocate or re-allocate (if texture was deleted externally).
+ if (texture_state_ == kStateGenerated || TextureWasDeleted()) {
+ LOG_FRAME("GLFrame: Allocating texture: %d", texture_id_);
+ glBindTexture(GL_TEXTURE_2D, texture_id_);
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ width_,
+ height_,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ NULL);
+ if (!GLEnv::CheckGLError("Texture Allocation")) {
+ UpdateTexParameters();
+ texture_state_ = kStateComplete;
+ }
+ }
+ return texture_state_ == kStateComplete;
+}
+
+bool GLFrame::TextureWasDeleted() const {
+ return texture_state_ == kStateComplete && !glIsTexture(texture_id_);
+}
+
+bool GLFrame::GenerateFboName() {
+ if (fbo_state_ == kStateUninitialized) {
+ // Make sure FBO not in use already
+ if (glIsFramebuffer(fbo_id_)) {
+ ALOGE("GLFrame: Cannot generate FBO id %d, as it is in use already!", fbo_id_);
+ return false;
+ }
+
+ // Create FBO
+ glGenFramebuffers(1, &fbo_id_);
+ if (GLEnv::CheckGLError("FBO Generation"))
+ return false;
+ fbo_state_ = kStateGenerated;
+ owns_fbo_ = true;
+ }
+
+ return true;
+}
+
+bool GLFrame::ReadFboPixels(uint8_t* pixels) const {
+ if (fbo_state_ == kStateComplete) {
+ BindFrameBuffer();
+ glReadPixels(0,
+ 0,
+ width_,
+ height_,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ pixels);
+ return !GLEnv::CheckGLError("FBO Pixel Readout");
+ }
+ return false;
+}
+
+bool GLFrame::ReadTexturePixels(uint8_t* pixels) const {
+ // Read pixels from texture if we do not have an FBO
+ // NOTE: OpenGL ES does NOT support glGetTexImage() for reading out texture
+ // data. The only way for us to get texture data is to create a new FBO and
+ // render the current texture frame into it. As this is quite inefficient,
+ // and unnecessary (this can only happen if the user is reading out data
+ // that was just set, and not run through a filter), we warn the user about
+ // this here.
+ ALOGW("Warning: Reading pixel data from unfiltered GL frame. This is highly "
+ "inefficient. Please consider using your original pixel buffer "
+ "instead!");
+
+ // Create source frame set (unfortunately this requires an ugly const-cast,
+ // as we need to wrap ourselves in a frame-set. Still, as this set is used
+ // as input only, we are certain we will not be modified).
+ std::vector<const GLFrame*> sources;
+ sources.push_back(this);
+
+ // Create target frame
+ GLFrame target(gl_env_);
+ target.Init(width_, height_);
+
+ // Render the texture to the target
+ GetIdentity()->Process(sources, &target);
+
+ // Get the pixel data
+ return target.ReadFboPixels(pixels);
+}
+
+bool GLFrame::AttachTextureToFbo() {
+ // Check FBO and texture state. We do not do anything if we are not managing the texture.
+ if (fbo_state_ == kStateComplete || texture_state_ == kStateUnmanaged) {
+ return true;
+ } else if (fbo_state_ != kStateGenerated) {
+ ALOGE("Attempting to attach texture to FBO with no FBO in place!");
+ return false;
+ }
+
+ // If texture has been generated, make sure it is allocated.
+ if (!AllocateTexture())
+ return false;
+
+ // Bind the frame buffer, and check if we it is already bound
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+
+ // Bind the texture to the frame buffer
+ LOG_FRAME("Attaching tex %d w %d h %d to fbo %d", texture_id_, width_, height_, fbo_id_);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ texture_id_,
+ 0);
+
+ // Cleanup
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ if (GLEnv::CheckGLError("Texture Binding to FBO"))
+ return false;
+ else
+ fbo_state_ = kStateComplete;
+
+ return true;
+}
+
+bool GLFrame::ReattachTextureToFbo() {
+ return (fbo_state_ == kStateGenerated) ? AttachTextureToFbo() : true;
+}
+
+bool GLFrame::DetachTextureFromFbo() {
+ if (fbo_state_ == kStateComplete && texture_state_ == kStateComplete) {
+ LOG_FRAME("Detaching tex %d w %d h %d from fbo %d", texture_id_, width_, height_, fbo_id_);
+ glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
+ glFramebufferTexture2D(GL_FRAMEBUFFER,
+ GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D,
+ 0,
+ 0);
+ if (GLEnv::CheckGLError("Detaching texture to FBO"))
+ return false;
+ else
+ fbo_state_ = kStateGenerated;
+ }
+ return true;
+}
+
+bool GLFrame::UploadTexturePixels(const uint8_t* pixels) {
+ // Bind the texture object
+ FocusTexture();
+
+ // Load mipmap level 0
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Set the user specified texture parameters
+ UpdateTexParameters();
+
+ if (GLEnv::CheckGLError("Texture Pixel Upload"))
+ return false;
+
+ texture_state_ = kStateComplete;
+ return true;
+}
+
+} // namespace filterfw
+} // namespace android