FileDocCategorySizeDatePackage
ParameterUtils.javaAPI DocAndroid 5.1 API42352Thu Mar 12 22:22:10 GMT 2015android.hardware.camera2.legacy

ParameterUtils

public class ParameterUtils extends Object
Various utilities for dealing with camera API1 parameters.

Fields Summary
public static final int
NORMALIZED_RECTANGLE_MIN
Upper/left minimal point of a normalized rectangle
public static final int
NORMALIZED_RECTANGLE_MAX
Lower/right maximal point of a normalized rectangle
public static final android.graphics.Rect
NORMALIZED_RECTANGLE_DEFAULT
The default normalized rectangle spans the entire size of the preview viewport
public static final android.hardware.Camera.Area
CAMERA_AREA_DEFAULT
The default normalized area uses the default normalized rectangle with a weight=1
public static final android.graphics.Rect
RECTANGLE_EMPTY
Empty rectangle {@code 0x0+0,0}
private static final String
TAG
private static final boolean
VERBOSE
private static final int
ZOOM_RATIO_MULTIPLIER
getZoomRatios stores zoom ratios in 1/100 increments, e.x. a zoom of 3.2 is 320
Constructors Summary
private ParameterUtils()

        throw new AssertionError();
    
Methods Summary
public static booleancontainsSize(java.util.List sizeList, int width, int height)
Check if the camera API1 list of sizes contains a size with the given dimens.

        checkNotNull(sizeList, "sizeList must not be null");
        for (Camera.Size s : sizeList) {
            if (s.height == height && s.width == width) {
                return true;
            }
        }
        return false;
    
public static android.hardware.camera2.legacy.ParameterUtils$WeightedRectangleconvertCameraAreaToActiveArrayRectangle(android.graphics.Rect activeArray, android.hardware.camera2.legacy.ParameterUtils$ZoomData zoomData, android.hardware.Camera.Area area)
Convert the normalized camera area from [-1000, 1000] coordinate space into the active array-based coordinate space.

Values out of range are clipped to be within the resulting (reported) crop region. It is possible to have values larger than the preview crop.

Weights out of range of [0, 1000] are clipped to be within the range.

param
activeArraySize active array size of the sensor (e.g. max jpeg size)
param
zoomData the calculated zoom data corresponding to this request
param
area the normalized camera area
return
the weighed rectangle in active array coordinate space, with the weight

        return convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, area,
                /*usePreviewCrop*/true);
    
private static android.hardware.camera2.legacy.ParameterUtils$WeightedRectangleconvertCameraAreaToActiveArrayRectangle(android.graphics.Rect activeArray, android.hardware.camera2.legacy.ParameterUtils$ZoomData zoomData, android.hardware.Camera.Area area, boolean usePreviewCrop)

        Rect previewCrop = zoomData.previewCrop;
        Rect reportedCrop = zoomData.reportedCrop;

        float scaleW = previewCrop.width() * 1.0f /
                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
        float scaleH = previewCrop.height() * 1.0f /
                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);

        /*
         * Calculate the reported metering region from the non-intersected normalized region
         * by scaling and translating back into active array-relative coordinates.
         */
        Matrix transform = new Matrix();

        // Move top left from (-1000, -1000) to (0, 0)
        transform.setTranslate(/*dx*/NORMALIZED_RECTANGLE_MAX, /*dy*/NORMALIZED_RECTANGLE_MAX);

        // Scale from [0, 2000] back into the preview rectangle
        transform.postScale(scaleW, scaleH);

        // Move the rect so that the [-1000,-1000] point ends up at the preview [left, top]
        transform.postTranslate(previewCrop.left, previewCrop.top);

        Rect cropToIntersectAgainst = usePreviewCrop ? previewCrop : reportedCrop;

        // Now apply the transformation backwards to get the reported metering region
        Rect reportedMetering = ParamsUtils.mapRect(transform, area.rect);
        // Intersect it with the crop region, to avoid reporting out-of-bounds
        // metering regions
        if (!reportedMetering.intersect(cropToIntersectAgainst)) {
            reportedMetering.set(RECTANGLE_EMPTY);
        }

        int weight = area.weight;
        if (weight < MeteringRectangle.METERING_WEIGHT_MIN) {
            Log.w(TAG,
                    "convertCameraAreaToMeteringRectangle - rectangle "
                            + stringFromArea(area) + " has too small weight, clip to 0");
            weight = 0;
        }

        return new WeightedRectangle(reportedMetering, area.weight);
    
