You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
763 lines
26 KiB
763 lines
26 KiB
/*
|
|
* 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).
|
|
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);
|
|
}
|
|
|
|
// @return false if there was an error
|
|
bool checkGlError(const char* op) {
|
|
GLint error = glGetError();
|
|
if (error != 0) {
|
|
LOGE("after %s() glError (0x%x)", op, error);
|
|
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_init(&gPreviewImage_semaphore, 0, 1);
|
|
|
|
sem_wait(&gPreviewImage_semaphore);
|
|
gPreviewImage[LR] = ImageUtils::allocateImage(gPreviewImageWidth[LR],
|
|
gPreviewImageHeight[LR], 4);
|
|
ClearPreviewImage(LR);
|
|
gPreviewImage[HR] = ImageUtils::allocateImage(gPreviewImageWidth[HR],
|
|
gPreviewImageHeight[HR], 4);
|
|
ClearPreviewImage(HR);
|
|
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);
|
|
|
|
sem_destroy(&gPreviewImage_semaphore);
|
|
}
|
|
|
|
extern "C"
|
|
{
|
|
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,
|
|
jboolean isLandscapeOrientation);
|
|
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_setWarping(
|
|
JNIEnv * env, jobject obj, jboolean flag);
|
|
};
|
|
|
|
JNIEXPORT jint JNICALL Java_com_android_camera_panorama_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];
|
|
}
|
|
|
|
|
|
void calculateUILayoutScaling(int width, int height, bool isLandscape) {
|
|
if (isLandscape) {
|
|
// __________ ______
|
|
// |__________| => |______|
|
|
// (Preview FBO) (View)
|
|
//
|
|
// 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) (PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[LR])
|
|
/ (PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[LR]) * PREVIEW_FBO_HEIGHT_SCALE)
|
|
/ ((float) width / height);
|
|
} else {
|
|
// __
|
|
// __________ | |
|
|
// |__________| => | |
|
|
// (Preview FBO) | |
|
|
// |__|
|
|
// (View)
|
|
// Scale the preview FBO's height to the width of view and
|
|
// maintain the aspect ratio of the current frame on the screen.
|
|
gUILayoutScalingX = PREVIEW_FBO_HEIGHT_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.
|
|
gUILayoutScalingY = ((float) (PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[LR])
|
|
/ (PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[LR]) * PREVIEW_FBO_HEIGHT_SCALE)
|
|
/ ((float) height / width);
|
|
|
|
}
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_com_android_camera_panorama_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);
|
|
|
|
sem_wait(&gPreviewImage_semaphore);
|
|
ClearPreviewImage(LR);
|
|
ClearPreviewImage(HR);
|
|
sem_post(&gPreviewImage_semaphore);
|
|
|
|
// 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);
|
|
|
|
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);
|
|
|
|
gYVURenderer[LR].SetupGraphics(&gBufferInputYVU[LR]);
|
|
gYVURenderer[LR].Clear(0.0, 0.0, 0.0, 1.0);
|
|
gYVURenderer[LR].SetInputTextureName(gBufferInput[LR].GetTextureName());
|
|
gYVURenderer[LR].SetInputTextureType(GL_TEXTURE_2D);
|
|
|
|
gYVURenderer[HR].SetupGraphics(&gBufferInputYVU[HR]);
|
|
gYVURenderer[HR].Clear(0.0, 0.0, 0.0, 1.0);
|
|
gYVURenderer[HR].SetInputTextureName(gBufferInput[HR].GetTextureName());
|
|
gYVURenderer[HR].SetInputTextureType(GL_TEXTURE_2D);
|
|
|
|
// gBuffer[1-gCurrentFBOIndex] --> gWarper1 --> gBuffer[gCurrentFBOIndex]
|
|
gWarper1.SetupGraphics(&gBuffer[gCurrentFBOIndex]);
|
|
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]);
|
|
gWarper2.Clear(0.0, 0.0, 0.0, 1.0);
|
|
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);
|
|
|
|
gPreview.SetupGraphics(width, height);
|
|
gPreview.Clear(0.0, 0.0, 0.0, 1.0);
|
|
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_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_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_panorama_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");
|
|
|
|
// 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");
|
|
|
|
sem_post(&gPreviewImage_semaphore);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_com_android_camera_panorama_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_panorama_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);
|
|
}
|
|
|
|
gWarpImage = (bool)flag;
|
|
}
|
|
|
|
|
|
JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_ready(
|
|
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];
|
|
}
|
|
}
|