blob: 78f41da02a8a1f7dbcf7dc69da9a11923ba3e512 [file] [log] [blame]
/*
* Copyright (C) 2019 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.android.cts.verifier.camera.bokeh;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import android.app.AlertDialog;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.Capability;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import android.util.SparseArray;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Surface;
import android.view.TextureView;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.content.Context;
import java.lang.Math;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
/**
* Tests for manual verification of bokeh modes supported by the camera device.
*/
public class CameraBokehActivity extends PassFailButtons.Activity
implements TextureView.SurfaceTextureListener,
ImageReader.OnImageAvailableListener {
private static final String TAG = "CameraBokehActivity";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int SESSION_READY_TIMEOUT_MS = 5000;
private static final Size FULLHD = new Size(1920, 1080);
private static final ColorMatrixColorFilter sJFIF_YUVToRGB_Filter =
new ColorMatrixColorFilter(new float[] {
1f, 0f, 1.402f, 0f, -179.456f,
1f, -0.34414f, -0.71414f, 0f, 135.46f,
1f, 1.772f, 0f, 0f, -226.816f,
0f, 0f, 0f, 1f, 0f
});
private TextureView mPreviewView;
private SurfaceTexture mPreviewTexture;
private Surface mPreviewSurface;
private int mPreviewTexWidth, mPreviewTexHeight;
private ImageView mImageView;
private ColorFilter mCurrentColorFilter;
private Spinner mCameraSpinner;
private TextView mTestLabel;
private TextView mPreviewLabel;
private TextView mImageLabel;
private CameraManager mCameraManager;
private String[] mCameraIdList;
private HandlerThread mCameraThread;
private Handler mCameraHandler;
private BlockingCameraManager mBlockingCameraManager;
private CameraCharacteristics mCameraCharacteristics;
private BlockingStateCallback mCameraListener;
private BlockingSessionCallback mSessionListener;
private CaptureRequest.Builder mPreviewRequestBuilder;
private CaptureRequest mPreviewRequest;
private CaptureRequest.Builder mStillCaptureRequestBuilder;
private CaptureRequest mStillCaptureRequest;
private HashMap<String, ArrayList<Capability>> mTestCases = new HashMap<>();
private int mCurrentCameraIndex = -1;
private String mCameraId;
private CameraCaptureSession mCaptureSession;
private CameraDevice mCameraDevice;
SizeComparator mSizeComparator = new SizeComparator();
private Size mPreviewSize;
private Size mJpegSize;
private ImageReader mJpegImageReader;
private ImageReader mYuvImageReader;
private SparseArray<String> mModeNames;
private CameraCombination mNextCombination;
private Size mMaxBokehStreamingSize;
private Button mNextButton;
private final TreeSet<CameraCombination> mTestedCombinations = new TreeSet<>(COMPARATOR);
private final TreeSet<CameraCombination> mUntestedCombinations = new TreeSet<>(COMPARATOR);
private final TreeSet<String> mUntestedCameras = new TreeSet<>();
// Menu to show the test progress
private static final int MENU_ID_PROGRESS = Menu.FIRST + 1;
private class CameraCombination {
private final int mCameraIndex;
private final int mMode;
private final Size mPreviewSize;
private final boolean mIsStillCapture;
private final String mCameraId;
private final String mModeName;
private CameraCombination(int cameraIndex, int mode,
int streamingWidth, int streamingHeight,
String cameraId, String modeName,
boolean isStillCapture) {
this.mCameraIndex = cameraIndex;
this.mMode = mode;
this.mPreviewSize = new Size(streamingWidth, streamingHeight);
this.mCameraId = cameraId;
this.mModeName = modeName;
this.mIsStillCapture = isStillCapture;
}
@Override
public String toString() {
return String.format("Camera %s, mode %s, intent %s",
mCameraId, mModeName, mIsStillCapture ? "PREVIEW" : "STILL_CAPTURE");
}
}
private static final Comparator<CameraCombination> COMPARATOR =
Comparator.<CameraCombination, Integer>comparing(c -> c.mCameraIndex)
.thenComparing(c -> c.mMode)
.thenComparing(c -> c.mIsStillCapture);
private CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureProgressed(CameraCaptureSession session,
CaptureRequest request,
CaptureResult partialResult) {
// Don't need to do anything here.
}
@Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request,
TotalCaptureResult result) {
// Don't need to do anything here.
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cb_main);
setPassFailButtonClickListeners();
mPreviewView = (TextureView) findViewById(R.id.preview_view);
mImageView = (ImageView) findViewById(R.id.image_view);
mPreviewView.setSurfaceTextureListener(this);
mCameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
mCameraIdList = mCameraManager.getCameraIdList();
for (String id : mCameraIdList) {
CameraCharacteristics characteristics =
mCameraManager.getCameraCharacteristics(id);
Key<Capability[]> key =
CameraCharacteristics.CONTROL_AVAILABLE_EXTENDED_SCENE_MODE_CAPABILITIES;
Capability[] extendedSceneModeCaps = characteristics.get(key);
if (extendedSceneModeCaps == null) {
continue;
}
ArrayList<Capability> nonOffModes = new ArrayList<>();
for (Capability cap : extendedSceneModeCaps) {
int mode = cap.getMode();
if (mode == CameraMetadata.CONTROL_EXTENDED_SCENE_MODE_BOKEH_STILL_CAPTURE ||
mode == CameraMetadata.CONTROL_EXTENDED_SCENE_MODE_BOKEH_CONTINUOUS) {
nonOffModes.add(cap);
}
}
if (nonOffModes.size() > 0) {
mUntestedCameras.add("All combinations for Camera " + id);
mTestCases.put(id, nonOffModes);
}
}
} catch (CameraAccessException e) {
e.printStackTrace();
}
// If no supported bokeh modes, mark the test as pass
if (mTestCases.size() == 0) {
setInfoResources(R.string.camera_bokeh_test, R.string.camera_bokeh_no_support, -1);
setPassButtonEnabled(true);
} else {
setInfoResources(R.string.camera_bokeh_test, R.string.camera_bokeh_test_info, -1);
// disable "Pass" button until all combinations are tested
setPassButtonEnabled(false);
}
Set<String> cameraIdSet = mTestCases.keySet();
String[] cameraNames = new String[cameraIdSet.size()];
int i = 0;
for (String id : cameraIdSet) {
cameraNames[i++] = "Camera " + id;
}
mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection);
mCameraSpinner.setAdapter(
new ArrayAdapter<String>(
this, R.layout.camera_list_item, cameraNames));
mCameraSpinner.setOnItemSelectedListener(mCameraSpinnerListener);
mTestLabel = (TextView) findViewById(R.id.test_label);
mPreviewLabel = (TextView) findViewById(R.id.preview_label);
mImageLabel = (TextView) findViewById(R.id.image_label);
// Must be kept in sync with camera bokeh mode manually
mModeNames = new SparseArray(2);
mModeNames.append(
CameraMetadata.CONTROL_EXTENDED_SCENE_MODE_BOKEH_STILL_CAPTURE, "STILL_CAPTURE");
mModeNames.append(
CameraMetadata.CONTROL_EXTENDED_SCENE_MODE_BOKEH_CONTINUOUS, "CONTINUOUS");
mNextButton = findViewById(R.id.next_button);
mNextButton.setOnClickListener(v -> {
if (mNextCombination != null) {
mUntestedCombinations.remove(mNextCombination);
mTestedCombinations.add(mNextCombination);
}
setUntestedCombination();
if (mNextCombination != null) {
if (mNextCombination.mIsStillCapture) {
takePicture();
} else {
if (mCaptureSession != null) {
mCaptureSession.close();
}
startPreview();
}
}
});
mBlockingCameraManager = new BlockingCameraManager(mCameraManager);
mCameraListener = new BlockingStateCallback();
}
/**
* Set an untested combination of resolution and bokeh mode for the current camera.
* Triggered by next button click.
*/
private void setUntestedCombination() {
Optional<CameraCombination> combination = mUntestedCombinations.stream().filter(
c -> c.mCameraIndex == mCurrentCameraIndex).findFirst();
if (!combination.isPresent()) {
Toast.makeText(this, "All Camera " + mCurrentCameraIndex + " tests are done.",
Toast.LENGTH_SHORT).show();
mNextCombination = null;
if (mUntestedCombinations.isEmpty() && mUntestedCameras.isEmpty()) {
setPassButtonEnabled(true);
}
return;
}
// There is untested combination for the current camera, set the next untested combination.
mNextCombination = combination.get();
int nextMode = mNextCombination.mMode;
ArrayList<Capability> bokehCaps = mTestCases.get(mCameraId);
for (Capability cap : bokehCaps) {
if (cap.getMode() == nextMode) {
mMaxBokehStreamingSize = cap.getMaxStreamingSize();
}
}
// Update bokeh mode and use case
String testString = "Mode: " + mModeNames.get(mNextCombination.mMode);
if (mNextCombination.mIsStillCapture) {
testString += "\nIntent: Capture";
} else {
testString += "\nIntent: Preview";
}
testString += "\n\nPress Next if the bokeh effect works as intended";
mTestLabel.setText(testString);
// Update preview view and image view bokeh expectation
boolean previewIsBokehCompatible =
mSizeComparator.compare(mNextCombination.mPreviewSize, mMaxBokehStreamingSize) <= 0;
String previewLabel = "Normal preview";
if (previewIsBokehCompatible || mNextCombination.mIsStillCapture) {
previewLabel += " with bokeh";
}
mPreviewLabel.setText(previewLabel);
String imageLabel;
if (mNextCombination.mIsStillCapture) {
imageLabel = "JPEG with bokeh";
} else {
imageLabel = "YUV";
if (previewIsBokehCompatible) {
imageLabel += " with bokeh";
}
}
mImageLabel.setText(imageLabel);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(Menu.NONE, MENU_ID_PROGRESS, Menu.NONE, "Current Progress");
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean ret = true;
switch (item.getItemId()) {
case MENU_ID_PROGRESS:
showCombinationsDialog();
ret = true;
break;
default:
ret = super.onOptionsItemSelected(item);
break;
}
return ret;
}
private void showCombinationsDialog() {
AlertDialog.Builder builder =
new AlertDialog.Builder(CameraBokehActivity.this);
builder.setMessage(getTestDetails())
.setTitle("Current Progress")
.setPositiveButton("OK", null);
builder.show();
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
int cameraIndex = mCameraSpinner.getSelectedItemPosition();
if (cameraIndex >= 0) {
setUpCamera(mCameraSpinner.getSelectedItemPosition());
}
}
@Override
public void onPause() {
shutdownCamera();
stopBackgroundThread();
super.onPause();
}
@Override
public String getTestDetails() {
StringBuilder reportBuilder = new StringBuilder();
reportBuilder.append("Tested combinations:\n");
for (CameraCombination combination: mTestedCombinations) {
reportBuilder.append(combination);
reportBuilder.append("\n");
}
reportBuilder.append("Untested cameras:\n");
for (String untestedCamera : mUntestedCameras) {
reportBuilder.append(untestedCamera);
reportBuilder.append("\n");
}
reportBuilder.append("Untested combinations:\n");
for (CameraCombination combination: mUntestedCombinations) {
reportBuilder.append(combination);
reportBuilder.append("\n");
}
return reportBuilder.toString();
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
int width, int height) {
mPreviewTexture = surfaceTexture;
mPreviewTexWidth = width;
mPreviewTexHeight = height;
mPreviewSurface = new Surface(mPreviewTexture);
if (mCameraDevice != null) {
startPreview();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Ignored, Camera does all the work for us
if (VERBOSE) {
Log.v(TAG, "onSurfaceTextureSizeChanged: " + width + " x " + height);
}
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mPreviewTexture = null;
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Invoked every time there's a new Camera preview frame
}
@Override
public void onImageAvailable(ImageReader reader) {
Image img = null;
try {
img = reader.acquireNextImage();
final int format = img.getFormat();
Size configuredSize = (format == ImageFormat.YUV_420_888 ? mPreviewSize : mJpegSize);
Bitmap imgBitmap = null;
if (format == ImageFormat.YUV_420_888) {
ByteBuffer yBuffer = img.getPlanes()[0].getBuffer();
ByteBuffer uBuffer = img.getPlanes()[1].getBuffer();
ByteBuffer vBuffer = img.getPlanes()[2].getBuffer();
yBuffer.rewind();
uBuffer.rewind();
vBuffer.rewind();
int w = configuredSize.getWidth();
int h = configuredSize.getHeight();
int stride = img.getPlanes()[0].getRowStride();
int uStride = img.getPlanes()[1].getRowStride();
int vStride = img.getPlanes()[2].getRowStride();
int uPStride = img.getPlanes()[1].getPixelStride();
int vPStride = img.getPlanes()[2].getPixelStride();
byte[] row = new byte[configuredSize.getWidth()];
byte[] uRow = new byte[(configuredSize.getWidth()/2-1)*uPStride + 1];
byte[] vRow = new byte[(configuredSize.getWidth()/2-1)*vPStride + 1];
int[] imgArray = new int[w * h];
for (int y = 0, j = 0, rowStart = 0, uRowStart = 0, vRowStart = 0; y < h;
y++, rowStart += stride) {
yBuffer.position(rowStart);
yBuffer.get(row);
if (y % 2 == 0) {
uBuffer.position(uRowStart);
uBuffer.get(uRow);
vBuffer.position(vRowStart);
vBuffer.get(vRow);
uRowStart += uStride;
vRowStart += vStride;
}
for (int x = 0, i = 0; x < w; x++) {
int yval = row[i] & 0xFF;
int uval = uRow[i/2 * uPStride] & 0xFF;
int vval = vRow[i/2 * vPStride] & 0xFF;
// Write YUV directly; the ImageView color filter will convert to RGB for us.
imgArray[j] = Color.rgb(yval, uval, vval);
i++;
j++;
}
}
img.close();
imgBitmap = Bitmap.createBitmap(imgArray, w, h, Bitmap.Config.ARGB_8888);
} else if (format == ImageFormat.JPEG) {
ByteBuffer jpegBuffer = img.getPlanes()[0].getBuffer();
jpegBuffer.rewind();
byte[] jpegData = new byte[jpegBuffer.limit()];
jpegBuffer.get(jpegData);
imgBitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length);
img.close();
} else {
Log.i(TAG, "Unsupported image format: " + format);
}
if (imgBitmap != null) {
final Bitmap bitmap = imgBitmap;
runOnUiThread(new Runnable() {
@Override
public void run() {
if (format == ImageFormat.YUV_420_888 && (mCurrentColorFilter == null ||
!mCurrentColorFilter.equals(sJFIF_YUVToRGB_Filter))) {
mCurrentColorFilter = sJFIF_YUVToRGB_Filter;
mImageView.setColorFilter(mCurrentColorFilter);
} else if (format == ImageFormat.JPEG && mCurrentColorFilter != null &&
mCurrentColorFilter.equals(sJFIF_YUVToRGB_Filter)) {
mCurrentColorFilter = null;
mImageView.clearColorFilter();
}
mImageView.setImageBitmap(bitmap);
}
});
}
} catch (java.lang.IllegalStateException e) {
// Swallow exceptions
e.printStackTrace();
} finally {
if (img != null) {
img.close();
}
}
}
private AdapterView.OnItemSelectedListener mCameraSpinnerListener =
new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
if (mCurrentCameraIndex != pos) {
setUpCamera(pos);
}
}
public void onNothingSelected(AdapterView parent) {
}
};
private class SizeComparator implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
long lha = lhs.getWidth() * lhs.getHeight();
long rha = rhs.getWidth() * rhs.getHeight();
if (lha == rha) {
lha = lhs.getWidth();
rha = rhs.getWidth();
}
return (lha < rha) ? -1 : (lha > rha ? 1 : 0);
}
}
private void setUpCamera(int index) {
shutdownCamera();
mCurrentCameraIndex = index;
mCameraId = mCameraIdList[index];
try {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
mCameraDevice = mBlockingCameraManager.openCamera(mCameraId,
mCameraListener, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (BlockingOpenException e) {
e.printStackTrace();
}
// Update untested cameras
mUntestedCameras.remove("All combinations for Camera " + mCameraId);
StreamConfigurationMap config =
mCameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] jpegSizes = config.getOutputSizes(ImageFormat.JPEG);
Arrays.sort(jpegSizes, mSizeComparator);
mJpegSize = jpegSizes[jpegSizes.length-1];
Size[] yuvSizes = config.getOutputSizes(ImageFormat.YUV_420_888);
Arrays.sort(yuvSizes, mSizeComparator);
Size maxYuvSize = yuvSizes[yuvSizes.length-1];
if (mSizeComparator.compare(maxYuvSize, FULLHD) > 1) {
maxYuvSize = FULLHD;
}
// Update untested entries
ArrayList<Capability> currentTestCase = mTestCases.get(mCameraId);
for (Capability bokehCap : currentTestCase) {
Size maxStreamingSize = bokehCap.getMaxStreamingSize();
Size previewSize;
if ((maxStreamingSize.getWidth() == 0 && maxStreamingSize.getHeight() == 0) ||
(mSizeComparator.compare(maxStreamingSize, maxYuvSize) > 0)) {
previewSize = maxYuvSize;
} else {
previewSize = maxStreamingSize;
}
CameraCombination combination = new CameraCombination(
index, bokehCap.getMode(), previewSize.getWidth(),
previewSize.getHeight(), mCameraId,
mModeNames.get(bokehCap.getMode()),
/*isStillCapture*/false);
if (!mTestedCombinations.contains(combination)) {
mUntestedCombinations.add(combination);
}
// For BOKEH_MODE_STILL_CAPTURE, add 2 combinations: one streaming, one still capture.
if (bokehCap.getMode() ==
CaptureRequest.CONTROL_EXTENDED_SCENE_MODE_BOKEH_STILL_CAPTURE) {
CameraCombination combination2 = new CameraCombination(
index, bokehCap.getMode(), previewSize.getWidth(),
previewSize.getHeight(), mCameraId,
mModeNames.get(bokehCap.getMode()),
/*isStillCapture*/true);
if (!mTestedCombinations.contains(combination2)) {
mUntestedCombinations.add(combination2);
}
}
}
mJpegImageReader = ImageReader.newInstance(
mJpegSize.getWidth(), mJpegSize.getHeight(), ImageFormat.JPEG, 1);
mJpegImageReader.setOnImageAvailableListener(this, mCameraHandler);
setUntestedCombination();
if (mPreviewTexture != null) {
startPreview();
}
}
private void shutdownCamera() {
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mJpegImageReader) {
mJpegImageReader.close();
mJpegImageReader = null;
}
if (null != mYuvImageReader) {
mYuvImageReader.close();
mYuvImageReader = null;
}
}
private void configurePreviewTextureTransform() {
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Configuration config = getResources().getConfiguration();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0: degrees = 0; break;
case Surface.ROTATION_90: degrees = 90; break;
case Surface.ROTATION_180: degrees = 180; break;
case Surface.ROTATION_270: degrees = 270; break;
}
Matrix matrix = mPreviewView.getTransform(null);
int deviceOrientation = Configuration.ORIENTATION_PORTRAIT;
if ((degrees % 180 == 0 && config.orientation == Configuration.ORIENTATION_LANDSCAPE) ||
(degrees % 180 == 90 && config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
deviceOrientation = Configuration.ORIENTATION_LANDSCAPE;
}
int effectiveWidth = mPreviewSize.getWidth();
int effectiveHeight = mPreviewSize.getHeight();
if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
int temp = effectiveWidth;
effectiveWidth = effectiveHeight;
effectiveHeight = temp;
}
RectF viewRect = new RectF(0, 0, mPreviewTexWidth, mPreviewTexHeight);
RectF bufferRect = new RectF(0, 0, effectiveWidth, effectiveHeight);
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
bufferRect.offset(centerX - bufferRect.centerX(),
centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
matrix.postRotate((360 - degrees) % 360, centerX, centerY);
if ((degrees % 180) == 90) {
int temp = effectiveWidth;
effectiveWidth = effectiveHeight;
effectiveHeight = temp;
}
// Scale to fit view, avoiding any crop
float scale = Math.min(mPreviewTexWidth / (float) effectiveWidth,
mPreviewTexHeight / (float) effectiveHeight);
matrix.postScale(scale, scale, centerX, centerY);
mPreviewView.setTransform(matrix);
}
/**
* Starts a background thread and its {@link Handler}.
*/
private void startBackgroundThread() {
mCameraThread = new HandlerThread("CameraBokehBackground");
mCameraThread.start();
mCameraHandler = new Handler(mCameraThread.getLooper());
}
/**
* Stops the background thread and its {@link Handler}.
*/
private void stopBackgroundThread() {
mCameraThread.quitSafely();
try {
mCameraThread.join();
mCameraThread = null;
mCameraHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void startPreview() {
try {
if (mPreviewSize == null || !mPreviewSize.equals(mNextCombination.mPreviewSize)) {
mPreviewSize = mNextCombination.mPreviewSize;
mYuvImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),
mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 1);
mYuvImageReader.setOnImageAvailableListener(this, mCameraHandler);
};
mPreviewTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
mPreviewRequestBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(mPreviewSurface);
mPreviewRequestBuilder.addTarget(mYuvImageReader.getSurface());
mStillCaptureRequestBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
mStillCaptureRequestBuilder.addTarget(mPreviewSurface);
mStillCaptureRequestBuilder.addTarget(mJpegImageReader.getSurface());
mSessionListener = new BlockingSessionCallback();
List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/3);
outputSurfaces.add(mPreviewSurface);
outputSurfaces.add(mYuvImageReader.getSurface());
outputSurfaces.add(mJpegImageReader.getSurface());
mCameraDevice.createCaptureSession(outputSurfaces, mSessionListener, mCameraHandler);
mCaptureSession = mSessionListener.waitAndGetSession(/*timeoutMs*/3000);
configurePreviewTextureTransform();
/* Set bokeh mode and start streaming */
int bokehMode = mNextCombination.mMode;
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_EXTENDED_SCENE_MODE, bokehMode);
mStillCaptureRequestBuilder.set(CaptureRequest.CONTROL_EXTENDED_SCENE_MODE, bokehMode);
mPreviewRequest = mPreviewRequestBuilder.build();
mStillCaptureRequest = mStillCaptureRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void takePicture() {
try {
mCaptureSession.stopRepeating();
mSessionListener.getStateWaiter().waitForState(
BlockingSessionCallback.SESSION_READY, SESSION_READY_TIMEOUT_MS);
mCaptureSession.capture(mStillCaptureRequest, mCaptureCallback, mCameraHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void setPassButtonEnabled(boolean enabled) {
ImageButton pass_button = (ImageButton) findViewById(R.id.pass_button);
pass_button.setEnabled(enabled);
}
}