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