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