diff options
Diffstat (limited to 'jni')
-rwxr-xr-x | jni/Android.mk | 1 | ||||
-rw-r--r-- | jni/feature_mos/src/mosaic/ImageUtils.cpp | 45 | ||||
-rw-r--r-- | jni/feature_mos/src/mosaic/ImageUtils.h | 2 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp | 382 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h | 77 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp | 6 | ||||
-rwxr-xr-x | jni/feature_mos/src/mosaic_renderer/WarpRenderer.h | 4 | ||||
-rw-r--r-- | jni/feature_mos_jni.cpp | 65 | ||||
-rw-r--r-- | jni/mosaic_renderer_jni.cpp | 266 | ||||
-rw-r--r-- | jni/mosaic_renderer_jni.h | 14 |
10 files changed, 776 insertions, 86 deletions
diff --git a/jni/Android.mk b/jni/Android.mk index d653573..0802754 100755 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -22,6 +22,7 @@ LOCAL_SRC_FILES := \ feature_mos/src/mosaic/Mosaic.cpp \ feature_mos/src/mosaic/Pyramid.cpp \ feature_mos/src/mosaic_renderer/WarpRenderer.cpp \ + feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp \ feature_mos/src/mosaic_renderer/FrameBuffer.cpp \ feature_stab/db_vlvm/db_feature_detection.cpp \ feature_stab/db_vlvm/db_feature_matching.cpp \ diff --git a/jni/feature_mos/src/mosaic/ImageUtils.cpp b/jni/feature_mos/src/mosaic/ImageUtils.cpp index 792b90d..2671dd4 100644 --- a/jni/feature_mos/src/mosaic/ImageUtils.cpp +++ b/jni/feature_mos/src/mosaic/ImageUtils.cpp @@ -25,6 +25,51 @@ #include "ImageUtils.h" +void ImageUtils::rgba2yvu(ImageType out, ImageType in, int width, int height) +{ + int r,g,b, a; + ImageType yimg = out; + ImageType vimg = yimg + width*height; + ImageType uimg = vimg + width*height; + ImageType image = in; + + for (int ii = 0; ii < height; ii++) { + for (int ij = 0; ij < width; ij++) { + r = (*image++); + g = (*image++); + b = (*image++); + a = (*image++); + + if (r < 0) r = 0; + if (r > 255) r = 255; + if (g < 0) g = 0; + if (g > 255) g = 255; + if (b < 0) b = 0; + if (b > 255) b = 255; + + int val = (int) (REDY * r + GREENY * g + BLUEY * b) / 1000 + 16; + if (val < 0) val = 0; + if (val > 255) val = 255; + *(yimg) = val; + + val = (int) (REDV * r - GREENV * g - BLUEV * b) / 1000 + 128; + if (val < 0) val = 0; + if (val > 255) val = 255; + *(vimg) = val; + + val = (int) (-REDU * r - GREENU * g + BLUEU * b) / 1000 + 128; + if (val < 0) val = 0; + if (val > 255) val = 255; + *(uimg) = val; + + yimg++; + uimg++; + vimg++; + } + } +} + + void ImageUtils::rgb2yvu(ImageType out, ImageType in, int width, int height) { int r,g,b; diff --git a/jni/feature_mos/src/mosaic/ImageUtils.h b/jni/feature_mos/src/mosaic/ImageUtils.h index 9a47cc1..8778238 100644 --- a/jni/feature_mos/src/mosaic/ImageUtils.h +++ b/jni/feature_mos/src/mosaic/ImageUtils.h @@ -61,6 +61,8 @@ public: */ static void rgb2yvu(ImageType out, ImageType in, int width, int height); + static void rgba2yvu(ImageType out, ImageType in, int width, int height); + /** * Convert image from YVU (non-interlaced) to BGR (interlaced) * diff --git a/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp b/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp new file mode 100755 index 0000000..fb124aa --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp @@ -0,0 +1,382 @@ +#include "SurfaceTextureRenderer.h" + +#include <GLES2/gl2ext.h> + +#include <android/log.h> +#define LOG_TAG "SurfaceTextureRenderer" +#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 uSTMatrix;\n" +"uniform mat4 u_scalingtrans; \n" +"attribute vec4 aPosition;\n" +"attribute vec4 aTextureCoord;\n" +"varying vec2 vTextureCoord;\n" +"varying vec2 vTextureNormCoord;\n" +"void main() {\n" +" gl_Position = u_scalingtrans * aPosition;\n" +" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +" vTextureNormCoord = aTextureCoord.xy;\n" +"}\n"; + +static const char gFragmentShader[] = +"#extension GL_OES_EGL_image_external : require\n" +"precision mediump float;\n" +"varying vec2 vTextureCoord;\n" +"varying vec2 vTextureNormCoord;\n" +"uniform samplerExternalOES sTexture;\n" +"void main() {\n" +" gl_FragColor = texture2D(sTexture, vTextureNormCoord);\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 +}; +GLushort g_iIndices2[] = { 0, 1, 2, 3 }; + +const int GL_TEXTURE_EXTERNAL_OES_ENUM = 0x8D65; + +const int VERTEX_STRIDE = 6 * sizeof(GLfloat); + +SurfaceTextureRenderer::SurfaceTextureRenderer() + : mGlProgram(0), + mInputTextureName(-1), + mInputTextureWidth(0), + mInputTextureHeight(0), + mSurfaceWidth(0), + mSurfaceHeight(0) +{ + memset(mSTMatrix, 0.0, 16*sizeof(float)); + mSTMatrix[0] = 1.0f; + mSTMatrix[5] = 1.0f; + mSTMatrix[10] = 1.0f; + mSTMatrix[15] = 1.0f; + + InitializeGLContext(); +} + +SurfaceTextureRenderer::~SurfaceTextureRenderer() { +} + +void SurfaceTextureRenderer::SetSTMatrix(float *stmat) +{ + memcpy(mSTMatrix, stmat, 16*sizeof(float)); +} + +GLuint SurfaceTextureRenderer::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 SurfaceTextureRenderer::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 SurfaceTextureRenderer::InitializeGLProgram() +{ + bool succeeded = false; + do { + GLuint glProgram; + glProgram = createProgram(VertexShaderSource(), + FragmentShaderSource()); + if (!glProgram) { + break; + } + + glUseProgram(glProgram); + if (!checkGlError("glUseProgram")) break; + + maPositionHandle = glGetAttribLocation(glProgram, "aPosition"); + checkGlError("glGetAttribLocation aPosition"); + maTextureHandle = glGetAttribLocation(glProgram, "aTextureCoord"); + checkGlError("glGetAttribLocation aTextureCoord"); + muSTMatrixHandle = glGetUniformLocation(glProgram, "uSTMatrix"); + checkGlError("glGetUniformLocation uSTMatrix"); + mScalingtransLoc = glGetUniformLocation(glProgram, "u_scalingtrans"); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + mGlProgram = glProgram; + succeeded = true; + } while (false); + + if (!succeeded && (mGlProgram != 0)) + { + glDeleteProgram(mGlProgram); + checkGlError("glDeleteProgram"); + mGlProgram = 0; + } + return succeeded; +} + +void SurfaceTextureRenderer::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 SurfaceTextureRenderer::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 SurfaceTextureRenderer::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 SurfaceTextureRenderer::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 SurfaceTextureRenderer::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 SurfaceTextureRenderer::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; + + glUniformMatrix4fv(mScalingtransLoc, 1, GL_FALSE, mScalingMatrix); + + // Load the vertex position + glVertexAttribPointer(maPositionHandle, 4, GL_FLOAT, + GL_FALSE, VERTEX_STRIDE, g_vVertices); + glEnableVertexAttribArray(maPositionHandle); + // Load the texture coordinate + glVertexAttribPointer(maTextureHandle, 2, GL_FLOAT, + GL_FALSE, VERTEX_STRIDE, &g_vVertices[4]); + glEnableVertexAttribArray(maTextureHandle); + + // And, finally, execute the GL draw command. + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, g_iIndices2); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glFinish(); + succeeded = true; + } while (false); + return succeeded; +} + +void SurfaceTextureRenderer::InitializeGLContext() +{ + if(mFrameBuffer != NULL) + { + delete mFrameBuffer; + mFrameBuffer = NULL; + } + + mInputTextureName = -1; + mInputTextureType = GL_TEXTURE_EXTERNAL_OES_ENUM; + mGlProgram = 0; +} + +int SurfaceTextureRenderer::GetTextureName() +{ + return mInputTextureName; +} + +void SurfaceTextureRenderer::SetInputTextureName(GLuint textureName) +{ + mInputTextureName = textureName; +} + +void SurfaceTextureRenderer::SetInputTextureType(GLenum textureType) +{ + mInputTextureType = textureType; +} + +void SurfaceTextureRenderer::SetInputTextureDimensions(int width, int height) +{ + mInputTextureWidth = width; + mInputTextureHeight = height; +} + + +const char* SurfaceTextureRenderer::VertexShaderSource() const +{ + return gVertexShader; +} + +const char* SurfaceTextureRenderer::FragmentShaderSource() const +{ + return gFragmentShader; +} diff --git a/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h b/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h new file mode 100755 index 0000000..e74bd64 --- /dev/null +++ b/jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h @@ -0,0 +1,77 @@ +#pragma once + +#include "FrameBuffer.h" + +#include <GLES2/gl2.h> + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +//TODO: Add a base class Renderer for WarpRenderer and SurfaceTextureRenderer. +class SurfaceTextureRenderer { + public: + SurfaceTextureRenderer(); + virtual ~SurfaceTextureRenderer(); + + // 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 SetInputTextureType(GLenum textureType); + + void InitializeGLContext(); + + void SetSTMatrix(float *stmat); + + 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 mInputTextureType; } + + GLuint mGlProgram; + GLuint mInputTextureName; + GLenum mInputTextureType; + int mInputTextureWidth; + int mInputTextureHeight; + + // Attribute locations + GLint mScalingtransLoc; + GLint muSTMatrixHandle; + GLint maPositionHandle; + GLint maTextureHandle; + + GLfloat mViewportMatrix[16]; + GLfloat mScalingMatrix[16]; + GLfloat mSTMatrix[16]; + + int mSurfaceWidth; // Width of target surface. + int mSurfaceHeight; // Height of target surface. + + FrameBuffer *mFrameBuffer; +}; + diff --git a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp index 9274721..b2ca8d4 100755 --- a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp +++ b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp @@ -346,6 +346,7 @@ void WarpRenderer::InitializeGLContext() } mInputTextureName = -1; + mInputTextureType = GL_TEXTURE_2D; mGlProgram = 0; mTexHandle = 0; } @@ -360,6 +361,11 @@ void WarpRenderer::SetInputTextureName(GLuint textureName) mInputTextureName = textureName; } +void WarpRenderer::SetInputTextureType(GLenum textureType) +{ + mInputTextureType = textureType; +} + void WarpRenderer::SetInputTextureDimensions(int width, int height) { mInputTextureWidth = width; diff --git a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h index 7986215..774b33f 100755 --- a/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h +++ b/jni/feature_mos/src/mosaic_renderer/WarpRenderer.h @@ -29,6 +29,7 @@ class WarpRenderer { int GetTextureName(); void SetInputTextureName(GLuint textureName); void SetInputTextureDimensions(int width, int height); + void SetInputTextureType(GLenum textureType); void InitializeGLContext(); @@ -47,11 +48,12 @@ class WarpRenderer { // Redefine this to use special texture types such as // GL_TEXTURE_EXTERNAL_OES. - virtual GLenum InputTextureType() const { return GL_TEXTURE_2D; } + virtual GLenum InputTextureType() const { return mInputTextureType; } GLuint mGlProgram; GLuint mInputTextureName; + GLenum mInputTextureType; int mInputTextureWidth; int mInputTextureHeight; diff --git a/jni/feature_mos_jni.cpp b/jni/feature_mos_jni.cpp index c858728..b06c85f 100644 --- a/jni/feature_mos_jni.cpp +++ b/jni/feature_mos_jni.cpp @@ -59,7 +59,6 @@ const int MAX_FRAMES_LR = 200; static double mTx; -enum { LR=0, HR, NR }; int tWidth[NR]; int tHeight[NR]; int H2L_FACTOR = 4; // Can be 2 @@ -311,7 +310,7 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_allocateMosaicMem ImageUtils::IMAGE_TYPE_NUM_CHANNELS); } - AllocateTextureMemory(tWidth[LR], tHeight[LR]); + AllocateTextureMemory(tWidth[HR], tHeight[HR], tWidth[LR], tHeight[LR]); } JNIEXPORT void JNICALL Java_com_android_camera_panorama_Mosaic_freeMosaicMemory( @@ -366,6 +365,63 @@ void decodeYUV444SP(unsigned char* rgb, unsigned char* yuv420sp, int width, } } +static int count = 0; + +JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceImageFromGPU( + JNIEnv* env, jobject thiz) +{ + double t0, t1, time_c; + t0 = now_ms(); + + if(frame_number_HR<MAX_FRAMES_HR && frame_number_LR<MAX_FRAMES_LR) + { + double last_tx = mTx; + + t0 = now_ms(); + + sem_wait(&gPreviewImageRGB_semaphore); + ImageUtils::rgba2yvu(tImage[LR][frame_number_LR], gPreviewImageRGB[LR], + tWidth[LR], tHeight[LR]); + sem_post(&gPreviewImageRGB_semaphore); + + t1 = now_ms(); + time_c = t1 - t0; + LOGV("[%d] RGB => YVU [%d]: %g ms", frame_number_HR, frame_number_LR, + time_c); + + int ret_code = AddFrame(LR, frame_number_LR, gTRS); + + if(ret_code == Mosaic::MOSAIC_RET_OK) + { + // Copy into HR buffer only if this is a valid frame + sem_wait(&gPreviewImageRGB_semaphore); + ImageUtils::rgba2yvu(tImage[HR][frame_number_HR], gPreviewImageRGB[HR], + tWidth[HR], tHeight[HR]); + sem_post(&gPreviewImageRGB_semaphore); + + frame_number_LR++; + frame_number_HR++; + } + } + else + { + gTRS[1] = gTRS[2] = gTRS[3] = gTRS[5] = gTRS[6] = gTRS[7] = 0.0f; + gTRS[0] = gTRS[4] = gTRS[8] = 1.0f; + } + + UpdateWarpTransformation(gTRS); + + gTRS[9] = frame_number_HR; + + jfloatArray bytes = env->NewFloatArray(10); + if(bytes != 0) + { + env->SetFloatArrayRegion(bytes, 0, 10, (jfloat*) gTRS); + } + return bytes; +} + + JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceImage( JNIEnv* env, jobject thiz, jbyteArray photo_data) @@ -402,11 +458,10 @@ JNIEXPORT jfloatArray JNICALL Java_com_android_camera_panorama_Mosaic_setSourceI sem_wait(&gPreviewImageRGB_semaphore); - decodeYUV444SP(gPreviewImageRGB, tImage[LR][frame_number_LR], - gPreviewImageRGBWidth, gPreviewImageRGBHeight); + decodeYUV444SP(gPreviewImageRGB[LR], tImage[LR][frame_number_LR], + gPreviewImageRGBWidth[LR], gPreviewImageRGBHeight[LR]); sem_post(&gPreviewImageRGB_semaphore); - t1 = now_ms(); time_c = t1 - t0; LOGV("[%d] HR->LR [%d]: %g ms", frame_number_HR, frame_number_LR, diff --git a/jni/mosaic_renderer_jni.cpp b/jni/mosaic_renderer_jni.cpp index 21d3d39..c7e11b0 100644 --- a/jni/mosaic_renderer_jni.cpp +++ b/jni/mosaic_renderer_jni.cpp @@ -6,6 +6,7 @@ #include "mosaic/ImageUtils.h" #include "mosaic_renderer/FrameBuffer.h" #include "mosaic_renderer/WarpRenderer.h" +#include "mosaic_renderer/SurfaceTextureRenderer.h" #include <stdio.h> #include <stdlib.h> #include <math.h> @@ -14,22 +15,25 @@ #define LOG_TAG "MosaicRenderer" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) +#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) // Texture handle -GLuint textureId[1]; +GLuint gSurfaceTextureID[1]; -bool warp_image = true; +bool gWarpImage = 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; +// Low-Res input image frame in RGB format for preview rendering and processing +// and high-res RGB input image for processing. +unsigned char* gPreviewImageRGB[NR]; +// Low-Res & high-res preview image width +int gPreviewImageRGBWidth[NR]; +// Low-Res & high-res preview image height +int gPreviewImageRGBHeight[NR]; // Semaphore to protect simultaneous read/writes from gPreviewImageRGB sem_t gPreviewImageRGB_semaphore; +sem_t gPreviewImageReady_semaphore; // Off-screen preview FBO width (large enough to store the entire // preview mosaic). @@ -38,19 +42,26 @@ int gPreviewFBOWidth; // preview mosaic). int gPreviewFBOHeight; +// Shader to copy input SurfaceTexture into and RGBA FBO. The two shaders +// render to the textures with dimensions corresponding to the low-res and +// high-res image frames. +SurfaceTextureRenderer gSurfTexRenderer[NR]; +// Off-screen FBOs to store the low-res and high-res RGBA copied out from +// the SurfaceTexture by the gSurfTexRenderers. +FrameBuffer gBufferInput[NR]; // 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; +FrameBuffer gBuffer; // Affine transformation in GL 4x4 format (column-major) to warp the -// current frame into the first frame coordinate system. +// 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. +// preview FBO into the current frame coordinate system. GLfloat g_dAffinetransInvGL[16]; // GL 4x4 Identity transformation @@ -60,6 +71,7 @@ GLfloat g_dAffinetransIdent[] = { 0., 0., 1., 0., 0., 0., 0., 1.}; +const int GL_TEXTURE_EXTERNAL_OES_ENUM = 0x8D65; static void printGLString(const char *name, GLenum s) { const char *v = (const char *) glGetString(s); @@ -76,28 +88,35 @@ bool checkGlError(const char* op) { return true; } -void LoadTexture(unsigned char *buffer, int width, int height, GLuint texId) +void bindSurfaceTexture(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); + glBindTexture(GL_TEXTURE_EXTERNAL_OES_ENUM, texId); + + // Can't do mipmapping with camera source + glTexParameterf(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_MIN_FILTER, + GL_LINEAR); + glTexParameterf(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_MAG_FILTER, + GL_LINEAR); + // Clamp to edge is the only option + glTexParameteri(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_WRAP_S, + GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_WRAP_T, + GL_CLAMP_TO_EDGE); } -void ReloadTexture(unsigned char *buffer, int width, int height, GLuint texId) +void ClearPreviewImageRGB(int mID) { - 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); + unsigned char* ptr = gPreviewImageRGB[mID]; + for(int j = 0, i = 0; + j < gPreviewImageRGBWidth[mID] * gPreviewImageRGBHeight[mID] * 4; + j += 4) + { + ptr[i++] = 0; + ptr[i++] = 0; + ptr[i++] = 0; + ptr[i++] = 255; + } + } void ConvertAffine3x3toGL4x4(double *matGL44, double *mat33) @@ -139,8 +158,8 @@ void UpdateWarpTransformation(float *trs) double H[9], Hinv[9], Hp[9], Htemp[9]; double K[9], Kinv[9]; - int w = gPreviewImageRGBWidth; - int h = gPreviewImageRGBHeight; + int w = gPreviewImageRGBWidth[LR]; + int h = gPreviewImageRGBHeight[LR]; // K is the transformation to map the canonical [-1,1] vertex coordinate // system to the [0,w] image coordinate system before applying the given @@ -164,8 +183,8 @@ void UpdateWarpTransformation(float *trs) } // Move the origin such that the frame is centered in the previewFBO - H[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth / 2); - H[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight / 2); + H[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth[LR] / 2); + H[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight[LR] / 2); // Hp = inv(K) * H * K db_Identity3x3(Htemp); @@ -197,8 +216,8 @@ void UpdateWarpTransformation(float *trs) db_Identity3x3(Hinv); db_InvertAffineTransform(Hinv, H); - Hinv[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth / 2); - Hinv[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight / 2); + Hinv[2] += (gPreviewFBOWidth / 2 - gPreviewImageRGBWidth[LR] / 2); + Hinv[5] -= (gPreviewFBOHeight / 2 - gPreviewImageRGBHeight[LR] / 2); // Hp = inv(K) * Hinv * K db_Identity3x3(Htemp); @@ -208,22 +227,28 @@ void UpdateWarpTransformation(float *trs) ConvertAffine3x3toGL4x4(g_dAffinetransInv, Hp); } -void AllocateTextureMemory(int width, int height) +void AllocateTextureMemory(int widthHR, int heightHR, int widthLR, int heightLR) { - gPreviewImageRGBWidth = width; - gPreviewImageRGBHeight = height; + gPreviewImageRGBWidth[HR] = widthHR; + gPreviewImageRGBHeight[HR] = heightHR; + + gPreviewImageRGBWidth[LR] = widthLR; + gPreviewImageRGBHeight[LR] = heightLR; sem_init(&gPreviewImageRGB_semaphore, 0, 1); + sem_init(&gPreviewImageReady_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)); + gPreviewImageRGB[LR] = ImageUtils::allocateImage(gPreviewImageRGBWidth[LR], + gPreviewImageRGBHeight[LR], 4); + ClearPreviewImageRGB(LR); + gPreviewImageRGB[HR] = ImageUtils::allocateImage(gPreviewImageRGBWidth[HR], + gPreviewImageRGBHeight[HR], 4); + ClearPreviewImageRGB(HR); sem_post(&gPreviewImageRGB_semaphore); - gPreviewFBOWidth = PREVIEW_FBO_WIDTH_SCALE * gPreviewImageRGBWidth; - gPreviewFBOHeight = PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageRGBHeight; + gPreviewFBOWidth = PREVIEW_FBO_WIDTH_SCALE * gPreviewImageRGBWidth[LR]; + gPreviewFBOHeight = PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageRGBHeight[LR]; UpdateWarpTransformation(g_dAffinetransIdent); } @@ -231,63 +256,146 @@ void AllocateTextureMemory(int width, int height) void FreeTextureMemory() { sem_wait(&gPreviewImageRGB_semaphore); - ImageUtils::freeImage(gPreviewImageRGB); + ImageUtils::freeImage(gPreviewImageRGB[LR]); + ImageUtils::freeImage(gPreviewImageRGB[HR]); sem_post(&gPreviewImageRGB_semaphore); sem_destroy(&gPreviewImageRGB_semaphore); + sem_destroy(&gPreviewImageReady_semaphore); } extern "C" { - JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( + JNIEXPORT jint 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_preprocess( + JNIEnv * env, jobject obj, jfloatArray stMatrix); + JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_transferGPUtoCPU( + JNIEnv * env, jobject obj); 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_setWarping( + JNIEnv * env, jobject obj, jboolean flag); }; -JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( +JNIEXPORT jint JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( JNIEnv * env, jobject obj) { + gSurfTexRenderer[LR].InitializeGLProgram(); + gSurfTexRenderer[HR].InitializeGLProgram(); gWarper.InitializeGLProgram(); gPreview.InitializeGLProgram(); + gBuffer.InitializeGLContext(); + gBufferInput[LR].InitializeGLContext(); + gBufferInput[HR].InitializeGLContext(); glBindFramebuffer(GL_FRAMEBUFFER, 0); - glGenTextures(1, &textureId[0]); + + glGenTextures(1, gSurfaceTextureID); + // bind the surface texture + bindSurfaceTexture(gSurfaceTextureID[0]); + + return (jint) gSurfaceTextureID[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); + gBuffer.Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); + + gBufferInput[LR].Init(gPreviewImageRGBWidth[LR], + gPreviewImageRGBHeight[LR], GL_RGBA); + + gBufferInput[HR].Init(gPreviewImageRGBWidth[HR], + gPreviewImageRGBHeight[HR], GL_RGBA); sem_wait(&gPreviewImageRGB_semaphore); - memset(gPreviewImageRGB, 0, gPreviewImageRGBWidth * - gPreviewImageRGBHeight * 3 * sizeof(unsigned char)); + ClearPreviewImageRGB(LR); + ClearPreviewImageRGB(HR); sem_post(&gPreviewImageRGB_semaphore); - // Load texture - LoadTexture(gPreviewImageRGB, gPreviewImageRGBWidth, - gPreviewImageRGBHeight, textureId[0]); + // bind the surface texture + bindSurfaceTexture(gSurfaceTextureID[0]); + + gSurfTexRenderer[LR].SetupGraphics(&gBufferInput[LR]); + gSurfTexRenderer[LR].Clear(0.0, 0.0, 0.0, 1.0); + gSurfTexRenderer[LR].SetViewportMatrix(1, 1, 1, 1); + gSurfTexRenderer[LR].SetScalingMatrix(1.0f, -1.0f); + gSurfTexRenderer[LR].SetInputTextureName(gSurfaceTextureID[0]); + gSurfTexRenderer[LR].SetInputTextureType(GL_TEXTURE_EXTERNAL_OES_ENUM); - gWarper.SetupGraphics(gBuffer); + gSurfTexRenderer[HR].SetupGraphics(&gBufferInput[HR]); + gSurfTexRenderer[HR].Clear(0.0, 0.0, 0.0, 1.0); + gSurfTexRenderer[HR].SetViewportMatrix(1, 1, 1, 1); + gSurfTexRenderer[HR].SetScalingMatrix(1.0f, -1.0f); + gSurfTexRenderer[HR].SetInputTextureName(gSurfaceTextureID[0]); + gSurfTexRenderer[HR].SetInputTextureType(GL_TEXTURE_EXTERNAL_OES_ENUM); + + gWarper.SetupGraphics(&gBuffer); gWarper.Clear(0.0, 0.0, 0.0, 1.0); - gWarper.SetViewportMatrix(gPreviewImageRGBWidth, - gPreviewImageRGBHeight, gBuffer->GetWidth(), gBuffer->GetHeight()); + gWarper.SetViewportMatrix(gPreviewImageRGBWidth[LR], + gPreviewImageRGBHeight[LR], gBuffer.GetWidth(), + gBuffer.GetHeight()); gWarper.SetScalingMatrix(1.0f, 1.0f); - gWarper.SetInputTextureName(textureId[0]); + gWarper.SetInputTextureName(gBufferInput[LR].GetTextureName()); + gWarper.SetInputTextureType(GL_TEXTURE_2D); 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()); + gPreview.SetInputTextureName(gBuffer.GetTextureName()); + gPreview.SetInputTextureType(GL_TEXTURE_2D); +} + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_preprocess( + JNIEnv * env, jobject obj, jfloatArray stMatrix) +{ + jfloat *stmat = env->GetFloatArrayElements(stMatrix, 0); + + gSurfTexRenderer[LR].SetSTMatrix((float*) stmat); + gSurfTexRenderer[HR].SetSTMatrix((float*) stmat); + + env->ReleaseFloatArrayElements(stMatrix, stmat, 0); + + gSurfTexRenderer[LR].DrawTexture(g_dAffinetransIdent); + gSurfTexRenderer[HR].DrawTexture(g_dAffinetransIdent); +} + +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_transferGPUtoCPU( + JNIEnv * env, jobject obj) +{ + sem_wait(&gPreviewImageRGB_semaphore); + // Bind to the input LR FBO and read the Low-Res data from there... + glBindFramebuffer(GL_FRAMEBUFFER, gBufferInput[LR].GetFrameBufferName()); + glReadPixels(0, + 0, + gBufferInput[LR].GetWidth(), + gBufferInput[LR].GetHeight(), + GL_RGBA, + GL_UNSIGNED_BYTE, + gPreviewImageRGB[LR]); + + checkGlError("glReadPixels LR"); + + // Bind to the input HR FBO and read the high-res data from there... + glBindFramebuffer(GL_FRAMEBUFFER, gBufferInput[HR].GetFrameBufferName()); + glReadPixels(0, + 0, + gBufferInput[HR].GetWidth(), + gBufferInput[HR].GetHeight(), + GL_RGBA, + GL_UNSIGNED_BYTE, + gPreviewImageRGB[HR]); + + checkGlError("glReadPixels HR"); + + sem_post(&gPreviewImageRGB_semaphore); } JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_step( @@ -297,37 +405,43 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_step( // 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) +JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_setWarping( + JNIEnv * env, jobject obj, jboolean flag) { - warp_image = !warp_image; + // TODO: Review this logic + if(gWarpImage != (bool) flag) //switching from viewfinder to capture or vice-versa + { + gWarper.Clear(0.0, 0.0, 0.0, 1.0); + gPreview.Clear(0.0, 0.0, 0.0, 1.0); + // Clear the screen to black. + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + gWarpImage = (bool)flag; } 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) + if(!gWarpImage) { + // TODO: Review this logic... + UpdateWarpTransformation(g_dAffinetransIdent); + for(int i=0; i<16; i++) { g_dAffinetrans[i] = g_dAffinetransIdent[i]; + g_dAffinetransInv[i] = g_dAffinetransIdent[i]; } + g_dAffinetrans[12] = 1.0f; + g_dAffinetrans[13] = 1.0f; } for(int i=0; i<16; i++) diff --git a/jni/mosaic_renderer_jni.h b/jni/mosaic_renderer_jni.h index ed4180f..c4ba500 100644 --- a/jni/mosaic_renderer_jni.h +++ b/jni/mosaic_renderer_jni.h @@ -9,15 +9,21 @@ const int PREVIEW_FBO_WIDTH_SCALE = 4; const int PREVIEW_FBO_HEIGHT_SCALE = 2; -extern "C" void AllocateTextureMemory(int width, int height); +const int LR = 0; // Low-resolution mode +const int HR = 1; // High-resolution mode +const int NR = 2; // Number of resolution modes + +extern "C" void AllocateTextureMemory(int widthHR, int heightHR, + int widthLR, int heightLR); extern "C" void FreeTextureMemory(); extern "C" void UpdateWarpTransformation(float *trs); -extern unsigned char* gPreviewImageRGB; -extern int gPreviewImageRGBWidth; -extern int gPreviewImageRGBHeight; +extern unsigned char* gPreviewImageRGB[NR]; +extern int gPreviewImageRGBWidth[NR]; +extern int gPreviewImageRGBHeight[NR]; extern sem_t gPreviewImageRGB_semaphore; +extern sem_t gPreviewImageReady_semaphore; extern double g_dAffinetrans[16]; extern double g_dAffinetransInv[16]; |