1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <GLES2/gl2.h> 18 #include <GLES2/gl2ext.h> 19 #include <jni.h> 20 #include <math.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include "db_utilities_camera.h" 24 #include "mosaic/ImageUtils.h" 25 #include "mosaic_renderer/FrameBuffer.h" 26 #include "mosaic_renderer/WarpRenderer.h" 27 #include "mosaic_renderer/SurfaceTextureRenderer.h" 28 #include "mosaic_renderer/YVURenderer.h" 29 30 #include "mosaic/Log.h" 31 #define LOG_TAG "MosaicRenderer" 32 33 #include "mosaic_renderer_jni.h" 34 35 // Texture handle 36 GLuint gSurfaceTextureID[1]; 37 38 bool gWarpImage = true; 39 40 // Low-Res input image frame in YUVA format for preview rendering and processing 41 // and high-res YUVA input image for processing. 42 unsigned char* gPreviewImage[NR]; 43 // Low-Res & high-res preview image width 44 int gPreviewImageWidth[NR]; 45 // Low-Res & high-res preview image height 46 int gPreviewImageHeight[NR]; 47 48 // Semaphore to protect simultaneous read/writes from gPreviewImage 49 sem_t gPreviewImage_semaphore; 50 51 // Off-screen preview FBO width (large enough to store the entire 52 // preview mosaic). FBO is frame buffer object. 53 int gPreviewFBOWidth; 54 // Off-screen preview FBO height (large enough to store the entire 55 // preview mosaic). 56 int gPreviewFBOHeight; 57 58 // gK is the transformation to map the canonical {-1,1} vertex coordinate system 59 // to the {0,gPreviewImageWidth[LR]} input image frame coordinate system before 60 // applying the given affine transformation trs. gKm is the corresponding 61 // transformation for going to the {0,gPreviewFBOWidth}. 62 double gK[9]; 63 double gKinv[9]; 64 double gKm[9]; 65 double gKminv[9]; 66 67 // Shader to copy input SurfaceTexture into and RGBA FBO. The two shaders 68 // render to the textures with dimensions corresponding to the low-res and 69 // high-res image frames. 70 SurfaceTextureRenderer gSurfTexRenderer[NR]; 71 // Off-screen FBOs to store the low-res and high-res RGBA copied out from 72 // the SurfaceTexture by the gSurfTexRenderers. 73 FrameBuffer gBufferInput[NR]; 74 75 // Shader to convert RGBA textures into YVU textures for processing 76 YVURenderer gYVURenderer[NR]; 77 // Off-screen FBOs to store the low-res and high-res YVU textures for processing 78 FrameBuffer gBufferInputYVU[NR]; 79 80 // Shader to translate the flip-flop FBO - gBuffer[1-current] -> gBuffer[current] 81 WarpRenderer gWarper1; 82 // Shader to add warped current frame to the flip-flop FBO - gBuffer[current] 83 WarpRenderer gWarper2; 84 // Off-screen FBOs (flip-flop) to store the result of gWarper1 & gWarper2 85 FrameBuffer gBuffer[2]; 86 87 // Shader to warp and render the preview FBO to the screen 88 WarpRenderer gPreview; 89 90 // Index of the gBuffer FBO gWarper1 is going to write into 91 int gCurrentFBOIndex = 0; 92 93 // 3x3 Matrices holding the transformation of this frame (gThisH1t) and of 94 // the last frame (gLastH1t) w.r.t the first frame. 95 double gThisH1t[9]; 96 double gLastH1t[9]; 97 98 // Variables to represent the fixed position of the top-left corner of the 99 // current frame in the previewFBO 100 double gCenterOffsetX = 0.0f; 101 double gCenterOffsetY = 0.0f; 102 103 // X-Offset of the viewfinder (current frame) w.r.t 104 // (gCenterOffsetX, gCenterOffsetY). This offset varies with time and is 105 // used to pan the viewfinder across the UI layout. 106 double gPanOffset = 0.0f; 107 108 // Variables tracking the translation value for the current frame and the 109 // last frame (both w.r.t the first frame). The difference between these 110 // values is used to control the panning speed of the viewfinder display 111 // on the UI screen. 112 double gThisTx = 0.0f; 113 double gLastTx = 0.0f; 114 115 // These are the scale factors used by the gPreview shader to ensure that 116 // the image frame is correctly scaled to the full UI layout height while 117 // maintaining its aspect ratio 118 double gUILayoutScalingX = 1.0f; 119 double gUILayoutScalingY = 1.0f; 120 121 // Whether the view that we will render preview FBO onto is in landscape or portrait 122 // orientation. 123 bool gIsLandscapeOrientation = true; 124 125 // State of the viewfinder. Set to false when the viewfinder hits the UI edge. 126 bool gPanViewfinder = true; 127 128 // Affine transformation in GL 4x4 format (column-major) to warp the 129 // last frame mosaic into the current frame coordinate system. 130 GLfloat g_dAffinetransGL[16]; 131 double g_dAffinetrans[16]; 132 133 // Affine transformation in GL 4x4 format (column-major) to translate the 134 // preview FBO across the screen (viewfinder panning). 135 GLfloat g_dAffinetransPanGL[16]; 136 double g_dAffinetransPan[16]; 137 138 // XY translation in GL 4x4 format (column-major) to center the current 139 // preview mosaic in the preview FBO 140 GLfloat g_dTranslationToFBOCenterGL[16]; 141 double g_dTranslationToFBOCenter[16]; 142 143 // GL 4x4 Identity transformation 144 GLfloat g_dAffinetransIdentGL[] = { 145 1., 0., 0., 0., 146 0., 1., 0., 0., 147 0., 0., 1., 0., 148 0., 0., 0., 1.}; 149 150 // GL 4x4 Rotation transformation (column-majored): 90 degree 151 GLfloat g_dAffinetransRotation90GL[] = { 152 0., 1., 0., 0., 153 -1., 0., 0., 0., 154 0., 0., 1., 0., 155 0., 0., 0., 1.}; 156 157 // 3x3 Rotation transformation (row-majored): 90 degree 158 double gRotation90[] = { 159 0., -1., 0., 160 1., 0., 0., 161 0., 0., 1.,}; 162 163 164 float g_dIdent3x3[] = { 165 1.0, 0.0, 0.0, 166 0.0, 1.0, 0.0, 167 0.0, 0.0, 1.0}; 168 169 const int GL_TEXTURE_EXTERNAL_OES_ENUM = 0x8D65; 170 171 static void printGLString(const char *name, GLenum s) { 172 const char *v = (const char *) glGetString(s); 173 LOGI("GL %s = %s", name, v); 174 } 175 176 void checkFramebufferStatus(const char* name) { 177 GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); 178 if (status == 0) { 179 LOGE("Checking completeness of Framebuffer:%s", name); 180 checkGlError("checkFramebufferStatus (is the target \"GL_FRAMEBUFFER\"?)"); 181 } else if (status != GL_FRAMEBUFFER_COMPLETE) { 182 const char* msg = "not listed"; 183 switch (status) { 184 case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: msg = "attachment"; break; 185 case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS: msg = "dimensions"; break; 186 case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: msg = "missing attachment"; break; 187 case GL_FRAMEBUFFER_UNSUPPORTED: msg = "unsupported"; break; 188 } 189 LOGE("Framebuffer: %s is INCOMPLETE: %s, %x", name, msg, status); 190 } 191 } 192 193 // @return false if there was an error 194 bool checkGLErrorDetail(const char* file, int line, const char* op) { 195 GLint error = glGetError(); 196 const char* err_msg = "NOT_LISTED"; 197 if (error != 0) { 198 switch (error) { 199 case GL_INVALID_VALUE: err_msg = "NOT_LISTED_YET"; break; 200 case GL_INVALID_OPERATION: err_msg = "INVALID_OPERATION"; break; 201 case GL_INVALID_ENUM: err_msg = "INVALID_ENUM"; break; 202 } 203 LOGE("Error after %s(). glError: %s (0x%x) in line %d of %s", op, err_msg, error, line, file); 204 return false; 205 } 206 return true; 207 } 208 209 void bindSurfaceTexture(GLuint texId) 210 { 211 glBindTexture(GL_TEXTURE_EXTERNAL_OES_ENUM, texId); 212 213 // Can't do mipmapping with camera source 214 glTexParameterf(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_MIN_FILTER, 215 GL_LINEAR); 216 glTexParameterf(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_MAG_FILTER, 217 GL_LINEAR); 218 // Clamp to edge is the only option 219 glTexParameteri(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_WRAP_S, 220 GL_CLAMP_TO_EDGE); 221 glTexParameteri(GL_TEXTURE_EXTERNAL_OES_ENUM, GL_TEXTURE_WRAP_T, 222 GL_CLAMP_TO_EDGE); 223 } 224 225 void ClearPreviewImage(int mID) 226 { 227 unsigned char* ptr = gPreviewImage[mID]; 228 for(int j = 0, i = 0; 229 j < gPreviewImageWidth[mID] * gPreviewImageHeight[mID] * 4; 230 j += 4) 231 { 232 ptr[i++] = 0; 233 ptr[i++] = 0; 234 ptr[i++] = 0; 235 ptr[i++] = 255; 236 } 237 238 } 239 240 void ConvertAffine3x3toGL4x4(double *matGL44, double *mat33) 241 { 242 matGL44[0] = mat33[0]; 243 matGL44[1] = mat33[3]; 244 matGL44[2] = 0.0; 245 matGL44[3] = mat33[6]; 246 247 matGL44[4] = mat33[1]; 248 matGL44[5] = mat33[4]; 249 matGL44[6] = 0.0; 250 matGL44[7] = mat33[7]; 251 252 matGL44[8] = 0; 253 matGL44[9] = 0; 254 matGL44[10] = 1.0; 255 matGL44[11] = 0.0; 256 257 matGL44[12] = mat33[2]; 258 matGL44[13] = mat33[5]; 259 matGL44[14] = 0.0; 260 matGL44[15] = mat33[8]; 261 } 262 263 bool continuePanningFBO(double panOffset) { 264 double normalizedScreenLimitLeft = -1.0 + VIEWPORT_BORDER_FACTOR_HORZ * 2.0; 265 double normalizedScreenLimitRight = 1.0 - VIEWPORT_BORDER_FACTOR_HORZ * 2.0; 266 double normalizedXPositionOnScreenLeft; 267 double normalizedXPositionOnScreenRight; 268 269 // Compute the position of the current frame in the screen coordinate system 270 if (gIsLandscapeOrientation) { 271 normalizedXPositionOnScreenLeft = (2.0 * 272 (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) * 273 gUILayoutScalingX; 274 normalizedXPositionOnScreenRight = (2.0 * 275 ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) / 276 gPreviewFBOWidth - 1.0) * gUILayoutScalingX; 277 } else { 278 normalizedXPositionOnScreenLeft = (2.0 * 279 (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) * 280 gUILayoutScalingY; 281 normalizedXPositionOnScreenRight = (2.0 * 282 ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) / 283 gPreviewFBOWidth - 1.0) * gUILayoutScalingY; 284 } 285 286 // Stop the viewfinder panning if we hit the maximum border allowed for 287 // this UI layout 288 if (normalizedXPositionOnScreenRight > normalizedScreenLimitRight || 289 normalizedXPositionOnScreenLeft < normalizedScreenLimitLeft) { 290 return false; 291 } else { 292 return true; 293 } 294 } 295 296 // This function computes fills the 4x4 matrices g_dAffinetrans, 297 // and g_dAffinetransPan using the specified 3x3 affine 298 // transformation between the first captured frame and the current frame. 299 // The computed g_dAffinetrans is such that it warps the preview mosaic in 300 // the last frame's coordinate system into the coordinate system of the 301 // current frame. Thus, applying this transformation will create the current 302 // frame mosaic but with the current frame missing. This frame will then be 303 // pasted in by gWarper2 after translating it by g_dTranslationToFBOCenter. 304 // The computed g_dAffinetransPan is such that it offsets the computed preview 305 // mosaic horizontally to make the viewfinder pan within the UI layout. 306 void UpdateWarpTransformation(float *trs) 307 { 308 double H[9], Hp[9], Htemp1[9], Htemp2[9], T[9]; 309 310 for(int i = 0; i < 9; i++) 311 { 312 gThisH1t[i] = trs[i]; 313 } 314 315 // Alignment is done based on low-res data. 316 // To render the preview mosaic, the translation of the high-res mosaic is estimated to 317 // H2L_FACTOR x low-res-based tranlation. 318 gThisH1t[2] *= H2L_FACTOR; 319 gThisH1t[5] *= H2L_FACTOR; 320 321 db_Identity3x3(T); 322 T[2] = -gCenterOffsetX; 323 T[5] = -gCenterOffsetY; 324 325 // H = ( inv(gThisH1t) * gLastH1t ) * T 326 db_Identity3x3(Htemp1); 327 db_Identity3x3(Htemp2); 328 db_Identity3x3(H); 329 db_InvertAffineTransform(Htemp1, gThisH1t); 330 db_Multiply3x3_3x3(Htemp2, Htemp1, gLastH1t); 331 db_Multiply3x3_3x3(H, Htemp2, T); 332 333 for(int i = 0; i < 9; i++) 334 { 335 gLastH1t[i] = gThisH1t[i]; 336 } 337 338 // Move the origin such that the frame is centered in the previewFBO 339 // i.e. H = inv(T) * H 340 H[2] += gCenterOffsetX; 341 H[5] += gCenterOffsetY; 342 343 // Hp = inv(Km) * H * Km 344 // Km moves the coordinate system from openGL to image pixels so 345 // that the alignment transform H can be applied to them. 346 // inv(Km) moves the coordinate system back to openGL normalized 347 // coordinates so that the shader can correctly render it. 348 db_Identity3x3(Htemp1); 349 db_Multiply3x3_3x3(Htemp1, H, gKm); 350 db_Multiply3x3_3x3(Hp, gKminv, Htemp1); 351 352 ConvertAffine3x3toGL4x4(g_dAffinetrans, Hp); 353 354 //////////////////////////////////////////////// 355 ////// Compute g_dAffinetransPan now... ////// 356 //////////////////////////////////////////////// 357 358 gThisTx = trs[2]; 359 360 if(gPanViewfinder) 361 { 362 gPanOffset += (gThisTx - gLastTx) * VIEWFINDER_PAN_FACTOR_HORZ; 363 } 364 365 gLastTx = gThisTx; 366 gPanViewfinder = continuePanningFBO(gPanOffset); 367 368 db_Identity3x3(H); 369 H[2] = gPanOffset; 370 371 // Hp = inv(Km) * H * Km 372 db_Identity3x3(Htemp1); 373 db_Multiply3x3_3x3(Htemp1, H, gKm); 374 db_Multiply3x3_3x3(Hp, gKminv, Htemp1); 375 376 if (gIsLandscapeOrientation) { 377 ConvertAffine3x3toGL4x4(g_dAffinetransPan, Hp); 378 } else { 379 // rotate Hp by 90 degress. 380 db_Multiply3x3_3x3(Htemp1, gRotation90, Hp); 381 ConvertAffine3x3toGL4x4(g_dAffinetransPan, Htemp1); 382 } 383 } 384 385 void AllocateTextureMemory(int widthHR, int heightHR, int widthLR, int heightLR) 386 { 387 gPreviewImageWidth[HR] = widthHR; 388 gPreviewImageHeight[HR] = heightHR; 389 390 gPreviewImageWidth[LR] = widthLR; 391 gPreviewImageHeight[LR] = heightLR; 392 393 sem_wait(&gPreviewImage_semaphore); 394 gPreviewImage[LR] = ImageUtils::allocateImage(gPreviewImageWidth[LR], 395 gPreviewImageHeight[LR], 4); 396 gPreviewImage[HR] = ImageUtils::allocateImage(gPreviewImageWidth[HR], 397 gPreviewImageHeight[HR], 4); 398 sem_post(&gPreviewImage_semaphore); 399 400 gPreviewFBOWidth = PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[HR]; 401 gPreviewFBOHeight = PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[HR]; 402 403 // The origin is such that the current frame will sit with its center 404 // at the center of the previewFBO 405 gCenterOffsetX = (gPreviewFBOWidth / 2 - gPreviewImageWidth[HR] / 2); 406 gCenterOffsetY = (gPreviewFBOHeight / 2 - gPreviewImageHeight[HR] / 2); 407 408 gPanOffset = 0.0f; 409 410 db_Identity3x3(gThisH1t); 411 db_Identity3x3(gLastH1t); 412 413 gPanViewfinder = true; 414 415 int w = gPreviewImageWidth[HR]; 416 int h = gPreviewImageHeight[HR]; 417 418 int wm = gPreviewFBOWidth; 419 int hm = gPreviewFBOHeight; 420 421 // K is the transformation to map the canonical [-1,1] vertex coordinate 422 // system to the [0,w] image coordinate system before applying the given 423 // affine transformation trs. 424 gKm[0] = wm / 2.0 - 0.5; 425 gKm[1] = 0.0; 426 gKm[2] = wm / 2.0 - 0.5; 427 gKm[3] = 0.0; 428 gKm[4] = hm / 2.0 - 0.5; 429 gKm[5] = hm / 2.0 - 0.5; 430 gKm[6] = 0.0; 431 gKm[7] = 0.0; 432 gKm[8] = 1.0; 433 434 gK[0] = w / 2.0 - 0.5; 435 gK[1] = 0.0; 436 gK[2] = w / 2.0 - 0.5; 437 gK[3] = 0.0; 438 gK[4] = h / 2.0 - 0.5; 439 gK[5] = h / 2.0 - 0.5; 440 gK[6] = 0.0; 441 gK[7] = 0.0; 442 gK[8] = 1.0; 443 444 db_Identity3x3(gKinv); 445 db_InvertCalibrationMatrix(gKinv, gK); 446 447 db_Identity3x3(gKminv); 448 db_InvertCalibrationMatrix(gKminv, gKm); 449 450 ////////////////////////////////////////// 451 ////// Compute g_Translation now... ////// 452 ////////////////////////////////////////// 453 double T[9], Tp[9], Ttemp[9]; 454 455 db_Identity3x3(T); 456 T[2] = gCenterOffsetX; 457 T[5] = gCenterOffsetY; 458 459 // Tp = inv(K) * T * K 460 db_Identity3x3(Ttemp); 461 db_Multiply3x3_3x3(Ttemp, T, gK); 462 db_Multiply3x3_3x3(Tp, gKinv, Ttemp); 463 464 ConvertAffine3x3toGL4x4(g_dTranslationToFBOCenter, Tp); 465 466 UpdateWarpTransformation(g_dIdent3x3); 467 } 468 469 void FreeTextureMemory() 470 { 471 sem_wait(&gPreviewImage_semaphore); 472 ImageUtils::freeImage(gPreviewImage[LR]); 473 ImageUtils::freeImage(gPreviewImage[HR]); 474 sem_post(&gPreviewImage_semaphore); 475 } 476 477 extern "C" 478 { 479 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved); 480 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved); 481 JNIEXPORT jint JNICALL Java_com_android_camera_MosaicRenderer_init( 482 JNIEnv * env, jobject obj); 483 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_reset( 484 JNIEnv * env, jobject obj, jint width, jint height, 485 jboolean isLandscapeOrientation); 486 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_preprocess( 487 JNIEnv * env, jobject obj, jfloatArray stMatrix); 488 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_transferGPUtoCPU( 489 JNIEnv * env, jobject obj); 490 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_step( 491 JNIEnv * env, jobject obj); 492 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_updateMatrix( 493 JNIEnv * env, jobject obj); 494 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_setWarping( 495 JNIEnv * env, jobject obj, jboolean flag); 496 }; 497 498 499 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) 500 { 501 sem_init(&gPreviewImage_semaphore, 0, 1); 502 503 return JNI_VERSION_1_4; 504 } 505 506 507 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) 508 { 509 sem_destroy(&gPreviewImage_semaphore); 510 } 511 JNIEXPORT jint JNICALL Java_com_android_camera_MosaicRenderer_init( 512 JNIEnv * env, jobject obj) 513 { 514 gSurfTexRenderer[LR].InitializeGLProgram(); 515 gSurfTexRenderer[HR].InitializeGLProgram(); 516 gYVURenderer[LR].InitializeGLProgram(); 517 gYVURenderer[HR].InitializeGLProgram(); 518 gWarper1.InitializeGLProgram(); 519 gWarper2.InitializeGLProgram(); 520 gPreview.InitializeGLProgram(); 521 gBuffer[0].InitializeGLContext(); 522 gBuffer[1].InitializeGLContext(); 523 gBufferInput[LR].InitializeGLContext(); 524 gBufferInput[HR].InitializeGLContext(); 525 gBufferInputYVU[LR].InitializeGLContext(); 526 gBufferInputYVU[HR].InitializeGLContext(); 527 528 glBindFramebuffer(GL_FRAMEBUFFER, 0); 529 530 glGenTextures(1, gSurfaceTextureID); 531 // bind the surface texture 532 bindSurfaceTexture(gSurfaceTextureID[0]); 533 534 return (jint) gSurfaceTextureID[0]; 535 } 536 537 // width: the width of the view 538 // height: the height of the view 539 // isLandscape: whether the device is in landscape or portrait. Android 540 // Compatibility Definition Document specifies that the long side of the 541 // camera aligns with the long side of the screen. 542 void calculateUILayoutScaling(int width, int height, bool isLandscape) { 543 if (isLandscape) { 544 // __________ ________ 545 // | | => |________| 546 // |__________| => (View) 547 // (Preview FBO) 548 // 549 // Scale the preview FBO's height to the height of view and 550 // maintain the aspect ratio of the current frame on the screen. 551 gUILayoutScalingY = PREVIEW_FBO_HEIGHT_SCALE; 552 553 // Note that OpenGL scales a texture to view's width and height automatically. 554 // The "width / height" inverts the scaling, so as to maintain the aspect ratio 555 // of the current frame. 556 gUILayoutScalingX = ((float) gPreviewFBOWidth / gPreviewFBOHeight) 557 / ((float) width / height) * PREVIEW_FBO_HEIGHT_SCALE; 558 } else { 559 // ___ 560 // __________ | | ______ 561 // | | => | | => |______| 562 // |__________| => | | => (View) 563 // (Preview FBO) | | 564 // |___| 565 // 566 // Scale the preview FBO's height to the width of view and 567 // maintain the aspect ratio of the current frame on the screen. 568 // In preview, Java_com_android_camera_MosaicRenderer_step rotates the 569 // preview FBO by 90 degrees. In capture, UpdateWarpTransformation 570 // rotates the preview FBO. 571 gUILayoutScalingY = PREVIEW_FBO_WIDTH_SCALE; 572 573 // Note that OpenGL scales a texture to view's width and height automatically. 574 // The "height / width" inverts the scaling, so as to maintain the aspect ratio 575 // of the current frame. 576 gUILayoutScalingX = ((float) gPreviewFBOHeight / gPreviewFBOWidth) 577 / ((float) width / height) * PREVIEW_FBO_WIDTH_SCALE; 578 } 579 } 580 581 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_reset( 582 JNIEnv * env, jobject obj, jint width, jint height, jboolean isLandscapeOrientation) 583 { 584 gIsLandscapeOrientation = isLandscapeOrientation; 585 calculateUILayoutScaling(width, height, gIsLandscapeOrientation); 586 587 gBuffer[0].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); 588 gBuffer[1].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); 589 590 gBufferInput[LR].Init(gPreviewImageWidth[LR], 591 gPreviewImageHeight[LR], GL_RGBA); 592 593 gBufferInput[HR].Init(gPreviewImageWidth[HR], 594 gPreviewImageHeight[HR], GL_RGBA); 595 596 gBufferInputYVU[LR].Init(gPreviewImageWidth[LR], 597 gPreviewImageHeight[LR], GL_RGBA); 598 599 gBufferInputYVU[HR].Init(gPreviewImageWidth[HR], 600 gPreviewImageHeight[HR], GL_RGBA); 601 602 // bind the surface texture 603 bindSurfaceTexture(gSurfaceTextureID[0]); 604 605 // To speed up, there is no need to clear the destination buffers 606 // (offscreen/screen buffers) of gSurfTexRenderer, gYVURenderer 607 // and gPreview because we always fill the whole destination buffers 608 // when we draw something to those offscreen/screen buffers. 609 gSurfTexRenderer[LR].SetupGraphics(&gBufferInput[LR]); 610 gSurfTexRenderer[LR].SetViewportMatrix(1, 1, 1, 1); 611 gSurfTexRenderer[LR].SetScalingMatrix(1.0f, -1.0f); 612 gSurfTexRenderer[LR].SetInputTextureName(gSurfaceTextureID[0]); 613 gSurfTexRenderer[LR].SetInputTextureType(GL_TEXTURE_EXTERNAL_OES_ENUM); 614 615 gSurfTexRenderer[HR].SetupGraphics(&gBufferInput[HR]); 616 gSurfTexRenderer[HR].SetViewportMatrix(1, 1, 1, 1); 617 gSurfTexRenderer[HR].SetScalingMatrix(1.0f, -1.0f); 618 gSurfTexRenderer[HR].SetInputTextureName(gSurfaceTextureID[0]); 619 gSurfTexRenderer[HR].SetInputTextureType(GL_TEXTURE_EXTERNAL_OES_ENUM); 620 621 gYVURenderer[LR].SetupGraphics(&gBufferInputYVU[LR]); 622 gYVURenderer[LR].SetInputTextureName(gBufferInput[LR].GetTextureName()); 623 gYVURenderer[LR].SetInputTextureType(GL_TEXTURE_2D); 624 625 gYVURenderer[HR].SetupGraphics(&gBufferInputYVU[HR]); 626 gYVURenderer[HR].SetInputTextureName(gBufferInput[HR].GetTextureName()); 627 gYVURenderer[HR].SetInputTextureType(GL_TEXTURE_2D); 628 629 // gBuffer[1-gCurrentFBOIndex] --> gWarper1 --> gBuffer[gCurrentFBOIndex] 630 gWarper1.SetupGraphics(&gBuffer[gCurrentFBOIndex]); 631 632 // Clear the destination buffer of gWarper1. 633 gWarper1.Clear(0.0, 0.0, 0.0, 1.0); 634 gWarper1.SetViewportMatrix(1, 1, 1, 1); 635 gWarper1.SetScalingMatrix(1.0f, 1.0f); 636 gWarper1.SetInputTextureName(gBuffer[1 - gCurrentFBOIndex].GetTextureName()); 637 gWarper1.SetInputTextureType(GL_TEXTURE_2D); 638 639 // gBufferInput[HR] --> gWarper2 --> gBuffer[gCurrentFBOIndex] 640 gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]); 641 642 // gWarp2's destination buffer is the same to gWarp1's. No need to clear it 643 // again. 644 gWarper2.SetViewportMatrix(gPreviewImageWidth[HR], 645 gPreviewImageHeight[HR], gBuffer[gCurrentFBOIndex].GetWidth(), 646 gBuffer[gCurrentFBOIndex].GetHeight()); 647 gWarper2.SetScalingMatrix(1.0f, 1.0f); 648 gWarper2.SetInputTextureName(gBufferInput[HR].GetTextureName()); 649 gWarper2.SetInputTextureType(GL_TEXTURE_2D); 650 651 // gBuffer[gCurrentFBOIndex] --> gPreview --> Screen 652 gPreview.SetupGraphics(width, height); 653 gPreview.SetViewportMatrix(1, 1, 1, 1); 654 655 // Scale the previewFBO so that the viewfinder window fills the layout height 656 // while maintaining the image aspect ratio 657 gPreview.SetScalingMatrix(gUILayoutScalingX, -1.0f * gUILayoutScalingY); 658 gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName()); 659 gPreview.SetInputTextureType(GL_TEXTURE_2D); 660 } 661 662 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_preprocess( 663 JNIEnv * env, jobject obj, jfloatArray stMatrix) 664 { 665 jfloat *stmat = env->GetFloatArrayElements(stMatrix, 0); 666 667 gSurfTexRenderer[LR].SetSTMatrix((float*) stmat); 668 gSurfTexRenderer[HR].SetSTMatrix((float*) stmat); 669 670 env->ReleaseFloatArrayElements(stMatrix, stmat, 0); 671 672 gSurfTexRenderer[LR].DrawTexture(g_dAffinetransIdentGL); 673 gSurfTexRenderer[HR].DrawTexture(g_dAffinetransIdentGL); 674 } 675 676 #ifndef now_ms 677 #include <time.h> 678 static double 679 now_ms(void) 680 { 681 //struct timespec res; 682 struct timeval res; 683 //clock_gettime(CLOCK_REALTIME, &res); 684 gettimeofday(&res, NULL); 685 return 1000.0*res.tv_sec + (double)res.tv_usec/1e3; 686 } 687 #endif 688 689 690 691 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_transferGPUtoCPU( 692 JNIEnv * env, jobject obj) 693 { 694 double t0, t1, time_c; 695 696 gYVURenderer[LR].DrawTexture(); 697 gYVURenderer[HR].DrawTexture(); 698 699 sem_wait(&gPreviewImage_semaphore); 700 // Bind to the input LR FBO and read the Low-Res data from there... 701 glBindFramebuffer(GL_FRAMEBUFFER, gBufferInputYVU[LR].GetFrameBufferName()); 702 t0 = now_ms(); 703 glReadPixels(0, 704 0, 705 gBufferInput[LR].GetWidth(), 706 gBufferInput[LR].GetHeight(), 707 GL_RGBA, 708 GL_UNSIGNED_BYTE, 709 gPreviewImage[LR]); 710 711 checkGlError("glReadPixels LR (MosaicRenderer.transferGPUtoCPU())"); 712 713 // Bind to the input HR FBO and read the high-res data from there... 714 glBindFramebuffer(GL_FRAMEBUFFER, gBufferInputYVU[HR].GetFrameBufferName()); 715 t0 = now_ms(); 716 glReadPixels(0, 717 0, 718 gBufferInput[HR].GetWidth(), 719 gBufferInput[HR].GetHeight(), 720 GL_RGBA, 721 GL_UNSIGNED_BYTE, 722 gPreviewImage[HR]); 723 724 checkGlError("glReadPixels HR (MosaicRenderer.transferGPUtoCPU())"); 725 726 sem_post(&gPreviewImage_semaphore); 727 } 728 729 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_step( 730 JNIEnv * env, jobject obj) 731 { 732 if(!gWarpImage) // ViewFinder 733 { 734 gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]); 735 gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName()); 736 737 gWarper2.DrawTexture(g_dTranslationToFBOCenterGL); 738 739 if (gIsLandscapeOrientation) { 740 gPreview.DrawTexture(g_dAffinetransIdentGL); 741 } else { 742 gPreview.DrawTexture(g_dAffinetransRotation90GL); 743 } 744 } 745 else 746 { 747 gWarper1.SetupGraphics(&gBuffer[gCurrentFBOIndex]); 748 // Clear the destination so that we can paint on it afresh 749 gWarper1.Clear(0.0, 0.0, 0.0, 1.0); 750 gWarper1.SetInputTextureName( 751 gBuffer[1 - gCurrentFBOIndex].GetTextureName()); 752 gWarper2.SetupGraphics(&gBuffer[gCurrentFBOIndex]); 753 gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName()); 754 755 gWarper1.DrawTexture(g_dAffinetransGL); 756 gWarper2.DrawTexture(g_dTranslationToFBOCenterGL); 757 gPreview.DrawTexture(g_dAffinetransPanGL); 758 759 gCurrentFBOIndex = 1 - gCurrentFBOIndex; 760 } 761 } 762 763 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_setWarping( 764 JNIEnv * env, jobject obj, jboolean flag) 765 { 766 // TODO: Review this logic 767 if(gWarpImage != (bool) flag) //switching from viewfinder to capture or vice-versa 768 { 769 // Clear gBuffer[0] 770 gWarper1.SetupGraphics(&gBuffer[0]); 771 gWarper1.Clear(0.0, 0.0, 0.0, 1.0); 772 // Clear gBuffer[1] 773 gWarper1.SetupGraphics(&gBuffer[1]); 774 gWarper1.Clear(0.0, 0.0, 0.0, 1.0); 775 // Clear the screen to black. 776 gPreview.Clear(0.0, 0.0, 0.0, 1.0); 777 778 gLastTx = 0.0f; 779 gPanOffset = 0.0f; 780 gPanViewfinder = true; 781 782 db_Identity3x3(gThisH1t); 783 db_Identity3x3(gLastH1t); 784 // Make sure g_dAffinetransGL and g_dAffinetransPanGL are updated. 785 // Otherwise, the first frame after setting the flag to true will be 786 // incorrectly drawn. 787 if ((bool) flag) { 788 UpdateWarpTransformation(g_dIdent3x3); 789 } 790 } 791 792 gWarpImage = (bool)flag; 793 } 794 795 JNIEXPORT void JNICALL Java_com_android_camera_MosaicRenderer_updateMatrix( 796 JNIEnv * env, jobject obj) 797 { 798 for(int i=0; i<16; i++) 799 { 800 g_dAffinetransGL[i] = g_dAffinetrans[i]; 801 g_dAffinetransPanGL[i] = g_dAffinetransPan[i]; 802 g_dTranslationToFBOCenterGL[i] = g_dTranslationToFBOCenter[i]; 803 } 804 } 805