import com.android.camera.burst.ResultsAccessor;
import com.android.camera.session.CaptureSession;
import com.android.camera.settings.SettingsManager;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Size;
import java.io.File;
public static interface FocusDistanceListener {
/**
* Called when physical lens distance on the camera changes.
- *
- * @param diopter the lens diopter from the last known position.
- * @param isActive whether the lens is moving.
*/
- public void onFocusDistance(float diopter, boolean isActive);
+ public void onFocusDistance(float distance, LinearScale lensRange);
}
/**
* Single instance of the current camera AF state.
*/
public static class FocusState {
- public final float diopter;
+ public final float lensDistance;
public final boolean isActive;
/**
- * @param diopter The current focal distance.
+ * @param lensDistance The current focal distance.
* @param isActive Whether the lens is moving, e.g. because of either an
* "active scan" or a "passive scan".
*/
- public FocusState(float diopter, boolean isActive) {
- this.diopter = diopter;
+ public FocusState(float lensDistance, boolean isActive) {
+ this.lensDistance = lensDistance;
this.isActive = isActive;
}
FocusState that = (FocusState) o;
- if (Float.compare(that.diopter, diopter) != 0)
+ if (Float.compare(that.lensDistance, lensDistance) != 0)
return false;
if (isActive != that.isActive)
return false;
@Override
public int hashCode() {
- int result = (diopter != +0.0f ? Float.floatToIntBits(diopter) : 0);
+ int result = (lensDistance != +0.0f ? Float.floatToIntBits(lensDistance) : 0);
result = 31 * result + (isActive ? 1 : 0);
return result;
}
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Size;
import java.util.List;
* @return The supported hardware level.
*/
public SupportedHardwareLevel getSupportedHardwareLevel();
+
+ /**
+ * A converter from the physical focus range of the camera to a ratio.
+ */
+ public LinearScale getLensFocusRange();
}
import com.android.camera.one.OneCamera;
import com.android.camera.one.OneCameraCharacteristics;
+import com.android.camera.ui.focus.LensRangeCalculator;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Size;
import java.util.ArrayList;
public SupportedHardwareLevel getSupportedHardwareLevel() {
throw new RuntimeException("Not implemented yet.");
}
+
+ @Override
+ public LinearScale getLensFocusRange() {
+ // Diopter range is not supported on legacy camera devices.
+ return LensRangeCalculator.getNoOp();
+ }
}
package com.android.camera.one.v2;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import com.android.camera.one.OneCamera;
import com.android.camera.one.OneCameraCharacteristics;
+import com.android.camera.ui.focus.LensRangeCalculator;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Size;
import java.util.ArrayList;
import java.util.List;
-import static com.google.common.base.Preconditions.checkNotNull;
-
/**
* Describes a OneCamera device which is on top of camera2 API. This is
* essential a wrapper for #{link
throw new IllegalStateException("Invalid value for INFO_SUPPORTED_HARDWARE_LEVEL");
}
}
+
+ @Override
+ public LinearScale getLensFocusRange() {
+ return LensRangeCalculator
+ .getDiopterToRatioCalculator(mCameraCharacteristics);
+ }
}
import com.android.camera.one.Settings3A;
import com.android.camera.one.v2.camera2proxy.AndroidImageProxy;
import com.android.camera.session.CaptureSession;
+import com.android.camera.ui.focus.LensRangeCalculator;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.CaptureDataSerializer;
import com.android.camera.util.JpegUtilNative;
Float diopter = result.get(CaptureResult.LENS_FOCUS_DISTANCE);
if(diopter != null && mFocusDistanceListener != null) {
- mFocusDistanceListener.onFocusDistance(diopter, true);
+ mFocusDistanceListener.onFocusDistance(diopter, mLensRange);
}
if (request.getTag() == RequestTag.CAPTURE) {
private final Handler mCameraHandler;
/** The characteristics of this camera. */
private final CameraCharacteristics mCharacteristics;
+ private final LinearScale mLensRange;
/** The underlying Camera2 API camera device. */
private final CameraDevice mDevice;
private final CameraDirectionProvider mDirectionProvider;
OneCameraImpl(CameraDevice device, CameraCharacteristics characteristics, Size pictureSize) {
mDevice = device;
mCharacteristics = characteristics;
+ mLensRange = LensRangeCalculator.getDiopterToRatioCalculator(characteristics);
mDirectionProvider = new CameraDirectionProvider(characteristics);
mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics);
import com.android.camera.one.v2.ImageCaptureManager.MetadataChangeListener;
import com.android.camera.one.v2.camera2proxy.AndroidImageProxy;
import com.android.camera.session.CaptureSession;
+import com.android.camera.ui.focus.LensRangeCalculator;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.JpegUtilNative;
import com.android.camera.util.ListenerCombiner;
/** The characteristics of this camera. */
private final CameraCharacteristics mCharacteristics;
+ /** Converts focus distance units into ratio values */
+ private final LinearScale mLensRange;
/** The underlying Camera2 API camera device. */
private final CameraDevice mDevice;
private final CameraDirectionProvider mDirection;
mDevice = device;
mCharacteristics = characteristics;
+ mLensRange = LensRangeCalculator
+ .getDiopterToRatioCalculator(characteristics);
mDirection = new CameraDirectionProvider(mCharacteristics);
mFullSizeAspectRatio = calculateFullSizeAspectRatio(characteristics);
Object newValue,
CaptureResult result) {
Integer state = result.get(CaptureResult.LENS_STATE);
- if (newValue != null && state != null) {
- mFocusDistanceListener.onFocusDistance((float) newValue, state == 1);
- } else if (newValue != null) {
- mFocusDistanceListener.onFocusDistance((float) newValue, true);
+
+ // Forward changes if we have a new value and the camera
+ // A) Doesn't support lens state or B) lens state is
+ // reported and it is reported as moving.
+ if (newValue != null &&
+ (state == null || state == CameraMetadata.LENS_STATE_MOVING)) {
+ mFocusDistanceListener.onFocusDistance((float) newValue, mLensRange);
}
}
});
OneCamera.Facing direction = characteristics.getCameraDirection();
return new InitializedOneCameraFactory(lifetime, cameraStarter, device, outputSurfaces,
- mainExecutor, new HandlerFactory(), maxZoom, supportedPreviewSizes, direction)
+ mainExecutor, new HandlerFactory(), maxZoom, supportedPreviewSizes,
+ characteristics.getLensFocusRange(), direction)
.provideOneCamera();
}
}
OneCamera.Facing direction = characteristics.getCameraDirection();
return new InitializedOneCameraFactory(lifetime, cameraStarter, device,
outputSurfaces, mainThread, new HandlerFactory(), maxZoom,
- supportedPreviewSizes, direction).provideOneCamera();
+ supportedPreviewSizes, characteristics.getLensFocusRange(),
+ direction).provideOneCamera();
}
}
import android.content.Context;
import android.view.Surface;
-import com.android.camera.async.ConcurrentState;
import com.android.camera.async.Listenable;
import com.android.camera.async.SafeCloseable;
import com.android.camera.async.Updatable;
import com.android.camera.one.v2.autofocus.ManualAutoFocus;
import com.android.camera.one.v2.photo.PictureTaker;
import com.android.camera.session.CaptureSession;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Callback;
import com.android.camera.util.Size;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.concurrent.Executor;
-import java.util.concurrent.Future;
import javax.annotation.Nonnull;
private final SafeCloseable mCloseListener;
private final PictureTaker mPictureTaker;
private final ManualAutoFocus mManualAutoFocus;
+ private final LinearScale mLensRange;
private final Executor mMainExecutor;
private final Listenable<Integer> mAFStateListenable;
private final Listenable<FocusState> mFocusStateListenable;
private final PreviewStarter mPreviewStarter;
public GenericOneCameraImpl(SafeCloseable closeListener, PictureTaker pictureTaker,
- ManualAutoFocus manualAutoFocus, Executor mainExecutor,
+ ManualAutoFocus manualAutoFocus, LinearScale lensRange, Executor mainExecutor,
Listenable<Integer> afStateProvider, Listenable<FocusState> focusStateProvider,
Listenable<Boolean> readyStateListenable, float maxZoom, Updatable<Float> zoom,
Facing direction, PreviewSizeSelector previewSizeSelector,
mPreviewSizeSelector = previewSizeSelector;
mPictureTaker = pictureTaker;
mManualAutoFocus = manualAutoFocus;
+ mLensRange = lensRange;
mAFStateListenable = afStateProvider;
mFocusStateListenable = focusStateProvider;
mReadyStateListenable = readyStateListenable;
public void setFocusStateListener(final FocusStateListener listener) {
mAFStateListenable.setCallback(new Callback<Integer>() {
@Override
- public void onCallback(Integer afState) {
+ public void onCallback(@Nonnull Integer afState) {
// TODO delete frameNumber from FocusStateListener callback. It
// is optional and never actually used.
long frameNumber = -1;
public void setFocusDistanceListener(final FocusDistanceListener listener) {
mFocusStateListenable.setCallback(new Callback<FocusState>() {
@Override
- public void onCallback(FocusState focusState) {
- listener.onFocusDistance(focusState.diopter, focusState.isActive);
+ public void onCallback(@Nonnull FocusState focusState) {
+ if (focusState.isActive) {
+ listener.onFocusDistance(focusState.lensDistance, mLensRange);
+ }
}
});
}
import com.android.camera.one.v2.camera2proxy.CameraCaptureSessionProxy;
import com.android.camera.one.v2.camera2proxy.CameraDeviceProxy;
import com.android.camera.one.v2.photo.PictureTaker;
+import com.android.camera.ui.motion.LinearScale;
import com.android.camera.util.Size;
import com.google.common.util.concurrent.SettableFuture;
final Lifetime lifetime, final CameraStarter cameraStarter, CameraDeviceProxy device,
List<Surface> outputSurfaces, Executor mainThreadExecutor,
HandlerFactory handlerFactory, float maxZoom, List<Size> supportedPreviewSizes,
- OneCamera.Facing direction) {
+ LinearScale lensRange, OneCamera.Facing direction) {
// Assembles and returns a OneCamera based on the CameraStarter.
// Create/wrap required threads.
PreviewSizeSelector previewSizeSelector = new PreviewSizeSelector(supportedPreviewSizes);
- mOneCamera = new GenericOneCameraImpl(lifetime, pictureTaker, manualAutoFocus,
+ mOneCamera = new GenericOneCameraImpl(lifetime, pictureTaker, manualAutoFocus, lensRange,
mainThreadExecutor, afStateListenable, focusStateListenable, readyStateListenable,
maxZoom, zoomState, direction, previewSizeSelector, mPreviewStarter);
}
*/
public class FocusController implements FocusDistanceListener {
private static final Tag TAG = new Tag("FocusController");
- // A Diopter of 0.0f ish is infinity.
- // A Diopter of about 15f or so is focused "as close as possible"
- // Diopter max is computed from device testing
- private static final float DIOPTER_MIN = 0.0f;
- private static final float DIOPTER_MAX = 15.0f;
private final FocusRing mFocusRing;
private final FocusSound mFocusSound;
private final MainThread mMainThread;
- private final LinearScale mDiopterToRatio;
public FocusController(FocusRing focusRing, FocusSound focusSound, MainThread mainThread) {
mFocusRing = focusRing;
mFocusSound = focusSound;
mMainThread = mainThread;
- mDiopterToRatio = new LinearScale(DIOPTER_MIN, DIOPTER_MAX, 0, 1);
}
/**
}
/**
- * Set the physical radius of the focus ring in pixels.
+ * Set the radius of the focus ring as a radius between 0 and 1.
+ * This will map to the min and max values computed for the UI.
*/
public void setFocusRatio(final float ratio) {
mMainThread.execute(new Runnable() {
}
@Override
- public void onFocusDistance(final float diopter, final boolean isActive) {
- mMainThread.execute(new Runnable() {
- @Override
- public void run() {
- if (isActive || mFocusRing.isPassiveFocusRunning() ||
- mFocusRing.isActiveFocusRunning()) {
- mFocusRing.setRadiusRatio(mDiopterToRatio.scale(diopter));
- }
- }
- });
+ public void onFocusDistance(float lensDistance, LinearScale lensRange) {
+ if (lensRange.isInDomain(lensDistance)) {
+ setFocusRatio(lensRange.scale(lensDistance));
+ }
}
}
--- /dev/null
+/*
+ * 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.ui.focus;
+
+import android.annotation.TargetApi;
+import android.hardware.camera2.CameraCharacteristics;
+import android.os.Build.VERSION_CODES;
+
+import com.android.camera.debug.Log;
+import com.android.camera.debug.Log.Tag;
+import com.android.camera.ui.motion.LinearScale;
+
+/**
+ * Compute diopter range scale to convert lens focus distances into
+ * a ratio value.
+ */
+@TargetApi(VERSION_CODES.L)
+public class LensRangeCalculator {
+
+ /**
+ * A NoOp linear scale for computing diopter values will always return 0
+ */
+ public static LinearScale getNoOp() {
+ return new LinearScale(0, 0, 0, 0);
+ }
+
+ /**
+ * Compute the focus range from the camera characteristics and build
+ * a linear scale model that maps a focus distance to a ratio between
+ * the min and max range.
+ */
+ public static LinearScale getDiopterToRatioCalculator(CameraCharacteristics characteristics) {
+ // From the android documentation:
+ // 0.0f represents farthest focus, and LENS_INFO_MINIMUM_FOCUS_DISTANCE
+ // represents the nearest focus the device can achieve.
+ Float nearest = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+ Float hyperfocal = characteristics.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE);
+
+ if (nearest == null && hyperfocal == null) {
+ return getNoOp();
+ }
+
+ nearest = (nearest == null) ? 0.0f : nearest;
+ hyperfocal = (hyperfocal == null) ? 0.0f : hyperfocal;
+
+ if (nearest > hyperfocal) {
+ return new LinearScale(hyperfocal, nearest, 0, 1);
+ }
+
+ return new LinearScale(nearest, hyperfocal, 0, 1);
+ }
+}
}
/**
+ * Returns true if the value is within the domain.
+ */
+ public boolean isInDomain(float domainValue) {
+ if (mDomainA > mDomainB) {
+ return domainValue >= mDomainA && domainValue <= mDomainB;
+ }
+ return domainValue >= mDomainB && domainValue <= mDomainA;
+ }
+
+ /**
* Linearly scale a given domain value into the output range.
*/
public float scale(float domainValue) {