summaryrefslogtreecommitdiffstats
path: root/jni/mosaic_renderer_jni.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'jni/mosaic_renderer_jni.cpp')
-rw-r--r--jni/mosaic_renderer_jni.cpp804
1 files changed, 804 insertions, 0 deletions
diff --git a/jni/mosaic_renderer_jni.cpp b/jni/mosaic_renderer_jni.cpp
new file mode 100644
index 000000000..36f8064c7
--- /dev/null
+++ b/jni/mosaic_renderer_jni.cpp
@@ -0,0 +1,804 @@
+/*
+ * 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.
+ */
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <jni.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "db_utilities_camera.h"
+#include "mosaic/ImageUtils.h"
+#include "mosaic_renderer/FrameBuffer.h"
+#include "mosaic_renderer/WarpRenderer.h"
+#include "mosaic_renderer/SurfaceTextureRenderer.h"
+#include "mosaic_renderer/YVURenderer.h"
+
+#include "mosaic/Log.h"
+#define LOG_TAG "MosaicRenderer"
+
+#include "mosaic_renderer_jni.h"
+
+// Texture handle
+GLuint gSurfaceTextureID[1];
+
+bool gWarpImage = true;
+
+// Low-Res input image frame in YUVA format for preview rendering and processing
+// and high-res YUVA input image for processing.
+unsigned char* gPreviewImage[NR];
+// Low-Res & high-res preview image width
+int gPreviewImageWidth[NR];
+// Low-Res & high-res preview image height
+int gPreviewImageHeight[NR];
+
+// Semaphore to protect simultaneous read/writes from gPreviewImage
+sem_t gPreviewImage_semaphore;
+
+// Off-screen preview FBO width (large enough to store the entire
+// preview mosaic). FBO is frame buffer object.
+int gPreviewFBOWidth;
+// Off-screen preview FBO height (large enough to store the entire
+// preview mosaic).
+int gPreviewFBOHeight;
+
+// gK is the transformation to map the canonical {-1,1} vertex coordinate system
+// to the {0,gPreviewImageWidth[LR]} input image frame coordinate system before
+// applying the given affine transformation trs. gKm is the corresponding
+// transformation for going to the {0,gPreviewFBOWidth}.
+double gK[9];
+double gKinv[9];
+double gKm[9];
+double gKminv[9];
+
+// 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 convert RGBA textures into YVU textures for processing
+YVURenderer gYVURenderer[NR];
+// Off-screen FBOs to store the low-res and high-res YVU textures for processing
+FrameBuffer gBufferInputYVU[NR];
+
+// Shader to translate the flip-flop FBO - gBuffer[1-current] -> gBuffer[current]
+WarpRenderer gWarper1;
+// Shader to add warped current frame to the flip-flop FBO - gBuffer[current]
+WarpRenderer gWarper2;
+// Off-screen FBOs (flip-flop) to store the result of gWarper1 & gWarper2
+FrameBuffer gBuffer[2];
+
+// Shader to warp and render the preview FBO to the screen
+WarpRenderer gPreview;
+
+// Index of the gBuffer FBO gWarper1 is going to write into
+int gCurrentFBOIndex = 0;
+
+// 3x3 Matrices holding the transformation of this frame (gThisH1t) and of
+// the last frame (gLastH1t) w.r.t the first frame.
+double gThisH1t[9];
+double gLastH1t[9];
+
+// Variables to represent the fixed position of the top-left corner of the
+// current frame in the previewFBO
+double gCenterOffsetX = 0.0f;
+double gCenterOffsetY = 0.0f;
+
+// X-Offset of the viewfinder (current frame) w.r.t
+// (gCenterOffsetX, gCenterOffsetY). This offset varies with time and is
+// used to pan the viewfinder across the UI layout.
+double gPanOffset = 0.0f;
+
+// Variables tracking the translation value for the current frame and the
+// last frame (both w.r.t the first frame). The difference between these
+// values is used to control the panning speed of the viewfinder display
+// on the UI screen.
+double gThisTx = 0.0f;
+double gLastTx = 0.0f;
+
+// These are the scale factors used by the gPreview shader to ensure that
+// the image frame is correctly scaled to the full UI layout height while
+// maintaining its aspect ratio
+double gUILayoutScalingX = 1.0f;
+double gUILayoutScalingY = 1.0f;
+
+// Whether the view that we will render preview FBO onto is in landscape or portrait
+// orientation.
+bool gIsLandscapeOrientation = true;
+
+// State of the viewfinder. Set to false when the viewfinder hits the UI edge.
+bool gPanViewfinder = true;
+
+// Affine transformation in GL 4x4 format (column-major) to warp the
+// last frame mosaic into the current frame coordinate system.
+GLfloat g_dAffinetransGL[16];
+double g_dAffinetrans[16];
+
+// Affine transformation in GL 4x4 format (column-major) to translate the
+// preview FBO across the screen (viewfinder panning).
+GLfloat g_dAffinetransPanGL[16];
+double g_dAffinetransPan[16];
+
+// XY translation in GL 4x4 format (column-major) to center the current
+// preview mosaic in the preview FBO
+GLfloat g_dTranslationToFBOCenterGL[16];
+double g_dTranslationToFBOCenter[16];
+
+// GL 4x4 Identity transformation
+GLfloat g_dAffinetransIdentGL[] = {
+ 1., 0., 0., 0.,
+ 0., 1., 0., 0.,
+ 0., 0., 1., 0.,
+ 0., 0., 0., 1.};
+
+// GL 4x4 Rotation transformation (column-majored): 90 degree
+GLfloat g_dAffinetransRotation90GL[] = {
+ 0., 1., 0., 0.,
+ -1., 0., 0., 0.,
+ 0., 0., 1., 0.,
+ 0., 0., 0., 1.};
+
+// 3x3 Rotation transformation (row-majored): 90 degree
+double gRotation90[] = {
+ 0., -1., 0.,
+ 1., 0., 0.,
+ 0., 0., 1.,};
+
+
+float g_dIdent3x3[] = {
+ 1.0, 0.0, 0.0,
+ 0.0, 1.0, 0.0,
+ 0.0, 0.0, 1.0};
+
+const int GL_TEXTURE_EXTERNAL_OES_ENUM = 0x8D65;
+
+static void printGLString(const char *name, GLenum s) {
+ const char *v = (const char *) glGetString(s);
+ LOGI("GL %s = %s", name, v);
+}
+
+void checkFramebufferStatus(const char* name) {
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status == 0) {
+ LOGE("Checking completeness of Framebuffer:%s", name);
+ checkGlError("checkFramebufferStatus (is the target \"GL_FRAMEBUFFER\"?)");
+ } else if (status != GL_FRAMEBUFFER_COMPLETE) {
+ const char* msg = "not listed";
+ switch (status) {
+ case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: msg = "attachment"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: msg = "dimensions"; break;
+ case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: msg = "missing attachment"; break;
+ case GL_FRAMEBUFFER_UNSUPPORTED: msg = "unsupported"; break;
+ }
+ LOGE("Framebuffer: %s is INCOMPLETE: %s, %x", name, msg, status);
+ }
+}
+
+// @return false if there was an error
+bool checkGLErrorDetail(const char* file, int line, const char* op) {
+ GLint error = glGetError();
+ const char* err_msg = "NOT_LISTED";
+ if (error != 0) {
+ switch (error) {
+ case GL_INVALID_VALUE: err_msg = "NOT_LISTED_YET"; break;
+ case GL_INVALID_OPERATION: err_msg = "INVALID_OPERATION"; break;
+ case GL_INVALID_ENUM: err_msg = "INVALID_ENUM"; break;
+ }
+ LOGE("Error after %s(). glError: %s (0x%x) in line %d of %s", op, err_msg, error, line, file);
+ return false;
+ }
+ return true;
+}
+
+void bindSurfaceTexture(GLuint texId)
+{
+ 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 ClearPreviewImage(int mID)
+{
+ unsigned char* ptr = gPreviewImage[mID];
+ for(int j = 0, i = 0;
+ j < gPreviewImageWidth[mID] * gPreviewImageHeight[mID] * 4;
+ j += 4)
+ {
+ ptr[i++] = 0;
+ ptr[i++] = 0;
+ ptr[i++] = 0;
+ ptr[i++] = 255;
+ }
+
+}
+
+void ConvertAffine3x3toGL4x4(double *matGL44, double *mat33)
+{
+ matGL44[0] = mat33[0];
+ matGL44[1] = mat33[3];
+ matGL44[2] = 0.0;
+ matGL44[3] = mat33[6];
+
+ matGL44[4] = mat33[1];
+ matGL44[5] = mat33[4];
+ matGL44[6] = 0.0;
+ matGL44[7] = mat33[7];
+
+ matGL44[8] = 0;
+ matGL44[9] = 0;
+ matGL44[10] = 1.0;
+ matGL44[11] = 0.0;
+
+ matGL44[12] = mat33[2];
+ matGL44[13] = mat33[5];
+ matGL44[14] = 0.0;
+ matGL44[15] = mat33[8];
+}
+
+bool continuePanningFBO(double panOffset) {
+ double normalizedScreenLimitLeft = -1.0 + VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
+ double normalizedScreenLimitRight = 1.0 - VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
+ double normalizedXPositionOnScreenLeft;
+ double normalizedXPositionOnScreenRight;
+
+ // Compute the position of the current frame in the screen coordinate system
+ if (gIsLandscapeOrientation) {
+ normalizedXPositionOnScreenLeft = (2.0 *
+ (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) *
+ gUILayoutScalingX;
+ normalizedXPositionOnScreenRight = (2.0 *
+ ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) /
+ gPreviewFBOWidth - 1.0) * gUILayoutScalingX;
+ } else {
+ normalizedXPositionOnScreenLeft = (2.0 *
+ (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) *
+ gUILayoutScalingY;
+ normalizedXPositionOnScreenRight = (2.0 *
+ ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) /
+ gPreviewFBOWidth - 1.0) * gUILayoutScalingY;
+ }
+
+ // Stop the viewfinder panning if we hit the maximum border allowed for
+ // this UI layout
+ if (normalizedXPositionOnScreenRight > normalizedScreenLimitRight ||
+ normalizedXPositionOnScreenLeft < normalizedScreenLimitLeft) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+// This function computes fills the 4x4 matrices g_dAffinetrans,
+// and g_dAffinetransPan using the specified 3x3 affine
+// transformation between the first captured frame and the current frame.
+// The computed g_dAffinetrans is such that it warps the preview mosaic in
+// the last frame's coordinate system into the coordinate system of the
+// current frame. Thus, applying this transformation will create the current
+// frame mosaic but with the current frame missing. This frame will then be
+// pasted in by gWarper2 after translating it by g_dTranslationToFBOCenter.
+// The computed g_dAffinetransPan is such that it offsets the computed preview
+// mosaic horizontally to make the viewfinder pan within the UI layout.
+void UpdateWarpTransformation(float *trs)
+{
+ double H[9], Hp[9], Htemp1[9], Htemp2[9], T[9];
+
+ for(int i = 0; i < 9; i++)
+ {
+ gThisH1t[i] = trs[i];
+ }
+
+ // Alignment is done based on low-res data.
+ // To render the preview mosaic, the translation of the high-res mosaic is estimated to
+ // H2L_FACTOR x low-res-based tranlation.
+ gThisH1t[2] *= H2L_FACTOR;
+ gThisH1t[5] *= H2L_FACTOR;
+
+ db_Identity3x3(T);
+ T[2] = -gCenterOffsetX;
+ T[5] = -gCenterOffsetY;
+
+ // H = ( inv(gThisH1t) * gLastH1t ) * T
+ db_Identity3x3(Htemp1);
+ db_Identity3x3(Htemp2);
+ db_Identity3x3(H);
+ db_InvertAffineTransform(Htemp1, gThisH1t);
+ db_Multiply3x3_3x3(Htemp2, Htemp1, gLastH1t);
+ db_Multiply3x3_3x3(H, Htemp2, T);
+
+ for(int i = 0; i < 9; i++)
+ {
+ gLastH1t[i] = gThisH1t[i];
+ }
+
+ // Move the origin such that the frame is centered in the previewFBO
+ // i.e. H = inv(T) * H
+ H[2] += gCenterOffsetX;
+ H[5] += gCenterOffsetY;
+
+ // Hp = inv(Km) * H * Km
+ // Km moves the coordinate system from openGL to image pixels so
+ // that the alignment transform H can be applied to them.
+ // inv(Km) moves the coordinate system back to openGL normalized
+ // coordinates so that the shader can correctly render it.
+ db_Identity3x3(Htemp1);
+ db_Multiply3x3_3x3(Htemp1, H, gKm);
+ db_Multiply3x3_3x3(Hp, gKminv, Htemp1);
+
+ ConvertAffine3x3toGL4x4(g_dAffinetrans, Hp);
+
+ ////////////////////////////////////////////////
+ ////// Compute g_dAffinetransPan now... //////
+ ////////////////////////////////////////////////
+
+ gThisTx = trs[2];
+
+ if(gPanViewfinder)
+ {
+ gPanOffset += (gThisTx - gLastTx) * VIEWFINDER_PAN_FACTOR_HORZ;
+ }
+
+ gLastTx = gThisTx;
+ gPanViewfinder = continuePanningFBO(gPanOffset);
+
+ db_Identity3x3(H);
+ H[2] = gPanOffset;
+
+ // Hp = inv(Km) * H * Km
+ db_Identity3x3(Htemp1);
+ db_Multiply3x3_3x3(Htemp1, H, gKm);
+ db_Multiply3x3_3x3(Hp, gKminv, Htemp1);
+
+ if (gIsLandscapeOrientation) {
+ ConvertAffine3x3toGL4x4(g_dAffinetransPan, Hp);
+ } else {
+ // rotate Hp by 90 degress.
+ db_Multiply3x3_3x3(Htemp1, gRotation90, Hp);
+ ConvertAffine3x3toGL4x4(g_dAffinetransPan, Htemp1);
+ }
+}
+
+void AllocateTextureMemory(int widthHR, int heightHR, int widthLR, int heightLR)
+{
+ gPreviewImageWidth[HR] = widthHR;
+ gPreviewImageHeight[HR] = heightHR;
+
+ gPreviewImageWidth[LR] = widthLR;
+ gPreviewImageHeight[LR] = heightLR;
+
+ sem_wait(&gPreviewImage_semaphore);
+ gPreviewImage[LR] = ImageUtils::allocateImage(gPreviewImageWidth[LR],
+ gPreviewImageHeight[LR], 4);
+ gPreviewImage[HR] = ImageUtils::allocateImage(gPreviewImageWidth[HR],
+ gPreviewImageHeight[HR], 4);
+ sem_post(&gPreviewImage_semaphore);
+
+ gPreviewFBOWidth = PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[HR];
+ gPreviewFBOHeight = PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[HR];
+
+ // The origin is such that the current frame will sit with its center
+ // at the center of the previewFBO
+ gCenterOffsetX = (gPreviewFBOWidth / 2 - gPreviewImageWidth[HR] / 2);
+ gCenterOffsetY = (gPreviewFBOHeight / 2 - gPreviewImageHeight[HR] / 2);
+
+ gPanOffset = 0.0f;
+
+ db_Identity3x3(gThisH1t);
+ db_Identity3x3(gLastH1t);
+
+ gPanViewfinder = true;
+
+ int w = gPreviewImageWidth[HR];
+ int h = gPreviewImageHeight[HR];
+
+ int wm = gPreviewFBOWidth;
+ int hm = gPreviewFBOHeight;
+
+ // K is the transformation to map the canonical [-1,1] vertex coordinate
+ // system to the [0,w] image coordinate system before applying the given
+ // affine transformation trs.
+ gKm[0] = wm / 2.0 - 0.5;
+ gKm[1] = 0.0;
+ gKm[2] = wm / 2.0 - 0.5;
+ gKm[3] = 0.0;
+ gKm[4] = hm / 2.0 - 0.5;
+ gKm[5] = hm / 2.0 - 0.5;
+ gKm[6] = 0.0;
+ gKm[7] = 0.0;
+ gKm[8] = 1.0;
+
+ gK[0] = w / 2.0 - 0.5;
+ gK[1] = 0.0;
+ gK[2] = w / 2.0 - 0.5;
+ gK[3] = 0.0;
+ gK[4] = h / 2.0 - 0.5;
+ gK[5] = h / 2.0 - 0.5;
+ gK[6] = 0.0;
+ gK[7] = 0.0;
+ gK[8] = 1.0;
+
+ db_Identity3x3(gKinv);
+ db_InvertCalibrationMatrix(gKinv, gK);
+
+ db_Identity3x3(gKminv);
+ db_InvertCalibrationMatrix(gKminv, gKm);
+
+ //////////////////////////////////////////
+ ////// Compute g_Translation now... //////
+ //////////////////////////////////////////
+ double T[9], Tp[9], Ttemp[9];
+
+ db_Identity3x3(T);
+ T[2] = gCenterOffsetX;
+ T[5] = gCenterOffsetY;
+
+ // Tp = inv(K) * T * K
+ db_Identity3x3(Ttemp);
+ db_Multiply3x3_3x3(Ttemp, T, gK);
+ db_Multiply3x3_3x3(Tp, gKinv, Ttemp);
+
+ ConvertAffine3x3toGL4x4(g_dTranslationToFBOCenter, Tp);
+
+ UpdateWarpTransformation(g_dIdent3x3);
+}
+
+void FreeTextureMemory()
+{
+ sem_wait(&gPreviewImage_semaphore);
+ ImageUtils::freeImage(gPreviewImage[LR]);
+ ImageUtils::freeImage(gPreviewImage[HR]);
+ sem_post(&gPreviewImage_semaphore);
+}
+
+extern "C"
+{
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
+ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
+ JNIEXPORT jint JNICALL Java_com_android_camera_MosaicRenderer_init(
+ JNIEnv * env, jobject obj);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_reset(
+ JNIEnv * env, jobject obj, jint width, jint height,
+ jboolean isLandscapeOrientation);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_preprocess(
+ JNIEnv * env, jobject obj, jfloatArray stMatrix);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_transferGPUtoCPU(
+ JNIEnv * env, jobject obj);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_step(
+ JNIEnv * env, jobject obj);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_updateMatrix(
+ JNIEnv * env, jobject obj);
+ JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_setWarping(
+ JNIEnv * env, jobject obj, jboolean flag);
+};
+
+
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
+{
+ sem_init(&gPreviewImage_semaphore, 0, 1);
+
+ return JNI_VERSION_1_4;
+}
+
+
+JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
+{
+ sem_destroy(&gPreviewImage_semaphore);
+}
+JNIEXPORT jint JNICALL Java_com_android_camera_MosaicRenderer_init(
+ JNIEnv * env, jobject obj)
+{
+ gSurfTexRenderer[LR].InitializeGLProgram();
+ gSurfTexRenderer[HR].InitializeGLProgram();
+ gYVURenderer[LR].InitializeGLProgram();
+ gYVURenderer[HR].InitializeGLProgram();
+ gWarper1.InitializeGLProgram();
+ gWarper2.InitializeGLProgram();
+ gPreview.InitializeGLProgram();
+ gBuffer[0].InitializeGLContext();
+ gBuffer[1].InitializeGLContext();
+ gBufferInput[LR].InitializeGLContext();
+ gBufferInput[HR].InitializeGLContext();
+ gBufferInputYVU[LR].InitializeGLContext();
+ gBufferInputYVU[HR].InitializeGLContext();
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glGenTextures(1, gSurfaceTextureID);
+ // bind the surface texture
+ bindSurfaceTexture(gSurfaceTextureID[0]);
+
+ return (jint) gSurfaceTextureID[0];
+}
+
+// width: the width of the view
+// height: the height of the view
+// isLandscape: whether the device is in landscape or portrait. Android
+// Compatibility Definition Document specifies that the long side of the
+// camera aligns with the long side of the screen.
+void calculateUILayoutScaling(int width, int height, bool isLandscape) {
+ if (isLandscape) {
+ // __________ ________
+ // | | => |________|
+ // |__________| => (View)
+ // (Preview FBO)
+ //
+ // Scale the preview FBO's height to the height of view and
+ // maintain the aspect ratio of the current frame on the screen.
+ gUILayoutScalingY = PREVIEW_FBO_HEIGHT_SCALE;
+
+ // Note that OpenGL scales a texture to view's width and height automatically.
+ // The "width / height" inverts the scaling, so as to maintain the aspect ratio
+ // of the current frame.
+ gUILayoutScalingX = ((float) gPreviewFBOWidth / gPreviewFBOHeight)
+ / ((float) width / height) * PREVIEW_FBO_HEIGHT_SCALE;
+ } else {
+ // ___
+ // __________ | | ______
+ // | | => | | => |______|
+ // |__________| => | | => (View)
+ // (Preview FBO) | |
+ // |___|
+ //
+ // Scale the preview FBO's height to the width of view and
+ // maintain the aspect ratio of the current frame on the screen.
+ // In preview, Java_com_android_camera_MosaicRenderer_step rotates the
+ // preview FBO by 90 degrees. In capture, UpdateWarpTransformation
+ // rotates the preview FBO.
+ gUILayoutScalingY = PREVIEW_FBO_WIDTH_SCALE;
+
+ // Note that OpenGL scales a texture to view's width and height automatically.
+ // The "height / width" inverts the scaling, so as to maintain the aspect ratio
+ // of the current frame.
+ gUILayoutScalingX = ((float) gPreviewFBOHeight / gPreviewFBOWidth)
+ / ((float) width / height) * PREVIEW_FBO_WIDTH_SCALE;
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_reset(
+ JNIEnv * env, jobject obj, jint width, jint height, jboolean isLandscapeOrientation)
+{
+ gIsLandscapeOrientation = isLandscapeOrientation;
+ calculateUILayoutScaling(width, height, gIsLandscapeOrientation);
+
+ gBuffer[0].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA);
+ gBuffer[1].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA);
+
+ gBufferInput[LR].Init(gPreviewImageWidth[LR],
+ gPreviewImageHeight[LR], GL_RGBA);
+
+ gBufferInput[HR].Init(gPreviewImageWidth[HR],
+ gPreviewImageHeight[HR], GL_RGBA);
+
+ gBufferInputYVU[LR].Init(gPreviewImageWidth[LR],
+ gPreviewImageHeight[LR], GL_RGBA);
+
+ gBufferInputYVU[HR].Init(gPreviewImageWidth[HR],
+ gPreviewImageHeight[HR], GL_RGBA);
+
+ // bind the surface texture
+ bindSurfaceTexture(gSurfaceTextureID[0]);
+
+ // To speed up, there is no need to clear the destination buffers
+ // (offscreen/screen buffers) of gSurfTexRenderer, gYVURenderer
+ // and gPreview because we always fill the whole destination buffers
+ // when we draw something to those offscreen/screen buffers.
+ gSurfTexRenderer[LR].SetupGraphics(&gBufferInput[LR]);
+ 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);
+
+ gSurfTexRenderer[HR].SetupGraphics(&gBufferInput[HR]);
+ 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);
+
+ gYVURenderer[LR].SetupGraphics(&gBufferInputYVU[LR]);
+ gYVURenderer[LR].SetInputTextureName(gBufferInput[LR].GetTextureName());
+ gYVURenderer[LR].SetInputTextureType(GL_TEXTURE_2D);
+
+ gYVURenderer[HR].SetupGraphics(&gBufferInputYVU[HR]);
+ gYVURenderer[HR].SetInputTextureName(gBufferInput[HR].GetTextureName());
+ gYVURenderer[HR].SetInputTextureType(GL_TEXTURE_2D);
+
+ // gBuffer[1-gCurrentFBOIndex] --> gWarper1 --> gBuffer[gCurrentFBOIndex]
+ gWarper1.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
+
+ // Clear the destination buffer of gWarper1.
+ gWarper1.Clear(0.0, 0.0, 0.0, 1.0);
+ gWarper1.SetViewportMatrix(1, 1, 1, 1);
+ gWarper1.SetScalingMatrix(1.0f, 1.0f);
+ gWarper1.SetInputTextureName(gBuffer[1 - gCurrentFBOIndex].GetTextureName());
+ gWarper1.SetInputTextureType(GL_TEXTURE_2D);
+
+ // gBufferInput[HR] --> gWarper2 --> gBuffer[gCurrentFBOIndex]
+ gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
+
+ // gWarp2's destination buffer is the same to gWarp1's. No need to clear it
+ // again.
+ gWarper2.SetViewportMatrix(gPreviewImageWidth[HR],
+ gPreviewImageHeight[HR], gBuffer[gCurrentFBOIndex].GetWidth(),
+ gBuffer[gCurrentFBOIndex].GetHeight());
+ gWarper2.SetScalingMatrix(1.0f, 1.0f);
+ gWarper2.SetInputTextureName(gBufferInput[HR].GetTextureName());
+ gWarper2.SetInputTextureType(GL_TEXTURE_2D);
+
+ // gBuffer[gCurrentFBOIndex] --> gPreview --> Screen
+ gPreview.SetupGraphics(width, height);
+ gPreview.SetViewportMatrix(1, 1, 1, 1);
+
+ // Scale the previewFBO so that the viewfinder window fills the layout height
+ // while maintaining the image aspect ratio
+ gPreview.SetScalingMatrix(gUILayoutScalingX, -1.0f * gUILayoutScalingY);
+ gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName());
+ gPreview.SetInputTextureType(GL_TEXTURE_2D);
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_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_dAffinetransIdentGL);
+ gSurfTexRenderer[HR].DrawTexture(g_dAffinetransIdentGL);
+}
+
+#ifndef now_ms
+#include <time.h>
+static double
+now_ms(void)
+{
+ //struct timespec res;
+ struct timeval res;
+ //clock_gettime(CLOCK_REALTIME, &res);
+ gettimeofday(&res, NULL);
+ return 1000.0*res.tv_sec + (double)res.tv_usec/1e3;
+}
+#endif
+
+
+
+JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_transferGPUtoCPU(
+ JNIEnv * env, jobject obj)
+{
+ double t0, t1, time_c;
+
+ gYVURenderer[LR].DrawTexture();
+ gYVURenderer[HR].DrawTexture();
+
+ sem_wait(&gPreviewImage_semaphore);
+ // Bind to the input LR FBO and read the Low-Res data from there...
+ glBindFramebuffer(GL_FRAMEBUFFER, gBufferInputYVU[LR].GetFrameBufferName());
+ t0 = now_ms();
+ glReadPixels(0,
+ 0,
+ gBufferInput[LR].GetWidth(),
+ gBufferInput[LR].GetHeight(),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ gPreviewImage[LR]);
+
+ checkGlError("glReadPixels LR (MosaicRenderer.transferGPUtoCPU())");
+
+ // Bind to the input HR FBO and read the high-res data from there...
+ glBindFramebuffer(GL_FRAMEBUFFER, gBufferInputYVU[HR].GetFrameBufferName());
+ t0 = now_ms();
+ glReadPixels(0,
+ 0,
+ gBufferInput[HR].GetWidth(),
+ gBufferInput[HR].GetHeight(),
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ gPreviewImage[HR]);
+
+ checkGlError("glReadPixels HR (MosaicRenderer.transferGPUtoCPU())");
+
+ sem_post(&gPreviewImage_semaphore);
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_step(
+ JNIEnv * env, jobject obj)
+{
+ if(!gWarpImage) // ViewFinder
+ {
+ gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
+ gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName());
+
+ gWarper2.DrawTexture(g_dTranslationToFBOCenterGL);
+
+ if (gIsLandscapeOrientation) {
+ gPreview.DrawTexture(g_dAffinetransIdentGL);
+ } else {
+ gPreview.DrawTexture(g_dAffinetransRotation90GL);
+ }
+ }
+ else
+ {
+ gWarper1.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
+ // Clear the destination so that we can paint on it afresh
+ gWarper1.Clear(0.0, 0.0, 0.0, 1.0);
+ gWarper1.SetInputTextureName(
+ gBuffer[1 - gCurrentFBOIndex].GetTextureName());
+ gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
+ gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName());
+
+ gWarper1.DrawTexture(g_dAffinetransGL);
+ gWarper2.DrawTexture(g_dTranslationToFBOCenterGL);
+ gPreview.DrawTexture(g_dAffinetransPanGL);
+
+ gCurrentFBOIndex = 1 - gCurrentFBOIndex;
+ }
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_setWarping(
+ JNIEnv * env, jobject obj, jboolean flag)
+{
+ // TODO: Review this logic
+ if(gWarpImage != (bool) flag) //switching from viewfinder to capture or vice-versa
+ {
+ // Clear gBuffer[0]
+ gWarper1.SetupGraphics(&gBuffer[0]);
+ gWarper1.Clear(0.0, 0.0, 0.0, 1.0);
+ // Clear gBuffer[1]
+ gWarper1.SetupGraphics(&gBuffer[1]);
+ gWarper1.Clear(0.0, 0.0, 0.0, 1.0);
+ // Clear the screen to black.
+ gPreview.Clear(0.0, 0.0, 0.0, 1.0);
+
+ gLastTx = 0.0f;
+ gPanOffset = 0.0f;
+ gPanViewfinder = true;
+
+ db_Identity3x3(gThisH1t);
+ db_Identity3x3(gLastH1t);
+ // Make sure g_dAffinetransGL and g_dAffinetransPanGL are updated.
+ // Otherwise, the first frame after setting the flag to true will be
+ // incorrectly drawn.
+ if ((bool) flag) {
+ UpdateWarpTransformation(g_dIdent3x3);
+ }
+ }
+
+ gWarpImage = (bool)flag;
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_updateMatrix(
+ JNIEnv * env, jobject obj)
+{
+ for(int i=0; i<16; i++)
+ {
+ g_dAffinetransGL[i] = g_dAffinetrans[i];
+ g_dAffinetransPanGL[i] = g_dAffinetransPan[i];
+ g_dTranslationToFBOCenterGL[i] = g_dTranslationToFBOCenter[i];
+ }
+}