private static android.graphics.PointconvertCameraPointToActiveArrayPoint(android.graphics.Rect activeArray, android.hardware.camera2.legacy.ParameterUtils$ZoomData zoomData, android.graphics.Point point, boolean usePreviewCrop)

        Rect pointedRect = new Rect(point.x, point.y, point.x, point.y);
        Camera.Area pointedArea = new Area(pointedRect, /*weight*/1);

        WeightedRectangle adjustedRect =
                convertCameraAreaToActiveArrayRectangle(activeArray,
                        zoomData, pointedArea, usePreviewCrop);

        Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top);

        return transformedPoint;
    
public static android.hardware.camera2.params.FaceconvertFaceFromLegacy(Camera.Face face, android.graphics.Rect activeArray, android.hardware.camera2.legacy.ParameterUtils$ZoomData zoomData)
Convert an api1 face into an active-array based api2 face.

Out-of-ranges scores and ids will be clipped to be within range (with a warning).

param
face a non-{@code null} api1 face
param
activeArraySize active array size of the sensor (e.g. max jpeg size)
param
zoomData the calculated zoom data corresponding to this request
return
a non-{@code null} api2 face
throws
NullPointerException if the {@code face} was {@code null}

        checkNotNull(face, "face must not be null");

        Face api2Face;

        Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1);

        WeightedRectangle faceRect =
                convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);

        Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
        if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
                leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
                mouth.x != -2000 && mouth.y != -2000) {
            leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
                    leftEye, /*usePreviewCrop*/true);
            rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
                    leftEye, /*usePreviewCrop*/true);
            mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
                    leftEye, /*usePreviewCrop*/true);

            api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth);
        } else {
            api2Face = faceRect.toFace();
        }

        return api2Face;
    
public static android.hardware.camera2.legacy.ParameterUtils$MeteringDataconvertMeteringRectangleToLegacy(android.graphics.Rect activeArray, android.hardware.camera2.params.MeteringRectangle meteringRect, android.hardware.camera2.legacy.ParameterUtils$ZoomData zoomData)
Calculate the actual/effective/reported normalized rectangle data from a metering rectangle.

If any of the rectangles are out-of-range of their intended bounding box, the {@link #RECTANGLE_EMPTY empty rectangle} is substituted instead (with a weight of {@code 0}).

The metering rectangle is bound by the crop region (effective/reported respectively). The metering {@link Camera.Area area} is bound by {@code [-1000, 1000]}.

No parameters are mutated; returns the new metering data.

param
activeArraySize active array size of the sensor (e.g. max jpeg size)
param
meteringRect the user-specified metering rectangle
param
zoomData the calculated zoom data corresponding to this request
return
the metering area, the reported/effective metering rectangles

        Rect previewCrop = zoomData.previewCrop;

        float scaleW = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
                previewCrop.width();
        float scaleH = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
                previewCrop.height();

        Matrix transform = new Matrix();
        // Move the preview crop so that top,left is at (0,0), otherwise after scaling
        // the corner bounds will be outside of [-1000, 1000]
        transform.setTranslate(-previewCrop.left, -previewCrop.top);
        // Scale into [0, 2000] range about the center of the preview
        transform.postScale(scaleW, scaleH);
        // Move so that top left of a typical rect is at [-1000, -1000]
        transform.postTranslate(/*dx*/NORMALIZED_RECTANGLE_MIN, /*dy*/NORMALIZED_RECTANGLE_MIN);

        /*
         * Calculate the preview metering region (effective), and the camera1 api
         * normalized metering region.
         */
        Rect normalizedRegionUnbounded = ParamsUtils.mapRect(transform, meteringRect.getRect());

        /*
         * Try to intersect normalized area with [-1000, 1000] rectangle; otherwise
         * it's completely out of range
         */
        Rect normalizedIntersected = new Rect(normalizedRegionUnbounded);

        Camera.Area meteringArea;
        if (!normalizedIntersected.intersect(NORMALIZED_RECTANGLE_DEFAULT)) {
            Log.w(TAG,
                    "convertMeteringRectangleToLegacy - metering rectangle too small, " +
                    "no metering will be done");
            normalizedIntersected.set(RECTANGLE_EMPTY);
            meteringArea = new Camera.Area(RECTANGLE_EMPTY,
                    MeteringRectangle.METERING_WEIGHT_DONT_CARE);
        } else {
            meteringArea = new Camera.Area(normalizedIntersected,
                    meteringRect.getMeteringWeight());
        }

        /*
         * Calculate effective preview metering region
         */
        Rect previewMetering = meteringRect.getRect();
        if (!previewMetering.intersect(previewCrop)) {
            previewMetering.set(RECTANGLE_EMPTY);
        }

        /*
         * Calculate effective reported metering region
         * - Transform the calculated metering area back into active array space
         * - Clip it to be a subset of the reported crop region
         */
        Rect reportedMetering;
        {
            Camera.Area normalizedAreaUnbounded = new Camera.Area(
                    normalizedRegionUnbounded, meteringRect.getMeteringWeight());
            WeightedRectangle reportedMeteringRect = convertCameraAreaToActiveArrayRectangle(
                    activeArray, zoomData, normalizedAreaUnbounded, /*usePreviewCrop*/false);
            reportedMetering = reportedMeteringRect.rect;
        }

        if (VERBOSE) {
            Log.v(TAG, String.format(
                    "convertMeteringRectangleToLegacy - activeArray = %s, meteringRect = %s, " +
                    "previewCrop = %s, meteringArea = %s, previewMetering = %s, " +
                    "reportedMetering = %s, normalizedRegionUnbounded = %s",
                    activeArray, meteringRect,
                    previewCrop, stringFromArea(meteringArea), previewMetering,
                    reportedMetering, normalizedRegionUnbounded));
        }

        return new MeteringData(meteringArea, previewMetering, reportedMetering);
    
public static android.hardware.camera2.legacy.ParameterUtils$ZoomDataconvertScalerCropRegion(android.graphics.Rect activeArraySize, android.graphics.Rect cropRegion, android.util.Size previewSize, Camera.Parameters params)
Convert the user-specified crop region into zoom data; which can be used to set the parameters to a specific zoom index, or to report back to the user what the actual zoom was, or for other calculations requiring the current preview crop region.

None of the parameters are mutated.

param
activeArraySize active array size of the sensor (e.g. max jpeg size)
param
cropRegion the user-specified crop region
param
previewSize the current preview size (in pixels)
param
params the current camera parameters (not mutated)
return
the zoom index, and the effective/reported crop regions (relative to active array)

        Rect activeArraySizeOnly = new Rect(
                /*left*/0, /*top*/0,
                activeArraySize.width(), activeArraySize.height());

        Rect userCropRegion = cropRegion;

        if (userCropRegion == null) {
            userCropRegion = activeArraySizeOnly;
        }

        if (VERBOSE) {
            Log.v(TAG, "convertScalerCropRegion - user crop region was " + userCropRegion);
        }

        final Rect reportedCropRegion = new Rect();
        final Rect previewCropRegion = new Rect();
        final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
                previewSize, userCropRegion,
                /*out*/reportedCropRegion, /*out*/previewCropRegion);

        if (VERBOSE) {
            Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
                    "zoomIndex = " + zoomIdx +
                    ", reported crop region = " + reportedCropRegion +
                    ", preview crop region = " + previewCropRegion);
        }

        return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
    
public static android.util.SizeconvertSize(Camera.Size size)
Convert a camera API1 size into a util size


                  
         
        checkNotNull(size, "size must not be null");

        return new Size(size.width, size.height);
    
public static java.util.ListconvertSizeList(java.util.List sizeList)
Convert a camera API1 list of sizes into a util list of sizes

        checkNotNull(sizeList, "sizeList must not be null");

        List<Size> sizes = new ArrayList<>(sizeList.size());
        for (Camera.Size s : sizeList) {
            sizes.add(new Size(s.width, s.height));
        }
        return sizes;
    
