1ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong/* 2ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * Copyright (C) 2011 The Android Open Source Project 3ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * 4ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * Licensed under the Apache License, Version 2.0 (the "License"); 5ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * you may not use this file except in compliance with the License. 6ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * You may obtain a copy of the License at 7ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * 8ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * http://www.apache.org/licenses/LICENSE-2.0 9ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * 10ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * Unless required by applicable law or agreed to in writing, software 11ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * distributed under the License is distributed on an "AS IS" BASIS, 12ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * See the License for the specific language governing permissions and 14ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * limitations under the License. 15ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong */ 16ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 17ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kongpackage com.android.camera; 18ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 19ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kongimport android.util.Log; 20ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 21ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong/** 22ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong * A singleton to handle the processing of each frame by {@link Mosaic}. 23ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong */ 24ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kongpublic class MosaicFrameProcessor { 25ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final String TAG = "MosaicFrameProcessor"; 26ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int NUM_FRAMES_IN_BUFFER = 2; 27ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int MAX_NUMBER_OF_FRAMES = 100; 28ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int MOSAIC_RET_CODE_INDEX = 10; 29ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int FRAME_COUNT_INDEX = 9; 30ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int X_COORD_INDEX = 2; 31ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int Y_COORD_INDEX = 5; 32ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4; 33ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static final int WINDOW_SIZE = 3; 34ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 35ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private Mosaic mMosaicer; 36ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private boolean mIsMosaicMemoryAllocated = false; 37ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mTranslationLastX; 38ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mTranslationLastY; 39ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 40ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mFillIn = 0; 41ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mTotalFrameCount = 0; 42ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mLastProcessFrameIdx = -1; 43ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mCurrProcessFrameIdx = -1; 44ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private boolean mFirstRun; 45ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 46ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Panning rate is in unit of percentage of image content translation per 47ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // frame. Use moving average to calculate the panning rate. 48ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mPanningRateX; 49ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mPanningRateY; 50ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 51ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float[] mDeltaX = new float[WINDOW_SIZE]; 52ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float[] mDeltaY = new float[WINDOW_SIZE]; 53ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mOldestIdx = 0; 54ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mTotalTranslationX = 0f; 55ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private float mTotalTranslationY = 0f; 56ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 57ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private ProgressListener mProgressListener; 58ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 59ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mPreviewWidth; 60ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mPreviewHeight; 61ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private int mPreviewBufferSize; 62ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 63ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton 64ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 65ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public interface ProgressListener { 66ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void onProgress(boolean isFinished, float panningRateX, float panningRateY, 67ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong float progressX, float progressY); 68ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 69ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 70ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public static MosaicFrameProcessor getInstance() { 71ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (sMosaicFrameProcessor == null) { 72ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong sMosaicFrameProcessor = new MosaicFrameProcessor(); 73ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 74ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return sMosaicFrameProcessor; 75ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 76ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 77ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private MosaicFrameProcessor() { 78ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mMosaicer = new Mosaic(); 79ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 80ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 81ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void setProgressListener(ProgressListener listener) { 82ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mProgressListener = listener; 83ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 84ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 85ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public int reportProgress(boolean hires, boolean cancel) { 86ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return mMosaicer.reportProgress(hires, cancel); 87ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 88ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 89ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void initialize(int previewWidth, int previewHeight, int bufSize) { 90ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPreviewWidth = previewWidth; 91ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPreviewHeight = previewHeight; 92ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPreviewBufferSize = bufSize; 93ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); 94ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong setStripType(Mosaic.STRIPTYPE_WIDE); 95ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // no need to call reset() here. reset() should be called by the client 96ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // after this initialization before calling other methods of this object. 97ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 98ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 99ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void clear() { 100ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mIsMosaicMemoryAllocated) { 101ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mMosaicer.freeMosaicMemory(); 102ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mIsMosaicMemoryAllocated = false; 103ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 104ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong synchronized (this) { 105ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong notify(); 106ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 107ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 108ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 109ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public boolean isMosaicMemoryAllocated() { 110ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return mIsMosaicMemoryAllocated; 111ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 112ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 113ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void setStripType(int type) { 114ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mMosaicer.setStripType(type); 115ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 116ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 117ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) { 118ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize); 119ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 120ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!"); 121ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mIsMosaicMemoryAllocated = true; 122ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mMosaicer.allocateMosaicMemory(previewWidth, previewHeight); 123ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 124ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 125ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void reset() { 126ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // reset() can be called even if MosaicFrameProcessor is not initialized. 127ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Only counters will be changed. 128ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mFirstRun = true; 129ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalFrameCount = 0; 130ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mFillIn = 0; 131ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationX = 0; 132ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastX = 0; 133ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationY = 0; 134ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastY = 0; 135ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPanningRateX = 0; 136ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPanningRateY = 0; 137ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mLastProcessFrameIdx = -1; 138ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mCurrProcessFrameIdx = -1; 139ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong for (int i = 0; i < WINDOW_SIZE; ++i) { 140ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mDeltaX[i] = 0f; 141ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mDeltaY[i] = 0f; 142ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 143ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mMosaicer.reset(); 144ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 145ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 146ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public int createMosaic(boolean highRes) { 147ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return mMosaicer.createMosaic(highRes); 148ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 149ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 150ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public byte[] getFinalMosaicNV21() { 151ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return mMosaicer.getFinalMosaicNV21(); 152ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 153ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 154ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Processes the last filled image frame through the mosaicer and 155ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // updates the UI to show progress. 156ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // When done, processes and displays the final mosaic. 157ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void processFrame() { 158ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (!mIsMosaicMemoryAllocated) { 159ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // clear() is called and buffers are cleared, stop computation. 160ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // This can happen when the onPause() is called in the activity, but still some frames 161ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // are not processed yet and thus the callback may be invoked. 162ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return; 163ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 164ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 165ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mCurrProcessFrameIdx = mFillIn; 166ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER); 167ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 168ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Check that we are trying to process a frame different from the 169ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // last one processed (useful if this class was running asynchronously) 170ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mCurrProcessFrameIdx != mLastProcessFrameIdx) { 171ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mLastProcessFrameIdx = mCurrProcessFrameIdx; 172ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 173ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // TODO: make the termination condition regarding reaching 174ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // MAX_NUMBER_OF_FRAMES solely determined in the library. 175ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) { 176ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // If we are still collecting new frames for the current mosaic, 177ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // process the new frame. 178ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong calculateTranslationRate(); 179ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 180ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Publish progress of the ongoing processing 181ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mProgressListener != null) { 182ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mProgressListener.onProgress(false, mPanningRateX, mPanningRateY, 183ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, 184ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); 185ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 186ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } else { 187ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mProgressListener != null) { 188ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mProgressListener.onProgress(true, mPanningRateX, mPanningRateY, 189ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, 190ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); 191ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 192ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 193ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 194ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 195ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 196ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong public void calculateTranslationRate() { 197ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong float[] frameData = mMosaicer.setSourceImageFromGPU(); 198ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX]; 199ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX]; 200ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong float translationCurrX = frameData[X_COORD_INDEX]; 201ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong float translationCurrY = frameData[Y_COORD_INDEX]; 202ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 203ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong if (mFirstRun) { 204ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // First time: no need to update delta values. 205ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastX = translationCurrX; 206ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastY = translationCurrY; 207ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mFirstRun = false; 208ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong return; 209ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 210ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 211ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // Moving average: remove the oldest translation/deltaTime and 212ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // add the newest translation/deltaTime in 213ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong int idx = mOldestIdx; 214ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationX -= mDeltaX[idx]; 215ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationY -= mDeltaY[idx]; 216ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX); 217ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY); 218ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationX += mDeltaX[idx]; 219ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTotalTranslationY += mDeltaY[idx]; 220ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 221ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // The panning rate is measured as the rate of the translation percentage in 222ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // image width/height. Take the horizontal panning rate for example, the image width 223ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR). 224ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // To get the horizontal translation percentage, the horizontal translation, 225ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // (translationCurrX - mTranslationLastX), is divided by the 226ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // image width. We then get the rate by dividing the translation percentage with the 227ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong // number of frames. 228ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPanningRateX = mTotalTranslationX / 229ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; 230ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mPanningRateY = mTotalTranslationY / 231ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; 232ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong 233ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastX = translationCurrX; 234ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mTranslationLastY = translationCurrY; 235ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE; 236ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong } 237ed15d1a140986473bbe7fffd72ec9618c41c5979Angus Kong} 238