| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.camera; |
| |
| import android.util.Log; |
| |
| /** |
| * Class to handle the processing of each frame by Mosaicer. |
| */ |
| public class MosaicFrameProcessor { |
| private static final String TAG = "MosaicFrameProcessor"; |
| private static final int NUM_FRAMES_IN_BUFFER = 2; |
| private static final int MAX_NUMBER_OF_FRAMES = 100; |
| private static final int MOSAIC_RET_CODE_INDEX = 10; |
| private static final int FRAME_COUNT_INDEX = 9; |
| private static final int X_COORD_INDEX = 2; |
| private static final int Y_COORD_INDEX = 5; |
| private static final int HR_TO_LR_DOWNSAMPLE_FACTOR = 4; |
| private static final int WINDOW_SIZE = 3; |
| |
| private Mosaic mMosaicer; |
| private boolean mIsMosaicMemoryAllocated = false; |
| private float mTranslationLastX; |
| private float mTranslationLastY; |
| |
| private int mFillIn = 0; |
| private int mTotalFrameCount = 0; |
| private int mLastProcessFrameIdx = -1; |
| private int mCurrProcessFrameIdx = -1; |
| private boolean mFirstRun; |
| |
| // Panning rate is in unit of percentage of image content translation per |
| // frame. Use moving average to calculate the panning rate. |
| private float mPanningRateX; |
| private float mPanningRateY; |
| |
| private float[] mDeltaX = new float[WINDOW_SIZE]; |
| private float[] mDeltaY = new float[WINDOW_SIZE]; |
| private int mOldestIdx = 0; |
| private float mTotalTranslationX = 0f; |
| private float mTotalTranslationY = 0f; |
| |
| private ProgressListener mProgressListener; |
| |
| private int mPreviewWidth; |
| private int mPreviewHeight; |
| private int mPreviewBufferSize; |
| |
| private static MosaicFrameProcessor sMosaicFrameProcessor; // singleton |
| |
| public interface ProgressListener { |
| public void onProgress(boolean isFinished, float panningRateX, float panningRateY, |
| float progressX, float progressY); |
| } |
| |
| public static MosaicFrameProcessor getInstance() { |
| if (sMosaicFrameProcessor == null) { |
| sMosaicFrameProcessor = new MosaicFrameProcessor(); |
| } |
| return sMosaicFrameProcessor; |
| } |
| |
| private MosaicFrameProcessor() { |
| mMosaicer = new Mosaic(); |
| } |
| |
| public void setProgressListener(ProgressListener listener) { |
| mProgressListener = listener; |
| } |
| |
| public int reportProgress(boolean hires, boolean cancel) { |
| return mMosaicer.reportProgress(hires, cancel); |
| } |
| |
| public void initialize(int previewWidth, int previewHeight, int bufSize) { |
| mPreviewWidth = previewWidth; |
| mPreviewHeight = previewHeight; |
| mPreviewBufferSize = bufSize; |
| setupMosaicer(mPreviewWidth, mPreviewHeight, mPreviewBufferSize); |
| setStripType(Mosaic.STRIPTYPE_WIDE); |
| reset(); |
| } |
| |
| public void clear() { |
| if (mIsMosaicMemoryAllocated) { |
| mMosaicer.freeMosaicMemory(); |
| mIsMosaicMemoryAllocated = false; |
| } |
| synchronized (this) { |
| notify(); |
| } |
| } |
| |
| public boolean isMosaicMemoryAllocated() { |
| return mIsMosaicMemoryAllocated; |
| } |
| |
| public void setStripType(int type) { |
| mMosaicer.setStripType(type); |
| } |
| |
| private void setupMosaicer(int previewWidth, int previewHeight, int bufSize) { |
| Log.v(TAG, "setupMosaicer w, h=" + previewWidth + ',' + previewHeight + ',' + bufSize); |
| |
| if (mIsMosaicMemoryAllocated) throw new RuntimeException("MosaicFrameProcessor in use!"); |
| mIsMosaicMemoryAllocated = true; |
| mMosaicer.allocateMosaicMemory(previewWidth, previewHeight); |
| } |
| |
| public void reset() { |
| // reset() can be called even if MosaicFrameProcessor is not initialized. |
| // Only counters will be changed. |
| mFirstRun = true; |
| mTotalFrameCount = 0; |
| mFillIn = 0; |
| mTotalTranslationX = 0; |
| mTranslationLastX = 0; |
| mTotalTranslationY = 0; |
| mTranslationLastY = 0; |
| mPanningRateX = 0; |
| mPanningRateY = 0; |
| mLastProcessFrameIdx = -1; |
| mCurrProcessFrameIdx = -1; |
| for (int i = 0; i < WINDOW_SIZE; ++i) { |
| mDeltaX[i] = 0f; |
| mDeltaY[i] = 0f; |
| } |
| mMosaicer.reset(); |
| } |
| |
| public int createMosaic(boolean highRes) { |
| return mMosaicer.createMosaic(highRes); |
| } |
| |
| public byte[] getFinalMosaicNV21() { |
| return mMosaicer.getFinalMosaicNV21(); |
| } |
| |
| // Processes the last filled image frame through the mosaicer and |
| // updates the UI to show progress. |
| // When done, processes and displays the final mosaic. |
| public void processFrame() { |
| if (!mIsMosaicMemoryAllocated) { |
| // clear() is called and buffers are cleared, stop computation. |
| // This can happen when the onPause() is called in the activity, but still some frames |
| // are not processed yet and thus the callback may be invoked. |
| return; |
| } |
| |
| mCurrProcessFrameIdx = mFillIn; |
| mFillIn = ((mFillIn + 1) % NUM_FRAMES_IN_BUFFER); |
| |
| // Check that we are trying to process a frame different from the |
| // last one processed (useful if this class was running asynchronously) |
| if (mCurrProcessFrameIdx != mLastProcessFrameIdx) { |
| mLastProcessFrameIdx = mCurrProcessFrameIdx; |
| |
| // TODO: make the termination condition regarding reaching |
| // MAX_NUMBER_OF_FRAMES solely determined in the library. |
| if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) { |
| // If we are still collecting new frames for the current mosaic, |
| // process the new frame. |
| calculateTranslationRate(); |
| |
| // Publish progress of the ongoing processing |
| if (mProgressListener != null) { |
| mProgressListener.onProgress(false, mPanningRateX, mPanningRateY, |
| mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, |
| mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); |
| } |
| } else { |
| if (mProgressListener != null) { |
| mProgressListener.onProgress(true, mPanningRateX, mPanningRateY, |
| mTranslationLastX * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewWidth, |
| mTranslationLastY * HR_TO_LR_DOWNSAMPLE_FACTOR / mPreviewHeight); |
| } |
| } |
| } |
| } |
| |
| public void calculateTranslationRate() { |
| float[] frameData = mMosaicer.setSourceImageFromGPU(); |
| int ret_code = (int) frameData[MOSAIC_RET_CODE_INDEX]; |
| mTotalFrameCount = (int) frameData[FRAME_COUNT_INDEX]; |
| float translationCurrX = frameData[X_COORD_INDEX]; |
| float translationCurrY = frameData[Y_COORD_INDEX]; |
| |
| if (mFirstRun) { |
| // First time: no need to update delta values. |
| mTranslationLastX = translationCurrX; |
| mTranslationLastY = translationCurrY; |
| mFirstRun = false; |
| return; |
| } |
| |
| // Moving average: remove the oldest translation/deltaTime and |
| // add the newest translation/deltaTime in |
| int idx = mOldestIdx; |
| mTotalTranslationX -= mDeltaX[idx]; |
| mTotalTranslationY -= mDeltaY[idx]; |
| mDeltaX[idx] = Math.abs(translationCurrX - mTranslationLastX); |
| mDeltaY[idx] = Math.abs(translationCurrY - mTranslationLastY); |
| mTotalTranslationX += mDeltaX[idx]; |
| mTotalTranslationY += mDeltaY[idx]; |
| |
| // The panning rate is measured as the rate of the translation percentage in |
| // image width/height. Take the horizontal panning rate for example, the image width |
| // used in finding the translation is (PreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR). |
| // To get the horizontal translation percentage, the horizontal translation, |
| // (translationCurrX - mTranslationLastX), is divided by the |
| // image width. We then get the rate by dividing the translation percentage with the |
| // number of frames. |
| mPanningRateX = mTotalTranslationX / |
| (mPreviewWidth / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; |
| mPanningRateY = mTotalTranslationY / |
| (mPreviewHeight / HR_TO_LR_DOWNSAMPLE_FACTOR) / WINDOW_SIZE; |
| |
| mTranslationLastX = translationCurrX; |
| mTranslationLastY = translationCurrY; |
| mOldestIdx = (mOldestIdx + 1) % WINDOW_SIZE; |
| } |
| } |