public static android.util.Size[]convertSizeListToArray(java.util.List sizeList)
Convert a camera API1 list of sizes into an array of sizes

        checkNotNull(sizeList, "sizeList must not be null");

        Size[] array = new Size[sizeList.size()];
        int ctr = 0;
        for (Camera.Size s : sizeList) {
            array[ctr++] = new Size(s.width, s.height);
        }
        return array;
    
private static java.util.ListgetAvailableCropRectangles(Camera.Parameters params, android.graphics.Rect activeArray, android.util.Size streamSize)
Get the available 'crop' (zoom) rectangles for this camera.

When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size, where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).

Each crop rectangle is changed to have the same aspect ratio as {@code streamSize}, by shrinking the rectangle if necessary.

To get the reported crop region when applying a zoom to the sensor, use {@code streamSize} = {@code activeArray size}.

param
params non-{@code null} camera api1 parameters
param
activeArray active array dimensions, in sensor space
param
streamSize stream size dimensions, in pixels
return
a list of available zoom rectangles, sorted from least zoomed to most zoomed

        checkNotNull(params, "params must not be null");
        checkNotNull(activeArray, "activeArray must not be null");
        checkNotNull(streamSize, "streamSize must not be null");

        // TODO: change all uses of Rect activeArray to Size activeArray,
        // since we want the crop to be active-array relative, not pixel-array relative

        Rect unzoomedStreamCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);

        if (!params.isZoomSupported()) {
            // Trivial case: No zoom -> only support the full size as the crop region
            return new ArrayList<>(Arrays.asList(unzoomedStreamCrop));
        }

        List<Rect> zoomCropRectangles = new ArrayList<>(params.getMaxZoom() + 1);
        Matrix scaleMatrix = new Matrix();
        RectF scaledRect = new RectF();

        for (int zoom : params.getZoomRatios()) {
            float shrinkRatio = ZOOM_RATIO_MULTIPLIER * 1.0f / zoom; // normalize to 1.0 and smaller

            // set scaledRect to unzoomedStreamCrop
            ParamsUtils.convertRectF(unzoomedStreamCrop, /*out*/scaledRect);

            scaleMatrix.setScale(
                    shrinkRatio, shrinkRatio,
                    activeArray.exactCenterX(),
                    activeArray.exactCenterY());

            scaleMatrix.mapRect(scaledRect);

            Rect intRect = ParamsUtils.createRect(scaledRect);

            // Round the rect corners towards the nearest integer values
            zoomCropRectangles.add(intRect);
        }

        return zoomCropRectangles;
    
public static java.util.ListgetAvailablePreviewZoomCropRectangles(Camera.Parameters params, android.graphics.Rect activeArray, android.util.Size previewSize)
Get the available 'crop' (zoom) rectangles for this camera.

This is the effective (real) crop that is applied by the camera api1 device when projecting the zoom onto the intermediate preview buffer. Use this when deciding which zoom ratio to apply.

When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size, where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).

Each crop rectangle is changed to have the same aspect ratio as {@code streamSize}, by shrinking the rectangle if necessary.

To get the reported crop region when applying a zoom to the sensor, use {@code streamSize} = {@code activeArray size}.

param
params non-{@code null} camera api1 parameters
param
activeArray active array dimensions, in sensor space
param
streamSize stream size dimensions, in pixels
return
a list of available zoom rectangles, sorted from least zoomed to most zoomed

        checkNotNull(params, "params must not be null");
        checkNotNull(activeArray, "activeArray must not be null");
        checkNotNull(previewSize, "previewSize must not be null");

        return getAvailableCropRectangles(params, activeArray, previewSize);
    
public static java.util.ListgetAvailableZoomCropRectangles(Camera.Parameters params, android.graphics.Rect activeArray)
Get the available 'crop' (zoom) rectangles for this camera that will be reported via a {@code CaptureResult} when a zoom is requested.

These crops ignores the underlying preview buffer size, and will always be reported the same values regardless of what configuration of outputs is used.

When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size, where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).

Each crop rectangle is changed to have the same aspect ratio as {@code streamSize}, by shrinking the rectangle if necessary.

To get the reported crop region when applying a zoom to the sensor, use {@code streamSize} = {@code activeArray size}.

