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
17package com.example.android.hdrviewfinder;
18
19import android.graphics.ImageFormat;
20import android.os.Handler;
21import android.os.HandlerThread;
22import android.renderscript.Allocation;
23import android.renderscript.Element;
24import android.renderscript.RenderScript;
25import android.renderscript.Type;
26import android.util.Size;
27import android.view.Surface;
28
29/**
30 * Renderscript-based merger for an HDR viewfinder
31 */
32public 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