diff options
Diffstat (limited to 'jni')
-rwxr-xr-x | jni/Android.mk | 6 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/FrameBuffer.cpp | 99 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/FrameBuffer.h | 31 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp | 378 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/WarpRenderer.h | 80 | ||||
-rw-r--r-- | jni/feature_mos_jni.cpp | 93 | ||||
-rw-r--r-- | jni/mosaic_renderer_jni.cpp | 338 | ||||
-rw-r--r-- | jni/mosaic_renderer_jni.h | 23 |
8 files changed, 1034 insertions, 14 deletions
diff --git a/jni/Android.mk b/jni/Android.mk index 9d04bf5..d653573 100755 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -13,6 +13,7 @@ LOCAL_CFLAGS := -O3 -DNDEBUG LOCAL_SRC_FILES := \ feature_mos_jni.cpp \ + mosaic_renderer_jni.cpp \ feature_mos/src/mosaic/trsMatrix.cpp \ feature_mos/src/mosaic/AlignFeatures.cpp \ feature_mos/src/mosaic/Blend.cpp \ @@ -20,6 +21,8 @@ LOCAL_SRC_FILES := \ feature_mos/src/mosaic/ImageUtils.cpp \ feature_mos/src/mosaic/Mosaic.cpp \ feature_mos/src/mosaic/Pyramid.cpp \ + feature_mos/src/mosaic_renderer/WarpRenderer.cpp \ + feature_mos/src/mosaic_renderer/FrameBuffer.cpp \ feature_stab/db_vlvm/db_feature_detection.cpp \ feature_stab/db_vlvm/db_feature_matching.cpp \ feature_stab/db_vlvm/db_framestitching.cpp \ @@ -34,7 +37,8 @@ LOCAL_SRC_FILES := \ feature_stab/src/dbreg/dbstabsmooth.cpp \ feature_stab/src/dbreg/vp_motionmodel.c -LOCAL_SHARED_LIBRARIES := liblog libnativehelper +LOCAL_SHARED_LIBRARIES := liblog libnativehelper libGLESv2 +#LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -ldl -llog -lGLESv2 -L$(TARGET_OUT) LOCAL_MODULE_TAGS := optional diff --git a/jni/feature_mos/src/mosaic_renderer/FrameBuffer.cpp b/jni/feature_mos/src/mosaic_renderer/FrameBuffer.cpp new file mode 100755 index 0000000..9a07e49 --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/FrameBuffer.cpp @@ -0,0 +1,99 @@ +#include "FrameBuffer.h" + +FrameBuffer::FrameBuffer() +{ + Reset(); +} + +FrameBuffer::~FrameBuffer() { +} + +void FrameBuffer::Reset() { + mFrameBufferName = -1; + mTextureName = -1; + mWidth = 0; + mHeight = 0; + mFormat = -1; +} + +bool FrameBuffer::InitializeGLContext() { + Reset(); + return CreateBuffers(); +} + +bool FrameBuffer::Init(int width, int height, GLenum format) { + if (mFrameBufferName == (GLuint)-1) { + if (!CreateBuffers()) { + return false; + } + } + glBindFramebuffer(GL_FRAMEBUFFER, mFrameBufferName); + glBindTexture(GL_TEXTURE_2D, mTextureName); + + glTexImage2D(GL_TEXTURE_2D, + 0, + format, + width, + height, + 0, + format, + GL_UNSIGNED_BYTE, + NULL); + if (!checkGlError("bind/teximage")) { + return false; + } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // This is necessary to work with user-generated frame buffers with + // dimensions that are NOT powers of 2. + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Attach texture to frame buffer. + glFramebufferTexture2D(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + mTextureName, + 0); + + if (!checkGlError("texture setup")) { + return false; + } + mWidth = width; + mHeight = height; + mFormat = format; + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return true; +} + +bool FrameBuffer::CreateBuffers() { + glGenFramebuffers(1, &mFrameBufferName); + glGenTextures(1, &mTextureName); + if (!checkGlError("texture generation")) { + return false; + } + return true; +} + +GLuint FrameBuffer::GetTextureName() const { + return mTextureName; +} + +GLuint FrameBuffer::GetFrameBufferName() const { + return mFrameBufferName; +} + +GLenum FrameBuffer::GetFormat() const { + return mFormat; +} + +int FrameBuffer::GetWidth() const { + return mWidth; +} + +int FrameBuffer::GetHeight() const { + return mHeight; +} + + + diff --git a/jni/feature_mos/src/mosaic_renderer/FrameBuffer.h b/jni/feature_mos/src/mosaic_renderer/FrameBuffer.h new file mode 100755 index 0000000..b6a20ad --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/FrameBuffer.h @@ -0,0 +1,31 @@ +#pragma once + +#include <EGL/egl.h> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +extern bool checkGlError(const char* op); + +class FrameBuffer { + public: + FrameBuffer(); + virtual ~FrameBuffer(); + + bool InitializeGLContext(); + bool Init(int width, int height, GLenum format); + GLuint GetTextureName() const; + GLuint GetFrameBufferName() const; + GLenum GetFormat() const; + + int GetWidth() const; + int GetHeight() const; + + private: + void Reset(); + bool CreateBuffers(); + GLuint mFrameBufferName; + GLuint mTextureName; + int mWidth; + int mHeight; + GLenum mFormat; +}; diff --git a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp new file mode 100755 index 0000000..9274721 --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp @@ -0,0 +1,378 @@ +#include "WarpRenderer.h" + +#include <GLES2/gl2ext.h> + +#include <android/log.h> +#define LOG_TAG "WarpRenderer" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + + +static const char gVertexShader[] = +"uniform mat4 u_affinetrans; \n" +"uniform mat4 u_viewporttrans; \n" +"uniform mat4 u_scalingtrans; \n" +"attribute vec4 a_position; \n" +"attribute vec2 a_texCoord; \n" +"varying vec2 v_texCoord; \n" +"void main() \n" +"{ \n" +" gl_Position = u_scalingtrans * u_viewporttrans * u_affinetrans * a_position; \n" +" v_texCoord = a_texCoord; \n" +"} \n"; + +static const char gFragmentShader[] = +"precision mediump float; \n" +"varying vec2 v_texCoord; \n" +"uniform sampler2D s_texture; \n" +"void main() \n" +"{ \n" +" vec4 color; \n" +" color = texture2D(s_texture, v_texCoord); \n" +" gl_FragColor = color; \n" +"} \n"; + + +const GLfloat g_vVertices[] = { + -1.f, -1.f, 0.0f, 1.0f, // Position 0 + 0.0f, 1.0f, // TexCoord 0 + 1.f, -1.f, 0.0f, 1.0f, // Position 1 + 1.0f, 1.0f, // TexCoord 1 + -1.f, 1.f, 0.0f, 1.0f, // Position 2 + 0.0f, 0.0f, // TexCoord 2 + 1.f, 1.f, 0.0f, 1.0f, // Position 3 + 1.0f, 0.0f // TexCoord 3 +}; + +const int VERTEX_STRIDE = 6 * sizeof(GLfloat); + +GLushort g_iIndices[] = { 0, 1, 2, 3 }; + +WarpRenderer::WarpRenderer() + : mGlProgram(0), + mInputTextureName(-1), + mInputTextureWidth(0), + mInputTextureHeight(0), + mSurfaceWidth(0), + mSurfaceHeight(0) + { + InitializeGLContext(); +} + +WarpRenderer::~WarpRenderer() { +} + +GLuint WarpRenderer::loadShader(GLenum shaderType, const char* pSource) { + GLuint shader = glCreateShader(shaderType); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + LOGE("Could not compile shader %d:\n%s\n", + shaderType, buf); + free(buf); + } + glDeleteShader(shader); + shader = 0; + } + } + } + return shader; +} + +GLuint WarpRenderer::createProgram(const char* pVertexSource, const char* pFragmentSource) +{ + GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); + if (!vertexShader) + { + return 0; + } + LOGI("VertexShader Loaded!"); + + GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); + if (!pixelShader) + { + return 0; + } + LOGI("FragmentShader Loaded!"); + + GLuint program = glCreateProgram(); + if (program) + { + glAttachShader(program, vertexShader); + checkGlError("glAttachShader"); + glAttachShader(program, pixelShader); + checkGlError("glAttachShader"); + + LOGI("Shaders Attached!"); + + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + + LOGI("Program Linked!"); + + if (linkStatus != GL_TRUE) + { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) + { + char* buf = (char*) malloc(bufLength); + if (buf) + { + glGetProgramInfoLog(program, bufLength, NULL, buf); + LOGE("Could not link program:\n%s\n", buf); + free(buf); + } + } + glDeleteProgram(program); + program = 0; + } + } + return program; +} + +bool WarpRenderer::InitializeGLProgram() +{ + bool succeeded = false; + do { + GLuint glProgram; + glProgram = createProgram(VertexShaderSource(), + FragmentShaderSource()); + if (!glProgram) { + break; + } + + glUseProgram(glProgram); + if (!checkGlError("glUseProgram")) break; + + // Get attribute locations + mPositionLoc = glGetAttribLocation(glProgram, "a_position"); + mAffinetransLoc = glGetUniformLocation(glProgram, "u_affinetrans"); + mViewporttransLoc = glGetUniformLocation(glProgram, "u_viewporttrans"); + mScalingtransLoc = glGetUniformLocation(glProgram, "u_scalingtrans"); + mTexCoordLoc = glGetAttribLocation(glProgram, "a_texCoord"); + + // Get sampler location + mSamplerLoc = glGetUniformLocation(glProgram, "s_texture"); + + mGlProgram = glProgram; + succeeded = true; + } while (false); + + if (!succeeded && (mGlProgram != 0)) + { + glDeleteProgram(mGlProgram); + checkGlError("glDeleteProgram"); + mGlProgram = 0; + } + return succeeded; +} + +void WarpRenderer::SetViewportMatrix(int w, int h, int W, int H) +{ + for(int i=0; i<16; i++) + { + mViewportMatrix[i] = 0.0f; + } + + mViewportMatrix[0] = float(w)/float(W); + mViewportMatrix[5] = float(h)/float(H); + mViewportMatrix[10] = 1.0f; + mViewportMatrix[12] = -1.0f + float(w)/float(W); + mViewportMatrix[13] = -1.0f + float(h)/float(H); + mViewportMatrix[15] = 1.0f; +} + +void WarpRenderer::SetScalingMatrix(float xscale, float yscale) +{ + for(int i=0; i<16; i++) + { + mScalingMatrix[i] = 0.0f; + } + + mScalingMatrix[0] = xscale; + mScalingMatrix[5] = yscale; + mScalingMatrix[10] = 1.0f; + mScalingMatrix[15] = 1.0f; +} + +// Set this renderer to use the default frame-buffer (screen) and +// set the viewport size to be the given width and height (pixels). +bool WarpRenderer::SetupGraphics(int width, int height) +{ + bool succeeded = false; + do { + if (mGlProgram == 0) + { + if (!InitializeGLProgram()) + { + break; + } + } + glUseProgram(mGlProgram); + if (!checkGlError("glUseProgram")) break; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + mFrameBuffer = NULL; + mSurfaceWidth = width; + mSurfaceHeight = height; + + glViewport(0, 0, mSurfaceWidth, mSurfaceHeight); + if (!checkGlError("glViewport")) break; + succeeded = true; + } while (false); + + return succeeded; +} + + +// Set this renderer to use the specified FBO and +// set the viewport size to be the width and height of this FBO. +bool WarpRenderer::SetupGraphics(FrameBuffer* buffer) +{ + bool succeeded = false; + do { + if (mGlProgram == 0) + { + if (!InitializeGLProgram()) + { + break; + } + } + glUseProgram(mGlProgram); + if (!checkGlError("glUseProgram")) break; + + glBindFramebuffer(GL_FRAMEBUFFER, buffer->GetFrameBufferName()); + + mFrameBuffer = buffer; + mSurfaceWidth = mFrameBuffer->GetWidth(); + mSurfaceHeight = mFrameBuffer->GetHeight(); + + glViewport(0, 0, mSurfaceWidth, mSurfaceHeight); + if (!checkGlError("glViewport")) break; + succeeded = true; + } while (false); + + return succeeded; +} + +bool WarpRenderer::Clear(float r, float g, float b, float a) +{ + bool succeeded = false; + do { + bool rt = (mFrameBuffer == NULL)? + SetupGraphics(mSurfaceWidth, mSurfaceHeight) : + SetupGraphics(mFrameBuffer); + + if(!rt) + break; + + glClearColor(r, g, b, a); + glClear(GL_COLOR_BUFFER_BIT); + + succeeded = true; + } while (false); + return succeeded; + +} + +bool WarpRenderer::DrawTexture(GLfloat *affine) +{ + bool succeeded = false; + do { + bool rt = (mFrameBuffer == NULL)? + SetupGraphics(mSurfaceWidth, mSurfaceHeight) : + SetupGraphics(mFrameBuffer); + + if(!rt) + break; + + glDisable(GL_BLEND); + + glActiveTexture(GL_TEXTURE0); + if (!checkGlError("glActiveTexture")) break; + + const GLenum texture_type = InputTextureType(); + glBindTexture(texture_type, mInputTextureName); + if (!checkGlError("glBindTexture")) break; + + // Set the sampler texture unit to 0 + glUniform1i(mSamplerLoc, 0); + + // Load the vertex position + glVertexAttribPointer(mPositionLoc, 4, GL_FLOAT, + GL_FALSE, VERTEX_STRIDE, g_vVertices); + + // Load the texture coordinate + glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, + GL_FALSE, VERTEX_STRIDE, &g_vVertices[4]); + + glEnableVertexAttribArray(mPositionLoc); + glEnableVertexAttribArray(mTexCoordLoc); + + // pass matrix information to the vertex shader + glUniformMatrix4fv(mAffinetransLoc, 1, GL_FALSE, affine); + glUniformMatrix4fv(mViewporttransLoc, 1, GL_FALSE, mViewportMatrix); + glUniformMatrix4fv(mScalingtransLoc, 1, GL_FALSE, mScalingMatrix); + + // And, finally, execute the GL draw command. + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, g_iIndices); + + checkGlError("glDrawElements"); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + succeeded = true; + } while (false); + return succeeded; +} + +void WarpRenderer::InitializeGLContext() +{ + if(mFrameBuffer != NULL) + { + delete mFrameBuffer; + mFrameBuffer = NULL; + } + + mInputTextureName = -1; + mGlProgram = 0; + mTexHandle = 0; +} + +int WarpRenderer::GetTextureName() +{ + return mInputTextureName; +} + +void WarpRenderer::SetInputTextureName(GLuint textureName) +{ + mInputTextureName = textureName; +} + +void WarpRenderer::SetInputTextureDimensions(int width, int height) +{ + mInputTextureWidth = width; + mInputTextureHeight = height; +} + + +const char* WarpRenderer::VertexShaderSource() const +{ + return gVertexShader; +} + +const char* WarpRenderer::FragmentShaderSource() const +{ + return gFragmentShader; +} diff --git a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h new file mode 100755 index 0000000..7986215 --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h @@ -0,0 +1,80 @@ +#pragma once + +#include "FrameBuffer.h" + +#include <GLES2/gl2.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +class WarpRenderer { + public: + WarpRenderer(); + virtual ~WarpRenderer(); + + // Initialize OpenGL resources + // @return true if successful + bool InitializeGLProgram(); + + bool SetupGraphics(FrameBuffer* buffer); + bool SetupGraphics(int width, int height); + + bool Clear(float r, float g, float b, float a); + + void SetViewportMatrix(int w, int h, int W, int H); + void SetScalingMatrix(float xscale, float yscale); + bool DrawTexture(GLfloat *affine); + + int GetTextureName(); + void SetInputTextureName(GLuint textureName); + void SetInputTextureDimensions(int width, int height); + + void InitializeGLContext(); + + protected: + + GLuint loadShader(GLenum shaderType, const char* pSource); + GLuint createProgram(const char*, const char* ); + + int SurfaceWidth() const { return mSurfaceWidth; } + int SurfaceHeight() const { return mSurfaceHeight; } + + private: + // Source code for shaders. + virtual const char* VertexShaderSource() const; + virtual const char* FragmentShaderSource() const; + + // Redefine this to use special texture types such as + // GL_TEXTURE_EXTERNAL_OES. + virtual GLenum InputTextureType() const { return GL_TEXTURE_2D; } + + + GLuint mGlProgram; + GLuint mInputTextureName; + int mInputTextureWidth; + int mInputTextureHeight; + + GLuint mTexHandle; // Handle to s_texture. + GLuint mTexCoordHandle; // Handle to a_texCoord. + GLuint mTriangleVerticesHandle; // Handle to vPosition. + + // Attribute locations + GLint mPositionLoc; + GLint mAffinetransLoc; + GLint mViewporttransLoc; + GLint mScalingtransLoc; + GLint mTexCoordLoc; + + GLfloat mViewportMatrix[16]; + GLfloat mScalingMatrix[16]; + + // Sampler location + GLint mSamplerLoc; + + int mSurfaceWidth; // Width of target surface. + int mSurfaceHeight; // Height of target surface. + + FrameBuffer *mFrameBuffer; +}; + diff --git a/jni/feature_mos_jni.cpp b/jni/feature_mos_jni.cpp index 577da24..c858728 100644 --- a/jni/feature_mos_jni.cpp +++ b/jni/feature_mos_jni.cpp @@ -47,8 +47,13 @@ extern "C" { #endif +#include "mosaic_renderer_jni.h" + char buffer[1024]; +double g_dAffinetrans[16]; +double g_dAffinetransInv[16]; + const int MAX_FRAMES_HR = 100; const int MAX_FRAMES_LR = 200; @@ -109,7 +114,8 @@ int Init(int mID, int nmax) t0 = now_ms(); - // When processing higher than 720x480 video, process low-res at quarter resolution + // When processing higher than 720x480 video, process low-res at + // quarter resolution if(tWidth[LR]>180) quarter_res[LR] = true; @@ -117,7 +123,8 @@ int Init(int mID, int nmax) // Check for initialization and if not, initialize if (!mosaic[mID]->isInitialized()) { - mosaic[mID]->initialize(blendingType, tWidth[mID], tHeight[mID], nmax, quarter_res[mID], thresh_still[mID]); + mosaic[mID]->initialize(blendingType, tWidth[mID], tHeight[mID], + nmax, quarter_res[mID], thresh_still[mID]); } t1 = now_ms(); @@ -126,7 +133,8 @@ int Init(int mID, int nmax) return 1; } -void GenerateQuarterResImagePlanar(ImageType im, int input_w, int input_h, ImageType &out) +void GenerateQuarterResImagePlanar(ImageType im, int input_w, int input_h, + ImageType &out) { ImageType imp; ImageType outp; @@ -246,7 +254,8 @@ void YUV420toYVU24(ImageType yvu24, ImageType yuv420sp, int width, int height) } } -void YUV420toYVU24_NEW(ImageType yvu24, ImageType yuv420sp, int width, int height) +void YUV420toYVU24_NEW(ImageType yvu24, ImageType yuv420sp, int width, + int height) { int frameSize = width * height; @@ -283,7 +292,8 @@ void YUV420toYVU24_NEW(ImageType yvu24, ImageType yuv420sp, int width, int heigh } -JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_allocateMosaicMemory(JNIEnv* env, jobject thiz, jint width, jint height) +JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_allocateMosaicMemory( + JNIEnv* env, jobject thiz, jint width, jint height) { tWidth[HR] = width; tHeight[HR] = height; @@ -300,8 +310,9 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_allocateMosaicMem tImage[HR][i] = ImageUtils::allocateImage(tWidth[HR], tHeight[HR], ImageUtils::IMAGE_TYPE_NUM_CHANNELS); } -} + AllocateTextureMemory(tWidth[LR], tHeight[LR]); +} JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_freeMosaicMemory( JNIEnv* env, jobject thiz) @@ -314,6 +325,45 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_freeMosaicMemory( { ImageUtils::freeImage(tImage[HR][i]); } + + FreeTextureMemory(); +} + + +void decodeYUV444SP(unsigned char* rgb, unsigned char* yuv420sp, int width, + int height) +{ + int frameSize = width * height; + + for (int j = 0, yp = 0; j < height; j++) + { + int vp = frameSize + j * width, u = 0, v = 0; + int up = vp + frameSize; + + for (int i = 0; i < width; i++, yp++, vp++, up++) + { + int y = (0xff & ((int) yuv420sp[yp])) - 16; + if (y < 0) y = 0; + + v = (0xff & yuv420sp[vp]) - 128; + u = (0xff & yuv420sp[up]) - 128; + + int y1192 = 1192 * y; + int r = (y1192 + 1634 * v); + int g = (y1192 - 833 * v - 400 * u); + int b = (y1192 + 2066 * u); + + if (r < 0) r = 0; else if (r > 262143) r = 262143; + if (g < 0) g = 0; else if (g > 262143) g = 262143; + if (b < 0) b = 0; else if (b > 262143) b = 262143; + + //rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); + int p = j*width*3+i*3; + rgb[p+0] = (r<<6 & 0xFF0000)>>16; + rgb[p+1] = (g>>2 & 0xFF00)>>8; + rgb[p+2] = b>>10 & 0xFF; + } + } } @@ -349,6 +399,14 @@ JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceI t0 = now_ms(); GenerateQuarterResImagePlanar(tImage[HR][frame_number_HR], tWidth[HR], tHeight[HR], tImage[LR][frame_number_LR]); + + + sem_wait(&gPreviewImageRGB_semaphore); + decodeYUV444SP(gPreviewImageRGB, tImage[LR][frame_number_LR], + gPreviewImageRGBWidth, gPreviewImageRGBHeight); + sem_post(&gPreviewImageRGB_semaphore); + + t1 = now_ms(); time_c = t1 - t0; LOGV("[%d] HR->LR [%d]: %g ms", frame_number_HR, frame_number_LR, @@ -369,6 +427,8 @@ JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceI gTRS[0] = gTRS[4] = gTRS[8] = 1.0f; } + UpdateWarpTransformation(gTRS); + gTRS[9] = frame_number_HR; jfloatArray bytes = env->NewFloatArray(10); @@ -379,12 +439,14 @@ JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceI return bytes; } -JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_setBlendingType(JNIEnv* env, jobject thiz, jint type) +JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_setBlendingType( + JNIEnv* env, jobject thiz, jint type) { blendingType = int(type); } -JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_reset(JNIEnv* env, jobject thiz) +JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_reset( + JNIEnv* env, jobject thiz) { frame_number_HR = 0; frame_number_LR = 0; @@ -392,7 +454,8 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_reset(JNIEnv* env Init(LR,MAX_FRAMES_LR); } -JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_createMosaic(JNIEnv* env, jobject thiz, jboolean value) +JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_createMosaic( + JNIEnv* env, jobject thiz, jboolean value) { high_res = bool(value); @@ -414,7 +477,8 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_createMosaic(JNIE } } -JNIEXPORT jintArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosaic(JNIEnv* env, jobject thiz) +JNIEXPORT jintArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosaic( + JNIEnv* env, jobject thiz) { int y,x; int width = mosaicWidth; @@ -422,7 +486,8 @@ JNIEXPORT jintArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosa int imageSize = width * height; // Convert back to RGB24 - resultBGR = ImageUtils::allocateImage(mosaicWidth, mosaicHeight, ImageUtils::IMAGE_TYPE_NUM_CHANNELS); + resultBGR = ImageUtils::allocateImage(mosaicWidth, mosaicHeight, + ImageUtils::IMAGE_TYPE_NUM_CHANNELS); ImageUtils::yvu2bgr(resultBGR, resultYVU, mosaicWidth, mosaicHeight); LOGV("MosBytes: %d, W = %d, H = %d", imageSize, width, height); @@ -434,7 +499,8 @@ JNIEXPORT jintArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosa { for(x=0; x<width; x++) { - image[y*width+x] = (0xFF<<24) | (resultBGR[y*width*3+x*3+2]<<16)| (resultBGR[y*width*3+x*3+1]<<8)| (resultBGR[y*width*3+x*3]); + image[y*width+x] = (0xFF<<24) | (resultBGR[y*width*3+x*3+2]<<16)| + (resultBGR[y*width*3+x*3+1]<<8)| (resultBGR[y*width*3+x*3]); } } @@ -456,7 +522,8 @@ JNIEXPORT jintArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosa return bytes; } -JNIEXPORT jbyteArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosaicNV21(JNIEnv* env, jobject thiz) +JNIEXPORT jbyteArray JNICALL Java_com_android_camera_panorama_Mosaic_getFinalMosaicNV21( + JNIEnv* env, jobject thiz) { int y,x; int width; diff --git a/jni/mosaic_renderer_jni.cpp b/jni/mosaic_renderer_jni.cpp new file mode 100644 index 0000000..21d3d39 --- /dev/null +++ b/jni/mosaic_renderer_jni.cpp @@ -0,0 +1,338 @@ +// OpenGL ES 2.0 code +#include <jni.h> +#include <android/log.h> + +#include <db_utilities_camera.h> +#include "mosaic/ImageUtils.h" +#include "mosaic_renderer/FrameBuffer.h" +#include "mosaic_renderer/WarpRenderer.h" +#include <stdio.h> +#include <stdlib.h> +#include <math.h> + +#include "mosaic_renderer_jni.h" + +#define LOG_TAG "MosaicRenderer" +#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) + +// Texture handle +GLuint textureId[1]; + +bool warp_image = true; + +// Low-Res input image frame in RGB format for preview rendering +unsigned char* gPreviewImageRGB; +// Low-Res preview image width +int gPreviewImageRGBWidth; +// Low-Res preview image height +int gPreviewImageRGBHeight; + +// Semaphore to protect simultaneous read/writes from gPreviewImageRGB +sem_t gPreviewImageRGB_semaphore; + +// Off-screen preview FBO width (large enough to store the entire +// preview mosaic). +int gPreviewFBOWidth; +// Off-screen preview FBO height (large enough to store the entire +// preview mosaic). +int gPreviewFBOHeight; + +// Shader to add warped current frame to the preview FBO +WarpRenderer gWarper; +// Shader to warp and render the preview FBO to the screen +WarpRenderer gPreview; +// Off-screen FBO to store the result of gWarper +FrameBuffer *gBuffer; + +// Affine transformation in GL 4x4 format (column-major) to warp the +// current frame into the first frame coordinate system. +GLfloat g_dAffinetransGL[16]; + +// Affine transformation in GL 4x4 format (column-major) to warp the +// preview FBO into the current frame coordinate system. +GLfloat g_dAffinetransInvGL[16]; + +// GL 4x4 Identity transformation +GLfloat g_dAffinetransIdent[] = { + 1., 0., 0., 0., + 0., 1., 0., 0., + 0., 0., 1., 0., + 0., 0., 0., 1.}; + + +static void printGLString(const char *name, GLenum s) { + const char *v = (const char *) glGetString(s); + LOGI("GL %s = %s\n", name, v); +} + +// @return false if there was an error +bool checkGlError(const char* op) { + GLint error = glGetError(); + if (error != 0) { + LOGE("after %s() glError (0x%x)\n", op, error); + return false; + } + return true; +} + +void LoadTexture(unsigned char *buffer, int width, int height, GLuint texId) +{ + glBindTexture(GL_TEXTURE_2D, texId); + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, + GL_UNSIGNED_BYTE, buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void ReloadTexture(unsigned char *buffer, int width, int height, GLuint texId) +{ + glBindTexture(GL_TEXTURE_2D, texId); + + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, + GL_UNSIGNED_BYTE, buffer); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); +} + +void ConvertAffine3x3toGL4x4(double *matGL44, double *mat33) +{ + matGL44[0] = mat33[0]; + matGL44[1] = mat33[3]; + matGL44[2] = 0.0; + matGL44[3] = mat33[6]; + + matGL44[4] = mat33[1]; + matGL44[5] = mat33[4]; + matGL44[6] = 0.0; + matGL44[7] = mat33[7]; + + matGL44[8] = 0; + matGL44[9] = 0; + matGL44[10] = 1.0; + matGL44[11] = 0.0; + + matGL44[12] = mat33[2]; + matGL44[13] = mat33[5]; + matGL44[14] = 0.0; + matGL44[15] = mat33[8]; +} + +// This function computes fills the 4x4 matrices g_dAffinetrans and +// g_dAffinetransInv using the specified 3x3 affine transformation +// between the first captured frame and the current frame. The computed +// g_dAffinetrans is such that it warps the current frame into the +// coordinate system of the first frame. Thus, applying this transformation +// to each successive frame builds up the preview mosaic in the first frame +// coordinate system. Then the computed g_dAffinetransInv is such that it +// warps the computed preview mosaic into the coordinate system of the +// original (as captured) current frame. This has the effect of showing +// the current frame as is (without warping) but warping the rest of the +// mosaic data to still be in alignment with this frame. +void UpdateWarpTransformation(float *trs) +{ + double H[9], Hinv[9], Hp[9], Htemp[9]; + double K[9], Kinv[9]; + + int w = gPreviewImageRGBWidth; + int h = gPreviewImageRGBHeight; + + // K is the transformation to map the canonical [-1,1] vertex coordinate + // system to the [0,w] image coordinate system before applying the given + // affine transformation trs. + K[0] = w / 2.0; + K[1] = 0.0; + K[2] = w / 2.0; + K[3] = 0.0; + K[4] = -h / 2.0; + K[5] = h / 2.0; + K[6] = 0.0; + K[7] = 0.0; + K[8] = 1.0; + + db_Identity3x3(Kinv); + db_InvertCalibrationMatrix(Kinv, K); + + for(int i=0; i<9; i++) + { + H[i] = trs[i]; + } + + // Move the origin such that the frame is centered in the previewFBO + H[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth / 2); + H[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight / 2); + + // Hp = inv(K) * H * K + db_Identity3x3(Htemp); + db_Multiply3x3_3x3(Htemp, H, K); + db_Multiply3x3_3x3(Hp, Kinv, Htemp); + + ConvertAffine3x3toGL4x4(g_dAffinetrans, Hp); + + //////////////////////////////////////////////// + ////// Compute g_dAffinetransInv now... ////// + //////////////////////////////////////////////// + + w = gPreviewFBOWidth; + h = gPreviewFBOHeight; + + K[0] = w / 2.0; + K[1] = 0.0; + K[2] = w / 2.0; + K[3] = 0.0; + K[4] = h / 2.0; + K[5] = h / 2.0; + K[6] = 0.0; + K[7] = 0.0; + K[8] = 1.0; + + db_Identity3x3(Kinv); + db_InvertCalibrationMatrix(Kinv, K); + + db_Identity3x3(Hinv); + db_InvertAffineTransform(Hinv, H); + + Hinv[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth / 2); + Hinv[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight / 2); + + // Hp = inv(K) * Hinv * K + db_Identity3x3(Htemp); + db_Multiply3x3_3x3(Htemp, Hinv, K); + db_Multiply3x3_3x3(Hp, Kinv, Htemp); + + ConvertAffine3x3toGL4x4(g_dAffinetransInv, Hp); +} + +void AllocateTextureMemory(int width, int height) +{ + gPreviewImageRGBWidth = width; + gPreviewImageRGBHeight = height; + + sem_init(&gPreviewImageRGB_semaphore, 0, 1); + + sem_wait(&gPreviewImageRGB_semaphore); + gPreviewImageRGB = ImageUtils::allocateImage(gPreviewImageRGBWidth, + gPreviewImageRGBHeight, ImageUtils::IMAGE_TYPE_NUM_CHANNELS); + memset(gPreviewImageRGB, 0, gPreviewImageRGBWidth * + gPreviewImageRGBHeight * 3 * sizeof(unsigned char)); + sem_post(&gPreviewImageRGB_semaphore); + + gPreviewFBOWidth = PREVIEW_FBO_WIDTH_SCALE * gPreviewImageRGBWidth; + gPreviewFBOHeight = PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageRGBHeight; + + UpdateWarpTransformation(g_dAffinetransIdent); +} + +void FreeTextureMemory() +{ + sem_wait(&gPreviewImageRGB_semaphore); + ImageUtils::freeImage(gPreviewImageRGB); + sem_post(&gPreviewImageRGB_semaphore); + + sem_destroy(&gPreviewImageRGB_semaphore); +} + +extern "C" +{ + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( + JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset( + JNIEnv * env, jobject obj, jint width, jint height); + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_step( + JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_ready( + JNIEnv * env, jobject obj); + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_togglewarping( + JNIEnv * env, jobject obj); +}; + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( + JNIEnv * env, jobject obj) +{ + gWarper.InitializeGLProgram(); + gPreview.InitializeGLProgram(); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glGenTextures(1, &textureId[0]); +} + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset( + JNIEnv * env, jobject obj, jint width, jint height) +{ + gBuffer = new FrameBuffer(); + gBuffer->Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); + + sem_wait(&gPreviewImageRGB_semaphore); + memset(gPreviewImageRGB, 0, gPreviewImageRGBWidth * + gPreviewImageRGBHeight * 3 * sizeof(unsigned char)); + sem_post(&gPreviewImageRGB_semaphore); + + // Load texture + LoadTexture(gPreviewImageRGB, gPreviewImageRGBWidth, + gPreviewImageRGBHeight, textureId[0]); + + gWarper.SetupGraphics(gBuffer); + gWarper.Clear(0.0, 0.0, 0.0, 1.0); + gWarper.SetViewportMatrix(gPreviewImageRGBWidth, + gPreviewImageRGBHeight, gBuffer->GetWidth(), gBuffer->GetHeight()); + gWarper.SetScalingMatrix(1.0f, 1.0f); + gWarper.SetInputTextureName(textureId[0]); + + gPreview.SetupGraphics(width, height); + gPreview.Clear(0.0, 0.0, 0.0, 1.0); + gPreview.SetViewportMatrix(1, 1, 1, 1); + gPreview.SetScalingMatrix(1.0f, -1.0f); + gPreview.SetInputTextureName(gBuffer->GetTextureName()); +} + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_step( + JNIEnv * env, jobject obj) +{ + // Use the gWarper shader to apply the current frame transformation to the + // current frame and then add it to the gBuffer FBO. + gWarper.DrawTexture(g_dAffinetransGL); + + // Clear the screen to black. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Use the gPreview shader to apply the inverse of the current frame + // transformation to the gBuffer FBO and render it to the screen. + gPreview.DrawTexture(g_dAffinetransInvGL); +} + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_togglewarping( + JNIEnv * env, jobject obj) +{ + warp_image = !warp_image; +} + + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_ready( + JNIEnv * env, jobject obj) +{ + sem_wait(&gPreviewImageRGB_semaphore); + ReloadTexture(gPreviewImageRGB, gPreviewImageRGBWidth, + gPreviewImageRGBHeight, textureId[0]); + sem_post(&gPreviewImageRGB_semaphore); + + if(!warp_image) + { + for(int i=0; i<16; i++) + { + g_dAffinetrans[i] = g_dAffinetransIdent[i]; + } + } + + for(int i=0; i<16; i++) + { + g_dAffinetransGL[i] = g_dAffinetrans[i]; + g_dAffinetransInvGL[i] = g_dAffinetransInv[i]; + } +} diff --git a/jni/mosaic_renderer_jni.h b/jni/mosaic_renderer_jni.h new file mode 100644 index 0000000..ed4180f --- /dev/null +++ b/jni/mosaic_renderer_jni.h @@ -0,0 +1,23 @@ +#pragma once +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <semaphore.h> + +// The Preview FBO dimensions are determined from the low-res +// frame dimensions (gPreviewImageRGBWidth, gPreviewImageRGBHeight) +// using the scale factors below. +const int PREVIEW_FBO_WIDTH_SCALE = 4; +const int PREVIEW_FBO_HEIGHT_SCALE = 2; + +extern "C" void AllocateTextureMemory(int width, int height); +extern "C" void FreeTextureMemory(); +extern "C" void UpdateWarpTransformation(float *trs); + +extern unsigned char* gPreviewImageRGB; +extern int gPreviewImageRGBWidth; +extern int gPreviewImageRGBHeight; + +extern sem_t gPreviewImageRGB_semaphore; + +extern double g_dAffinetrans[16]; +extern double g_dAffinetransInv[16]; |