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 { 66 public void onProgress(boolean isFinished, float panningRateX, float panningRateY, 67 float progressX, float progressY); 68 } 69 70 public static MosaicFrameProcessor getInstance() { 71 if (sMosaicFrameProcessor == null) { 72 sMosaicFrameProcessor = new MosaicFrameProcessor(); 73 } 74 return sMosaicFrameProcessor; 75 } 76 77 private MosaicFrameProcessor() { 78 mMosaicer = new Mosaic(); 79 } 80 81 public void setProgressListener(ProgressListener listener) { 82 mProgressListener = listener; 83 } 84 85 public int reportProgress(boolean hires, boolean cancel) { 86 return mMosaicer.reportProgress(hires, cancel); 87 } 88 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 99 public void clear() { 100 if (mIsMosaicMemoryAllocated) { 101 mMosaicer.freeMosaicMemory(); 102 mIsMosaicMemoryAllocated = false; 103 } 104 synchronized (this) { 105 notify(); 106 } 107 } 108 109 public boolean isMosaicMemoryAllocated() { 110 return mIsMosaicMemoryAllocated; 111 } 112 113 public void setStripType(int type) { 114 mMosaicer.setStripType(type); 115 } 116 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 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 146 public int createMosaic(boolean highRes) { 147 return mMosaicer.createMosaic(highRes); 148 } 149 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. 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 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