diff options
| author | Sean Owen <srowen@gmail.com> | 2014-04-30 18:12:13 +0400 |
|---|---|---|
| committer | Sean Owen <srowen@gmail.com> | 2014-04-30 18:12:13 +0400 |
| commit | c1b1b631705922cfdf3e50a1f7b9d3f92ff7ba70 (patch) | |
| tree | 7fe5edb08ed29335ce36303d63b91f08299b7ed3 /android-core/src/main/java/com | |
| parent | 49e1127e064af44da8dade70af1e2ef59cd23fe0 (diff) | |
| download | zxing-c1b1b631705922cfdf3e50a1f7b9d3f92ff7ba70.tar.xz | |
Refactor common code from across Android apps into module
Diffstat (limited to 'android-core/src/main/java/com')
| -rw-r--r-- | android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java | 383 |
1 files changed, 383 insertions, 0 deletions
diff --git a/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java b/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java new file mode 100644 index 000000000..4fd44f6cf --- /dev/null +++ b/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java @@ -0,0 +1,383 @@ +/* + * Copyright (C) 2014 ZXing authors + * + * 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.google.zxing.client.android.camera; + +import android.graphics.Point; +import android.graphics.Rect; +import android.hardware.Camera; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +/** + * Utility methods for configuring the Android camera. + * + * @author Sean Owen + */ +public final class CameraConfigurationUtils { + + private static final String TAG = "CameraConfiguration"; + + private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen + private static final float MAX_EXPOSURE_COMPENSATION = 1.5f; + private static final float MIN_EXPOSURE_COMPENSATION = 0.0f; + private static final double MAX_ASPECT_DISTORTION = 0.15; + private static final int MIN_FPS = 10; + private static final int AREA_PER_1000 = 400; + + private CameraConfigurationUtils() { + } + + public static void setFocus(Camera.Parameters parameters, + boolean autoFocus, + boolean disableContinuous, + boolean safeMode) { + List<String> supportedFocusModes = parameters.getSupportedFocusModes(); + String focusMode = null; + if (autoFocus) { + if (safeMode || disableContinuous) { + focusMode = CameraConfigurationUtils.findSettableValue("focus mode", + supportedFocusModes, + Camera.Parameters.FOCUS_MODE_AUTO); + } else { + focusMode = CameraConfigurationUtils.findSettableValue("focus mode", + supportedFocusModes, + Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE, + Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, + Camera.Parameters.FOCUS_MODE_AUTO); + } + } + // Maybe selected auto-focus but not available, so fall through here: + if (!safeMode && focusMode == null) { + focusMode = CameraConfigurationUtils.findSettableValue("focus mode", + supportedFocusModes, + Camera.Parameters.FOCUS_MODE_MACRO, + Camera.Parameters.FOCUS_MODE_EDOF); + } + if (focusMode != null) { + if (focusMode.equals(parameters.getFocusMode())) { + Log.i(TAG, "Focus mode already set to " + focusMode); + } else { + parameters.setFocusMode(focusMode); + } + } + } + + public static void setTorch(Camera.Parameters parameters, boolean on) { + List<String> supportedFlashModes = parameters.getSupportedFlashModes(); + String flashMode; + if (on) { + flashMode = CameraConfigurationUtils.findSettableValue("flash mode", + supportedFlashModes, + Camera.Parameters.FLASH_MODE_TORCH, + Camera.Parameters.FLASH_MODE_ON); + } else { + flashMode = CameraConfigurationUtils.findSettableValue("flash mode", + supportedFlashModes, + Camera.Parameters.FLASH_MODE_OFF); + } + if (flashMode != null) { + if (flashMode.equals(parameters.getFlashMode())) { + Log.i(TAG, "Flash mode already set to " + flashMode); + } else { + Log.i(TAG, "Setting flash mode to " + flashMode); + parameters.setFlashMode(flashMode); + } + } + } + + public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) { + int minExposure = parameters.getMinExposureCompensation(); + int maxExposure = parameters.getMaxExposureCompensation(); + float step = parameters.getExposureCompensationStep(); + if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) { + // Set low when light is on + float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION; + int compensationSteps = Math.round(targetCompensation / step); + float actualCompensation = step * compensationSteps; + // Clamp value: + compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure); + if (parameters.getExposureCompensation() == compensationSteps) { + Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation); + } else { + Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation); + parameters.setExposureCompensation(compensationSteps); + } + } else { + Log.i(TAG, "Camera does not support exposure compensation"); + } + } + + public static void setBestPreviewFPS(Camera.Parameters parameters) { + setBestPreviewFPS(parameters, MIN_FPS); + } + + public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS) { + List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange(); + Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges)); + if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) { + int[] minimumSuitableFpsRange = null; + for (int[] fpsRange : supportedPreviewFpsRanges) { + int fpsMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]; + if (fpsMax >= minFPS * 1000 && + (minimumSuitableFpsRange == null || + fpsMax > minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])) { + minimumSuitableFpsRange = fpsRange; + } + } + if (minimumSuitableFpsRange == null) { + Log.i(TAG, "No suitable FPS range?"); + } else { + int[] currentFpsRange = new int[2]; + parameters.getPreviewFpsRange(currentFpsRange); + if (Arrays.equals(currentFpsRange, minimumSuitableFpsRange)) { + Log.i(TAG, "FPS range already set to " + Arrays.toString(minimumSuitableFpsRange)); + } else { + Log.i(TAG, "Setting FPS range to " + Arrays.toString(minimumSuitableFpsRange)); + parameters.setPreviewFpsRange(minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], + minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); + } + } + } + } + + public static void setFocusArea(Camera.Parameters parameters) { + if (parameters.getMaxNumFocusAreas() > 0) { + Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas())); + List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000); + Log.i(TAG, "Setting focus area to : " + toString(middleArea)); + parameters.setFocusAreas(middleArea); + } else { + Log.i(TAG, "Device does not support focus areas"); + } + } + + public static void setMetering(Camera.Parameters parameters) { + if (parameters.getMaxNumMeteringAreas() > 0) { + Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas()); + List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000); + Log.i(TAG, "Setting metering area to : " + toString(middleArea)); + parameters.setMeteringAreas(middleArea); + } else { + Log.i(TAG, "Device does not support metering areas"); + } + } + + private static List<Camera.Area> buildMiddleArea(int areaPer1000) { + return Collections.singletonList( + new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1)); + } + + public static void setVideoStabilization(Camera.Parameters parameters) { + if (parameters.isVideoStabilizationSupported()) { + if (parameters.getVideoStabilization()) { + Log.i(TAG, "Video stabilization already enabled"); + } else { + Log.i(TAG, "Enabling video stabilization..."); + parameters.setVideoStabilization(true); + } + } else { + Log.i(TAG, "This device does not support video stabilization"); + } + } + + public static void setBarcodeSceneMode(Camera.Parameters parameters) { + if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) { + Log.i(TAG, "Barcode scene mode already set"); + return; + } + String sceneMode = findSettableValue("scene mode", + parameters.getSupportedSceneModes(), + Camera.Parameters.SCENE_MODE_BARCODE); + if (sceneMode != null) { + parameters.setSceneMode(sceneMode); + } + } + + public static void setMaxZoom(Camera.Parameters parameters) { + int maxZoom = parameters.getMaxZoom(); + Log.i(TAG, "Setting zoom to max of " + maxZoom); + setZoom(parameters, maxZoom); + } + + public static void setZoom(Camera.Parameters parameters, int zoom) { + if (parameters.isZoomSupported()) { + if (parameters.getZoom() == zoom) { + Log.i(TAG, "Zoom is already set to " + zoom); + } else { + Log.i(TAG, "Setting zoom to " + zoom); + parameters.setZoom(zoom); + } + } else { + Log.i(TAG, "Zoom is not supported"); + } + } + + public static void setInvertColor(Camera.Parameters parameters) { + if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) { + Log.i(TAG, "Negative effect already set"); + return; + } + String colorMode = + CameraConfigurationUtils.findSettableValue("color effect", + parameters.getSupportedColorEffects(), + Camera.Parameters.EFFECT_NEGATIVE); + if (colorMode != null) { + parameters.setColorEffect(colorMode); + } + } + + public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) { + + List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes(); + if (rawSupportedSizes == null) { + Log.w(TAG, "Device returned no supported preview sizes; using default"); + Camera.Size defaultSize = parameters.getPreviewSize(); + if (defaultSize == null) { + throw new IllegalStateException("Parameters contained no preview size!"); + } + return new Point(defaultSize.width, defaultSize.height); + } + + // Sort by size, descending + List<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes); + Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() { + @Override + public int compare(Camera.Size a, Camera.Size b) { + int aPixels = a.height * a.width; + int bPixels = b.height * b.width; + if (bPixels < aPixels) { + return -1; + } + if (bPixels > aPixels) { + return 1; + } + return 0; + } + }); + + if (Log.isLoggable(TAG, Log.INFO)) { + StringBuilder previewSizesString = new StringBuilder(); + for (Camera.Size supportedPreviewSize : supportedPreviewSizes) { + previewSizesString.append(supportedPreviewSize.width).append('x') + .append(supportedPreviewSize.height).append(' '); + } + Log.i(TAG, "Supported preview sizes: " + previewSizesString); + } + + double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y; + + // Remove sizes that are unsuitable + Iterator<Camera.Size> it = supportedPreviewSizes.iterator(); + while (it.hasNext()) { + Camera.Size supportedPreviewSize = it.next(); + int realWidth = supportedPreviewSize.width; + int realHeight = supportedPreviewSize.height; + if (realWidth * realHeight < MIN_PREVIEW_PIXELS) { + it.remove(); + continue; + } + + boolean isCandidatePortrait = realWidth < realHeight; + int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth; + int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight; + double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight; + double distortion = Math.abs(aspectRatio - screenAspectRatio); + if (distortion > MAX_ASPECT_DISTORTION) { + it.remove(); + continue; + } + + if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) { + Point exactPoint = new Point(realWidth, realHeight); + Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint); + return exactPoint; + } + } + + // If no exact match, use largest preview size. This was not a great idea on older devices because + // of the additional computation needed. We're likely to get here on newer Android 4+ devices, where + // the CPU is much more powerful. + if (!supportedPreviewSizes.isEmpty()) { + Camera.Size largestPreview = supportedPreviewSizes.get(0); + Point largestSize = new Point(largestPreview.width, largestPreview.height); + Log.i(TAG, "Using largest suitable preview size: " + largestSize); + return largestSize; + } + + // If there is nothing at all suitable, return current preview size + Camera.Size defaultPreview = parameters.getPreviewSize(); + if (defaultPreview == null) { + throw new IllegalStateException("Parameters contained no preview size!"); + } + Point defaultSize = new Point(defaultPreview.width, defaultPreview.height); + Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize); + return defaultSize; + } + + private static String findSettableValue(String name, + Collection<String> supportedValues, + String... desiredValues) { + Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues)); + Log.i(TAG, "Supported " + name + " values: " + supportedValues); + if (supportedValues != null) { + for (String desiredValue : desiredValues) { + if (supportedValues.contains(desiredValue)) { + Log.i(TAG, "Can set " + name + " to: " + desiredValue); + return desiredValue; + } + } + } + Log.i(TAG, "No supported values match"); + return null; + } + + private static String toString(Collection<int[]> arrays) { + if (arrays == null || arrays.isEmpty()) { + return "[]"; + } + StringBuilder buffer = new StringBuilder(); + buffer.append('['); + Iterator<int[]> it = arrays.iterator(); + while (it.hasNext()) { + buffer.append(Arrays.toString(it.next())); + if (it.hasNext()) { + buffer.append(", "); + } + } + buffer.append(']'); + return buffer.toString(); + } + + private static String toString(Iterable<Camera.Area> areas) { + if (areas == null) { + return null; + } + StringBuilder result = new StringBuilder(); + for (Camera.Area area : areas) { + result.append(area.rect).append(':').append(area.weight).append(' '); + } + return result.toString(); + } + +} |
