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