Michael Kolb | 25668cd | 2013-01-29 10:33:22 -0800 | [diff] [blame] | 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 | /* |
| 18 | * |
| 19 | */ |
| 20 | #include <string.h> |
| 21 | #include <jni.h> |
| 22 | #include <stdio.h> |
| 23 | #include <stdlib.h> |
| 24 | #include <time.h> |
| 25 | #include <db_utilities_camera.h> |
| 26 | |
| 27 | #include "mosaic/AlignFeatures.h" |
| 28 | #include "mosaic/Blend.h" |
| 29 | #include "mosaic/Mosaic.h" |
| 30 | #include "mosaic/Log.h" |
| 31 | #define LOG_TAG "FEATURE_MOS_JNI" |
| 32 | |
| 33 | #ifdef __cplusplus |
| 34 | extern "C" { |
| 35 | #endif |
| 36 | |
| 37 | #include "mosaic_renderer_jni.h" |
| 38 | |
| 39 | char buffer[1024]; |
| 40 | |
| 41 | const int MAX_FRAMES = 100; |
| 42 | |
| 43 | static double mTx; |
| 44 | |
| 45 | int tWidth[NR]; |
| 46 | int tHeight[NR]; |
| 47 | |
| 48 | ImageType tImage[NR][MAX_FRAMES];// = {{ImageUtils::IMAGE_TYPE_NOIMAGE}}; // YVU24 format image |
| 49 | Mosaic *mosaic[NR] = {NULL,NULL}; |
| 50 | ImageType resultYVU = ImageUtils::IMAGE_TYPE_NOIMAGE; |
| 51 | ImageType resultBGR = ImageUtils::IMAGE_TYPE_NOIMAGE; |
| 52 | float gTRS[11]; // 9 elements of the transformation, 1 for frame-number, 1 for alignment error code. |
| 53 | // Variables to keep track of the mosaic computation progress for both LR & HR. |
| 54 | float gProgress[NR]; |
| 55 | // Variables to be able to cancel the mosaic computation when the GUI says so. |
| 56 | bool gCancelComputation[NR]; |
| 57 | |
| 58 | int c; |
| 59 | int width=0, height=0; |
| 60 | int mosaicWidth=0, mosaicHeight=0; |
| 61 | |
| 62 | //int blendingType = Blend::BLEND_TYPE_FULL; |
| 63 | //int blendingType = Blend::BLEND_TYPE_CYLPAN; |
| 64 | int blendingType = Blend::BLEND_TYPE_HORZ; |
| 65 | int stripType = Blend::STRIP_TYPE_THIN; |
| 66 | bool high_res = false; |
| 67 | bool quarter_res[NR] = {false,false}; |
| 68 | float thresh_still[NR] = {5.0f,0.0f}; |
| 69 | |
| 70 | /* return current time in milliseconds*/ |
| 71 | |
| 72 | #ifndef now_ms |
| 73 | static double |
| 74 | now_ms(void) |
| 75 | { |
| 76 | //struct timespec res; |
| 77 | struct timeval res; |
| 78 | //clock_gettime(CLOCK_REALTIME, &res); |
| 79 | gettimeofday(&res, NULL); |
| 80 | return 1000.0*res.tv_sec + (double)res.tv_usec/1e3; |
| 81 | } |
| 82 | #endif |
| 83 | |
| 84 | |
| 85 | static int frame_number_HR = 0; |
| 86 | static int frame_number_LR = 0; |
| 87 | |
| 88 | int Init(int mID, int nmax) |
| 89 | { |
| 90 | double t0, t1, time_c; |
| 91 | |
| 92 | if(mosaic[mID]!=NULL) |
| 93 | { |
| 94 | delete mosaic[mID]; |
| 95 | mosaic[mID] = NULL; |
| 96 | } |
| 97 | |
| 98 | mosaic[mID] = new Mosaic(); |
| 99 | |
| 100 | t0 = now_ms(); |
| 101 | |
| 102 | // When processing higher than 720x480 video, process low-res at |
| 103 | // quarter resolution |
| 104 | if(tWidth[LR]>180) |
| 105 | quarter_res[LR] = true; |
| 106 | |
| 107 | |
| 108 | // Check for initialization and if not, initialize |
| 109 | if (!mosaic[mID]->isInitialized()) |
| 110 | { |
| 111 | mosaic[mID]->initialize(blendingType, stripType, tWidth[mID], tHeight[mID], |
| 112 | nmax, quarter_res[mID], thresh_still[mID]); |
| 113 | } |
| 114 | |
| 115 | t1 = now_ms(); |
| 116 | time_c = t1 - t0; |
| 117 | LOGV("Init[%d]: %g ms [%d frames]",mID,time_c,nmax); |
| 118 | return 1; |
| 119 | } |
| 120 | |
| 121 | void GenerateQuarterResImagePlanar(ImageType im, int input_w, int input_h, |
| 122 | ImageType &out) |
| 123 | { |
| 124 | ImageType imp; |
| 125 | ImageType outp; |
| 126 | |
| 127 | int count = 0; |
| 128 | |
| 129 | for (int j = 0; j < input_h; j += H2L_FACTOR) |
| 130 | { |
| 131 | imp = im + j * input_w; |
| 132 | outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); |
| 133 | |
| 134 | for (int i = 0; i < input_w; i += H2L_FACTOR) |
| 135 | { |
| 136 | *outp++ = *(imp + i); |
| 137 | count++; |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | for (int j = input_h; j < 2 * input_h; j += H2L_FACTOR) |
| 142 | { |
| 143 | imp = im + j * input_w; |
| 144 | outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); |
| 145 | |
| 146 | for (int i = 0; i < input_w; i += H2L_FACTOR) |
| 147 | { |
| 148 | *outp++ = *(imp + i); |
| 149 | count++; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | for (int j = 2 * input_h; j < 3 * input_h; j += H2L_FACTOR) |
| 154 | { |
| 155 | imp = im + j * input_w; |
| 156 | outp = out + (j / H2L_FACTOR) * (input_w / H2L_FACTOR); |
| 157 | |
| 158 | for (int i = 0; i < input_w; i += H2L_FACTOR) |
| 159 | { |
| 160 | *outp++ = *(imp + i); |
| 161 | count++; |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | int AddFrame(int mID, int k, float* trs1d) |
| 167 | { |
| 168 | double t0, t1, time_c; |
| 169 | double trs[3][3]; |
| 170 | |
| 171 | int ret_code = mosaic[mID]->addFrame(tImage[mID][k]); |
| 172 | |
| 173 | mosaic[mID]->getAligner()->getLastTRS(trs); |
| 174 | |
| 175 | if(trs1d!=NULL) |
| 176 | { |
| 177 | |
| 178 | trs1d[0] = trs[0][0]; |
| 179 | trs1d[1] = trs[0][1]; |
| 180 | trs1d[2] = trs[0][2]; |
| 181 | trs1d[3] = trs[1][0]; |
| 182 | trs1d[4] = trs[1][1]; |
| 183 | trs1d[5] = trs[1][2]; |
| 184 | trs1d[6] = trs[2][0]; |
| 185 | trs1d[7] = trs[2][1]; |
| 186 | trs1d[8] = trs[2][2]; |
| 187 | } |
| 188 | |
| 189 | return ret_code; |
| 190 | } |
| 191 | |
| 192 | int Finalize(int mID) |
| 193 | { |
| 194 | double t0, t1, time_c; |
| 195 | |
| 196 | t0 = now_ms(); |
| 197 | // Create the mosaic |
| 198 | int ret = mosaic[mID]->createMosaic(gProgress[mID], gCancelComputation[mID]); |
| 199 | t1 = now_ms(); |
| 200 | time_c = t1 - t0; |
| 201 | LOGV("CreateMosaic: %g ms",time_c); |
| 202 | |
| 203 | // Get back the result |
| 204 | resultYVU = mosaic[mID]->getMosaic(mosaicWidth, mosaicHeight); |
| 205 | |
| 206 | return ret; |
| 207 | } |
| 208 | |
| 209 | void YUV420toYVU24(ImageType yvu24, ImageType yuv420sp, int width, int height) |
| 210 | { |
| 211 | int frameSize = width * height; |
| 212 | |
| 213 | ImageType oyp = yvu24; |
| 214 | ImageType ovp = yvu24+frameSize; |
| 215 | ImageType oup = yvu24+frameSize+frameSize; |
| 216 | |
| 217 | for (int j = 0, yp = 0; j < height; j++) |
| 218 | { |
| 219 | unsigned char u = 0, v = 0; |
| 220 | int uvp = frameSize + (j >> 1) * width; |
| 221 | for (int i = 0; i < width; i++, yp++) |
| 222 | { |
| 223 | *oyp++ = yuv420sp[yp]; |
| 224 | //int y = (0xff & (int)yuv420sp[yp]) -16; |
| 225 | //yvu24p[yp] = (y<0)?0:y; |
| 226 | |
| 227 | if ((i & 1) == 0) |
| 228 | { |
| 229 | v = yuv420sp[uvp++]; |
| 230 | u = yuv420sp[uvp++]; |
| 231 | } |
| 232 | |
| 233 | *ovp++ = v; |
| 234 | *oup++ = u; |
| 235 | } |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | void YUV420toYVU24_NEW(ImageType yvu24, ImageType yuv420sp, int width, |
| 240 | int height) |
| 241 | { |
| 242 | int frameSize = width * height; |
| 243 | |
| 244 | ImageType oyp = yvu24; |
| 245 | ImageType ovp = yvu24 + frameSize; |
| 246 | ImageType oup = yvu24 + frameSize + frameSize; |
| 247 | |
| 248 | memcpy(yvu24, yuv420sp, frameSize * sizeof(unsigned char)); |
| 249 | |
| 250 | for (int j = 0; j < height; j += 2) |
| 251 | { |
| 252 | unsigned char u = 0, v = 0; |
| 253 | int uvp = frameSize + (j >> 1) * width; |
| 254 | ovp = yvu24 + frameSize + j * width; |
| 255 | oup = ovp + frameSize; |
| 256 | |
| 257 | ImageType iuvp = yuv420sp + uvp; |
| 258 | |
| 259 | for (int i = 0; i < width; i += 2) |
| 260 | { |
| 261 | v = *iuvp++; |
| 262 | u = *iuvp++; |
| 263 | |
| 264 | *ovp++ = v; |
| 265 | *oup++ = u; |
| 266 | |
| 267 | *ovp++ = v; |
| 268 | *oup++ = u; |
| 269 | |
| 270 | } |
| 271 | memcpy(ovp, ovp - width, width * sizeof(unsigned char)); |
| 272 | memcpy(oup, oup - width, width * sizeof(unsigned char)); |
| 273 | } |
| 274 | } |
| 275 | |
| 276 | |
| 277 | JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_allocateMosaicMemory( |
| 278 | JNIEnv* env, jobject thiz, jint width, jint height) |
| 279 | { |
| 280 | tWidth[HR] = width; |
| 281 | tHeight[HR] = height; |
| 282 | tWidth[LR] = int(width / H2L_FACTOR); |
| 283 | tHeight[LR] = int(height / H2L_FACTOR); |
| 284 | |
| 285 | for(int i=0; i<MAX_FRAMES; i++) |
| 286 | { |
| 287 | tImage[LR][i] = ImageUtils::allocateImage(tWidth[LR], tHeight[LR], |
| 288 | ImageUtils::IMAGE_TYPE_NUM_CHANNELS); |
| 289 | tImage[HR][i] = ImageUtils::allocateImage(tWidth[HR], tHeight[HR], |
| 290 | ImageUtils::IMAGE_TYPE_NUM_CHANNELS); |
| 291 | } |
| 292 | |
| 293 | AllocateTextureMemory(tWidth[HR], tHeight[HR], tWidth[LR], tHeight[LR]); |
| 294 | } |
| 295 | |
| 296 | JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_freeMosaicMemory( |
| 297 | JNIEnv* env, jobject thiz) |
| 298 | { |
| 299 | for(int i = 0; i < MAX_FRAMES; i++) |
| 300 | { |
| 301 | ImageUtils::freeImage(tImage[LR][i]); |
| 302 | ImageUtils::freeImage(tImage[HR][i]); |
| 303 | } |
| 304 | |
| 305 | FreeTextureMemory(); |
| 306 | } |
| 307 | |
| 308 | |
| 309 | void decodeYUV444SP(unsigned char* rgb, unsigned char* yuv420sp, int width, |
| 310 | int height) |
| 311 | { |
| 312 | int frameSize = width * height; |
| 313 | |
| 314 | for (int j = 0, yp = 0; j < height; j++) |
| 315 | { |
| 316 | int vp = frameSize + j * width, u = 0, v = 0; |
| 317 | int up = vp + frameSize; |
| 318 | |
| 319 | for (int i = 0; i < width; i++, yp++, vp++, up++) |
| 320 | { |
| 321 | int y = (0xff & ((int) yuv420sp[yp])) - 16; |
| 322 | if (y < 0) y = 0; |
| 323 | |
| 324 | v = (0xff & yuv420sp[vp]) - 128; |
| 325 | u = (0xff & yuv420sp[up]) - 128; |
| 326 | |
| 327 | int y1192 = 1192 * y; |
| 328 | int r = (y1192 + 1634 * v); |
| 329 | int g = (y1192 - 833 * v - 400 * u); |
| 330 | int b = (y1192 + 2066 * u); |
| 331 | |
| 332 | if (r < 0) r = 0; else if (r > 262143) r = 262143; |
| 333 | if (g < 0) g = 0; else if (g > 262143) g = 262143; |
| 334 | if (b < 0) b = 0; else if (b > 262143) b = 262143; |
| 335 | |
| 336 | //rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff); |
| 337 | int p = j*width*3+i*3; |
| 338 | rgb[p+0] = (r<<6 & 0xFF0000)>>16; |
| 339 | rgb[p+1] = (g>>2 & 0xFF00)>>8; |
| 340 | rgb[p+2] = b>>10 & 0xFF; |
| 341 | } |
| 342 | } |
| 343 | } |
| 344 | |
| 345 | static int count = 0; |
| 346 | |
| 347 | void ConvertYVUAiToPlanarYVU(unsigned char *planar, unsigned char *in, int width, |
| 348 | int height) |
| 349 | { |
| 350 | int planeSize = width * height; |
| 351 | unsigned char* Yptr = planar; |
| 352 | unsigned char* Vptr = planar + planeSize; |
| 353 | unsigned char* Uptr = Vptr + planeSize; |
| 354 | |
| 355 | for (int i = 0; i < planeSize; i++) |
| 356 | { |
| 357 | *Yptr++ = *in++; |
| 358 | *Vptr++ = *in++; |
| 359 | *Uptr++ = *in++; |
| 360 | in++; // Alpha |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | JNIEXPORT jfloatArray JNICALL Java_com_android_camera_Mosaic_setSourceImageFromGPU( |
| 365 | JNIEnv* env, jobject thiz) |
| 366 | { |
| 367 | double t0, t1, time_c; |
| 368 | t0 = now_ms(); |
| 369 | int ret_code = Mosaic::MOSAIC_RET_OK; |
| 370 | |
| 371 | if(frame_number_HR<MAX_FRAMES && frame_number_LR<MAX_FRAMES) |
| 372 | { |
| 373 | double last_tx = mTx; |
| 374 | |
| 375 | sem_wait(&gPreviewImage_semaphore); |
| 376 | ConvertYVUAiToPlanarYVU(tImage[LR][frame_number_LR], gPreviewImage[LR], |
| 377 | tWidth[LR], tHeight[LR]); |
| 378 | |
| 379 | sem_post(&gPreviewImage_semaphore); |
| 380 | |
| 381 | ret_code = AddFrame(LR, frame_number_LR, gTRS); |
| 382 | |
| 383 | if(ret_code == Mosaic::MOSAIC_RET_OK || ret_code == Mosaic::MOSAIC_RET_FEW_INLIERS) |
| 384 | { |
| 385 | // Copy into HR buffer only if this is a valid frame |
| 386 | sem_wait(&gPreviewImage_semaphore); |
| 387 | ConvertYVUAiToPlanarYVU(tImage[HR][frame_number_HR], gPreviewImage[HR], |
| 388 | tWidth[HR], tHeight[HR]); |
| 389 | sem_post(&gPreviewImage_semaphore); |
| 390 | |
| 391 | frame_number_LR++; |
| 392 | frame_number_HR++; |
| 393 | } |
| 394 | } |
| 395 | else |
| 396 | { |
| 397 | gTRS[1] = gTRS[2] = gTRS[3] = gTRS[5] = gTRS[6] = gTRS[7] = 0.0f; |
| 398 | gTRS[0] = gTRS[4] = gTRS[8] = 1.0f; |
| 399 | } |
| 400 | |
| 401 | UpdateWarpTransformation(gTRS); |
| 402 | |
| 403 | gTRS[9] = frame_number_HR; |
| 404 | gTRS[10] = ret_code; |
| 405 | |
| 406 | jfloatArray bytes = env->NewFloatArray(11); |
| 407 | if(bytes != 0) |
| 408 | { |
| 409 | env->SetFloatArrayRegion(bytes, 0, 11, (jfloat*) gTRS); |
| 410 | } |
| 411 | return bytes; |
| 412 | } |
| 413 | |
| 414 | |
| 415 | |
| 416 | JNIEXPORT jfloatArray JNICALL Java_com_android_camera_Mosaic_setSourceImage( |
| 417 | JNIEnv* env, jobject thiz, jbyteArray photo_data) |
| 418 | { |
| 419 | double t0, t1, time_c; |
| 420 | t0 = now_ms(); |
| 421 | |
| 422 | int ret_code = Mosaic::MOSAIC_RET_OK; |
| 423 | |
| 424 | if(frame_number_HR<MAX_FRAMES && frame_number_LR<MAX_FRAMES) |
| 425 | { |
| 426 | jbyte *pixels = env->GetByteArrayElements(photo_data, 0); |
| 427 | |
| 428 | YUV420toYVU24_NEW(tImage[HR][frame_number_HR], (ImageType)pixels, |
| 429 | tWidth[HR], tHeight[HR]); |
| 430 | |
| 431 | env->ReleaseByteArrayElements(photo_data, pixels, 0); |
| 432 | |
| 433 | double last_tx = mTx; |
| 434 | |
| 435 | t0 = now_ms(); |
| 436 | GenerateQuarterResImagePlanar(tImage[HR][frame_number_HR], tWidth[HR], |
| 437 | tHeight[HR], tImage[LR][frame_number_LR]); |
| 438 | |
| 439 | |
| 440 | sem_wait(&gPreviewImage_semaphore); |
| 441 | decodeYUV444SP(gPreviewImage[LR], tImage[LR][frame_number_LR], |
| 442 | gPreviewImageWidth[LR], gPreviewImageHeight[LR]); |
| 443 | sem_post(&gPreviewImage_semaphore); |
| 444 | |
| 445 | ret_code = AddFrame(LR, frame_number_LR, gTRS); |
| 446 | |
| 447 | if(ret_code == Mosaic::MOSAIC_RET_OK || ret_code == Mosaic::MOSAIC_RET_FEW_INLIERS) |
| 448 | { |
| 449 | frame_number_LR++; |
| 450 | frame_number_HR++; |
| 451 | } |
| 452 | |
| 453 | } |
| 454 | else |
| 455 | { |
| 456 | gTRS[1] = gTRS[2] = gTRS[3] = gTRS[5] = gTRS[6] = gTRS[7] = 0.0f; |
| 457 | gTRS[0] = gTRS[4] = gTRS[8] = 1.0f; |
| 458 | } |
| 459 | |
| 460 | UpdateWarpTransformation(gTRS); |
| 461 | |
| 462 | gTRS[9] = frame_number_HR; |
| 463 | gTRS[10] = ret_code; |
| 464 | |
| 465 | jfloatArray bytes = env->NewFloatArray(11); |
| 466 | if(bytes != 0) |
| 467 | { |
| 468 | env->SetFloatArrayRegion(bytes, 0, 11, (jfloat*) gTRS); |
| 469 | } |
| 470 | return bytes; |
| 471 | } |
| 472 | |
| 473 | JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_setBlendingType( |
| 474 | JNIEnv* env, jobject thiz, jint type) |
| 475 | { |
| 476 | blendingType = int(type); |
| 477 | } |
| 478 | |
| 479 | JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_setStripType( |
| 480 | JNIEnv* env, jobject thiz, jint type) |
| 481 | { |
| 482 | stripType = int(type); |
| 483 | } |
| 484 | |
| 485 | JNIEXPORT void JNICALL Java_com_android_camera_Mosaic_reset( |
| 486 | JNIEnv* env, jobject thiz) |
| 487 | { |
| 488 | frame_number_HR = 0; |
| 489 | frame_number_LR = 0; |
| 490 | |
| 491 | gProgress[LR] = 0.0; |
| 492 | gProgress[HR] = 0.0; |
| 493 | |
| 494 | gCancelComputation[LR] = false; |
| 495 | gCancelComputation[HR] = false; |
| 496 | |
| 497 | Init(LR,MAX_FRAMES); |
| 498 | } |
| 499 | |
| 500 | JNIEXPORT jint JNICALL Java_com_android_camera_Mosaic_reportProgress( |
| 501 | JNIEnv* env, jobject thiz, jboolean hires, jboolean cancel_computation) |
| 502 | { |
| 503 | if(bool(hires)) |
| 504 | gCancelComputation[HR] = cancel_computation; |
| 505 | else |
| 506 | gCancelComputation[LR] = cancel_computation; |
| 507 | |
| 508 | if(bool(hires)) |
| 509 | return (jint) gProgress[HR]; |
| 510 | else |
| 511 | return (jint) gProgress[LR]; |
| 512 | } |
| 513 | |
| 514 | JNIEXPORT jint JNICALL Java_com_android_camera_Mosaic_createMosaic( |
| 515 | JNIEnv* env, jobject thiz, jboolean value) |
| 516 | { |
| 517 | high_res = bool(value); |
| 518 | |
| 519 | int ret; |
| 520 | |
| 521 | if(high_res) |
| 522 | { |
| 523 | LOGV("createMosaic() - High-Res Mode"); |
| 524 | double t0, t1, time_c; |
| 525 | |
| 526 | gProgress[HR] = 0.0; |
| 527 | t0 = now_ms(); |
| 528 | |
| 529 | Init(HR, frame_number_HR); |
| 530 | |
| 531 | for(int k = 0; k < frame_number_HR; k++) |
| 532 | { |
| 533 | if (gCancelComputation[HR]) |
| 534 | break; |
| 535 | AddFrame(HR, k, NULL); |
| 536 | gProgress[HR] += TIME_PERCENT_ALIGN/frame_number_HR; |
| 537 | } |
| 538 | |
| 539 | if (gCancelComputation[HR]) |
| 540 | { |
| 541 | ret = Mosaic::MOSAIC_RET_CANCELLED; |
| 542 | } |
| 543 | else |
| 544 | { |
| 545 | gProgress[HR] = TIME_PERCENT_ALIGN; |
| 546 | |
| 547 | t1 = now_ms(); |
| 548 | time_c = t1 - t0; |
| 549 | LOGV("AlignAll - %d frames [HR]: %g ms", frame_number_HR, time_c); |
| 550 | |
| 551 | ret = Finalize(HR); |
| 552 | |
| 553 | gProgress[HR] = 100.0; |
| 554 | } |
| 555 | |
| 556 | high_res = false; |
| 557 | } |
| 558 | else |
| 559 | { |
| 560 | LOGV("createMosaic() - Low-Res Mode"); |
| 561 | gProgress[LR] = TIME_PERCENT_ALIGN; |
| 562 | |
| 563 | ret = Finalize(LR); |
| 564 | |
| 565 | gProgress[LR] = 100.0; |
| 566 | } |
| 567 | |
| 568 | return (jint) ret; |
| 569 | } |
| 570 | |
| 571 | JNIEXPORT jintArray JNICALL Java_com_android_camera_Mosaic_getFinalMosaic( |
| 572 | JNIEnv* env, jobject thiz) |
| 573 | { |
| 574 | int y,x; |
| 575 | int width = mosaicWidth; |
| 576 | int height = mosaicHeight; |
| 577 | int imageSize = width * height; |
| 578 | |
| 579 | // Convert back to RGB24 |
| 580 | resultBGR = ImageUtils::allocateImage(mosaicWidth, mosaicHeight, |
| 581 | ImageUtils::IMAGE_TYPE_NUM_CHANNELS); |
| 582 | ImageUtils::yvu2bgr(resultBGR, resultYVU, mosaicWidth, mosaicHeight); |
| 583 | |
| 584 | LOGV("MosBytes: %d, W = %d, H = %d", imageSize, width, height); |
| 585 | |
| 586 | int* image = new int[imageSize]; |
| 587 | int* dims = new int[2]; |
| 588 | |
| 589 | for(y=0; y<height; y++) |
| 590 | { |
| 591 | for(x=0; x<width; x++) |
| 592 | { |
| 593 | image[y*width+x] = (0xFF<<24) | (resultBGR[y*width*3+x*3+2]<<16)| |
| 594 | (resultBGR[y*width*3+x*3+1]<<8)| (resultBGR[y*width*3+x*3]); |
| 595 | } |
| 596 | } |
| 597 | |
| 598 | dims[0] = width; |
| 599 | dims[1] = height; |
| 600 | |
| 601 | ImageUtils::freeImage(resultBGR); |
| 602 | |
| 603 | jintArray bytes = env->NewIntArray(imageSize+2); |
| 604 | if (bytes == 0) { |
| 605 | LOGE("Error in creating the image."); |
| 606 | delete[] image; |
| 607 | return 0; |
| 608 | } |
| 609 | env->SetIntArrayRegion(bytes, 0, imageSize, (jint*) image); |
| 610 | env->SetIntArrayRegion(bytes, imageSize, 2, (jint*) dims); |
| 611 | delete[] image; |
| 612 | delete[] dims; |
| 613 | return bytes; |
| 614 | } |
| 615 | |
| 616 | JNIEXPORT jbyteArray JNICALL Java_com_android_camera_Mosaic_getFinalMosaicNV21( |
| 617 | JNIEnv* env, jobject thiz) |
| 618 | { |
| 619 | int y,x; |
| 620 | int width; |
| 621 | int height; |
| 622 | |
| 623 | width = mosaicWidth; |
| 624 | height = mosaicHeight; |
| 625 | |
| 626 | int imageSize = 1.5*width * height; |
| 627 | |
| 628 | // Convert YVU to NV21 format in-place |
| 629 | ImageType V = resultYVU+mosaicWidth*mosaicHeight; |
| 630 | ImageType U = V+mosaicWidth*mosaicHeight; |
| 631 | for(int j=0; j<mosaicHeight/2; j++) |
| 632 | { |
| 633 | for(int i=0; i<mosaicWidth; i+=2) |
| 634 | { |
| 635 | V[j*mosaicWidth+i] = V[(2*j)*mosaicWidth+i]; // V |
| 636 | V[j*mosaicWidth+i+1] = U[(2*j)*mosaicWidth+i]; // U |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | LOGV("MosBytes: %d, W = %d, H = %d", imageSize, width, height); |
| 641 | |
| 642 | unsigned char* dims = new unsigned char[8]; |
| 643 | |
| 644 | dims[0] = (unsigned char)(width >> 24); |
| 645 | dims[1] = (unsigned char)(width >> 16); |
| 646 | dims[2] = (unsigned char)(width >> 8); |
| 647 | dims[3] = (unsigned char)width; |
| 648 | |
| 649 | dims[4] = (unsigned char)(height >> 24); |
| 650 | dims[5] = (unsigned char)(height >> 16); |
| 651 | dims[6] = (unsigned char)(height >> 8); |
| 652 | dims[7] = (unsigned char)height; |
| 653 | |
| 654 | jbyteArray bytes = env->NewByteArray(imageSize+8); |
| 655 | if (bytes == 0) { |
| 656 | LOGE("Error in creating the image."); |
| 657 | ImageUtils::freeImage(resultYVU); |
| 658 | return 0; |
| 659 | } |
| 660 | env->SetByteArrayRegion(bytes, 0, imageSize, (jbyte*) resultYVU); |
| 661 | env->SetByteArrayRegion(bytes, imageSize, 8, (jbyte*) dims); |
| 662 | delete[] dims; |
| 663 | ImageUtils::freeImage(resultYVU); |
| 664 | return bytes; |
| 665 | } |
| 666 | |
| 667 | #ifdef __cplusplus |
| 668 | } |
| 669 | #endif |