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 
ViewfinderProcessor(RenderScript rs, Size dimensions)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 
getInputHdrSurface()89     public Surface getInputHdrSurface() {
90         return mInputHdrAllocation.getSurface();
91     }
92 
getInputNormalSurface()93     public Surface getInputNormalSurface() {
94         return mInputNormalAllocation.getSurface();
95     }
96 
setOutputSurface(Surface output)97     public void setOutputSurface(Surface output) {
98         mOutputAllocation.setSurface(output);
99     }
100 
setRenderMode(int mode)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 
ProcessingTask(Allocation input, int cutPointX, boolean checkMerge)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
onBufferAvailable(Allocation a)125         public void onBufferAvailable(Allocation a) {
126             synchronized(this) {
127                 mPendingFrames++;
128                 mProcessingHandler.post(this);
129             }
130         }
131 
132         @Override
run()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