summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/camera/CaptureModule.java190
-rw-r--r--src/com/android/camera/module/ModulesInfo.java1
-rw-r--r--src/com/android/camera/one/OneCameraManager.java26
-rw-r--r--src/com/android/camera/one/v1/OneCameraManagerImpl.java6
-rw-r--r--src/com/android/camera/one/v2/OneCameraManagerImpl.java24
-rw-r--r--src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java190
-rw-r--r--src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java7
-rw-r--r--src/com/android/camera/processing/imagebackend/ImageBackend.java37
-rw-r--r--src/com/android/camera/processing/imagebackend/ImageConsumer.java43
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java35
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java139
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java18
-rw-r--r--src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java31
-rw-r--r--src/com/android/camera/util/ApiHelper.java2
-rw-r--r--src_pd/com/android/camera/one/v2/OneCameraCreator.java10
15 files changed, 522 insertions, 237 deletions
diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java
index c65d029ab..44428b1c6 100644
--- a/src/com/android/camera/CaptureModule.java
+++ b/src/com/android/camera/CaptureModule.java
@@ -59,12 +59,8 @@ import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
import com.android.camera.one.v2.OneCameraManagerImpl;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
-import com.android.camera.one.v2.imagesaver.YuvImageBackendImageSaver;
import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.one.v2.photo.ImageRotationCalculatorImpl;
-import com.android.camera.processing.ProcessingServiceManager;
-import com.android.camera.processing.imagebackend.ImageBackend;
import com.android.camera.remote.RemoteCameraModule;
import com.android.camera.session.CaptureSession;
import com.android.camera.settings.Keys;
@@ -252,7 +248,8 @@ public class CaptureModule extends CameraModule implements
mMainHandler.post(new Runnable() {
@Override
public void run() {
- mAppController.getServices().getRemoteShutterListener().onPictureTaken(jpegImage);
+ mAppController.getServices().getRemoteShutterListener()
+ .onPictureTaken(jpegImage);
}
});
}
@@ -1182,15 +1179,10 @@ public class CaptureModule extends CameraModule implements
return;
}
- // Create the image saver.
- // Used to rotate images the right way based on the sensor used
- // for taking the image.
+ // Derive objects necessary for camera creation.
MainThread mainThread = MainThread.create();
ImageRotationCalculator imageRotationCalculator = ImageRotationCalculatorImpl
.from(cameraCharacteristics);
- ImageBackend imageBackend = ProcessingServiceManager.getImageBackendInstance();
- ImageSaver.Builder imageSaverBuilder = new YuvImageBackendImageSaver(
- mainThread, imageRotationCalculator, imageBackend);
// Only enable HDR on the back camera
boolean useHdr = mHdrEnabled && mCameraFacing == Facing.BACK;
@@ -1203,96 +1195,96 @@ public class CaptureModule extends CameraModule implements
return;
}
- mCameraManager.open(mCameraFacing, useHdr, mPictureSize, imageSaverBuilder,
- new OpenCallback() {
- @Override
- public void onFailure() {
- Log.e(TAG, "Could not open camera.");
- mCamera = null;
- mCameraOpenCloseLock.release();
- mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
- }
-
- @Override
- public void onCameraClosed() {
- mCamera = null;
- mBurstController.onCameraDetached();
- mCameraOpenCloseLock.release();
- }
-
- @Override
- public void onCameraOpened(final OneCamera camera) {
- Log.d(TAG, "onCameraOpened: " + camera);
- mCamera = camera;
- mBurstController.onCameraAttached(mCamera);
- updatePreviewBufferDimension();
-
- // If the surface texture is not destroyed, it may have
- // the last frame lingering. We need to hold off setting
- // transform until preview is started.
- updateFrameDistributorBufferSize();
- mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
- Log.d(TAG, "starting preview ...");
-
- // TODO: make mFocusController final and remove null
- // check.
- if (mFocusController != null) {
- camera.setFocusDistanceListener(mFocusController);
- }
-
- // TODO: Consider rolling these two calls into one.
- camera.startPreview(new Surface(getPreviewSurfaceTexture()),
- new CaptureReadyCallback() {
- @Override
- public void onSetupFailed() {
- // We must release this lock here,
- // before posting to the main handler
- // since we may be blocked in pause(),
- // getting ready to close the camera.
- mCameraOpenCloseLock.release();
- Log.e(TAG, "Could not set up preview.");
- mMainHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mCamera == null) {
- Log.d(TAG, "Camera closed, aborting.");
- return;
+ mCameraManager.open(mCameraFacing, useHdr, mPictureSize,
+ new OpenCallback() {
+ @Override
+ public void onFailure() {
+ Log.e(TAG, "Could not open camera.");
+ mCamera = null;
+ mCameraOpenCloseLock.release();
+ mAppController.showErrorAndFinish(R.string.cannot_connect_camera);
+ }
+
+ @Override
+ public void onCameraClosed() {
+ mCamera = null;
+ mBurstController.onCameraDetached();
+ mCameraOpenCloseLock.release();
+ }
+
+ @Override
+ public void onCameraOpened(final OneCamera camera) {
+ Log.d(TAG, "onCameraOpened: " + camera);
+ mCamera = camera;
+ mBurstController.onCameraAttached(mCamera);
+ updatePreviewBufferDimension();
+
+ // If the surface texture is not destroyed, it may have
+ // the last frame lingering. We need to hold off setting
+ // transform until preview is started.
+ updateFrameDistributorBufferSize();
+ mState = ModuleState.WATCH_FOR_NEXT_FRAME_AFTER_PREVIEW_STARTED;
+ Log.d(TAG, "starting preview ...");
+
+ // TODO: make mFocusController final and remove null
+ // check.
+ if (mFocusController != null) {
+ camera.setFocusDistanceListener(mFocusController);
+ }
+
+ // TODO: Consider rolling these two calls into one.
+ camera.startPreview(new Surface(getPreviewSurfaceTexture()),
+ new CaptureReadyCallback() {
+ @Override
+ public void onSetupFailed() {
+ // We must release this lock here,
+ // before posting to the main handler
+ // since we may be blocked in pause(),
+ // getting ready to close the camera.
+ mCameraOpenCloseLock.release();
+ Log.e(TAG, "Could not set up preview.");
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (mCamera == null) {
+ Log.d(TAG, "Camera closed, aborting.");
+ return;
+ }
+ mCamera.close();
+ mCamera = null;
+ // TODO: Show an error message
+ // and exit.
}
- mCamera.close();
- mCamera = null;
- // TODO: Show an error message
- // and exit.
- }
- });
- }
-
- @Override
- public void onReadyForCapture() {
- // We must release this lock here,
- // before posting to the main handler
- // since we may be blocked in pause(),
- // getting ready to close the camera.
- mCameraOpenCloseLock.release();
- mMainHandler.post(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, "Ready for capture.");
- if (mCamera == null) {
- Log.d(TAG, "Camera closed, aborting.");
- return;
+ });
+ }
+
+ @Override
+ public void onReadyForCapture() {
+ // We must release this lock here,
+ // before posting to the main handler
+ // since we may be blocked in pause(),
+ // getting ready to close the camera.
+ mCameraOpenCloseLock.release();
+ mMainHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Log.d(TAG, "Ready for capture.");
+ if (mCamera == null) {
+ Log.d(TAG, "Camera closed, aborting.");
+ return;
+ }
+ onPreviewStarted();
+ // Enable zooming after preview
+ // has started.
+ mUI.initializeZoom(mCamera.getMaxZoom());
+ mCamera.setFocusStateListener(CaptureModule.this);
+ mCamera.setReadyStateChangedListener(CaptureModule.this);
}
- onPreviewStarted();
- // Enable zooming after preview
- // has started.
- mUI.initializeZoom(mCamera.getMaxZoom());
- mCamera.setFocusStateListener(CaptureModule.this);
- mCamera.setReadyStateChangedListener(CaptureModule.this);
- }
- });
- }
- });
- }
- }, mCameraHandler);
+ });
+ }
+ });
+ }
+ }, mCameraHandler, mainThread, imageRotationCalculator);
}
private void closeCamera() {
diff --git a/src/com/android/camera/module/ModulesInfo.java b/src/com/android/camera/module/ModulesInfo.java
index a8ecb68c9..327df75c8 100644
--- a/src/com/android/camera/module/ModulesInfo.java
+++ b/src/com/android/camera/module/ModulesInfo.java
@@ -81,6 +81,7 @@ public class ModulesInfo {
@Override
public ModuleController createModule(AppController app) {
+ Log.v(TAG, "EnableCaptureModule = " + ENABLE_CAPTURE_MODULE);
return ENABLE_CAPTURE_MODULE ? new CaptureModule(app) : new PhotoModule(app);
}
});
diff --git a/src/com/android/camera/one/OneCameraManager.java b/src/com/android/camera/one/OneCameraManager.java
index 9a4fe0c65..d23e5a37f 100644
--- a/src/com/android/camera/one/OneCameraManager.java
+++ b/src/com/android/camera/one/OneCameraManager.java
@@ -16,18 +16,19 @@
package com.android.camera.one;
-import com.google.common.base.Optional;
-
import android.os.Handler;
import android.util.DisplayMetrics;
import com.android.camera.CameraActivity;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log.Tag;
import com.android.camera.one.OneCamera.Facing;
import com.android.camera.one.OneCamera.OpenCallback;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
+import com.google.common.base.Optional;
+
/**
* The camera manager is responsible for instantiating {@link OneCamera}
* instances.
@@ -38,7 +39,7 @@ public abstract class OneCameraManager {
/**
* Attempts to open the camera facing the given direction with the given
* capture size.
- *
+ * <p>
* Exactly one call will always be made to a single method in the provided
* {@link OpenCallback}.
*
@@ -47,13 +48,16 @@ public abstract class OneCameraManager {
* @param enableHdr if an HDR feature exists, open a camera that supports it
* @param captureSize the capture size. This must be one of the supported
* sizes.
- * @param imageSaverBuilder the builder that will be used to create {@link ImageSaver}.
* @param callback this listener is called when the camera was opened or
* when it failed to open.
* @param handler the handler on which callback methods are invoked.
+ * @param mainThread Main thread executor
+ * @param imageRotationCalculator Image rotation calculator required for
+ * Camera Factory initialization
*/
public abstract void open(Facing facing, boolean enableHdr, Size captureSize,
- ImageSaver.Builder imageSaverBuilder, OpenCallback callback, Handler handler);
+ OpenCallback callback, Handler handler,
+ MainThread mainThread, final ImageRotationCalculator imageRotationCalculator);
// TODO: Move this to OneCameraCharacteristics class.
/**
@@ -62,13 +66,13 @@ public abstract class OneCameraManager {
public abstract boolean hasCameraFacing(Facing facing);
/**
- * Retrieve the characteristics for the camera facing at the given direction. The first camera
- * found in the given direction will be chosen.
+ * Retrieve the characteristics for the camera facing at the given
+ * direction. The first camera found in the given direction will be chosen.
*
* @param facing The facing direction of the camera.
- * @return A #{link com.android.camera.one.OneCameraCharacteristics} object to provide camera
- * characteristics information. Returns null if there is no camera facing the given
- * direction.
+ * @return A #{link com.android.camera.one.OneCameraCharacteristics} object
+ * to provide camera characteristics information. Returns null if
+ * there is no camera facing the given direction.
*/
public abstract OneCameraCharacteristics getCameraCharacteristics(Facing facing)
throws OneCameraAccessException;
diff --git a/src/com/android/camera/one/v1/OneCameraManagerImpl.java b/src/com/android/camera/one/v1/OneCameraManagerImpl.java
index 87b541905..4fa6be7e4 100644
--- a/src/com/android/camera/one/v1/OneCameraManagerImpl.java
+++ b/src/com/android/camera/one/v1/OneCameraManagerImpl.java
@@ -22,6 +22,7 @@ import android.hardware.Camera;
import android.os.Handler;
import com.android.camera.CameraActivity;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log;
import com.android.camera.one.OneCamera.Facing;
import com.android.camera.one.OneCamera.OpenCallback;
@@ -29,6 +30,7 @@ import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
/**
@@ -93,7 +95,9 @@ public class OneCameraManagerImpl extends OneCameraManager {
@Override
public void open(Facing facing, boolean enableHdr, Size pictureSize,
- ImageSaver.Builder imageSaverBuilder, OpenCallback callback, Handler handler) {
+ OpenCallback callback, Handler handler,
+ MainThread mainThread,
+ ImageRotationCalculator imageRotationCalculator) {
throw new RuntimeException("Not implemented yet.");
}
diff --git a/src/com/android/camera/one/v2/OneCameraManagerImpl.java b/src/com/android/camera/one/v2/OneCameraManagerImpl.java
index 340a6dfb3..6b3754b2b 100644
--- a/src/com/android/camera/one/v2/OneCameraManagerImpl.java
+++ b/src/com/android/camera/one/v2/OneCameraManagerImpl.java
@@ -28,6 +28,7 @@ import android.util.DisplayMetrics;
import com.android.camera.CameraActivity;
import com.android.camera.SoundPlayer;
import com.android.camera.app.AppController;
+import com.android.camera.async.MainThread;
import com.android.camera.debug.Log;
import com.android.camera.debug.Log.Tag;
import com.android.camera.one.OneCamera;
@@ -36,10 +37,11 @@ import com.android.camera.one.OneCamera.OpenCallback;
import com.android.camera.one.OneCameraAccessException;
import com.android.camera.one.OneCameraCharacteristics;
import com.android.camera.one.OneCameraManager;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.AndroidServices;
import com.android.camera.util.ApiHelper;
import com.android.camera.util.Size;
+
import com.google.common.base.Optional;
/**
@@ -55,7 +57,8 @@ public class OneCameraManagerImpl extends OneCameraManager {
private final DisplayMetrics mDisplayMetrics;
private final SoundPlayer mSoundPlayer;
- public static Optional<OneCameraManager> create(CameraActivity activity, DisplayMetrics displayMetrics) {
+ public static Optional<OneCameraManager> create(CameraActivity activity,
+ DisplayMetrics displayMetrics) {
if (!ApiHelper.HAS_CAMERA_2_API) {
return Optional.absent();
}
@@ -92,14 +95,16 @@ public class OneCameraManagerImpl extends OneCameraManager {
@Override
public void open(Facing facing, final boolean useHdr, final Size pictureSize,
- final ImageSaver.Builder imageSaverBuilder, final OpenCallback openCallback, Handler handler) {
+ final OpenCallback openCallback,
+ Handler handler, final MainThread mainThread,
+ final ImageRotationCalculator imageRotationCalculator) {
try {
final String cameraId = getCameraId(facing);
Log.i(TAG, "Opening Camera ID " + cameraId);
mCameraManager.openCamera(cameraId, new CameraDevice.StateCallback() {
// We may get multiple calls to StateCallback, but only the
// first callback indicates the status of the camera-opening
- // operation. For example, we may receive onOpened() and later
+ // operation. For example, we may receive onOpened() and later
// onClosed(), but only the first should be relayed to
// openCallback.
private boolean isFirstCallback = true;
@@ -139,10 +144,12 @@ public class OneCameraManagerImpl extends OneCameraManager {
try {
CameraCharacteristics characteristics = mCameraManager
.getCameraCharacteristics(device.getId());
- // TODO: Set boolean based on whether HDR+ is enabled.
+ // TODO: Set boolean based on whether HDR+ is
+ // enabled.
OneCamera oneCamera = OneCameraCreator.create(mAppController, useHdr,
- device, characteristics, pictureSize, imageSaverBuilder,
- mMaxMemoryMB, mDisplayMetrics, mSoundPlayer);
+ device, characteristics, pictureSize,
+ mMaxMemoryMB, mDisplayMetrics, mSoundPlayer,
+ mainThread, imageRotationCalculator);
openCallback.onCameraOpened(oneCamera);
} catch (CameraAccessException e) {
Log.d(TAG, "Could not get camera characteristics");
@@ -181,7 +188,8 @@ public class OneCameraManagerImpl extends OneCameraManager {
throws OneCameraAccessException {
String cameraId = getCameraId(facing);
try {
- CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
+ CameraCharacteristics characteristics = mCameraManager
+ .getCameraCharacteristics(cameraId);
return new OneCameraCharacteristicsImpl(characteristics);
} catch (CameraAccessException ex) {
throw new OneCameraAccessException("Unable to get camera characteristics", ex);
diff --git a/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java b/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java
new file mode 100644
index 000000000..ad2b8ebef
--- /dev/null
+++ b/src/com/android/camera/one/v2/imagesaver/JpegImageBackendImageSaver.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2015 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.camera.one.v2.imagesaver;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+
+import com.android.camera.app.OrientationManager;
+import com.android.camera.async.MainThread;
+import com.android.camera.debug.Log;
+import com.android.camera.one.OneCamera;
+import com.android.camera.one.v2.camera2proxy.ImageProxy;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
+import com.android.camera.processing.imagebackend.ImageBackend;
+import com.android.camera.processing.imagebackend.ImageConsumer;
+import com.android.camera.processing.imagebackend.ImageProcessorListener;
+import com.android.camera.processing.imagebackend.ImageProcessorProxyListener;
+import com.android.camera.processing.imagebackend.ImageToProcess;
+import com.android.camera.processing.imagebackend.TaskImageContainer;
+import com.android.camera.session.CaptureSession;
+
+import com.google.common.base.Optional;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+import javax.annotation.ParametersAreNonnullByDefault;
+
+/**
+ * Wires up the ImageBackend task submission process to save JPEG images. Camera
+ * delivers a JPEG-compressed full-size image. This class does very little work
+ * and just routes this image artifact as the thumbnail and to remote devices.
+ */
+public class JpegImageBackendImageSaver implements ImageSaver.Builder {
+ private static Log.Tag TAG = new Log.Tag("JpegImgBESaver");
+
+ @ParametersAreNonnullByDefault
+ private static class ImageSaverImpl implements SingleImageSaver {
+ private final MainThread mExecutor;
+ private final CaptureSession mSession;
+ private final OrientationManager.DeviceOrientation mImageRotation;
+ private final ImageBackend mImageBackend;
+ private final ImageProcessorListener mPreviewListener;
+
+ public ImageSaverImpl(MainThread executor,
+ CaptureSession session, OrientationManager.DeviceOrientation imageRotation,
+ ImageBackend imageBackend, ImageProcessorListener previewListener) {
+ mExecutor = executor;
+ mSession = session;
+ mImageRotation = imageRotation;
+ mImageBackend = imageBackend;
+ mPreviewListener = previewListener;
+ }
+
+ @Override
+ public void saveAndCloseImage(ImageProxy image, Optional<ImageProxy> thumbnail) {
+ // TODO: Use thumbnail to speed up RGB thumbnail creation whenever
+ // possible. For now, just close it.
+ if (thumbnail.isPresent()) {
+ thumbnail.get().close();
+ }
+ final ImageProcessorProxyListener listenerProxy = mImageBackend.getProxyListener();
+
+ listenerProxy.registerListener(mPreviewListener, image);
+
+ Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>();
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE);
+
+ try {
+ mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation),
+ mExecutor, taskFlagsSet, mSession);
+ } catch (InterruptedException e) {
+ // Impossible exception because receiveImage is nonblocking
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private static class PreviewListener implements ImageProcessorListener {
+ private final MainThread mExecutor;
+ private final ImageProcessorProxyListener mListenerProxy;
+ private final CaptureSession mSession;
+ private final OrientationManager.DeviceOrientation mImageRotation;
+ private final OneCamera.PictureSaverCallback mPictureSaverCallback;
+
+ private PreviewListener(MainThread executor,
+ ImageProcessorProxyListener listenerProxy, CaptureSession session,
+ OrientationManager.DeviceOrientation imageRotation,
+ OneCamera.PictureSaverCallback pictureSaverCallback) {
+ mExecutor = executor;
+ mListenerProxy = listenerProxy;
+ mSession = session;
+ mImageRotation = imageRotation;
+ mPictureSaverCallback = pictureSaverCallback;
+ }
+
+ @Override
+ public void onStart(TaskImageContainer.TaskInfo task) {
+ if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
+ mSession.startEmpty();
+ }
+ }
+
+ @Override
+ public void onResultCompressed(TaskImageContainer.TaskInfo task,
+ TaskImageContainer.CompressedPayload payload) {
+ if (task.destination == TaskImageContainer.TaskInfo.Destination.FINAL_IMAGE) {
+ // Just start the thumbnail now, since there's no earlier event.
+ mPictureSaverCallback.onThumbnailProcessingBegun();
+ final Bitmap bitmap = BitmapFactory.decodeByteArray(payload.data, 0,
+ payload.data.length);
+ // Send image to disk saver.
+ mPictureSaverCallback.onThumbnailAvailable(bitmap, mImageRotation.getDegrees());
+ // Send image to remote devices
+ mPictureSaverCallback.onRemoteThumbnailAvailable(payload.data);
+ }
+
+ }
+
+ @Override
+ public void onResultUncompressed(TaskImageContainer.TaskInfo task,
+ TaskImageContainer.UncompressedPayload payload) {
+ // Do Nothing
+ }
+
+ @Override
+ public void onResultUri(TaskImageContainer.TaskInfo task, Uri uri) {
+ // Remove yourself from the listener after JPEG save.
+ // TODO: This should really be done by the ImageBackend to guarantee
+ // ordering, since technically this could happen out of order.
+ mListenerProxy.unregisterListener(this);
+ }
+ }
+
+ private final MainThread mExecutor;
+ private final ImageRotationCalculator mImageRotationCalculator;
+ private final ImageBackend mImageBackend;
+
+ /**
+ * Constructor
+ *
+ * @param executor Executor to run listener events on the ImageBackend
+ * @param imageRotationCalculator the image rotation calculator to determine
+ */
+ public JpegImageBackendImageSaver(MainThread executor,
+ ImageRotationCalculator imageRotationCalculator,
+ ImageBackend imageBackend) {
+ mExecutor = executor;
+ mImageRotationCalculator = imageRotationCalculator;
+ mImageBackend = imageBackend;
+ }
+
+ /**
+ * Builder for the Zsl/ImageBackend Interface
+ *
+ * @return Instantiated interface object
+ */
+ @Override
+ public ImageSaver build(
+ @Nonnull OneCamera.PictureSaverCallback pictureSaverCallback,
+ @Nonnull OrientationManager.DeviceOrientation orientation,
+ @Nonnull CaptureSession session) {
+ final OrientationManager.DeviceOrientation imageRotation = mImageRotationCalculator
+ .toImageRotation(orientation);
+
+ ImageProcessorProxyListener proxyListener = mImageBackend.getProxyListener();
+
+ PreviewListener previewListener = new PreviewListener(mExecutor,
+ proxyListener, session, imageRotation, pictureSaverCallback);
+ return new MostRecentImageSaver(new ImageSaverImpl(mExecutor, session,
+ imageRotation, mImageBackend, previewListener));
+ }
+}
diff --git a/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java b/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
index 1f1dd95b3..e7a10fb3b 100644
--- a/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
+++ b/src/com/android/camera/one/v2/imagesaver/YuvImageBackendImageSaver.java
@@ -74,9 +74,10 @@ public class YuvImageBackendImageSaver implements ImageSaver.Builder {
listenerProxy.registerListener(mPreviewListener, image);
Set<ImageConsumer.ImageTaskFlags> taskFlagsSet = new HashSet<>();
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CONVERT_IMAGE_TO_RGB_PREVIEW);
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_IMAGE_TO_JPEG);
- taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_IMAGE_ON_RELEASE);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CONVERT_TO_RGB_PREVIEW);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK);
+ taskFlagsSet.add(ImageConsumer.ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE);
try {
mImageBackend.receiveImage(new ImageToProcess(image, mImageRotation),
diff --git a/src/com/android/camera/processing/imagebackend/ImageBackend.java b/src/com/android/camera/processing/imagebackend/ImageBackend.java
index 3630e90e9..7ba442cec 100644
--- a/src/com/android/camera/processing/imagebackend/ImageBackend.java
+++ b/src/com/android/camera/processing/imagebackend/ImageBackend.java
@@ -16,13 +16,10 @@
package com.android.camera.processing.imagebackend;
-import com.android.camera.app.CameraAppUI;
import com.android.camera.debug.Log;
-import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.session.CaptureSession;
import com.android.camera.util.Size;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -92,7 +89,7 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
* Approximate viewable size (in pixels) for the fast thumbnail in the
* current UX definition of the product. Note that these values will be the
* minimum size of FAST_THUMBNAIL target for the
- * CONVERT_IMAGE_TO_RGB_PREVIEW task.
+ * CONVERT_TO_RGB_PREVIEW task.
*/
private final static Size FAST_THUMBNAIL_TARGET_SIZE = new Size(160, 100);
@@ -100,7 +97,7 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
* A standard viewable size (in pixels) for the filmstrip thumbnail in the
* current UX definition of the product. Note that this size is the minimum
* size for the Preview on the filmstrip associated with
- * COMPRESS_IMAGE_TO_JPEG task.
+ * COMPRESS_TO_JPEG_AND_WRITE_TO_DISK task.
*/
private final static Size FILMSTRIP_THUMBNAIL_TARGET_SIZE = new Size(512, 384);
@@ -369,31 +366,29 @@ public class ImageBackend implements ImageConsumer, ImageTaskManager {
// Now add the pre-mixed versions of the tasks.
- if (processingFlags.contains(ImageTaskFlags.COMPRESS_IMAGE_TO_JPEG)
- || processingFlags.contains(ImageTaskFlags.WRITE_IMAGE_TO_DISK)) {
- // Add this type of task to the appropriate queue.
- // tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session));
- tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
- FILMSTRIP_THUMBNAIL_TARGET_SIZE));
+ if (processingFlags.contains(ImageTaskFlags.COMPRESS_TO_JPEG_AND_WRITE_TO_DISK)) {
+ if (processingFlags.contains(ImageTaskFlags.CREATE_EARLY_FILMSTRIP_PREVIEW)) {
+ // Request job that creates both filmstrip thumbnail from YUV,
+ // JPEG compression of the YUV Image, and writes the result to disk
+ tasksToExecute.add(new TaskPreviewChainedJpeg(img, executor, this, session,
+ FILMSTRIP_THUMBNAIL_TARGET_SIZE));
+ } else {
+ // Request job that only does JPEG compression and writes the result to disk
+ tasksToExecute.add(new TaskCompressImageToJpeg(img, executor, this, session));
+ }
}
- if (processingFlags.contains(ImageTaskFlags.CONVERT_IMAGE_TO_RGB_PREVIEW)) {
- // Add this type of task to the appropriate queue.
+ if (processingFlags.contains(ImageTaskFlags.CONVERT_TO_RGB_PREVIEW)) {
+ // Add an additional type of task to the appropriate queue.
tasksToExecute.add(new TaskConvertImageToRGBPreview(img, executor,
this, TaskImageContainer.ProcessingPriority.FAST, session,
FAST_THUMBNAIL_TARGET_SIZE,
TaskConvertImageToRGBPreview.ThumbnailShape.SQUARE_ASPECT_CIRCULAR_INSET));
}
- if (processingFlags.contains(ImageTaskFlags.WRITE_IMAGE_TO_DISK)) {
- // Add this type of task to the appropriate queue.
- // Has a dependency as well on the result JPEG_COMPRESSION
- // TODO: Put disk writing implementation within the framework.
- }
-
receiveImage(img, tasksToExecute,
- processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_IMAGE_RELEASE),
- processingFlags.contains(ImageTaskFlags.CLOSE_IMAGE_ON_RELEASE));
+ processingFlags.contains(ImageTaskFlags.BLOCK_UNTIL_ALL_TASKS_RELEASE),
+ processingFlags.contains(ImageTaskFlags.CLOSE_ON_ALL_TASKS_RELEASE));
return true;
}
diff --git a/src/com/android/camera/processing/imagebackend/ImageConsumer.java b/src/com/android/camera/processing/imagebackend/ImageConsumer.java
index 16e523536..43a5369e0 100644
--- a/src/com/android/camera/processing/imagebackend/ImageConsumer.java
+++ b/src/com/android/camera/processing/imagebackend/ImageConsumer.java
@@ -26,20 +26,45 @@ import java.util.concurrent.Executor;
* object that merely delivers images to the backend. After the tasks and
* Android image is submitted to the ImageConsumer, the responsibility to close
* on the Android image object as early as possible is transferred to the
- * implementation. Whether an image can be submitted again for process is up to
- * the implementation of the consumer. For integration of Camera Application,
- * we now pass in the CaptureSession in order to properly update filmstrip and UI.
+ * implementation. Whether an image can be submitted again for process is up to
+ * the implementation of the consumer. For integration of Camera Application, we
+ * now pass in the CaptureSession in order to properly update filmstrip and UI.
*/
public interface ImageConsumer {
/**
+ * ImageTaskFlags specifies the current tasks that will be run with an
+ * image.
+ * <ol>
+ * <li>CREATE_EARLY_FILMSTRIP_PREVIEW: Subsamples a YUV Image and converts
+ * it to an ARGB Image with nearly similar aspect ratio. ONLY Valid when
+ * specified with COMPRESS_TO_JPEG_AND_WRITE_TO_DISK. Otherwise, ignored.</li>
+ * <li>COMPRESS_TO_JPEG_AND_WRITE_TO_DISK: Compresses an YUV/JPEG Image to
+ * JPEG (when necessary), delivers the compressed artifact via the listener,
+ * and writes it to disk.</li>
+ * <li>CONVERT_TO_RGB_PREVIEW: Subsamples a YUV Image and converts the
+ * uncompressed output to ARGB image inset within a circle</li>
+ * <li>BLOCK_UNTIL_ALL_TASKS_RELEASE: Block on ReceiveImage call until image
+ * is released.</li>
+ * <li>CLOSE_ON_ALL_TASKS_RELEASE: Close the ImageProxy on ReceiveImage Call
+ * when all tasks release their image</li>
+ * </ol>
+ */
+ public enum ImageTaskFlags {
+ CREATE_EARLY_FILMSTRIP_PREVIEW,
+ COMPRESS_TO_JPEG_AND_WRITE_TO_DISK,
+ CONVERT_TO_RGB_PREVIEW,
+ BLOCK_UNTIL_ALL_TASKS_RELEASE,
+ CLOSE_ON_ALL_TASKS_RELEASE
+ }
+
+ /**
* Provides the basic functionality of camera processing via an easy-to-use
* method call.
*
* @param img The Image to be Processed
* @param executor The executor on which to execute events and image close
- * @param processingFlags Bit vector comprised of logically ORed TASK_FLAG*
- * constants
+ * @param processingFlags {@see ImageTaskFlags}
*/
public boolean receiveImage(ImageToProcess img, Executor executor,
Set<ImageTaskFlags> processingFlags, CaptureSession captureSession)
@@ -102,12 +127,4 @@ public interface ImageConsumer {
*/
public ImageProcessorProxyListener getProxyListener();
- // Current jobs that should be able to be tagged to an image.
- public enum ImageTaskFlags {
- COMPRESS_IMAGE_TO_JPEG,
- CONVERT_IMAGE_TO_RGB_PREVIEW,
- WRITE_IMAGE_TO_DISK,
- BLOCK_UNTIL_IMAGE_RELEASE,
- CLOSE_IMAGE_ON_RELEASE
- }
}
diff --git a/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java b/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
index 6e4cf4fd0..61b2c878d 100644
--- a/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskChainedCompressImageToJpeg.java
@@ -52,23 +52,26 @@ class TaskChainedCompressImageToJpeg extends TaskJpegEncode {
img.proxy.getHeight(), img.proxy.getFormat());
final TaskImage resultImage = new TaskImage(mImage.rotation, img.proxy.getWidth(),
img.proxy.getHeight(), ImageFormat.JPEG);
-
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
-
+ byte[] dataCopy;
int[] strides = new int[3];
- // Do the byte copy
- strides[0] = planeList.get(0).getRowStride()
- / planeList.get(0).getPixelStride();
- strides[1] = planeList.get(1).getRowStride()
- / planeList.get(1).getPixelStride();
- strides[2] = 2 * planeList.get(2).getRowStride()
- / planeList.get(2).getPixelStride();
-
- // TODO: For performance, use a cache subsystem for buffer reuse.
- byte[] dataCopy = convertYUV420ImageToPackedNV21(img.proxy);
-
- // Release the image now that you have a usable copy
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
+
+ // Do the byte copy
+ strides[0] = planeList.get(0).getRowStride()
+ / planeList.get(0).getPixelStride();
+ strides[1] = planeList.get(1).getRowStride()
+ / planeList.get(1).getPixelStride();
+ strides[2] = 2 * planeList.get(2).getRowStride()
+ / planeList.get(2).getPixelStride();
+
+ // TODO: For performance, use a cache subsystem for buffer reuse.
+ dataCopy = convertYUV420ImageToPackedNV21(img.proxy);
+ } finally {
+ // Release the image now that you have a usable copy
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
final byte[] chainedDataCopy = dataCopy;
final int[] chainedStrides = strides;
diff --git a/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java b/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
index ea69440cf..a13e596af 100644
--- a/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskCompressImageToJpeg.java
@@ -22,6 +22,7 @@ import android.net.Uri;
import com.android.camera.app.MediaSaver;
import com.android.camera.app.OrientationManager.DeviceOrientation;
import com.android.camera.exif.ExifInterface;
+import com.android.camera.one.v2.camera2proxy.ImageProxy;
import com.android.camera.session.CaptureSession;
import com.android.camera.util.JpegUtilNative;
import com.android.camera.util.Size;
@@ -31,12 +32,12 @@ import java.util.concurrent.Executor;
/**
* Implements the conversion of a YUV_420_888 image to compressed JPEG byte
- * array, using the native implementation of the Camera Application.
- * <p>
- * TODO: Instead of setting the orientation in EXIF, actually rotate the image
- * here and set a rotation of 0.
+ * array, using the native implementation of the Camera Application. If the
+ * image is already JPEG, then it passes it through properly with the assumption
+ * that the JPEG is already encoded in the proper orientation.
*/
public class TaskCompressImageToJpeg extends TaskJpegEncode {
+
private static final int DEFAULT_JPEG_COMPRESSION_QUALITY = 90;
/**
@@ -48,56 +49,100 @@ public class TaskCompressImageToJpeg extends TaskJpegEncode {
* @param captureSession Handler for UI/Disk events
*/
TaskCompressImageToJpeg(ImageToProcess image, Executor executor,
- ImageTaskManager imageTaskManager, CaptureSession captureSession) {
+ ImageTaskManager imageTaskManager,
+ CaptureSession captureSession) {
super(image, executor, imageTaskManager, ProcessingPriority.SLOW, captureSession);
}
- private void logWrapper(String message) {
- // Do nothing.
+ /**
+ * Wraps the static call to JpegUtilNative for testability. {@see
+ * JpegUtilNative#compressJpegFromYUV420Image}
+ */
+ public int compressJpegFromYUV420Image(ImageProxy img, ByteBuffer outBuf, int quality,
+ int degrees) {
+ return JpegUtilNative.compressJpegFromYUV420Image(img, outBuf, quality, degrees);
}
@Override
public void run() {
ImageToProcess img = mImage;
+ if (img.rotation != DeviceOrientation.CLOCKWISE_0
+ && img.proxy.getFormat() == ImageFormat.JPEG) {
+ // TODO: Ensure the capture for SimpleOneCameraFactory is always
+ // at zero rotation
+ // To avoid suboptimal rotation implementation, the JPEG should
+ // come with zero orientation. Any post-rotation of JPEG would be
+ // REALLY sub-optimal.
+
+ // Release image references on error
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+
+ throw new IllegalStateException(
+ "Image Rotation for JPEG should be zero, but is actually "
+ + img.rotation.getDegrees());
+ }
final TaskImage inputImage = new TaskImage(
- img.rotation, img.proxy.getWidth(), img.proxy.getHeight(), img.proxy.getFormat());
-
- // Resulting image will be rotated so that viewers won't have to rotate.
- // That's why the resulting image will have 0 rotation.
- Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(), img.proxy.getHeight(),
+ img.rotation, img.proxy.getWidth(), img.proxy.getHeight(),
+ img.proxy.getFormat());
+ Size resultSize = getImageSizeForOrientation(img.proxy.getWidth(),
+ img.proxy.getHeight(),
img.rotation);
final TaskImage resultImage = new TaskImage(
DeviceOrientation.CLOCKWISE_0, resultSize.getWidth(), resultSize.getHeight(),
ImageFormat.JPEG);
-
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
- logWrapper("TIMER_END Full-size YUV buffer available, w=" + img.proxy.getWidth() + " h="
- + img.proxy.getHeight() + " of format " + img.proxy.getFormat()
- + " (35==YUV_420_888)");
-
- ByteBuffer compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
- * resultImage.height);
-
- // If Orientation is UNKNOWN, treat input image orientation as
- // CLOCKWISE_0.
- int numBytes = JpegUtilNative.compressJpegFromYUV420Image(
- img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
- (inputImage.orientation == DeviceOrientation.UNKNOWN) ? 0 : inputImage.orientation
- .getDegrees());
-
- if (numBytes < 0) {
- throw new RuntimeException("Error compressing jpeg.");
+ byte[] writeOut;
+ int numBytes;
+
+ try {
+ // Resulting image will be rotated so that viewers won't have to
+ // rotate. That's why the resulting image will have 0 rotation.
+
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FINAL_IMAGE);
+ logWrapper("TIMER_END Full-size YUV buffer available, w=" + img.proxy.getWidth()
+ + " h="
+ + img.proxy.getHeight() + " of format " + img.proxy.getFormat()
+ + " (35==YUV_420_888)");
+
+ ByteBuffer compressedData;
+ switch (inputImage.format) {
+ case ImageFormat.JPEG:
+ compressedData = img.proxy.getPlanes().get(0).getBuffer();
+ numBytes = compressedData.capacity();
+ break;
+ case ImageFormat.YUV_420_888:
+ compressedData = ByteBuffer.allocateDirect(3 * resultImage.width
+ * resultImage.height);
+
+ // If Orientation is UNKNOWN, treat input image orientation
+ // as CLOCKWISE_0.
+ numBytes = compressJpegFromYUV420Image(
+ img.proxy, compressedData, DEFAULT_JPEG_COMPRESSION_QUALITY,
+ (inputImage.orientation == DeviceOrientation.UNKNOWN) ? 0
+ : inputImage.orientation
+ .getDegrees());
+
+ if (numBytes < 0) {
+ throw new RuntimeException("Error compressing jpeg.");
+ }
+ compressedData.limit(numBytes);
+ break;
+ default:
+ throw new IllegalArgumentException(
+ "Unsupported input image format for TaskCompressImageToJpeg");
+ }
+
+ writeOut = new byte[numBytes];
+ compressedData.get(writeOut);
+ compressedData.rewind();
+ } finally {
+ // Release the image now that you have a usable copy in local memory
+ // Or you failed to process
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
}
- compressedData.limit(numBytes);
- byte[] writeOut = new byte[numBytes];
- compressedData.get(writeOut);
- compressedData.rewind();
-
- // Release the image now that you have a usable copy
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
- onJpegEncodeDone(mId, inputImage, resultImage, writeOut, TaskInfo.Destination.FINAL_IMAGE);
+ onJpegEncodeDone(mId, inputImage, resultImage, writeOut,
+ TaskInfo.Destination.FINAL_IMAGE);
// TODO: the app actually crashes here on a race condition:
// TaskCompressImageToJpeg might complete before
@@ -113,7 +158,23 @@ public class TaskCompressImageToJpeg extends TaskJpegEncode {
});
}
- private static ExifInterface createExif(TaskImage image) {
+ /**
+ * Wraps a possible log message to be overridden for testability purposes.
+ *
+ * @param message
+ */
+ protected void logWrapper(String message) {
+ // Do nothing.
+ }
+
+ /**
+ * Wraps EXIF Interface for JPEG Metadata creation. Can be overridden for
+ * testing
+ *
+ * @param image Metadata for a jpeg image to create EXIF Interface
+ * @return the created Exif Interface
+ */
+ protected ExifInterface createExif(TaskImage image) {
ExifInterface exif = new ExifInterface();
exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_X_DIMENSION, image.width));
exif.setTag(exif.buildTag(ExifInterface.TAG_PIXEL_Y_DIMENSION, image.height));
diff --git a/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java b/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
index b355fad70..e4d50a0af 100644
--- a/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
+++ b/src/com/android/camera/processing/imagebackend/TaskConvertImageToRGBPreview.java
@@ -740,16 +740,20 @@ public class TaskConvertImageToRGBPreview extends TaskImageContainer {
new Size(inputImage.width, inputImage.height),
mTargetSize);
final TaskImage resultImage = calculateResultImage(img, subsample);
+ final int[] convertedImage;
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.FAST_THUMBNAIL);
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.FAST_THUMBNAIL);
- logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
- / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
- + subsample);
+ logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
+ / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
+ + subsample);
- final int[] convertedImage = runSelectedConversion(img.proxy, subsample);
- // Signal backend that reference has been released
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ convertedImage = runSelectedConversion(img.proxy, subsample);
+ } finally {
+ // Signal backend that reference has been released
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
onPreviewDone(resultImage, inputImage, convertedImage, TaskInfo.Destination.FAST_THUMBNAIL);
}
diff --git a/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java b/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
index 25a1f27a7..7d41d243c 100644
--- a/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
+++ b/src/com/android/camera/processing/imagebackend/TaskPreviewChainedJpeg.java
@@ -27,8 +27,6 @@ import java.util.concurrent.Executor;
* inscribed in a circle.
*/
public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
- protected final static Log.Tag TAG = new Log.Tag("TaskPreviewChainedJpeg");
-
/**
* Constructor
*
@@ -46,7 +44,8 @@ public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
}
public void logWrapper(String message) {
- Log.v(TAG, message);
+ // final Log.Tag TAG = new Log.Tag("TaskPreviewChainedJpeg");
+ // Log.v(TAG, message);
}
@Override
@@ -58,22 +57,26 @@ public class TaskPreviewChainedJpeg extends TaskConvertImageToRGBPreview {
new Size(inputImage.width, inputImage.height),
mTargetSize);
final TaskImage resultImage = calculateResultImage(img, subsample);
+ final int[] convertedImage;
- onStart(mId, inputImage, resultImage, TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
+ try {
+ onStart(mId, inputImage, resultImage, TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
- logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
- / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
- + subsample);
+ logWrapper("TIMER_END Rendering preview YUV buffer available, w=" + img.proxy.getWidth()
+ / subsample + " h=" + img.proxy.getHeight() / subsample + " of subsample "
+ + subsample);
- final int[] convertedImage = runSelectedConversion(img.proxy,subsample);
+ convertedImage = runSelectedConversion(img.proxy, subsample);
- // Chain JPEG task
- TaskImageContainer jpegTask = new TaskCompressImageToJpeg(img, mExecutor,
- mImageTaskManager, mSession);
- mImageTaskManager.appendTasks(img, jpegTask);
+ // Chain JPEG task
+ TaskImageContainer jpegTask = new TaskCompressImageToJpeg(img, mExecutor,
+ mImageTaskManager, mSession);
+ mImageTaskManager.appendTasks(img, jpegTask);
+ } finally {
+ // Signal backend that reference has been released
+ mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
+ }
- // Signal backend that reference has been released
- mImageTaskManager.releaseSemaphoreReference(img, mExecutor);
onPreviewDone(resultImage, inputImage, convertedImage,
TaskInfo.Destination.INTERMEDIATE_THUMBNAIL);
}
diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java
index 67a67a8c7..37209a636 100644
--- a/src/com/android/camera/util/ApiHelper.java
+++ b/src/com/android/camera/util/ApiHelper.java
@@ -97,6 +97,6 @@ public class ApiHelper {
public static boolean isLOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
- || "L".equals(Build.VERSION.CODENAME);
+ || "L".equals(Build.VERSION.CODENAME) || "LOLLIPOP".equals(Build.VERSION.CODENAME);
}
}
diff --git a/src_pd/com/android/camera/one/v2/OneCameraCreator.java b/src_pd/com/android/camera/one/v2/OneCameraCreator.java
index bf6b419dd..a1736ec31 100644
--- a/src_pd/com/android/camera/one/v2/OneCameraCreator.java
+++ b/src_pd/com/android/camera/one/v2/OneCameraCreator.java
@@ -20,17 +20,19 @@ import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.util.DisplayMetrics;
-import com.android.camera.app.AppController;
import com.android.camera.SoundPlayer;
+import com.android.camera.app.AppController;
+import com.android.camera.async.MainThread;
import com.android.camera.one.OneCamera;
-import com.android.camera.one.v2.imagesaver.ImageSaver;
+import com.android.camera.one.v2.photo.ImageRotationCalculator;
import com.android.camera.util.Size;
public class OneCameraCreator {
public static OneCamera create(AppController context, boolean useHdr, CameraDevice device,
CameraCharacteristics characteristics, Size pictureSize,
- ImageSaver.Builder imageSaverBuilder, int maxMemoryMB,
- DisplayMetrics displayMetrics, SoundPlayer soundPlayer) {
+ int maxMemoryMB,
+ DisplayMetrics displayMetrics, SoundPlayer soundPlayer,
+ MainThread mainThread, ImageRotationCalculator imageRotationCalculator) {
// TODO: Might want to switch current camera to vendor HDR.
return new OneCameraImpl(device, characteristics, pictureSize);
}