summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");