From 41a2e9735136f372de95652d0828600282c8e967 Mon Sep 17 00:00:00 2001 From: mbansal Date: Mon, 8 Aug 2011 20:23:02 -0400 Subject: Updates to allow using SurfaceTexture for reading the preview frames directly from GPU memory. 1) SurfaceTexture is now used to obtain the data processed by the mosaicing library. 2) SurfaceTexture in GPU memory is directly rendered using the transformation from the mosaicing library to generate the preview mosaic. 3) GPU is also used to generate the Low-Res frames from the High-res frames (was being done in CPU before). 4) SurfaceTexture is also used to render the viewfinder as soon as the mosaicing application starts (eliminating the need for a separate SurfaceHolder to render the camera). 5) Modified the XML layout during the preview state to be the same size as during the capture stage to accommodate the SurfaceTexture based viewfinder [this needs to be reviewed and adjusted]. 6) Fixed the viewfinder and back button issues identified by Wei-Ta. 7) Round-1 of removing trailing spaces and tabs. 8) Added documentation to new Java side interfaces and cleaned up code in general. 9) Cleaned up redundant and commented out code from the native side. 10) Merged with latest updates from the main trunk. 11) Fixed issues identified in code review and also cleaned up and refactored some code. 12) Added layout-w1024dp/pano_capture.xml for tablet layout. Change-Id: If8fb0116de6c7fc6652cc67ac453553726961c32 --- jni/Android.mk | 1 + jni/feature_mos/src/mosaic/ImageUtils.cpp | 45 +++ jni/feature_mos/src/mosaic/ImageUtils.h | 2 + .../src/mosaic_renderer/SurfaceTextureRenderer.cpp | 382 +++++++++++++++++++++ .../src/mosaic_renderer/SurfaceTextureRenderer.h | 77 +++++ .../src/mosaic_renderer/WarpRenderer.cpp | 6 + jni/feature_mos/src/mosaic_renderer/WarpRenderer.h | 4 +- jni/feature_mos_jni.cpp | 65 +++- jni/mosaic_renderer_jni.cpp | 266 ++++++++++---- jni/mosaic_renderer_jni.h | 14 +- res/layout-w1024dp/pano_capture.xml | 49 +++ res/layout/pano_capture.xml | 2 +- res/layout/pano_preview.xml | 4 +- src/com/android/camera/panorama/Mosaic.java | 14 + .../camera/panorama/MosaicFrameProcessor.java | 21 +- .../android/camera/panorama/MosaicRenderer.java | 34 +- .../camera/panorama/MosaicRendererSurfaceView.java | 78 ++++- .../MosaicRendererSurfaceViewRenderer.java | 67 +++- .../android/camera/panorama/PanoramaActivity.java | 193 +++++++---- 19 files changed, 1137 insertions(+), 187 deletions(-) create mode 100755 jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp create mode 100755 jni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h create mode 100644 res/layout-w1024dp/pano_capture.xml 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 + +#include +#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 + +#include +#include +#include + +//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 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 #include #include @@ -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]; diff --git a/res/layout-w1024dp/pano_capture.xml b/res/layout-w1024dp/pano_capture.xml new file mode 100644 index 0000000..0ce34b9 --- /dev/null +++ b/res/layout-w1024dp/pano_capture.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + +