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