param
params non-{@code null} camera api1 parameters
param
activeArray active array dimensions, in sensor space
param
streamSize stream size dimensions, in pixels
return
a list of available zoom rectangles, sorted from least zoomed to most zoomed

        checkNotNull(params, "params must not be null");
        checkNotNull(activeArray, "activeArray must not be null");

        return getAvailableCropRectangles(params, activeArray, ParamsUtils.createSize(activeArray));
    
public static intgetClosestAvailableZoomCrop(Camera.Parameters params, android.graphics.Rect activeArray, android.util.Size streamSize, android.graphics.Rect cropRegion, android.graphics.Rect reportedCropRegion, android.graphics.Rect previewCropRegion)
Calculate the closest zoom index for the user-requested crop region by rounding up to the closest (largest or equal) possible zoom crop.

If the requested crop region exceeds the size of the active array, it is shrunk to fit inside of the active array first.

Since all api1 camera devices only support a discrete set of zooms, we have to translate the per-pixel-granularity requested crop region into a per-zoom-index granularity.

Furthermore, since the zoom index and zoom levels also depends on the field-of-view of the preview, the current preview {@code streamSize} is also used.

The calculated crop regions are then written to in-place to {@code reportedCropRegion} and {@code previewCropRegion}, in coordinates relative to the active array.

param
params non-{@code null} camera api1 parameters
param
activeArray active array dimensions, in sensor space
param
streamSize stream size dimensions, in pixels
param
cropRegion user-specified crop region, in active array coordinates
param
reportedCropRegion (out parameter) what the result for {@code cropRegion} looks like
param
previewCropRegion (out parameter) what the visual preview crop is
return
the zoom index inclusively between 0 and {@code Parameters#getMaxZoom}, where 0 means the camera is not zoomed
throws
NullPointerException if any of the args were {@code null}

        checkNotNull(params, "params must not be null");
        checkNotNull(activeArray, "activeArray must not be null");
        checkNotNull(streamSize, "streamSize must not be null");
        checkNotNull(reportedCropRegion, "reportedCropRegion must not be null");
        checkNotNull(previewCropRegion, "previewCropRegion must not be null");

        Rect actualCrop = new Rect(cropRegion);

        /*
         * Shrink requested crop region to fit inside of the active array size
         */
        if (!actualCrop.intersect(activeArray)) {
            Log.w(TAG, "getClosestAvailableZoomCrop - Crop region out of range; " +
                    "setting to active array size");
            actualCrop.set(activeArray);
        }

        Rect previewCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);

        // Make the user-requested crop region the same aspect ratio as the preview stream size
        Rect cropRegionAsPreview =
                shrinkToSameAspectRatioCentered(previewCrop, actualCrop);

        if (VERBOSE) {
            Log.v(TAG, "getClosestAvailableZoomCrop - actualCrop = " + actualCrop);
            Log.v(TAG,
                    "getClosestAvailableZoomCrop - previewCrop = " + previewCrop);
            Log.v(TAG,
                    "getClosestAvailableZoomCrop - cropRegionAsPreview = " + cropRegionAsPreview);
        }

        /*
         * Iterate all available zoom rectangles and find the closest zoom index
         */
        Rect bestReportedCropRegion = null;
        Rect bestPreviewCropRegion = null;
        int bestZoomIndex = -1;

        List<Rect> availableReportedCropRegions =
                getAvailableZoomCropRectangles(params, activeArray);
        List<Rect> availablePreviewCropRegions =
                getAvailablePreviewZoomCropRectangles(params, activeArray, streamSize);

        if (VERBOSE) {
            Log.v(TAG,
                    "getClosestAvailableZoomCrop - availableReportedCropRegions = " +
                            ListUtils.listToString(availableReportedCropRegions));
            Log.v(TAG,
                    "getClosestAvailableZoomCrop - availablePreviewCropRegions = " +
                            ListUtils.listToString(availablePreviewCropRegions));
        }

        if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
            throw new AssertionError("available reported/preview crop region size mismatch");
        }

        for (int i = 0; i < availableReportedCropRegions.size(); ++i) {
            Rect currentPreviewCropRegion = availablePreviewCropRegions.get(i);
            Rect currentReportedCropRegion = availableReportedCropRegions.get(i);

            boolean isBest;
            if (bestZoomIndex == -1) {
                isBest = true;
            } else if (currentPreviewCropRegion.width() >= cropRegionAsPreview.width() &&
                    currentPreviewCropRegion.height() >= cropRegionAsPreview.height()) {
                isBest = true;
            } else {
                isBest = false;
            }

            // Sizes are sorted largest-to-smallest, so once the available crop is too small,
            // we the rest are too small. Furthermore, this is the final best crop,
            // since its the largest crop that still fits the requested crop
            if (isBest) {
                bestPreviewCropRegion = currentPreviewCropRegion;
                bestReportedCropRegion = currentReportedCropRegion;
                bestZoomIndex = i;
            } else {
                break;
            }
        }

        if (bestZoomIndex == -1) {
            // Even in the worst case, we should always at least return 0 here
            throw new AssertionError("Should've found at least one valid zoom index");
        }

        // Write the rectangles in-place
        reportedCropRegion.set(bestReportedCropRegion);
        previewCropRegion.set(bestPreviewCropRegion);

        return bestZoomIndex;
    
