| /* |
| * Copyright (C) 2014 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.example.android.hdrviewfinder; |
| |
| import android.graphics.ImageFormat; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.renderscript.Allocation; |
| import android.renderscript.Element; |
| import android.renderscript.RenderScript; |
| import android.renderscript.Type; |
| import android.util.Size; |
| import android.view.Surface; |
| |
| /** |
| * Renderscript-based merger for an HDR viewfinder |
| */ |
| public class ViewfinderProcessor { |
| |
| private Allocation mInputHdrAllocation; |
| private Allocation mInputNormalAllocation; |
| private Allocation mPrevAllocation; |
| private Allocation mOutputAllocation; |
| |
| private Surface mOutputSurface; |
| private HandlerThread mProcessingThread; |
| private Handler mProcessingHandler; |
| private ScriptC_hdr_merge mHdrMergeScript; |
| |
| public ProcessingTask mHdrTask; |
| public ProcessingTask mNormalTask; |
| |
| private Size mSize; |
| |
| private int mMode; |
| |
| public final static int MODE_NORMAL = 0; |
| public final static int MODE_SIDE_BY_SIDE = 1; |
| public final static int MODE_HDR = 2; |
| |
| public ViewfinderProcessor(RenderScript rs, Size dimensions) { |
| mSize = dimensions; |
| |
| Type.Builder yuvTypeBuilder = new Type.Builder(rs, Element.YUV(rs)); |
| yuvTypeBuilder.setX(dimensions.getWidth()); |
| yuvTypeBuilder.setY(dimensions.getHeight()); |
| yuvTypeBuilder.setYuvFormat(ImageFormat.YUV_420_888); |
| mInputHdrAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(), |
| Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT); |
| mInputNormalAllocation = Allocation.createTyped(rs, yuvTypeBuilder.create(), |
| Allocation.USAGE_IO_INPUT | Allocation.USAGE_SCRIPT); |
| |
| Type.Builder rgbTypeBuilder = new Type.Builder(rs, Element.RGBA_8888(rs)); |
| rgbTypeBuilder.setX(dimensions.getWidth()); |
| rgbTypeBuilder.setY(dimensions.getHeight()); |
| mPrevAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(), |
| Allocation.USAGE_SCRIPT); |
| mOutputAllocation = Allocation.createTyped(rs, rgbTypeBuilder.create(), |
| Allocation.USAGE_IO_OUTPUT | Allocation.USAGE_SCRIPT); |
| |
| mProcessingThread = new HandlerThread("ViewfinderProcessor"); |
| mProcessingThread.start(); |
| mProcessingHandler = new Handler(mProcessingThread.getLooper()); |
| |
| mHdrMergeScript = new ScriptC_hdr_merge(rs); |
| |
| mHdrMergeScript.set_gPrevFrame(mPrevAllocation); |
| |
| mHdrTask = new ProcessingTask(mInputHdrAllocation, dimensions.getWidth()/2, true); |
| mNormalTask = new ProcessingTask(mInputNormalAllocation, 0, false); |
| |
| setRenderMode(MODE_NORMAL); |
| } |
| |
| public Surface getInputHdrSurface() { |
| return mInputHdrAllocation.getSurface(); |
| } |
| |
| public Surface getInputNormalSurface() { |
| return mInputNormalAllocation.getSurface(); |
| } |
| |
| public void setOutputSurface(Surface output) { |
| mOutputAllocation.setSurface(output); |
| } |
| |
| public void setRenderMode(int mode) { |
| mMode = mode; |
| } |
| |
| /** |
| * Simple class to keep track of incoming frame count, |
| * and to process the newest one in the processing thread |
| */ |
| class ProcessingTask implements Runnable, Allocation.OnBufferAvailableListener { |
| private int mPendingFrames = 0; |
| private int mFrameCounter = 0; |
| private int mCutPointX; |
| private boolean mCheckMerge; |
| |
| private Allocation mInputAllocation; |
| |
| public ProcessingTask(Allocation input, int cutPointX, boolean checkMerge) { |
| mInputAllocation = input; |
| mInputAllocation.setOnBufferAvailableListener(this); |
| mCutPointX = cutPointX; |
| mCheckMerge = checkMerge; |
| } |
| |
| @Override |
| public void onBufferAvailable(Allocation a) { |
| synchronized(this) { |
| mPendingFrames++; |
| mProcessingHandler.post(this); |
| } |
| } |
| |
| @Override |
| public void run() { |
| |
| // Find out how many frames have arrived |
| int pendingFrames; |
| synchronized(this) { |
| pendingFrames = mPendingFrames; |
| mPendingFrames = 0; |
| |
| // Discard extra messages in case processing is slower than frame rate |
| mProcessingHandler.removeCallbacks(this); |
| } |
| |
| // Get to newest input |
| for (int i = 0; i < pendingFrames; i++) { |
| mInputAllocation.ioReceive(); |
| } |
| |
| mHdrMergeScript.set_gFrameCounter(mFrameCounter++); |
| mHdrMergeScript.set_gCurrentFrame(mInputAllocation); |
| mHdrMergeScript.set_gCutPointX(mCutPointX); |
| if (mCheckMerge && mMode == MODE_HDR) { |
| mHdrMergeScript.set_gDoMerge(1); |
| } else { |
| mHdrMergeScript.set_gDoMerge(0); |
| } |
| |
| // Run processing pass |
| mHdrMergeScript.forEach_mergeHdrFrames(mPrevAllocation, mOutputAllocation); |
| mOutputAllocation.ioSend(); |
| } |
| } |
| |
| } |