diff options
Diffstat (limited to 'jni/mosaic_renderer_jni.cpp')
-rw-r--r-- | jni/mosaic_renderer_jni.cpp | 804 |
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]; + } +} |