Home | History | Annotate | Download | only in camera
      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