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