18a2c41754655a3733175fce81fb7506ff7022959Angus Kong/*
28a2c41754655a3733175fce81fb7506ff7022959Angus Kong * Copyright (C) 2011 The Android Open Source Project
38a2c41754655a3733175fce81fb7506ff7022959Angus Kong *
48a2c41754655a3733175fce81fb7506ff7022959Angus Kong * Licensed under the Apache License, Version 2.0 (the "License");
58a2c41754655a3733175fce81fb7506ff7022959Angus Kong * you may not use this file except in compliance with the License.
68a2c41754655a3733175fce81fb7506ff7022959Angus Kong * You may obtain a copy of the License at
78a2c41754655a3733175fce81fb7506ff7022959Angus Kong *
88a2c41754655a3733175fce81fb7506ff7022959Angus Kong *      http://www.apache.org/licenses/LICENSE-2.0
98a2c41754655a3733175fce81fb7506ff7022959Angus Kong *
108a2c41754655a3733175fce81fb7506ff7022959Angus Kong * Unless required by applicable law or agreed to in writing, software
118a2c41754655a3733175fce81fb7506ff7022959Angus Kong * distributed under the License is distributed on an "AS IS" BASIS,
128a2c41754655a3733175fce81fb7506ff7022959Angus Kong * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138a2c41754655a3733175fce81fb7506ff7022959Angus Kong * See the License for the specific language governing permissions and
148a2c41754655a3733175fce81fb7506ff7022959Angus Kong * limitations under the License.
158a2c41754655a3733175fce81fb7506ff7022959Angus Kong */
168a2c41754655a3733175fce81fb7506ff7022959Angus Kong
179a59de880cc82d8175e40a3bfcebffbadbb33efdPin Tingpackage com.android.camera;
188a2c41754655a3733175fce81fb7506ff7022959Angus Kong
198a2c41754655a3733175fce81fb7506ff7022959Angus Kongimport android.util.Log;
208a2c41754655a3733175fce81fb7506ff7022959Angus Kong
21142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong/**
22142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong * Class to handle the processing of each frame by Mosaicer.
23142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong */
248a2c41754655a3733175fce81fb7506ff7022959Angus Kongpublic class MosaicFrameProcessor {
258a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final String TAG = "MosaicFrameProcessor";
268a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final int NUM_FRAMES_IN_BUFFER = 2;
278a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final int MAX_NUMBER_OF_FRAMES = 100;
28dd28e1cc00373c02adf88dff878dbbe5d8be9e59mbansal    private static final int MOSAIC_RET_CODE_INDEX = 10;
298a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final int FRAME_COUNT_INDEX = 9;
308a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final int X_COORD_INDEX = 2;
318a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private static final int Y_COORD_INDEX = 5;
3243b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen    private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4;
33e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private static final int WINDOW_SIZE = 3;
348a2c41754655a3733175fce81fb7506ff7022959Angus Kong
358a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private Mosaic mMosaicer;
360adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen    private boolean mIsMosaicMemoryAllocated = false;
378a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private float mTranslationLastX;
388a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private float mTranslationLastY;
398a2c41754655a3733175fce81fb7506ff7022959Angus Kong
408a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private int mFillIn = 0;
418a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private int mTotalFrameCount = 0;
428a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private int mLastProcessFrameIdx = -1;
438a2c41754655a3733175fce81fb7506ff7022959Angus Kong    private int mCurrProcessFrameIdx = -1;
44b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li    private boolean mFirstRun;
458a2c41754655a3733175fce81fb7506ff7022959Angus Kong
46b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li    // Panning rate is in unit of percentage of image content translation per
47b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li    // frame. Use moving average to calculate the panning rate.
4843b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen    private float mPanningRateX;
4943b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen    private float mPanningRateY;
508a2c41754655a3733175fce81fb7506ff7022959Angus Kong
51e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private float[] mDeltaX = new float[WINDOW_SIZE];
52e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private float[] mDeltaY = new float[WINDOW_SIZE];
53e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private int mOldestIdx = 0;
54e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private float mTotalTranslationX = 0f;
55e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private float mTotalTranslationY = 0f;
56e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen
57e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen    private ProgressListener mProgressListener;
58e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen
5994f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    private int mPreviewWidth;
6094f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    private int mPreviewHeight;
6194f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    private int mPreviewBufferSize;
6294f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
6303f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton
6403f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li
658a2c41754655a3733175fce81fb7506ff7022959Angus Kong    public interface ProgressListener {
66d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        public void onProgress(boolean isFinished, float panningRateX, float panningRateY,
67d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                float progressX, float progressY);
688a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
698a2c41754655a3733175fce81fb7506ff7022959Angus Kong
7003f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    public static MosaicFrameProcessor getInstance() {
7103f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        if (sMosaicFrameProcessor == null) {
7203f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li            sMosaicFrameProcessor = new MosaicFrameProcessor();
7303f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        }
7403f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        return sMosaicFrameProcessor;
7503f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    }
7603f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li
7703f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    private MosaicFrameProcessor() {
788a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mMosaicer = new Mosaic();
798a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
808a2c41754655a3733175fce81fb7506ff7022959Angus Kong
818a2c41754655a3733175fce81fb7506ff7022959Angus Kong    public void setProgressListener(ProgressListener listener) {
828a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mProgressListener = listener;
838a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
848a2c41754655a3733175fce81fb7506ff7022959Angus Kong
85e1178a73fd5756771d25d0b8375452450f509e99mbansal    public int reportProgress(boolean hires, boolean cancel) {
86e1178a73fd5756771d25d0b8375452450f509e99mbansal        return mMosaicer.reportProgress(hires, cancel);
8750b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal    }
8850b3c890986aadb3780b4da8c0b8dbb0f1422ebambansal
8903f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    public void initialize(int previewWidth, int previewHeight, int bufSize) {
9003f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        mPreviewWidth = previewWidth;
9103f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        mPreviewHeight = previewHeight;
9203f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        mPreviewBufferSize = bufSize;
930adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen        setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize);
94b28b9c0fa991bc97e8aa11da83d27f71fdfef6dambansal        setStripType(Mosaic.STRIPTYPE_WIDE);
95142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        reset();
9694f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    }
9794f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
98142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    public void clear() {
997d9eadd0c6c38b3761b7e8d3fa3658d194810d60Jim Miller        if (mIsMosaicMemoryAllocated) {
1007d9eadd0c6c38b3761b7e8d3fa3658d194810d60Jim Miller            mMosaicer.freeMosaicMemory();
10103f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li            mIsMosaicMemoryAllocated = false;
10203f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        }
10303f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        synchronized (this) {
10403f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li            notify();
1057d9eadd0c6c38b3761b7e8d3fa3658d194810d60Jim Miller        }
10694f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    }
10794f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
10803f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    public boolean isMosaicMemoryAllocated() {
10903f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        return mIsMosaicMemoryAllocated;
11003f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li    }
11103f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li
112b28b9c0fa991bc97e8aa11da83d27f71fdfef6dambansal    public void setStripType(int type) {
113b28b9c0fa991bc97e8aa11da83d27f71fdfef6dambansal        mMosaicer.setStripType(type);
114b28b9c0fa991bc97e8aa11da83d27f71fdfef6dambansal    }
115b28b9c0fa991bc97e8aa11da83d27f71fdfef6dambansal
1160adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen    private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) {
1178a2c41754655a3733175fce81fb7506ff7022959Angus Kong        Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize);
11803f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li
11903f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!");
1200adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen        mIsMosaicMemoryAllocated = true;
12103f6dd690441ee744bee2128cc194c2cdeb962e6Wu-cheng Li        mMosaicer.allocateMosaicMemory(previewWidth, previewHeight);
12294f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    }
12394f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
124142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong    public void reset() {
125142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        // reset() can be called even if MosaicFrameProcessor is not initialized.
126142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        // Only counters will be changed.
127b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li        mFirstRun = true;
128142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mTotalFrameCount = 0;
129142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mFillIn = 0;
130e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationX = 0;
131e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTranslationLastX = 0;
132e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationY = 0;
133e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTranslationLastY = 0;
134e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mPanningRateX = 0;
135e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mPanningRateY = 0;
136142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mLastProcessFrameIdx = -1;
137142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mCurrProcessFrameIdx = -1;
138e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        for (int i = 0; i < WINDOW_SIZE; ++i) {
139e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mDeltaX[i] = 0f;
140e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mDeltaY[i] = 0f;
141e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        }
142142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        mMosaicer.reset();
14394f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen    }
14494f592fc405ca45b8794007cd9083c3250924b50Wei-Ta Chen
145e1178a73fd5756771d25d0b8375452450f509e99mbansal    public int createMosaic(boolean highRes) {
146e1178a73fd5756771d25d0b8375452450f509e99mbansal        return mMosaicer.createMosaic(highRes);
1478a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
1488a2c41754655a3733175fce81fb7506ff7022959Angus Kong
1498a2c41754655a3733175fce81fb7506ff7022959Angus Kong    public byte[] getFinalMosaicNV21() {
1508a2c41754655a3733175fce81fb7506ff7022959Angus Kong        return mMosaicer.getFinalMosaicNV21();
1518a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
1528a2c41754655a3733175fce81fb7506ff7022959Angus Kong
1538a2c41754655a3733175fce81fb7506ff7022959Angus Kong    // Processes the last filled image frame through the mosaicer and
1548a2c41754655a3733175fce81fb7506ff7022959Angus Kong    // updates the UI to show progress.
1558a2c41754655a3733175fce81fb7506ff7022959Angus Kong    // When done, processes and displays the final mosaic.
1560adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen    public void processFrame() {
1570adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen        if (!mIsMosaicMemoryAllocated) {
158142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            // clear() is called and buffers are cleared, stop computation.
159142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            // This can happen when the onPause() is called in the activity, but still some frames
160142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            // are not processed yet and thus the callback may be invoked.
161142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong            return;
162142402d57c1689c1342d096c976b9b0826f8ce1aAngus Kong        }
16341a2e9735136f372de95652d0828600282c8e967mbansal
1648a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mCurrProcessFrameIdx = mFillIn;
1658a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER);
1668a2c41754655a3733175fce81fb7506ff7022959Angus Kong
1678a2c41754655a3733175fce81fb7506ff7022959Angus Kong        // Check that we are trying to process a frame different from the
1688a2c41754655a3733175fce81fb7506ff7022959Angus Kong        // last one processed (useful if this class was running asynchronously)
1698a2c41754655a3733175fce81fb7506ff7022959Angus Kong        if (mCurrProcessFrameIdx != mLastProcessFrameIdx) {
1708a2c41754655a3733175fce81fb7506ff7022959Angus Kong            mLastProcessFrameIdx = mCurrProcessFrameIdx;
1718a2c41754655a3733175fce81fb7506ff7022959Angus Kong
1721f227d20aa000d0798581f451b89b1d3b2bcd349Wei-Ta Chen            // TODO: make the termination condition regarding reaching
1731f227d20aa000d0798581f451b89b1d3b2bcd349Wei-Ta Chen            // MAX_NUMBER_OF_FRAMES solely determined in the library.
174c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382Angus Kong            if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) {
1758a2c41754655a3733175fce81fb7506ff7022959Angus Kong                // If we are still collecting new frames for the current mosaic,
1768a2c41754655a3733175fce81fb7506ff7022959Angus Kong                // process the new frame.
177b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li                calculateTranslationRate();
1788a2c41754655a3733175fce81fb7506ff7022959Angus Kong
1798a2c41754655a3733175fce81fb7506ff7022959Angus Kong                // Publish progress of the ongoing processing
1808a2c41754655a3733175fce81fb7506ff7022959Angus Kong                if (mProgressListener != null) {
181d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                    mProgressListener.onProgress(false, mPanningRateX, mPanningRateY,
182d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                            mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
183d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                            mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
1848a2c41754655a3733175fce81fb7506ff7022959Angus Kong                }
1858a2c41754655a3733175fce81fb7506ff7022959Angus Kong            } else {
1868a2c41754655a3733175fce81fb7506ff7022959Angus Kong                if (mProgressListener != null) {
187d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                    mProgressListener.onProgress(true, mPanningRateX, mPanningRateY,
188d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                            mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
189d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong                            mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
1908a2c41754655a3733175fce81fb7506ff7022959Angus Kong                }
1918a2c41754655a3733175fce81fb7506ff7022959Angus Kong            }
1928a2c41754655a3733175fce81fb7506ff7022959Angus Kong        }
1938a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
1948a2c41754655a3733175fce81fb7506ff7022959Angus Kong
195b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li    public void calculateTranslationRate() {
1960adf2489520d3a98a56e081aeacb1ab9336a012fWei-Ta Chen        float[] frameData = mMosaicer.setSourceImageFromGPU();
197dd28e1cc00373c02adf88dff878dbbe5d8be9e59mbansal        int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX];
1988a2c41754655a3733175fce81fb7506ff7022959Angus Kong        mTotalFrameCount  = (int) frameData[FRAME_COUNT_INDEX];
1998a2c41754655a3733175fce81fb7506ff7022959Angus Kong        float translationCurrX = frameData[X_COORD_INDEX];
2008a2c41754655a3733175fce81fb7506ff7022959Angus Kong        float translationCurrY = frameData[Y_COORD_INDEX];
20143b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen
202b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li        if (mFirstRun) {
203e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            // First time: no need to update delta values.
204e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mTranslationLastX = translationCurrX;
205e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            mTranslationLastY = translationCurrY;
206b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li            mFirstRun = false;
207e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen            return;
208e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        }
209e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen
210e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        // Moving average: remove the oldest translation/deltaTime and
211e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        // add the newest translation/deltaTime in
212e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        int idx = mOldestIdx;
213e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationX -= mDeltaX[idx];
214e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationY -= mDeltaY[idx];
215e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX);
216e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY);
217e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationX += mDeltaX[idx];
218e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen        mTotalTranslationY += mDeltaY[idx];
219e47b5624a1aa3cc7bd9763852bed4ac3215a77b2Wei-Ta Chen
22043b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // The panning rate is measured as the rate of the translation percentage in
22143b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // image width/height. Take the horizontal panning rate for example, the image width
22243b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR).
22343b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // To get the horizontal translation percentage, the horizontal translation,
22443b0b2ccd5949ee8fc377c37e7e2b82a26c88ca8Wei-Ta Chen        // (translationCurrX - mTranslationLastX), is divided by the
225b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li        // image width. We then get the rate by dividing the translation percentage with the
226b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li        // number of frames.
227d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        mPanningRateX = mTotalTranslationX /
228b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li                (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE;
229d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        mPanningRateY = mTotalTranslationY /
230b7e4d024214b85e8570ad0d8ce03472887364beeWu-cheng Li                (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE;
231d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong
232d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        mTranslationLastX = translationCurrX;
233d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        mTranslationLastY = translationCurrY;
234d991766dc3bcc03a02c6648e2cfd54ee4f8cbd9eAngus Kong        mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
2358a2c41754655a3733175fce81fb7506ff7022959Angus Kong    }
2368a2c41754655a3733175fce81fb7506ff7022959Angus Kong}
237