summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormbansal <mayank.bansal@sri.com>2011-08-08 20:23:02 -0400
committerWei-Ta Chen <weita@google.com>2011-08-12 15:05:43 -0700
commit41a2e9735136f372de95652d0828600282c8e967 (patch)
tree25e946121b6940cd3dfa19746a393e5c05179ef4
parent7058a9318dd5d6d5a1e79a84080cdc02975f68c1 (diff)
downloadLegacyCamera-41a2e9735136f372de95652d0828600282c8e967.zip
LegacyCamera-41a2e9735136f372de95652d0828600282c8e967.tar.gz
LegacyCamera-41a2e9735136f372de95652d0828600282c8e967.tar.bz2
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
-rwxr-xr-xjni/Android.mk1
-rw-r--r--jni/feature_mos/src/mosaic/ImageUtils.cpp45
-rw-r--r--jni/feature_mos/src/mosaic/ImageUtils.h2
-rwxr-xr-xjni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.cpp382
-rwxr-xr-xjni/feature_mos/src/mosaic_renderer/SurfaceTextureRenderer.h77
-rwxr-xr-xjni/feature_mos/src/mosaic_renderer/WarpRenderer.cpp6
-rwxr-xr-xjni/feature_mos/src/mosaic_renderer/WarpRenderer.h4
-rw-r--r--jni/feature_mos_jni.cpp65
-rw-r--r--jni/mosaic_renderer_jni.cpp266
-rw-r--r--jni/mosaic_renderer_jni.h14
-rw-r--r--res/layout-w1024dp/pano_capture.xml49
-rw-r--r--res/layout/pano_capture.xml2
-rw-r--r--res/layout/pano_preview.xml4
-rw-r--r--src/com/android/camera/panorama/Mosaic.java14
-rw-r--r--src/com/android/camera/panorama/MosaicFrameProcessor.java21
-rw-r--r--src/com/android/camera/panorama/MosaicRenderer.java34
-rw-r--r--src/com/android/camera/panorama/MosaicRendererSurfaceView.java78
-rw-r--r--src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java67
-rw-r--r--src/com/android/camera/panorama/PanoramaActivity.java193
19 files changed, 1137 insertions, 187 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];
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pano_capture_layout" android:visibility="invisible"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <FrameLayout android:layout_weight="1" android:gravity="center"
+ android:layout_width="match_parent" android:layout_height="match_parent">
+ <com.android.camera.panorama.CaptureView
+ android:id="@+id/pano_capture_view" android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+ <com.android.camera.panorama.MosaicRendererSurfaceView
+ android:id="@+id/pano_renderer" android:layout_gravity="center"
+ android:layout_width="960dp" android:layout_height="360dp" />
+ </FrameLayout>
+
+ <ImageView style="@style/PanoViewHorizontalGrayBar"
+ android:layout_alignParentTop="true" android:layout_width="match_parent" />
+
+ <LinearLayout android:id="@+id/pano_capture_control"
+ style="@style/PanoViewHorizontalGrayBar"
+ android:gravity="right"
+ android:orientation="horizontal"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent">
+
+ <Button android:id="@+id/pano_capture_stop_button"
+ android:text="@string/pano_capture_stop"
+ android:onClick="onStopButtonClicked"
+ android:textSize="24dp"
+ android:layout_width="180dp"
+ android:layout_height="180dp" />
+ </LinearLayout>
+</RelativeLayout>
diff --git a/res/layout/pano_capture.xml b/res/layout/pano_capture.xml
index 0ce34b9..e95218a 100644
--- a/res/layout/pano_capture.xml
+++ b/res/layout/pano_capture.xml
@@ -26,7 +26,7 @@
android:layout_height="match_parent" />
<com.android.camera.panorama.MosaicRendererSurfaceView
android:id="@+id/pano_renderer" android:layout_gravity="center"
- android:layout_width="960dp" android:layout_height="360dp" />
+ android:layout_width="640dp" android:layout_height="240dp" />
</FrameLayout>
<ImageView style="@style/PanoViewHorizontalGrayBar"
diff --git a/res/layout/pano_preview.xml b/res/layout/pano_preview.xml
index 96ab4ff..a2ac86b 100644
--- a/res/layout/pano_preview.xml
+++ b/res/layout/pano_preview.xml
@@ -29,8 +29,8 @@
<SurfaceView
android:id="@+id/pano_preview_view"
android:layout_gravity="center"
- android:layout_width="240dp"
- android:layout_height="180dp"/>
+ android:layout_width="960dp"
+ android:layout_height="360dp"/>
</FrameLayout>
<include layout="@layout/pano_control" android:id="@+id/pano_control_layout" />
diff --git a/src/com/android/camera/panorama/Mosaic.java b/src/com/android/camera/panorama/Mosaic.java
index 5012ff4..80e8263 100644
--- a/src/com/android/camera/panorama/Mosaic.java
+++ b/src/com/android/camera/panorama/Mosaic.java
@@ -99,6 +99,20 @@ public class Mosaic {
public native float[] setSourceImage(byte[] pixels);
/**
+ * This is an alternative to the setSourceImage function above. This should
+ * be called when the image data is already on the native side in a fixed
+ * byte array. In implementation, this array is filled by the GL thread
+ * using glReadPixels directly from GPU memory (where it is accessed by
+ * an associated SurfaceTexture).
+ *
+ * @return Float array of length 10; first 9 entries correspond to the 3x3
+ * transformation matrix between the first frame and the passed frame,
+ * and the last entry is the number of the passed frame,
+ * where the counting starts from 1.
+ */
+ public native float[] setSourceImageFromGPU();
+
+ /**
* Set the type of blending.
*
* @param type the blending type defined in the class. {BLENDTYPE_FULL,
diff --git a/src/com/android/camera/panorama/MosaicFrameProcessor.java b/src/com/android/camera/panorama/MosaicFrameProcessor.java
index 6496f11..502d41f 100644
--- a/src/com/android/camera/panorama/MosaicFrameProcessor.java
+++ b/src/com/android/camera/panorama/MosaicFrameProcessor.java
@@ -56,18 +56,20 @@ public class MosaicFrameProcessor {
private int mPreviewWidth;
private int mPreviewHeight;
private int mPreviewBufferSize;
+ private boolean mUseSurfaceTexture;
public interface ProgressListener {
public void onProgress(boolean isFinished, float translationRate,
int traversedAngleX, int traversedAngleY);
}
- public MosaicFrameProcessor(int sweepAngle, int previewWidth, int previewHeight, int bufSize) {
+ public MosaicFrameProcessor(int sweepAngle, int previewWidth, int previewHeight, int bufSize, boolean useSurfaceTexture) {
mMosaicer = new Mosaic();
mCompassThreshold = sweepAngle;
mPreviewWidth = previewWidth;
mPreviewHeight = previewHeight;
mPreviewBufferSize = bufSize;
+ mUseSurfaceTexture = useSurfaceTexture;
}
public void setProgressListener(ProgressListener listener) {
@@ -75,7 +77,7 @@ public class MosaicFrameProcessor {
}
public void initialize() {
- setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize);
+ setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize, mUseSurfaceTexture);
reset();
}
@@ -87,8 +89,7 @@ public class MosaicFrameProcessor {
}
}
-
- private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) {
+ private void setupMosaicer(int previewWidth, int previewHeight, int bufSize, boolean useSurfaceTexture) {
Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize);
mMosaicer.allocateMosaicMemory(previewWidth, previewHeight);
@@ -133,11 +134,14 @@ public class MosaicFrameProcessor {
}
long t1 = System.currentTimeMillis();
mFrameTimestamp[mFillIn] = t1;
- System.arraycopy(data, 0, mFrames[mFillIn], 0, data.length);
+
+ // TODO: Remove the case of byte data copy when SurfaceTexture is ready
+ if(!mUseSurfaceTexture)
+ System.arraycopy(data, 0, mFrames[mFillIn], 0, data.length);
+
mCurrProcessFrameIdx = mFillIn;
mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER);
-
// Check that we are trying to process a frame different from the
// last one processed (useful if this class was running asynchronously)
if (mCurrProcessFrameIdx != mLastProcessFrameIdx) {
@@ -184,11 +188,12 @@ public class MosaicFrameProcessor {
float deltaTime = (now - mLastProcessedFrameTimestamp) / 1000.0f;
mLastProcessedFrameTimestamp = now;
- float[] frameData = mMosaicer.setSourceImage(data);
+ float[] frameData = mUseSurfaceTexture ?
+ mMosaicer.setSourceImageFromGPU() :
+ mMosaicer.setSourceImage(data);
mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX];
float translationCurrX = frameData[X_COORD_INDEX];
float translationCurrY = frameData[Y_COORD_INDEX];
-
mTranslationRate = Math.max(Math.abs(translationCurrX - mTranslationLastX),
Math.abs(translationCurrY - mTranslationLastY)) / deltaTime;
mTranslationLastX = translationCurrX;
diff --git a/src/com/android/camera/panorama/MosaicRenderer.java b/src/com/android/camera/panorama/MosaicRenderer.java
index 8db95c0..2bf471c 100644
--- a/src/com/android/camera/panorama/MosaicRenderer.java
+++ b/src/com/android/camera/panorama/MosaicRenderer.java
@@ -30,9 +30,12 @@ public class MosaicRenderer
/**
* Function to be called in onSurfaceCreated() to initialize
* the GL context, load and link the shaders and create the
- * program.
+ * program. Returns a texture ID to be used for SurfaceTexture.
+ *
+ * @return textureID the texture ID of the newly generated texture to
+ * be assigned to the SurfaceTexture object.
*/
- public static native void init();
+ public static native int init();
/**
* Pass the drawing surface's width and height to initialize the
@@ -44,6 +47,23 @@ public class MosaicRenderer
public static native void reset(int width, int height);
/**
+ * Calling this function will render the SurfaceTexture to a new 2D texture
+ * using the provided STMatrix and then call glReadPixels to fill the data
+ * array that will be processed by the mosaicing thread.
+ *
+ * @param stMatrix texture coordinate transform matrix obtained from the
+ * Surface texture
+ */
+ public static native void preprocess(float[] stMatrix);
+
+ /**
+ * This function calls glReadPixels to transfer both the low-res and high-res
+ * data from the GPU memory to the CPU memory for further processing by the
+ * mosaicing library.
+ */
+ public static native void transferGPUtoCPU();
+
+ /**
* Function to be called in onDrawFrame() to update the screen with
* the new frame data.
*/
@@ -59,9 +79,11 @@ public class MosaicRenderer
/**
* This function allows toggling between showing the input image data
- * (without applying any warp) and the warped image data. This is more
- * for debugging purposes to see if the image data is being updated
- * correctly or not.
+ * (without applying any warp) and the warped image data. For running
+ * the renderer as a viewfinder, we set the flag to false. To see the
+ * preview mosaic, we set the flag to true.
+ *
+ * @param flag boolean flag to set the warping to true or false.
*/
- public static native void togglewarping();
+ public static native void setWarping(boolean flag);
}
diff --git a/src/com/android/camera/panorama/MosaicRendererSurfaceView.java b/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
index 3220b22..6767c87 100644
--- a/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
+++ b/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
@@ -21,9 +21,12 @@ import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
+import android.app.Activity;
import android.content.Context;
import android.graphics.PixelFormat;
+import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView;
+import android.os.ConditionVariable;
import android.util.AttributeSet;
import android.util.Log;
@@ -31,6 +34,7 @@ public class MosaicRendererSurfaceView extends GLSurfaceView {
private static String TAG = "MosaicRendererSurfaceView";
private static final boolean DEBUG = false;
private MosaicRendererSurfaceViewRenderer mRenderer;
+ private ConditionVariable mPreviewFrameReadyForProcessing;
public MosaicRendererSurfaceView(Context context) {
super(context);
@@ -50,7 +54,6 @@ public class MosaicRendererSurfaceView extends GLSurfaceView {
setZOrderMediaOverlay(true);
}
-
private void init(boolean translucent, int depth, int stencil) {
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
@@ -80,6 +83,7 @@ public class MosaicRendererSurfaceView extends GLSurfaceView {
mRenderer = new MosaicRendererSurfaceViewRenderer();
setRenderer(mRenderer);
setRenderMode(RENDERMODE_WHEN_DIRTY);
+ mPreviewFrameReadyForProcessing = new ConditionVariable();
}
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
@@ -298,6 +302,21 @@ public class MosaicRendererSurfaceView extends GLSurfaceView {
private int[] mValue = new int[1];
}
+ public void lockPreviewReadyFlag()
+ {
+ mPreviewFrameReadyForProcessing.close();
+ }
+
+ private void unlockPreviewReadyFlag()
+ {
+ mPreviewFrameReadyForProcessing.open();
+ }
+
+ public void waitUntilPreviewReady()
+ {
+ mPreviewFrameReadyForProcessing.block();
+ }
+
public void setReady()
{
queueEvent(new Runnable() {
@@ -309,14 +328,67 @@ public class MosaicRendererSurfaceView extends GLSurfaceView {
});
}
- public void toggleWarping()
+ public void preprocess()
+ {
+ queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mRenderer.preprocess();
+ }
+ });
+ }
+
+ public void transferGPUtoCPU()
+ {
+ queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mRenderer.transferGPUtoCPU();
+ unlockPreviewReadyFlag();
+ }
+ });
+ }
+
+ public void setUIObject(final Activity activity)
{
queueEvent(new Runnable() {
@Override
public void run() {
- mRenderer.toggleWarping();
+ mRenderer.setUIObject(activity);
}
});
}
+
+ public void setWarping(final boolean flag)
+ {
+ queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mRenderer.setWarping(flag);
+ }
+ });
+ }
+
+ public int getTextureID() {
+ return mRenderer.getTextureID();
+ }
+
+ public void setSurfaceTexture(SurfaceTexture surface) {
+ mRenderer.setSurfaceTexture(surface);
+ }
+
+ public void updateSurfaceTexture() {
+ queueEvent(new Runnable() {
+
+ @Override
+ public void run() {
+ mRenderer.updateSurfaceTexture();
+ }
+ });
+ }
+
}
diff --git a/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java b/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
index a27d4be..d29765a 100644
--- a/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
+++ b/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
@@ -19,37 +19,78 @@ package com.android.camera.panorama;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
+import android.app.Activity;
+import android.graphics.SurfaceTexture;
import android.opengl.GLSurfaceView;
-
+import android.util.Log;
public class MosaicRendererSurfaceViewRenderer implements GLSurfaceView.Renderer
{
@Override
- public void onDrawFrame(GL10 gl)
- {
+ public void onDrawFrame(GL10 gl) {
MosaicRenderer.step();
}
@Override
- public void onSurfaceChanged(GL10 gl, int width, int height)
- {
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ Log.i(TAG, "Renderer: onSurfaceChanged");
MosaicRenderer.reset(width, height);
+ Log.i(TAG, "Renderer: onSurfaceChanged");
}
@Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config)
- {
- MosaicRenderer.init();
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ mTextureID = MosaicRenderer.init();
+
+ mActivity.runOnUiThread(new Runnable() {
+
+ @Override
+ public void run() {
+ mActivity.createSurfaceTextureAndStartPreview(mTextureID);
+ setSurfaceTexture(mActivity.getSurfaceTexture());
+ }
+ });
}
- public void setReady()
- {
+ public void setReady() {
MosaicRenderer.ready();
}
- public void toggleWarping()
- {
- MosaicRenderer.togglewarping();
+ public void preprocess() {
+ MosaicRenderer.preprocess(mSTMatrix);
+ }
+
+ public void transferGPUtoCPU() {
+ MosaicRenderer.transferGPUtoCPU();
+ }
+
+ public void setWarping(boolean flag) {
+ MosaicRenderer.setWarping(flag);
+ }
+
+ public void updateSurfaceTexture() {
+ mSurface.updateTexImage();
+ mSurface.getTransformMatrix(mSTMatrix);
}
+ public void setUIObject(Activity activity) {
+ mActivity = (PanoramaActivity)activity;
+ }
+
+ public int getTextureID() {
+ return mTextureID;
+ }
+
+ public void setSurfaceTexture(SurfaceTexture surface) {
+ mSurface = surface;
+ }
+
+ private float[] mSTMatrix = new float[16];
+ private int mTextureID;
+
+ private PanoramaActivity mActivity;
+
+ private static String TAG = "MosaicRendererSurfaceViewRenderer";
+
+ private SurfaceTexture mSurface;
}
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 0b1ec60..d9645ff 100644
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -33,19 +33,19 @@ import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PixelFormat;
import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.graphics.YuvImage;
import android.hardware.Camera;
-import android.hardware.Camera.Parameters;
-import android.hardware.Camera.Size;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
+import android.hardware.Camera.Parameters;
+import android.hardware.Camera.Size;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
@@ -58,7 +58,8 @@ import java.util.List;
* Activity to handle panorama capturing.
*/
public class PanoramaActivity extends Activity implements
- ModePicker.OnModeChangeListener, SurfaceHolder.Callback {
+ ModePicker.OnModeChangeListener,
+ SurfaceTexture.OnFrameAvailableListener {
public static final int DEFAULT_SWEEP_ANGLE = 60;
public static final int DEFAULT_BLEND_MODE = Mosaic.BLENDTYPE_HORIZONTAL;
public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720;
@@ -69,6 +70,9 @@ public class PanoramaActivity extends Activity implements
private static final String TAG = "PanoramaActivity";
private static final int PREVIEW_STOPPED = 0;
private static final int PREVIEW_ACTIVE = 1;
+ private static final int CAPTURE_VIEWFINDER = 0;
+ private static final int CAPTURE_MOSAIC = 1;
+
// Ratio of nanosecond to second
private static final float NS2S = 1.0f / 1000000000.0f;
@@ -89,6 +93,7 @@ public class PanoramaActivity extends Activity implements
private int mPreviewHeight;
private Camera mCameraDevice;
private int mCameraState;
+ private int mCaptureState;
private SensorManager mSensorManager;
private Sensor mSensor;
private ModePicker mModePicker;
@@ -96,7 +101,8 @@ public class PanoramaActivity extends Activity implements
private String mCurrentImagePath = null;
private long mTimeTaken;
private Handler mMainHandler;
- private SurfaceHolder mSurfaceHolder;
+ private SurfaceTexture mSurface;
+ private boolean mUseSurfaceTexture = true;
private boolean mThreadRunning;
@@ -133,6 +139,22 @@ public class PanoramaActivity extends Activity implements
};
}
+ public void createSurfaceTextureAndStartPreview(int textureID)
+ {
+ /*
+ * Create the SurfaceTexture that will feed this textureID, and pass it to the camera
+ */
+ mSurface = new SurfaceTexture(textureID);
+ mSurface.setOnFrameAvailableListener(this);
+ startPreview();
+ Log.i(TAG, "Created Surface Texture");
+ }
+
+ public SurfaceTexture getSurfaceTexture()
+ {
+ return mSurface;
+ }
+
private void setupCamera() {
openCamera();
Parameters parameters = mCameraDevice.getParameters();
@@ -223,14 +245,16 @@ public class PanoramaActivity extends Activity implements
CameraHolder.instance().getBackCameraId());
mCameraDevice.setDisplayOrientation(orientation);
- int bufSize = getPreviewBufSize();
- Log.v(TAG, "BufSize = " + bufSize);
- for (int i = 0; i < 10; i++) {
- try {
- mCameraDevice.addCallbackBuffer(new byte[bufSize]);
- } catch (OutOfMemoryError e) {
- Log.v(TAG, "Buffer allocation failed: buffer " + i);
- break;
+ if(!mUseSurfaceTexture) {
+ int bufSize = getPreviewBufSize();
+ Log.v(TAG, "BufSize = " + bufSize);
+ for (int i = 0; i < 10; i++) {
+ try {
+ mCameraDevice.addCallbackBuffer(new byte[bufSize]);
+ } catch (OutOfMemoryError e) {
+ Log.v(TAG, "Buffer allocation failed: buffer " + i);
+ break;
+ }
}
}
}
@@ -252,9 +276,51 @@ public class PanoramaActivity extends Activity implements
}
}
+ public void runViewFinder() {
+ mRealTimeMosaicView.setWarping(false);
+
+ // First update the surface texture...
+ mRealTimeMosaicView.updateSurfaceTexture();
+ // ...then call preprocess to render it to low-res and high-res RGB textures
+ mRealTimeMosaicView.preprocess();
+
+ mRealTimeMosaicView.setReady();
+ mRealTimeMosaicView.requestRender();
+ }
+
+ public void runMosaicCapture() {
+ mRealTimeMosaicView.setWarping(true);
+
+ // Lock the condition variable
+ mRealTimeMosaicView.lockPreviewReadyFlag();
+ // First update the surface texture...
+ mRealTimeMosaicView.updateSurfaceTexture();
+ // ...then call preprocess to render it to low-res and high-res RGB textures
+ mRealTimeMosaicView.preprocess();
+ // Now, transfer the textures from GPU to CPU memory for processing
+ mRealTimeMosaicView.transferGPUtoCPU();
+ // Wait on the condition variable (will be opened when GPU->CPU transfer is done).
+ mRealTimeMosaicView.waitUntilPreviewReady();
+
+ mMosaicFrameProcessor.processFrame(null);
+ }
+
+ synchronized public void onFrameAvailable(SurfaceTexture surface) {
+ /* For simplicity, SurfaceTexture calls here when it has new
+ * data available. Call may come in from some random thread,
+ * so let's be safe and use synchronize. No OpenGL calls can be done here.
+ */
+ if (mCaptureState == CAPTURE_VIEWFINDER) {
+ runViewFinder();
+ } else {
+ runMosaicCapture();
+ }
+ }
+
public void startCapture() {
// Reset values so we can do this again.
mTimeTaken = System.currentTimeMillis();
+ mCaptureState = CAPTURE_MOSAIC;
mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() {
@Override
@@ -268,26 +334,38 @@ public class PanoramaActivity extends Activity implements
}
});
- // Preview callback used whenever new viewfinder frame is available
- mCameraDevice.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
- @Override
- public void onPreviewFrame(final byte[] data, Camera camera) {
- mMosaicFrameProcessor.processFrame(data);
- // The returned buffer needs be added back to callback buffer
- // again.
- camera.addCallbackBuffer(data);
- }
- });
+ if (!mUseSurfaceTexture) {
+ // Preview callback used whenever new viewfinder frame is available
+ mCameraDevice.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
+ @Override
+ public void onPreviewFrame(final byte[] data, Camera camera) {
+ mMosaicFrameProcessor.processFrame(data);
+ // The returned buffer needs be added back to callback buffer
+ // again.
+ camera.addCallbackBuffer(data);
+ }
+ });
+ }
mCaptureLayout.setVisibility(View.VISIBLE);
mPreview.setVisibility(View.INVISIBLE); // will be re-used, invisible is better than gone.
mRealTimeMosaicView.setVisibility(View.VISIBLE);
mPanoControlLayout.setVisibility(View.GONE);
+
}
private void stopCapture() {
+ mCaptureState = CAPTURE_VIEWFINDER;
+
mMosaicFrameProcessor.setProgressListener(null);
stopPreview();
+
+ if (!mUseSurfaceTexture) {
+ mCameraDevice.setPreviewCallbackWithBuffer(null);
+ }
+
+ mSurface.setOnFrameAvailableListener(null);
+
// TODO: show some dialog for long computation.
if (!mThreadRunning) {
mThreadRunning = true;
@@ -322,17 +400,19 @@ public class PanoramaActivity extends Activity implements
private void createContentView() {
setContentView(R.layout.panorama);
+ mCaptureState = CAPTURE_VIEWFINDER;
+
mCaptureLayout = (View) findViewById(R.id.pano_capture_layout);
mReviewLayout = (View) findViewById(R.id.pano_review_layout);
mPreview = (SurfaceView) findViewById(R.id.pano_preview_view);
- mPreview.getHolder().addCallback(this);
+
mCaptureView = (CaptureView) findViewById(R.id.pano_capture_view);
mCaptureView.setStartAngle(-DEFAULT_SWEEP_ANGLE / 2);
mReview = (ImageView) findViewById(R.id.pano_reviewarea);
mRealTimeMosaicView = (MosaicRendererSurfaceView) findViewById(R.id.pano_renderer);
- mRealTimeMosaicView.setVisibility(View.GONE);
+ mRealTimeMosaicView.setUIObject(this);
mShutterButton = (ShutterButton) findViewById(R.id.pano_shutter_button);
mShutterButton.setOnClickListener(new View.OnClickListener() {
@@ -349,6 +429,8 @@ public class PanoramaActivity extends Activity implements
mModePicker.setVisibility(View.VISIBLE);
mModePicker.setOnModeChangeListener(this);
mModePicker.setCurrentMode(ModePicker.MODE_PANORAMA);
+
+ mRealTimeMosaicView.setVisibility(View.VISIBLE);
}
@OnClickAttr
@@ -377,13 +459,19 @@ public class PanoramaActivity extends Activity implements
}
private void resetToPreview() {
+ mCaptureState = CAPTURE_VIEWFINDER;
+
+ mReviewLayout.setVisibility(View.GONE);
mPreview.setVisibility(View.VISIBLE);
mPanoControlLayout.setVisibility(View.VISIBLE);
- mRealTimeMosaicView.setVisibility(View.GONE);
mCaptureLayout.setVisibility(View.GONE);
- mReviewLayout.setVisibility(View.GONE);
mMosaicFrameProcessor.reset();
+
+ mSurface.setOnFrameAvailableListener(this);
+
if (!mPausing) startPreview();
+
+ mRealTimeMosaicView.setVisibility(View.VISIBLE);
}
private void showFinalMosaic(Bitmap bitmap) {
@@ -394,6 +482,7 @@ public class PanoramaActivity extends Activity implements
mReviewLayout.setVisibility(View.VISIBLE);
mCaptureView.setStatusText("");
mCaptureView.setSweepAngle(0);
+ mCaptureView.invalidate();
}
}
@@ -416,7 +505,7 @@ public class PanoramaActivity extends Activity implements
if (mMosaicFrameProcessor == null) {
// Start the activity for the first time.
mMosaicFrameProcessor = new MosaicFrameProcessor(DEFAULT_SWEEP_ANGLE - 5,
- mPreviewWidth, mPreviewHeight, getPreviewBufSize());
+ mPreviewWidth, mPreviewHeight, getPreviewBufSize(), mUseSurfaceTexture);
}
mMosaicFrameProcessor.initialize();
}
@@ -424,10 +513,12 @@ public class PanoramaActivity extends Activity implements
@Override
protected void onPause() {
super.onPause();
+ mSurface.setOnFrameAvailableListener(null);
releaseCamera();
mPausing = true;
- mCaptureView.onPause();
+
mRealTimeMosaicView.onPause();
+ mCaptureView.onPause();
mSensorManager.unregisterListener(mListener);
clearMosaicFrameProcessorIfNeeded();
System.gc();
@@ -446,12 +537,11 @@ public class PanoramaActivity extends Activity implements
* resources.
*/
mSensorManager.registerListener(mListener, mSensor, SensorManager.SENSOR_DELAY_UI);
-
+ mCaptureState = CAPTURE_VIEWFINDER;
setupCamera();
// Camera must be initialized before MosaicFrameProcessor is initialized. The preview size
// has to be decided by camera device.
initMosaicFrameProcessorIfNeeded();
- startPreview();
mCaptureView.onResume();
mRealTimeMosaicView.onResume();
}
@@ -526,45 +616,12 @@ public class PanoramaActivity extends Activity implements
System.gc();
}
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
- mSurfaceHolder = holder;
-
- if (mCameraDevice == null) return;
-
- // Set preview display if the surface is being created. Preview was
- // already started. Also restart the preview if display rotation has
- // changed. Sometimes this happens when the device is held in portrait
- // and camera app is opened. Rotation animation takes some time and
- // display rotation in onCreate may not be what we want.
- if (holder.isCreating()) {
- // Set preview display if the surface is being created and preview
- // was already started. That means preview display was set to null
- // and we need to set it now.
- setPreviewDisplay(holder);
- } else {
- // 1. Restart the preview if the size of surface was changed. The
- // framework may not support changing preview display on the fly.
- // 2. Start the preview now if surface was destroyed and preview
- // stopped.
- startPreview();
- }
- }
-
- private void setPreviewDisplay(SurfaceHolder holder) {
+ private void setPreviewTexture(SurfaceTexture surface) {
try {
- mCameraDevice.setPreviewDisplay(holder);
+ mCameraDevice.setPreviewTexture(surface);
} catch (Throwable ex) {
releaseCamera();
- throw new RuntimeException("setPreviewDisplay failed", ex);
+ throw new RuntimeException("setPreviewTexture failed", ex);
}
}
@@ -573,7 +630,7 @@ public class PanoramaActivity extends Activity implements
// the screen).
if (mCameraState != PREVIEW_STOPPED) stopPreview();
- setPreviewDisplay(mSurfaceHolder);
+ setPreviewTexture(mSurface);
try {
Log.v(TAG, "startPreview");