/* The contents of this file are subject to the Mozilla Public License Version 1.1 (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.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is collection of files collectively known as Open Camera. The Initial Developer of the Original Code is Almalence Inc. Portions created by Initial Developer are Copyright (C) 2013 by Almalence Inc. All Rights Reserved. */ package com.almalence.plugins.capture.night; import java.nio.FloatBuffer; import java.util.Arrays; import java.util.Date; import java.util.List; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.AlertDialog; import android.content.SharedPreferences; import android.hardware.camera2.CaptureResult; import android.opengl.GLES10; import android.opengl.GLU; import android.os.Message; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceChangeListener; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; import android.util.Log; import android.util.Range; import android.view.Gravity; import android.widget.LinearLayout; import android.widget.Toast; import com.almalence.SwapHeap; /* <!-- +++ import com.almalence.opencam_plus.cameracontroller.CameraController; import com.almalence.opencam_plus.CameraParameters; import com.almalence.opencam_plus.ApplicationScreen; import com.almalence.opencam_plus.PluginCapture; import com.almalence.opencam_plus.PluginManager; import com.almalence.opencam_plus.R; import com.almalence.opencam_plus.ui.GUI.CameraParameter; import com.almalence.opencam_plus.ApplicationInterface; +++ --> */ // <!-- -+- import com.almalence.opencam.CameraParameters; import com.almalence.opencam.ApplicationScreen; import com.almalence.opencam.PluginCapture; import com.almalence.opencam.PluginManager; import com.almalence.opencam.cameracontroller.CameraController; import com.almalence.opencam.ui.GUI.CameraParameter; import com.almalence.opencam.R; import com.almalence.opencam.ApplicationInterface; //-+- --> import com.almalence.plugins.processing.night.AlmaShotNight; import com.almalence.util.ImageConversion; /*** * Implements night capture plugin - different capture logics available ***/ public class NightCapturePlugin extends PluginCapture { private static final int HI_RES_FRAMES = 8; private static Toast capturingDialog; // shared between activities private static int total_frames; private boolean usingCamera2API; private boolean usingSuperMode; private boolean takingImageForExposure; // Night vision variables private GLCameraPreview cameraPreview; private byte[] data1; private byte[] data2; private byte[] dataS; private byte[] dataRotated; int onDrawFrameCounter = 1; int[] cameraTexture; // size of a texture must be a power of 2 byte[] glCameraFrame = new byte[256 * 256]; FloatBuffer cubeBuff; FloatBuffer texBuff; private byte[] yuvData; float currHalfWidth; float currHalfHeight; float cameraDist; private long minExposure = 1000; private int minSensitivity = 100; private int sensorGain = 0; private int burstGain = 0; private long exposureTime = 0; private int frameForExposure = 0; // preferences private static String FocusPreference; private static boolean OpenGLPreference = false; private int preferenceSceneMode; private int preferenceFocusMode; private int preferenceFlashMode; private static String nightVisionLayerShowPref; private static String nightCaptureFocusPref; public NightCapturePlugin() { super("com.almalence.plugins.nightcapture", R.xml.preferences_capture_night, R.xml.preferences_capture_night_more, R.drawable.plugin_capture_night_nightvision_on, ApplicationScreen.getAppResources().getString(R.string.NightVisionOn)); } @Override @TargetApi(21) public void onCreate() { nightVisionLayerShowPref = ApplicationScreen.getAppResources().getString(R.string.NightVisionLayerShow); nightCaptureFocusPref = ApplicationScreen.getAppResources().getString(R.string.NightCaptureFocusPref); } @Override @TargetApi(21) public void onStart() { //Between switches from camera1 to camera2 APIs via preference screen application doesn't calls onCreate //Re-initialize camera2 detection variables again. usingCamera2API = CameraController.isUseCamera2(); usingSuperMode = CameraController.isUseSuperMode(); if (usingSuperMode) { minExposure = CameraController.getMinimumExposureTime(); minSensitivity = CameraController.getMinimumSensitivity(); if (minExposure < 1000) minExposure = 1000; // not expecting minimum exposure to be below 1usec if (minSensitivity < 25) minSensitivity = 25; // not expecting minimum sensitivity to be below ISO25 } else { cameraPreview = new GLCameraPreview(ApplicationScreen.getMainContext()); } nightVisionLayerShowPref = ApplicationScreen.getAppResources().getString(R.string.NightVisionLayerShow); nightCaptureFocusPref = ApplicationScreen.getAppResources().getString(R.string.NightCaptureFocusPref); getPrefs(); if (OpenGLPreference) { quickControlIconID = R.drawable.plugin_capture_night_nightvision_on; quickControlTitle = ApplicationScreen.getAppResources().getString(R.string.NightVisionOn); } else { quickControlIconID = R.drawable.plugin_capture_night_nightvision_off; quickControlTitle = ApplicationScreen.getAppResources().getString(R.string.NightVisionOff); } if (usingSuperMode) { OpenGLPreference = false; quickControlIconID = -1; } } @Override public void onResume() { inCapture = false; aboutToTakePicture = false; isAllImagesTaken = false; isAllCaptureResultsCompleted = true; ApplicationScreen.instance.muteShutter(false); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); preferenceSceneMode = ApplicationScreen.instance.getSceneModePref(); preferenceFocusMode = ApplicationScreen.instance.getFocusModePref(ApplicationScreen.sDefaultFocusValue); preferenceFlashMode = ApplicationScreen.instance.getFlashModePref(ApplicationScreen.sDefaultFlashValue); if(usingSuperMode) { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(ApplicationScreen.sFlashModePref, CameraParameters.FLASH_MODE_OFF); editor.commit(); } ApplicationScreen.setCaptureFormat(CameraController.YUV); data1 = null; data2 = null; dataS = null; yuvData = null; } @Override public void onPause() { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); ApplicationScreen.instance.setSceneModePref(preferenceSceneMode); if(!usingSuperMode) ApplicationScreen.instance.setFocusModePref(preferenceFocusMode); prefs.edit().putInt(ApplicationScreen.sFlashModePref, preferenceFlashMode).commit(); CameraController.setCameraSceneMode(preferenceSceneMode); if(!usingSuperMode) CameraController.setCameraFocusMode(preferenceFocusMode); CameraController.setCameraFlashMode(preferenceFlashMode); if (OpenGLPreference) { Message msg = new Message(); msg.what = ApplicationInterface.MSG_OPENGL_LAYER_HIDE; ApplicationScreen.getMessageHandler().sendMessage(msg); } } @Override public void onGUICreate() { // FixMe: why are we doing it via ApplicationScreen and not directly via guiManager? if (!usingSuperMode) { ApplicationScreen.instance.disableCameraParameter(CameraParameter.CAMERA_PARAMETER_FOCUS, true, false, true); } ApplicationScreen.instance.disableCameraParameter(CameraParameter.CAMERA_PARAMETER_SCENE, true, true, true); ApplicationScreen.instance.disableCameraParameter(CameraParameter.CAMERA_PARAMETER_FLASH, true, false, true); } @Override public boolean needPreviewFrame() { if (usingSuperMode) return false; else return true; } @Override public boolean isGLSurfaceNeeded() { if (!usingSuperMode) return true; else return false; } @Override public void onQuickControlClick() { Message msg = new Message(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); SharedPreferences.Editor editor = prefs.edit(); if (quickControlIconID == R.drawable.plugin_capture_night_nightvision_on) { quickControlIconID = R.drawable.plugin_capture_night_nightvision_off; quickControlTitle = ApplicationScreen.getAppResources().getString(R.string.NightVisionOff); editor.putBoolean(nightVisionLayerShowPref, false); editor.commit(); OpenGLPreference = false; data1 = null; data2 = null; dataS = null; dataRotated = null; yuvData = null; msg.what = ApplicationInterface.MSG_OPENGL_LAYER_HIDE; } else if (quickControlIconID == R.drawable.plugin_capture_night_nightvision_off) { quickControlIconID = R.drawable.plugin_capture_night_nightvision_on; quickControlTitle = ApplicationScreen.getAppResources().getString(R.string.NightVisionOn); editor.putBoolean(nightVisionLayerShowPref, true); editor.commit(); OpenGLPreference = true; msg.what = ApplicationInterface.MSG_OPENGL_LAYER_SHOW; } ApplicationScreen.getMessageHandler().sendMessage(msg); } @SuppressLint("CommitPrefEdits") private void getPrefs() { String defaultFocus = "0"; // Get the xml/preferences.xml preferences SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); FocusPreference = prefs.getString(nightCaptureFocusPref, defaultFocus); if (!usingSuperMode) { if (CameraController.isNexus6) OpenGLPreference = prefs.getBoolean(nightVisionLayerShowPref, false); else OpenGLPreference = prefs.getBoolean(nightVisionLayerShowPref, false);//set to false. sm. 5/11/15 } else OpenGLPreference = false; } @Override public void onDefaultsSelect() { String defaultFocus = "0"; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); FocusPreference = prefs.getString(nightCaptureFocusPref, defaultFocus); } @Override public void onShowPreferences() { String defaultFocus = "0"; SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); FocusPreference = prefs.getString(nightCaptureFocusPref, defaultFocus); } @Override public void selectImageDimension() { //max size will be used in supermode int captureIndex = 0; if (!usingSuperMode) captureIndex = ApplicationScreen.instance.selectImageDimensionMultishot(); int imgCaptureWidth = CameraController.MultishotResolutionsSizeList.get(captureIndex).getWidth(); int imgCaptureHeight = CameraController.MultishotResolutionsSizeList.get(captureIndex).getHeight(); CameraController.setCameraImageSize(new CameraController.Size(imgCaptureWidth, imgCaptureHeight)); Log.i("NightCapturePlugin", "NightCapturePlugin.setCameraImageSize SX = " + imgCaptureWidth + " SY = " + imgCaptureHeight); } @Override public void setCameraPreviewSize() { List<CameraController.Size> cs = CameraController.getSupportedPreviewSizes(); CameraController.Size imageSize = CameraController.getCameraImageSize(); CameraController.Size os = getOptimalPreviewSize(cs, imageSize.getWidth(), imageSize.getHeight()); ApplicationScreen.instance.setCameraPreviewSize(os.getWidth(), os.getHeight()); } @Override public void setupCameraParameters() { CameraController.Size imageSize = CameraController.getCameraImageSize(); CameraController.setPictureSize(imageSize.getWidth(), imageSize.getHeight()); // ensure image data coming as intact as possible CameraController.setJpegQuality(100); int[] sceneModes = CameraController.getSupportedSceneModes(); // Note: excluding Nexus here because it will fire flash in night mode (?) // exlude also LG G3 and some others modifications to prevent camera error in takePicture call. // FixMe: probably Nexus should not be excluded if using Camera2 interface if (sceneModes != null && CameraController.isModeAvailable(sceneModes, CameraParameters.SCENE_MODE_NIGHT) && (!CameraController.isNexus && !CameraController.isG3) && !usingSuperMode) { CameraController.setCameraSceneMode(CameraParameters.SCENE_MODE_NIGHT); ApplicationScreen.instance.setSceneModePref(CameraParameters.SCENE_MODE_NIGHT); } try { int[] focusModes = CameraController.getSupportedFocusModes(); if (focusModes != null) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); SharedPreferences.Editor editor = prefs.edit(); if(!usingSuperMode) { if (FocusPreference.compareTo("0") == 0) { if (CameraController.isModeAvailable(focusModes, CameraParameters.AF_MODE_FIXED)) { // should set to hyperfocal distance as per android doc CameraController.setCameraFocusMode(CameraParameters.AF_MODE_FIXED); editor.putInt(CameraController.isFrontCamera() ? ApplicationScreen.sRearFocusModePref : ApplicationScreen.sFrontFocusModePref, CameraParameters.AF_MODE_FIXED); } else if (CameraController.isModeAvailable(focusModes, CameraParameters.AF_MODE_AUTO)) { CameraController.setCameraFocusMode(CameraParameters.AF_MODE_AUTO); editor.putInt(CameraController.isFrontCamera() ? ApplicationScreen.sRearFocusModePref : ApplicationScreen.sFrontFocusModePref, CameraParameters.AF_MODE_AUTO); editor.putString(nightCaptureFocusPref, "1"); FocusPreference = "1"; } } else if (CameraController.isModeAvailable(focusModes, CameraParameters.AF_MODE_AUTO)) { CameraController.setCameraFocusMode(CameraParameters.AF_MODE_AUTO); editor.putInt(CameraController.isFrontCamera() ? ApplicationScreen.sFrontFocusModePref : ApplicationScreen.sRearFocusModePref, CameraParameters.AF_MODE_AUTO); } editor.commit(); } ApplicationScreen.instance.setSceneModePref(CameraController.getSceneMode()); } Log.i("NightCapturePlugin", "ApplicationScreen.setupCamera setFocusMode success"); } catch (RuntimeException e) { Log.e("NightCapturePlugin", "ApplicationScreen.setupCamera unable to setFocusMode"); } try { int[] flashModes = CameraController.getSupportedFlashModes(); if (flashModes != null && flashModes.length > 0) { CameraController.setCameraSceneMode(CameraParameters.SCENE_MODE_AUTO); CameraController.setCameraFlashMode(CameraParameters.FLASH_MODE_OFF); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ApplicationScreen.getMainContext()); SharedPreferences.Editor editor = prefs.edit(); editor.putInt(ApplicationScreen.sFlashModePref, CameraParameters.FLASH_MODE_OFF); editor.commit(); } } catch (RuntimeException e) { Log.e("Night", "ApplicationScreen.setupCamera unable to setFlashMode"); } } @Override public void onCameraSetup() { Message msg = new Message(); if (OpenGLPreference) msg.what = ApplicationInterface.MSG_OPENGL_LAYER_SHOW; else msg.what = ApplicationInterface.MSG_OPENGL_LAYER_HIDE; ApplicationScreen.getMessageHandler().sendMessage(msg); } @Override public void onPreferenceCreate(PreferenceFragment prefActivity) { final PreferenceFragment mPref = prefActivity; Preference fp = prefActivity.findPreference(nightCaptureFocusPref); if (fp != null) { fp.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object focus_new) { int new_value = Integer.parseInt(focus_new.toString()); if ((new_value == 0) && CameraController.getSupportedFocusModes() != null && !CameraController.isModeAvailable(CameraController .getSupportedFocusModes(), CameraParameters.AF_MODE_FIXED)) { new AlertDialog.Builder(mPref.getActivity()) .setIcon(R.drawable.gui_almalence_alert_dialog_icon) .setTitle(R.string.Pref_NightCapture_FocusModeAlert_Title) .setMessage(R.string.Pref_NightCapture_FocusModeAlert_Msg) .setPositiveButton(android.R.string.ok, null).create().show(); ((ListPreference) preference).setValue("1"); return false; } return true; } }); } } public boolean delayedCaptureSupported() { return true; } @Override public void onShutterClick() { if (!inCapture) { inCapture = true; if (!aboutToTakePicture) startCaptureSequence(); } } private void startCaptureSequence() { ApplicationScreen.instance.muteShutter(true); Date curDate = new Date(); SessionID = curDate.getTime(); LinearLayout bottom_layout = (LinearLayout) ApplicationScreen.instance.findViewById(R.id.mainButtons); if (!usingCamera2API) { capturingDialog = Toast.makeText(ApplicationScreen.instance, R.string.hold_still, Toast.LENGTH_SHORT); capturingDialog.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, bottom_layout.getHeight()); capturingDialog.show(); } // reiniting for every shutter press imagesTaken = 0; total_frames = HI_RES_FRAMES; if (!usingSuperMode && FocusPreference.compareTo("0") == 0) takePicture(); else { if(CameraController.isAutoFocusPerform()) aboutToTakePicture = true; else takePicture(); } } // there is no warranty what comes first: // onImageTaken or onCaptureCompleted, so // this function can be called from either once both were called @TargetApi(21) public void AdjustExposureCaptureBurst() { takingImageForExposure = false; // analyze captured frame and determine if clipping correction needed // only scan cropped area CameraController.Size imageSize = CameraController.getCameraImageSize(); int imageWidth = imageSize.getWidth(); int imageHeight = imageSize.getHeight(); float zoom = CameraController.getZoom(); int w = (int)(imageWidth/zoom); int h = (int)(imageHeight/zoom); int x0 = imageWidth/2-w/2; int y0 = imageHeight/2-h/2; boolean clipped = AlmaShotNight.CheckClipping(frameForExposure, imageWidth, imageHeight, x0, y0, w, h); // free memory allocated for the frame SwapHeap.FreeFromHeap(frameForExposure); int currentSensitivity = CameraController.getCurrentSensitivity(); long currentExposure = CameraController.getCameraExposureTime(); burstGain = Math.max(currentSensitivity, minSensitivity); long burstExposure = currentExposure; if (clipped) { // find updated exposure and ISO parameters // Exposure compensation is not working in an optimal way // (appear to be changing exposure time, while it is optimal for us to reduce ISO, if possible) float fUnclip = -0.7f; float UnclipLinear = (float)Math.pow(2, -fUnclip); // first - attempt to reduce sensor ISO, but only if exposure time is short (<50msec) if ((currentSensitivity > minSensitivity) && (currentExposure <= 50000000)) { if (currentSensitivity / UnclipLinear < minSensitivity) { burstGain = minSensitivity; UnclipLinear /= currentSensitivity / minSensitivity; } else { burstGain = (int)(currentSensitivity / UnclipLinear); UnclipLinear = 1.0f; } } // if ISO reduction is not enough - decrease exposure time if (minExposure < currentExposure / UnclipLinear) { burstExposure = (long)(currentExposure / UnclipLinear); } else burstExposure = minExposure; int[] burstGainArray = new int[total_frames]; long[] burstExposureArray = new long[total_frames]; Arrays.fill(burstGainArray, burstGain); Arrays.fill(burstExposureArray, burstExposure); resultCompleted = 0; //Reset to get right capture result indexes in burst capturing. createRequestIDList(total_frames); // capture the burst CameraController.captureImagesWithParams( total_frames, CameraController.YUV_RAW, null, null, burstGainArray, burstExposureArray, true, true, true); } else { long[] burstExposureArray = null; //Samsung Galaxy S7 on Qualcomm chipset has special exposure compensation logic if (CameraController.isGalaxyS7Qualcomm) { burstExposure = calculateExposureCompensation(currentExposure, -1); burstExposureArray = new long[total_frames]; Arrays.fill(burstExposureArray, burstExposure); } resultCompleted = 0; //Reset to get right capture result indexes in burst capturing. createRequestIDList(total_frames); // capture the burst CameraController.captureImagesWithParams( total_frames, CameraController.YUV_RAW, null, null, null, burstExposureArray, true, true, true); } } //Helper function for super mode on Samsung Galaxy S7 taken from SuperSensor Demo code. @TargetApi(21) protected long calculateExposureCompensation(final long exposure, final float compensation) { final Range<Long> range = CameraController.getCameraExposureRange(); return range.clamp(exposureSec2Nanosec(exposureNanosec2Sec(exposure) / Math.pow(2.0d, -compensation))); } protected static long exposureSec2Nanosec(final double t) { return (long)(t * 1000000000); } protected static double exposureNanosec2Sec(final long t) { return t / 1000000000.0d; } final Object syncObject = new Object(); @Override public synchronized void onImageTaken(int frame, byte[] frameData, int frame_len, int format) { if (takingImageForExposure) { frameForExposure = frame; // Log.i("NightCapturePlugin", "frameForExposure arrived"); if (sensorGain>0 || exposureTime>0) AdjustExposureCaptureBurst(); } else { PluginManager.getInstance().addToSharedMem("frame" + (imagesTaken + 1) + SessionID, String.valueOf(frame)); PluginManager.getInstance().addToSharedMem("framelen" + (imagesTaken + 1) + SessionID, String.valueOf(frame_len)); // ToDo: there is no need to pass orientation for every frame, just for the first one // also, amountofcapturedframes can be set only once to total_frames PluginManager.getInstance().addToSharedMem("frameorientation" + (imagesTaken + 1) + SessionID, String.valueOf(ApplicationScreen.getGUIManager().getImageDataOrientation())); PluginManager.getInstance().addToSharedMem("framemirrored" + (imagesTaken + 1) + SessionID, String.valueOf(CameraController.isFrontCamera())); PluginManager.getInstance().addToSharedMem("amountofcapturedframes" + SessionID, String.valueOf(imagesTaken + 1)); // ToDo: should we also pass imageSize ? // On processing side image size is found via // CameraController.Size imageSize = CameraController.getCameraImageSize(); // Is there a possibility that imageSize will change when we reach processing stage (e.g. if there some other processing in the queue)? // Note: if capturing burst - these values can be wrong for the first frame (onCaptureCompleted happens after onImageAvailable) if (imagesTaken == total_frames-1) { PluginManager.getInstance().addToSharedMem("isSuperMode" + SessionID, String.valueOf(usingSuperMode)); // Note: a more memory-effective way would be to crop zoomed images right here // (only possible with Camera2) float zoom = CameraController.getZoom(); PluginManager.getInstance().addToSharedMem("zoom" + SessionID, String.valueOf(zoom)); // pass sensor gain to the image processing functions if it is known PluginManager.getInstance().addToSharedMem("burstGain" + SessionID, String.valueOf(burstGain)); } if (++imagesTaken == total_frames) { if(isAllCaptureResultsCompleted) { PluginManager.getInstance().sendMessage(ApplicationInterface.MSG_CAPTURE_FINISHED, String.valueOf(SessionID)); inCapture = false; resultCompleted = 0; imagesTaken = 0; isAllImagesTaken = false; } else isAllImagesTaken = true; } } } @TargetApi(21) @Override public synchronized void onCaptureCompleted(CaptureResult result) { //This is only one place in plugin's code where we can identify that capturing is occurs in camera2 mode isAllCaptureResultsCompleted = false; if(result.get(CaptureResult.SENSOR_SENSITIVITY) != null) sensorGain = result.get(CaptureResult.SENSOR_SENSITIVITY); if(result.get(CaptureResult.SENSOR_EXPOSURE_TIME) != null) exposureTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME); int requestID = requestIDArray[resultCompleted]; resultCompleted++; // Log.i("NightCapturePlugin", "onCaptureCompleted gain: "+sensorGain+" expoTime: "+exposureTime+"ns"); if (takingImageForExposure && (frameForExposure != 0)) AdjustExposureCaptureBurst(); if (result.getSequenceId() == requestID) PluginManager.getInstance().addToSharedMemExifTagsFromCaptureResult(result, SessionID, resultCompleted); if(resultCompleted == total_frames) { isAllCaptureResultsCompleted = true; if(isAllImagesTaken) { PluginManager.getInstance().sendMessage(ApplicationInterface.MSG_CAPTURE_FINISHED, String.valueOf(SessionID)); inCapture = false; resultCompleted = 0; imagesTaken = 0; isAllImagesTaken = false; } } } @Override public void onExportFinished() { } @Override public void takePicture() { // we do not know sensor gain and other parameters initially // they will be filled in onCaptureCompleted and onImageTaken sensorGain = 0; exposureTime = 0; frameForExposure = 0; resultCompleted = 0; imagesTaken = 0; if (usingSuperMode) { // ToDo: implement waiting for lens to finish it's focusing movement (like in camera3test) // capture single YUV image to figure out correct ISO/exposure for the consequent burst capture Log.wtf("SUPER", "takePicture. First frame. create IDList size 1"); createRequestIDList(1); takingImageForExposure = true; CameraController.captureImagesWithParams(1, CameraController.YUV_RAW, null, null, null, null, true, true, false); } else { createRequestIDList(total_frames); takingImageForExposure = false; CameraController.captureImagesWithParams(total_frames, CameraController.YUV_RAW, null, null, null, null, false, true, true); } } // onPreviewFrame is used to collect frames for brightened VF output @Override public void onPreviewFrame(byte[] data) { if (!usingSuperMode) { if (OpenGLPreference && !inCapture) { if (data1 == null) data1 = data; else if (data2 == null) { data2 = data; if (dataS == null) dataS = new byte[data2.length]; else if (dataS.length < data2.length) dataS = new byte[data2.length]; int imageWidth = ApplicationScreen.getPreviewWidth(); int imageHeight = ApplicationScreen.getPreviewHeight(); ImageConversion.sumByteArraysNV21(data1, data2, dataS, imageWidth, imageHeight); boolean cameraMirrored = CameraController.isFrontCamera(); int sensorOrientation = CameraController.getSensorOrientation(cameraMirrored); int flipLR = 0; int flipUD = 0; //Values of flipLR and flipUD was get from tests of devices with different sensor orientation //Right now it doesn't based on some sort of calculation. switch(sensorOrientation) { case 90: { if(cameraMirrored) flipUD = 1; } break; case 270: { if(cameraMirrored) flipLR = 1; else { flipLR = 1; flipUD = 1; } } break; } if(flipLR == 1 || flipUD == 1) //If any transform is need { dataRotated = new byte[dataS.length]; ImageConversion.TransformNV21(dataS, dataRotated, imageWidth, imageHeight, flipLR, flipUD, 0); yuvData = dataRotated; } else yuvData = dataS; data1 = data2; data2 = null; } } else if (inCapture && data1 != null) { data1 = null; data2 = null; dataS = null; } } } /****************************************************************************************************** * OpenGL layer functions ******************************************************************************************************/ @Override public void onGLSurfaceCreated(GL10 gl, EGLConfig config) { if (!usingSuperMode) { cameraPreview.generateGLTexture(gl); gl.glEnable(GL10.GL_TEXTURE_2D); // Enable Texture Mapping ( NEW ) gl.glShadeModel(GL10.GL_SMOOTH); // Enable Smooth Shading gl.glLineWidth(4.0f); gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background gl.glClearDepthf(1.0f); // Depth Buffer Setup gl.glEnable(GL10.GL_DEPTH_TEST); // Enables Depth Testing gl.glDepthFunc(GL10.GL_LEQUAL); // The Type Of Depth Testing To Do gl.glBlendFunc(GL10.GL_SRC_ALPHA, GLES10.GL_ONE); // Really Nice Perspective Calculations gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); } } @Override public void onGLSurfaceChanged(GL10 gl, int width, int height) { if (!usingSuperMode) { if (height == 0) { // Prevent A Divide By Zero By height = 1; // Making Height Equal One } currHalfWidth = width / 2; currHalfHeight = height / 2; cameraDist = (float) (currHalfHeight / Math.tan(Math.toRadians(45.0f / 2.0f))); gl.glViewport(0, 0, width, height); // Reset The Current Viewport gl.glMatrixMode(GL10.GL_PROJECTION); // Select The Projection Matrix gl.glLoadIdentity(); // Reset The Projection Matrix // Calculate The Aspect Ratio Of The Window GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, cameraDist / 10.0f, cameraDist * 10.0f); gl.glMatrixMode(GL10.GL_MODELVIEW); // Select The Modelview Matrix gl.glLoadIdentity(); // Reset The Modelview Matrix cameraPreview.setSurfaceSize(width, height); } } @Override public void onGLDrawFrame(GL10 gl) { if (!usingSuperMode) { // Clear Screen And Depth Buffer gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glLoadIdentity(); // Drawing gl.glTranslatef(0.0f, 0.0f, -(cameraDist)); // Move 5 units into the // screen gl.glRotatef(-90, 0.0f, 0.0f, 1.0f); // Z if (OpenGLPreference && !inCapture && yuvData != null) synchronized (this) { try { // Draw the square cameraPreview.draw(gl, yuvData, ApplicationScreen.getMainContext()); } catch (RuntimeException e) { Log.e("onGLDrawFrame", "onGLDrawFrame in Night some exception" + e.getMessage()); } } } } public boolean photoTimeLapseCaptureSupported() { return true; } }