Home | History | Annotate | Download | only in com.example.android.hdrviewfinder
      1 /*
      2  * Copyright (C) 2014 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.example.android.hdrviewfinder;
     18 
     19 import android.graphics.ImageFormat;
     20 import android.os.Handler;
     21 import android.os.HandlerThread;
     22 import android.renderscript.Allocation;
     23 import android.renderscript.Element;
     24 import android.renderscript.RenderScript;
     25 import android.renderscript.Type;
     26 import android.util.Size;
     27 import android.view.Surface;
     28 
     29 /**
     30  * Renderscript-based merger for an HDR viewfinder
     31  */
     32 public class ViewfinderProcessor {
     33 
     34     private Allocation mInputHdrAllocation;
     35     private Allocation mInputNormalAllocation;
     36     private Allocation mPrevAllocation;
     37     private Allocation mOutputAllocation;
     38 
     39     private Surface mOutputSurface;
     40     private HandlerThread mProcessingThread;
     41     private Handler mProcessingHandler;
     42     private ScriptC_hdr_merge mHdrMergeScript;
     43 
     44     public ProcessingTask mHdrTask;
     45     public ProcessingTask mNormalTask;
     46 
     47     private Size mSize;
     48 
     49     private int mMode;
     50 
     51     public final static int MODE_NORMAL = 0;
     52     public final static int MODE_SIDE_BY_SIDE = 1;
     53     public final static int MODE_HDR = 2;
     54 
     55     public ViewfinderProcessor(RenderScript rs, Size dimensions) {
     56         mSize = dimensions;
     57 
     58         Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs));
     59         yuvTypeBuilder.setX(dimensions.getWidth());
     60         yuvTypeBuilder.setY(dimensions.getHeight());
     61         yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
     62         mInputHdrAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
     63                 Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
     64         mInputNormalAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(),
     65                 Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT);
     66 
     67         Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs));
     68         rgbTypeBuilder.setX(dimensions.getWidth());
     69         rgbTypeBuilder.setY(dimensions.getHeight());
     70         mPrevAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
     71                 Allocation.USAGE_SCRIPT);
     72         mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(),
     73                 Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT);
     74 
     75         mProcessingThread = new HandlerThread("ViewfinderProcessor");
     76         mProcessingThread.start();
     77         mProcessingHandler = new Handler(mProcessingThread.getLooper());
     78 
     79         mHdrMergeScript = new ScriptC_hdr_merge(rs);
     80 
     81         mHdrMergeScript.set_gPrevFrame(mPrevAllocation);
     82 
     83         mHdrTask = new ProcessingTask(mInputHdrAllocation, dimensions.getWidth()/2, true);
     84         mNormalTask = new ProcessingTask(mInputNormalAllocation, 0, false);
     85 
     86         setRenderMode(MODE_NORMAL);
     87     }
     88 
     89     public Surface getInputHdrSurface() {
     90         return mInputHdrAllocation.getSurface();
     91     }
     92 
     93     public Surface getInputNormalSurface() {
     94         return mInputNormalAllocation.getSurface();
     95     }
     96 
     97     public void setOutputSurface(Surface output) {
     98         mOutputAllocation.setSurface(output);
     99     }
    100 
    101     public void setRenderMode(int mode) {
    102         mMode = mode;
    103     }
    104 
    105     /**
    106      * Simple class to keep track of incoming frame count,
    107      * and to process the newest one in the processing thread
    108      */
    109     class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener {
    110         private int mPendingFrames = 0;
    111         private int mFrameCounter = 0;
    112         private int mCutPointX;
    113         private boolean mCheckMerge;
    114 
    115         private Allocation mInputAllocation;
    116 
    117         public ProcessingTask(Allocation input, int cutPointX, boolean checkMerge) {
    118             mInputAllocation = input;
    119             mInputAllocation.setOnBufferAvailableListener(this);
    120             mCutPointX = cutPointX;
    121             mCheckMerge = checkMerge;
    122         }
    123 
    124         @Override
    125         public void onBufferAvailable(Allocation a) {
    126             synchronized(this) {
    127                 mPendingFrames++;
    128                 mProcessingHandler.post(this);
    129             }
    130         }
    131 
    132         @Override
    133         public void run() {
    134 
    135             // Find out how many frames have arrived
    136             int pendingFrames;
    137             synchronized(this) {
    138                 pendingFrames = mPendingFrames;
    139                 mPendingFrames = 0;
    140 
    141                 // Discard extra messages in case processing is slower than frame rate
    142                 mProcessingHandler.removeCallbacks(this);
    143             }
    144 
    145             // Get to newest input
    146             for (int i = 0; i < pendingFrames; i++) {
    147                 mInputAllocation.ioReceive();
    148             }
    149 
    150             mHdrMergeScript.set_gFrameCounter(mFrameCounter++);
    151             mHdrMergeScript.set_gCurrentFrame(mInputAllocation);
    152             mHdrMergeScript.set_gCutPointX(mCutPointX);
    153             if (mCheckMerge && mMode == MODE_HDR) {
    154                 mHdrMergeScript.set_gDoMerge(1);
    155             } else {
    156                 mHdrMergeScript.set_gDoMerge(0);
    157             }
    158 
    159             // Run processing pass
    160             mHdrMergeScript.forEach_mergeHdrFrames(mPrevAllocation, mOutputAllocation);
    161             mOutputAllocation.ioSend();
    162         }
    163     }
    164 
    165 }
    166