public static android.util.SizegetLargestSupportedJpegSizeByArea(Camera.Parameters params)
Returns the largest supported picture size, as compared by its area.

        checkNotNull(params, "params must not be null");

        List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes());
        return SizeAreaComparator.findLargestByArea(supportedJpegSizes);
    
public static floatgetMaxZoomRatio(Camera.Parameters params)
Get the largest possible zoom ratio (normalized to {@code 1.0f} and higher) that the camera can support.

If the camera does not support zoom, it always returns {@code 1.0f}.

param
params non-{@code null} camera api1 parameters
return
normalized max zoom ratio, at least {@code 1.0f}

        if (!params.isZoomSupported()) {
            return 1.0f; // no zoom
        }

        List<Integer> zoomRatios = params.getZoomRatios(); // sorted smallest->largest
        int zoom = zoomRatios.get(zoomRatios.size() - 1); // largest zoom ratio
        float zoomRatio = zoom * 1.0f / ZOOM_RATIO_MULTIPLIER; // normalize to 1.0 and smaller

        return zoomRatio;
    
private static android.graphics.RectgetPreviewCropRectangleUnzoomed(android.graphics.Rect activeArray, android.util.Size previewSize)
Calculate the effective crop rectangle for this preview viewport; assumes the preview is centered to the sensor and scaled to fit across one of the dimensions without skewing.

The preview size must be a subset of the active array size; the resulting rectangle will also be a subset of the active array rectangle.

The unzoomed crop rectangle is calculated only.

param
activeArray active array dimensions, in sensor space
param
previewSize size of the preview buffer render target, in pixels (not in sensor space)
return
a rectangle which serves as the preview stream's effective crop region (unzoomed), in sensor space
throws
NullPointerException if any of the args were {@code null}
throws
IllegalArgumentException if {@code previewSize} is wider or taller than {@code activeArray}

        if (previewSize.getWidth() > activeArray.width()) {
            throw new IllegalArgumentException("previewSize must not be wider than activeArray");
        } else if (previewSize.getHeight() > activeArray.height()) {
            throw new IllegalArgumentException("previewSize must not be taller than activeArray");
        }

        float aspectRatioArray = activeArray.width() * 1.0f / activeArray.height();
        float aspectRatioPreview = previewSize.getWidth() * 1.0f / previewSize.getHeight();

        float cropH, cropW;
        if (aspectRatioPreview < aspectRatioArray) {
            // The new width must be smaller than the height, so scale the width by AR
            cropH = activeArray.height();
            cropW = cropH * aspectRatioPreview;
        } else {
            // The new height must be smaller (or equal) than the width, so scale the height by AR
            cropW = activeArray.width();
            cropH = cropW / aspectRatioPreview;
        }

        Matrix translateMatrix = new Matrix();
        RectF cropRect = new RectF(/*left*/0, /*top*/0, cropW, cropH);

        // Now center the crop rectangle so its center is in the center of the active array
        translateMatrix.setTranslate(activeArray.exactCenterX(), activeArray.exactCenterY());
        translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());

        translateMatrix.mapRect(/*inout*/cropRect);

        // Round the rect corners towards the nearest integer values
        return ParamsUtils.createRect(cropRect);
    
