• 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  * A singleton to handle the processing of each frame by {@link Mosaic}.
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         // no need to call reset() here. reset() should be called by the client
96         // after this initialization before calling other methods of this object.
97     }
98 
clear()99     public void clear() {
100         if (mIsMosaicMemoryAllocated) {
101             mMosaicer.freeMosaicMemory();
102             mIsMosaicMemoryAllocated = false;
103         }
104         synchronized (this) {
105             notify();
106         }
107     }
108 
isMosaicMemoryAllocated()109     public boolean isMosaicMemoryAllocated() {
110         return mIsMosaicMemoryAllocated;
111     }
112 
setStripType(int type)113     public void setStripType(int type) {
114         mMosaicer.setStripType(type);
115     }
116 
setupMosaicer(int previewWidth, int previewHeight, int bufSize)117     private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) {
118         Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize);
119 
120         if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!");
121         mIsMosaicMemoryAllocated = true;
122         mMosaicer.allocateMosaicMemory(previewWidth, previewHeight);
123     }
124 
reset()125     public void reset() {
126         // reset() can be called even if MosaicFrameProcessor is not initialized.
127         // Only counters will be changed.
128         mFirstRun = true;
129         mTotalFrameCount = 0;
130         mFillIn = 0;
131         mTotalTranslationX = 0;
132         mTranslationLastX = 0;
133         mTotalTranslationY = 0;
134         mTranslationLastY = 0;
135         mPanningRateX = 0;
136         mPanningRateY = 0;
137         mLastProcessFrameIdx = -1;
138         mCurrProcessFrameIdx = -1;
139         for (int i = 0; i < WINDOW_SIZE; ++i) {
140             mDeltaX[i] = 0f;
141             mDeltaY[i] = 0f;
142         }
143         mMosaicer.reset();
144     }
145 
createMosaic(boolean highRes)146     public int createMosaic(boolean highRes) {
147         return mMosaicer.createMosaic(highRes);
148     }
149 
getFinalMosaicNV21()150     public byte[] getFinalMosaicNV21() {
151         return mMosaicer.getFinalMosaicNV21();
152     }
153 
154     // Processes the last filled image frame through the mosaicer and
155     // updates the UI to show progress.
156     // When done, processes and displays the final mosaic.
processFrame()157     public void processFrame() {
158         if (!mIsMosaicMemoryAllocated) {
159             // clear() is called and buffers are cleared, stop computation.
160             // This can happen when the onPause() is called in the activity, but still some frames
161             // are not processed yet and thus the callback may be invoked.
162             return;
163         }
164 
165         mCurrProcessFrameIdx = mFillIn;
166         mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER);
167 
168         // Check that we are trying to process a frame different from the
169         // last one processed (useful if this class was running asynchronously)
170         if (mCurrProcessFrameIdx != mLastProcessFrameIdx) {
171             mLastProcessFrameIdx = mCurrProcessFrameIdx;
172 
173             // TODO: make the termination condition regarding reaching
174             // MAX_NUMBER_OF_FRAMES solely determined in the library.
175             if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) {
176                 // If we are still collecting new frames for the current mosaic,
177                 // process the new frame.
178                 calculateTranslationRate();
179 
180                 // Publish progress of the ongoing processing
181                 if (mProgressListener != null) {
182                     mProgressListener.onProgress(false, mPanningRateX, mPanningRateY,
183                             mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
184                             mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
185                 }
186             } else {
187                 if (mProgressListener != null) {
188                     mProgressListener.onProgress(true, mPanningRateX, mPanningRateY,
189                             mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth,
190                             mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight);
191                 }
192             }
193         }
194     }
195 
calculateTranslationRate()196     public void calculateTranslationRate() {
197         float[] frameData = mMosaicer.setSourceImageFromGPU();
198         int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX];
199         mTotalFrameCount  = (int) frameData[FRAME_COUNT_INDEX];
200         float translationCurrX = frameData[X_COORD_INDEX];
201         float translationCurrY = frameData[Y_COORD_INDEX];
202 
203         if (mFirstRun) {
204             // First time: no need to update delta values.
205             mTranslationLastX = translationCurrX;
206             mTranslationLastY = translationCurrY;
207             mFirstRun = false;
208             return;
209         }
210 
211         // Moving average: remove the oldest translation/deltaTime and
212         // add the newest translation/deltaTime in
213         int idx = mOldestIdx;
214         mTotalTranslationX -= mDeltaX[idx];
215         mTotalTranslationY -= mDeltaY[idx];
216         mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX);
217         mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY);
218         mTotalTranslationX += mDeltaX[idx];
219         mTotalTranslationY += mDeltaY[idx];
220 
221         // The panning rate is measured as the rate of the translation percentage in
222         // image width/height. Take the horizontal panning rate for example, the image width
223         // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR).
224         // To get the horizontal translation percentage, the horizontal translation,
225         // (translationCurrX - mTranslationLastX), is divided by the
226         // image width. We then get the rate by dividing the translation percentage with the
227         // number of frames.
228         mPanningRateX = mTotalTranslationX /
229                 (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE;
230         mPanningRateY = mTotalTranslationY /
231                 (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE;
232 
233         mTranslationLastX = translationCurrX;
234         mTranslationLastY = translationCurrY;
235         mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE;
236     }
237 }
238