private static android.util.SizeFgetZoomRatio(android.util.Size activeArraySize, android.util.Size cropSize)
Returns the component-wise zoom ratio (each greater or equal than {@code 1.0}); largest values means more zoom.

param
activeArraySize active array size of the sensor (e.g. max jpeg size)
param
cropSize size of the crop/zoom
return
{@link SizeF} with width/height being the component-wise zoom ratio
throws
NullPointerException if any of the args were {@code null}
throws
IllegalArgumentException if any component of {@code cropSize} was {@code 0}

        checkNotNull(activeArraySize, "activeArraySize must not be null");
        checkNotNull(cropSize, "cropSize must not be null");
        checkArgumentPositive(cropSize.getWidth(), "cropSize.width must be positive");
        checkArgumentPositive(cropSize.getHeight(), "cropSize.height must be positive");

        float zoomRatioWidth = activeArraySize.getWidth() * 1.0f / cropSize.getWidth();
        float zoomRatioHeight = activeArraySize.getHeight() * 1.0f / cropSize.getHeight();

        return new SizeF(zoomRatioWidth, zoomRatioHeight);
    
private static android.graphics.RectshrinkToSameAspectRatioCentered(android.graphics.Rect reference, android.graphics.Rect shrinkTarget)
Shrink the {@code shrinkTarget} rectangle to snugly fit inside of {@code reference}; the aspect ratio of {@code shrinkTarget} will change to be the same aspect ratio as {@code reference}.

At most a single dimension will scale (down). Both dimensions will never be scaled.

param
reference the rectangle whose aspect ratio will be used as the new aspect ratio
param
shrinkTarget the rectangle which will be scaled down to have a new aspect ratio
return
a new rectangle, a subset of {@code shrinkTarget}, whose aspect ratio will match that of {@code reference}

        float aspectRatioReference = reference.width() * 1.0f / reference.height();
        float aspectRatioShrinkTarget = shrinkTarget.width() * 1.0f / shrinkTarget.height();

        float cropH, cropW;
        if (aspectRatioShrinkTarget < aspectRatioReference) {
            // The new width must be smaller than the height, so scale the width by AR
            cropH = reference.height();
            cropW = cropH * aspectRatioShrinkTarget;
        } else {
            // The new height must be smaller (or equal) than the width, so scale the height by AR
            cropW = reference.width();
            cropH = cropW / aspectRatioShrinkTarget;
        }

        Matrix translateMatrix = new Matrix();
        RectF shrunkRect = new RectF(shrinkTarget);

        // Scale the rectangle down, but keep its center in the same place as before
        translateMatrix.setScale(cropW / reference.width(), cropH / reference.height(),
                shrinkTarget.exactCenterX(), shrinkTarget.exactCenterY());

        translateMatrix.mapRect(/*inout*/shrunkRect);

        return ParamsUtils.createRect(shrunkRect);
    
public static java.lang.StringstringFromArea(android.hardware.Camera.Area area)
Convert a camera area into a human-readable string.

        if (area == null) {
            return null;
        } else {
            StringBuilder sb = new StringBuilder();
            Rect r = area.rect;

            sb.setLength(0);
            sb.append("(["); sb.append(r.left); sb.append(',");
            sb.append(r.top); sb.append("]["); sb.append(r.right);
            sb.append(',"); sb.append(r.bottom); sb.append(']");

            sb.append(',");
            sb.append(area.weight);
            sb.append(')");

            return sb.toString();
        }
    
public static java.lang.StringstringFromAreaList(java.util.List areaList)
Convert a camera area list into a human-readable string

param
areaList a list of areas (null is ok)

        StringBuilder sb = new StringBuilder();

        if (areaList == null) {
            return null;
        }

        int i = 0;
        for (Camera.Area area : areaList) {
            if (area == null) {
                sb.append("null");
            } else {
                sb.append(stringFromArea(area));
            }

            if (i != areaList.size() - 1) {
                sb.append(", ");
            }

            i++;
        }

        return sb.toString();