1df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin/*
2df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * Copyright (C) 2014 The Android Open Source Project
3df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin *
4df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
5df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * you may not use this file except in compliance with the License.
6df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * You may obtain a copy of the License at
7df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin *
8df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
9df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin *
10df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * Unless required by applicable law or agreed to in writing, software
11df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
12df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * See the License for the specific language governing permissions and
14df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * limitations under the License.
15df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin */
16df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
17df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinpackage android.hardware.camera2.legacy;
18df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
19df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.graphics.Matrix;
207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.graphics.Point;
21df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.graphics.Rect;
22df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.graphics.RectF;
23df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.Camera;
247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.Camera.Area;
257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.camera2.params.Face;
267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.camera2.params.MeteringRectangle;
27df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.ListUtils;
28df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.ParamsUtils;
29df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.SizeAreaComparator;
30df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.Size;
31df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.SizeF;
32df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
33df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.Log;
34df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
35df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.ArrayList;
36df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.Arrays;
37df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.List;
38df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
39df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport static com.android.internal.util.Preconditions.*;
40df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
41df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin/**
42df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * Various utilities for dealing with camera API1 parameters.
43df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin */
440a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin@SuppressWarnings("deprecation")
45df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinpublic class ParameterUtils {
467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Upper/left minimal point of a normalized rectangle */
477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final int NORMALIZED_RECTANGLE_MIN = -1000;
487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Lower/right maximal point of a normalized rectangle */
497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final int NORMALIZED_RECTANGLE_MAX = 1000;
507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** The default normalized rectangle spans the entire size of the preview viewport */
517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Rect NORMALIZED_RECTANGLE_DEFAULT = new Rect(
527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MIN,
537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MIN,
547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MAX,
557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MAX);
567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** The default normalized area uses the default normalized rectangle with a weight=1 */
577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Camera.Area CAMERA_AREA_DEFAULT =
587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            new Camera.Area(new Rect(NORMALIZED_RECTANGLE_DEFAULT),
597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                            /*weight*/1);
607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Empty rectangle {@code 0x0+0,0} */
617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Rect RECTANGLE_EMPTY =
627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            new Rect(/*left*/0, /*top*/0, /*right*/0, /*bottom*/0);
637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
64607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk    private static final double ASPECT_RATIO_TOLERANCE = 0.05f;
65607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk
667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Calculate effective/reported zoom data from a user-specified crop region.
687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static class ZoomData {
707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /** Zoom index used by {@link Camera.Parameters#setZoom} */
717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final int zoomIndex;
727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /** Effective crop-region given the zoom index, coordinates relative to active-array */
737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Rect previewCrop;
747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /** Reported crop-region given the zoom index, coordinates relative to active-array */
757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Rect reportedCrop;
767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public ZoomData(int zoomIndex, Rect previewCrop, Rect reportedCrop) {
787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.zoomIndex = zoomIndex;
797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.previewCrop = previewCrop;
807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.reportedCrop = reportedCrop;
817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Calculate effective/reported metering data from a user-specified metering region.
867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static class MeteringData {
887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * The metering area scaled to the range of [-1000, 1000].
907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>Values outside of this range are clipped to be within the range.</p>
917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Camera.Area meteringArea;
937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Effective preview metering region, coordinates relative to active-array.
957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>Clipped to fit inside of the (effective) preview crop region.</p>
977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Rect previewMetering;
997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
1007ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Reported metering region, coordinates relative to active-array.
1017ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1027ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>Clipped to fit inside of the (reported) resulting crop region.</p>
1037ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
1047ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Rect reportedMetering;
1057ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1067ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public MeteringData(Area meteringArea, Rect previewMetering, Rect reportedMetering) {
1077ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.meteringArea = meteringArea;
1087ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.previewMetering = previewMetering;
1097ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.reportedMetering = reportedMetering;
1107ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
1117ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
1127ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1137ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
1147ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * A weighted rectangle is an arbitrary rectangle (the coordinate system is unknown) with an
1157ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * arbitrary weight.
1167ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
1177ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>The user of this class must know what the coordinate system ahead of time; it's
1187ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * then possible to convert to a more concrete type such as a metering rectangle or a face.
1197ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * </p>
1207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
1217ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>When converting to a more concrete type, out-of-range values are clipped; this prevents
1227ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * possible illegal argument exceptions being thrown at runtime.</p>
1237ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
1247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static class WeightedRectangle {
1257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /** Arbitrary rectangle (the range is user-defined); never {@code null}. */
1267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final Rect rect;
1277ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /** Arbitrary weight (the range is user-defined). */
1287ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public final int weight;
1297ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1307ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
1317ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Create a new weighted-rectangle from a non-{@code null} rectangle; the {@code weight}
1327ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * can be unbounded.
1337ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
1347ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public WeightedRectangle(Rect rect, int weight) {
1357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.rect = checkNotNull(rect, "rect must not be null");
1367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            this.weight = weight;
1377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
1387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
1407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Convert to a metering rectangle, clipping any of the values to stay within range.
1417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>If values are clipped, a warning is printed to logcat.</p>
1437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * @return a new metering rectangle
1457ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
1467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public MeteringRectangle toMetering() {
1477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int weight = clip(this.weight,
1487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    MeteringRectangle.METERING_WEIGHT_MIN,
1497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    MeteringRectangle.METERING_WEIGHT_MAX,
1507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    rect,
1517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "weight");
1527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int x = clipLower(rect.left, /*lo*/0, rect, "left");
1547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int y = clipLower(rect.top, /*lo*/0, rect, "top");
1557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int w = clipLower(rect.width(), /*lo*/0, rect, "width");
1567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int h = clipLower(rect.height(), /*lo*/0, rect, "height");
1577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            return new MeteringRectangle(x, y, w, h, weight);
1597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
1607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
1627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Convert to a face; the rect is considered to be the bounds, and the weight
1637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * is considered to be the score.
1647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX},
1667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * the score is clipped first and a warning is printed to logcat.</p>
1677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1680a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin         * <p>If the id is negative, the id is changed to 0 and a warning is printed to
1690a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin         * logcat.</p>
1700a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin         *
1717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>All other parameters are passed-through as-is.</p>
1727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * @return a new face with the optional features set
1747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
1757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public Face toFace(
1767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition) {
1770a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            int idSafe = clipLower(id, /*lo*/0, rect, "id");
1787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int score = clip(weight,
1797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    Face.SCORE_MIN,
1807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    Face.SCORE_MAX,
1817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    rect,
1827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "score");
1837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1840a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            return new Face(rect, score, idSafe, leftEyePosition, rightEyePosition, mouthPosition);
1857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
1867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /**
1887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Convert to a face; the rect is considered to be the bounds, and the weight
1897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * is considered to be the score.
1907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX},
1927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * the score is clipped first and a warning is printed to logcat.</p>
1937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * <p>All other parameters are passed-through as-is.</p>
1957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         *
1967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * @return a new face without the optional features
1977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
1987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        public Face toFace() {
1997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            int score = clip(weight,
2007ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    Face.SCORE_MIN,
2017ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    Face.SCORE_MAX,
2027ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    rect,
2037ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "score");
2047ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
2057ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            return new Face(rect, score);
2067ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
2077ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
2087ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        private static int clipLower(int value, int lo, Rect rect, String name) {
2097ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            return clip(value, lo, /*hi*/Integer.MAX_VALUE, rect, name);
2107ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
2117ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
2127ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        private static int clip(int value, int lo, int hi, Rect rect, String name) {
2137ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            if (value < lo) {
2147ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                Log.w(TAG, "toMetering - Rectangle " + rect + " "
2157ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                        + name + " too small, clip to " + lo);
2167ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                value = lo;
2177ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            } else if (value > hi) {
2187ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                Log.w(TAG, "toMetering - Rectangle " + rect + " "
2197ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                        + name + " too small, clip to " + hi);
2207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                value = hi;
2217ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            }
2227ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
2237ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            return value;
2247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
2257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
2267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
227df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static final String TAG = "ParameterUtils";
228a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala    private static final boolean DEBUG = false;
229df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
230df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /** getZoomRatios stores zoom ratios in 1/100 increments, e.x. a zoom of 3.2 is 320 */
231df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static final int ZOOM_RATIO_MULTIPLIER = 100;
232df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
233df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
234df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Convert a camera API1 size into a util size
235df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
236df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static Size convertSize(Camera.Size size) {
237df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(size, "size must not be null");
238df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
239df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return new Size(size.width, size.height);
240df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
241df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
242df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
243df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Convert a camera API1 list of sizes into a util list of sizes
244df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
245df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static List<Size> convertSizeList(List<Camera.Size> sizeList) {
246df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(sizeList, "sizeList must not be null");
247df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
248df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Size> sizes = new ArrayList<>(sizeList.size());
249df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        for (Camera.Size s : sizeList) {
250df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sizes.add(new Size(s.width, s.height));
251df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
252df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return sizes;
253df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
254df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
255df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
2561dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk     * Convert a camera API1 list of sizes into an array of sizes
2571dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk     */
2581dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    public static Size[] convertSizeListToArray(List<Camera.Size> sizeList) {
2591dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        checkNotNull(sizeList, "sizeList must not be null");
2601dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk
2611dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        Size[] array = new Size[sizeList.size()];
2621dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        int ctr = 0;
2631dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        for (Camera.Size s : sizeList) {
2641dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk            array[ctr++] = new Size(s.width, s.height);
2651dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        }
2661dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        return array;
2671dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    }
2681dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk
2691dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    /**
2701dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk     * Check if the camera API1 list of sizes contains a size with the given dimens.
2711dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk     */
2721dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    public static boolean containsSize(List<Camera.Size> sizeList, int width, int height) {
2731dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        checkNotNull(sizeList, "sizeList must not be null");
2741dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        for (Camera.Size s : sizeList) {
2751dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk            if (s.height == height && s.width == width) {
2761dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk                return true;
2771dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk            }
2781dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        }
2791dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk        return false;
2801dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    }
2811dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk
2821dc1326eaedd11ffd8f85927b8f0195f4f7598d3Ruben Brunk    /**
283df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Returns the largest supported picture size, as compared by its area.
284df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
285df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static Size getLargestSupportedJpegSizeByArea(Camera.Parameters params) {
286df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
287df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
288df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes());
289df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return SizeAreaComparator.findLargestByArea(supportedJpegSizes);
290df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
291df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
292df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
293df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Convert a camera area into a human-readable string.
294df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
295df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static String stringFromArea(Camera.Area area) {
296df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (area == null) {
297df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return null;
298df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else {
299df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            StringBuilder sb = new StringBuilder();
300df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect r = area.rect;
301df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
302df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.setLength(0);
303df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append("(["); sb.append(r.left); sb.append(',');
304df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append(r.top); sb.append("]["); sb.append(r.right);
305df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append(','); sb.append(r.bottom); sb.append(']');
306df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
307df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append(',');
308df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append(area.weight);
309df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            sb.append(')');
310df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
311df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return sb.toString();
312df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
313df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
314df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
315df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
3167ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Convert a camera area list into a human-readable string
3177ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param areaList a list of areas (null is ok)
3187ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
3197ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static String stringFromAreaList(List<Camera.Area> areaList) {
3207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        StringBuilder sb = new StringBuilder();
3217ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3227ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (areaList == null) {
3237ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            return null;
3247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
3257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        int i = 0;
3277ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        for (Camera.Area area : areaList) {
3287ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            if (area == null) {
3297ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                sb.append("null");
3307ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            } else {
3317ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                sb.append(stringFromArea(area));
3327ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            }
3337ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3347ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            if (i != areaList.size() - 1) {
3357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                sb.append(", ");
3367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            }
3377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            i++;
3397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
3407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return sb.toString();
3427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
3437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
3447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
345df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Calculate the closest zoom index for the user-requested crop region by rounding
346df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * up to the closest (largest or equal) possible zoom crop.
347df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
348df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>If the requested crop region exceeds the size of the active array, it is
349df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * shrunk to fit inside of the active array first.</p>
350df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
351df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Since all api1 camera devices only support a discrete set of zooms, we have
352df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * to translate the per-pixel-granularity requested crop region into a per-zoom-index
353df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * granularity.</p>
354df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
355df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Furthermore, since the zoom index and zoom levels also depends on the field-of-view
356df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * of the preview, the current preview {@code streamSize} is also used.</p>
357df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
358df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>The calculated crop regions are then written to in-place to {@code reportedCropRegion}
359df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * and {@code previewCropRegion}, in coordinates relative to the active array.</p>
360df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
361df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
362df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
363df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
364df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param cropRegion user-specified crop region, in active array coordinates
365df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param reportedCropRegion (out parameter) what the result for {@code cropRegion} looks like
366df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param previewCropRegion (out parameter) what the visual preview crop is
367df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return
368df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          the zoom index inclusively between 0 and {@code Parameters#getMaxZoom},
369df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          where 0 means the camera is not zoomed
370df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
371df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws NullPointerException if any of the args were {@code null}
372df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
373df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static int getClosestAvailableZoomCrop(
374df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Camera.Parameters params, Rect activeArray, Size streamSize, Rect cropRegion,
375df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            /*out*/
376df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect reportedCropRegion,
377df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect previewCropRegion) {
378df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
379df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
380df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(streamSize, "streamSize must not be null");
381df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(reportedCropRegion, "reportedCropRegion must not be null");
382df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(previewCropRegion, "previewCropRegion must not be null");
383df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
384df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect actualCrop = new Rect(cropRegion);
385df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
386df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        /*
387df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin         * Shrink requested crop region to fit inside of the active array size
388df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin         */
389df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (!actualCrop.intersect(activeArray)) {
390df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.w(TAG, "getClosestAvailableZoomCrop - Crop region out of range; " +
391df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    "setting to active array size");
392df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            actualCrop.set(activeArray);
393df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
394df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
395df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect previewCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);
396df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
397df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Make the user-requested crop region the same aspect ratio as the preview stream size
398df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect cropRegionAsPreview =
399df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                shrinkToSameAspectRatioCentered(previewCrop, actualCrop);
400df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
401a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala        if (DEBUG) {
402df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.v(TAG, "getClosestAvailableZoomCrop - actualCrop = " + actualCrop);
403df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.v(TAG,
404df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    "getClosestAvailableZoomCrop - previewCrop = " + previewCrop);
405df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.v(TAG,
406df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    "getClosestAvailableZoomCrop - cropRegionAsPreview = " + cropRegionAsPreview);
407df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
408df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
409df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        /*
410df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin         * Iterate all available zoom rectangles and find the closest zoom index
411df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin         */
412df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect bestReportedCropRegion = null;
413df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect bestPreviewCropRegion = null;
414df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        int bestZoomIndex = -1;
415df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
416df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Rect> availableReportedCropRegions =
417df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                getAvailableZoomCropRectangles(params, activeArray);
418df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Rect> availablePreviewCropRegions =
419df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                getAvailablePreviewZoomCropRectangles(params, activeArray, streamSize);
420df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
421a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala        if (DEBUG) {
422df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.v(TAG,
423df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    "getClosestAvailableZoomCrop - availableReportedCropRegions = " +
424df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                            ListUtils.listToString(availableReportedCropRegions));
425df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Log.v(TAG,
426df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    "getClosestAvailableZoomCrop - availablePreviewCropRegions = " +
427df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                            ListUtils.listToString(availablePreviewCropRegions));
428df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
429df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
430df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (availableReportedCropRegions.size() != availablePreviewCropRegions.size()) {
431df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            throw new AssertionError("available reported/preview crop region size mismatch");
432df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
433df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
434df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        for (int i = 0; i < availableReportedCropRegions.size(); ++i) {
435df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect currentPreviewCropRegion = availablePreviewCropRegions.get(i);
436df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect currentReportedCropRegion = availableReportedCropRegions.get(i);
437df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
438df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            boolean isBest;
439df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            if (bestZoomIndex == -1) {
440df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                isBest = true;
441df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            } else if (currentPreviewCropRegion.width() >= cropRegionAsPreview.width() &&
442df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    currentPreviewCropRegion.height() >= cropRegionAsPreview.height()) {
443df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                isBest = true;
444df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            } else {
445df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                isBest = false;
446df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            }
447df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
448df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Sizes are sorted largest-to-smallest, so once the available crop is too small,
449df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // we the rest are too small. Furthermore, this is the final best crop,
450df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // since its the largest crop that still fits the requested crop
451df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            if (isBest) {
452df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                bestPreviewCropRegion = currentPreviewCropRegion;
453df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                bestReportedCropRegion = currentReportedCropRegion;
454df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                bestZoomIndex = i;
455df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            } else {
456df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                break;
457df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            }
458df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
459df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
460df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (bestZoomIndex == -1) {
461df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Even in the worst case, we should always at least return 0 here
462df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            throw new AssertionError("Should've found at least one valid zoom index");
463df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
464df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
465df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Write the rectangles in-place
466df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        reportedCropRegion.set(bestReportedCropRegion);
467df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        previewCropRegion.set(bestPreviewCropRegion);
468df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
469df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return bestZoomIndex;
470df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
471df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
472df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
473df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Calculate the effective crop rectangle for this preview viewport;
474df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * assumes the preview is centered to the sensor and scaled to fit across one of the dimensions
475df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * without skewing.
476df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
477df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>The preview size must be a subset of the active array size; the resulting
478df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * rectangle will also be a subset of the active array rectangle.</p>
479df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
480df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>The unzoomed crop rectangle is calculated only.</p>
481df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
482df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
483df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param previewSize size of the preview buffer render target, in pixels (not in sensor space)
484df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a rectangle which serves as the preview stream's effective crop region (unzoomed),
485df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *         in sensor space
486df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
487df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws NullPointerException
488df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          if any of the args were {@code null}
489df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws IllegalArgumentException
490df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          if {@code previewSize} is wider or taller than {@code activeArray}
491df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
492df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static Rect getPreviewCropRectangleUnzoomed(Rect activeArray, Size previewSize) {
493df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (previewSize.getWidth() > activeArray.width()) {
494df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            throw new IllegalArgumentException("previewSize must not be wider than activeArray");
495df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else if (previewSize.getHeight() > activeArray.height()) {
496df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            throw new IllegalArgumentException("previewSize must not be taller than activeArray");
497df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
498df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
499df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioArray = activeArray.width() * 1.0f / activeArray.height();
500df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioPreview = previewSize.getWidth() * 1.0f / previewSize.getHeight();
501df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
502df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float cropH, cropW;
503607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk        if (Math.abs(aspectRatioPreview - aspectRatioArray) < ASPECT_RATIO_TOLERANCE) {
504607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk            cropH = activeArray.height();
505607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk            cropW = activeArray.width();
506607e4cb9ec709a0cbdb3d925a020c43aea270241Ruben Brunk        } else if (aspectRatioPreview < aspectRatioArray) {
507df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new width must be smaller than the height, so scale the width by AR
508df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = activeArray.height();
509df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = cropH * aspectRatioPreview;
510df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else {
511df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new height must be smaller (or equal) than the width, so scale the height by AR
512df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = activeArray.width();
513df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = cropW / aspectRatioPreview;
514df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
515df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
516df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix translateMatrix = new Matrix();
517df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF cropRect = new RectF(/*left*/0, /*top*/0, cropW, cropH);
518df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
519df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Now center the crop rectangle so its center is in the center of the active array
520df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.setTranslate(activeArray.exactCenterX(), activeArray.exactCenterY());
521df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());
522df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
523df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.mapRect(/*inout*/cropRect);
524df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
525df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Round the rect corners towards the nearest integer values
526df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return ParamsUtils.createRect(cropRect);
527df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
528df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
529df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
530df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Shrink the {@code shrinkTarget} rectangle to snugly fit inside of {@code reference};
531df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * the aspect ratio of {@code shrinkTarget} will change to be the same aspect ratio as
532df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * {@code reference}.
533df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
534df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>At most a single dimension will scale (down). Both dimensions will never be scaled.</p>
535df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
536df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param reference the rectangle whose aspect ratio will be used as the new aspect ratio
537df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param shrinkTarget the rectangle which will be scaled down to have a new aspect ratio
538df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
539df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a new rectangle, a subset of {@code shrinkTarget},
540df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          whose aspect ratio will match that of {@code reference}
541df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
542df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static Rect shrinkToSameAspectRatioCentered(Rect reference, Rect shrinkTarget) {
543df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioReference = reference.width() * 1.0f / reference.height();
544df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioShrinkTarget = shrinkTarget.width() * 1.0f / shrinkTarget.height();
545df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
546df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float cropH, cropW;
547df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (aspectRatioShrinkTarget < aspectRatioReference) {
548df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new width must be smaller than the height, so scale the width by AR
549df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = reference.height();
550df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = cropH * aspectRatioShrinkTarget;
551df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else {
552df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new height must be smaller (or equal) than the width, so scale the height by AR
553df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = reference.width();
554df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = cropW / aspectRatioShrinkTarget;
555df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
556df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
557df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix translateMatrix = new Matrix();
558df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF shrunkRect = new RectF(shrinkTarget);
559df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
560df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Scale the rectangle down, but keep its center in the same place as before
561df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.setScale(cropW / reference.width(), cropH / reference.height(),
562df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                shrinkTarget.exactCenterX(), shrinkTarget.exactCenterY());
563df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
564df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.mapRect(/*inout*/shrunkRect);
565df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
566df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return ParamsUtils.createRect(shrunkRect);
567df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
568df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
569df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
570df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera that will be reported
571df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * via a {@code CaptureResult} when a zoom is requested.
572df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
573df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>These crops ignores the underlying preview buffer size, and will always be reported
574df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * the same values regardless of what configuration of outputs is used.</p>
575df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
576df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
577df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
578df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
579df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
580df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
581df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
582df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
583df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
584df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
585df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
586df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
587df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
588df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
589df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
590df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
591df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static List<Rect> getAvailableZoomCropRectangles(
592df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Camera.Parameters params, Rect activeArray) {
593df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
594df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
595df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
596df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return getAvailableCropRectangles(params, activeArray, ParamsUtils.createSize(activeArray));
597df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
598df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
599df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
600df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera.
601df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
602df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>This is the effective (real) crop that is applied by the camera api1 device
603df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * when projecting the zoom onto the intermediate preview buffer. Use this when
604df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * deciding which zoom ratio to apply.</p>
605df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
606df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
607df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
608df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
609df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
610df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
611df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
612df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
613df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
614df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
615df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
616df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
617df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
618df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
619df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
620df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
621df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static List<Rect> getAvailablePreviewZoomCropRectangles(Camera.Parameters params,
622df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect activeArray, Size previewSize) {
623df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
624df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
625df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(previewSize, "previewSize must not be null");
626df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
627df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return getAvailableCropRectangles(params, activeArray, previewSize);
628df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
629df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
630df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
631df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera.
632df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
633df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
634df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
635df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
636df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
637df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
638df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
639df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
640df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
641df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
642df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
643df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
644df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
645df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
646df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
647df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
648df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static List<Rect> getAvailableCropRectangles(Camera.Parameters params,
649df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect activeArray, Size streamSize) {
650df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
651df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
652df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(streamSize, "streamSize must not be null");
653df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
654df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // TODO: change all uses of Rect activeArray to Size activeArray,
655df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // since we want the crop to be active-array relative, not pixel-array relative
656df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
657df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect unzoomedStreamCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);
658df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
659df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (!params.isZoomSupported()) {
660df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Trivial case: No zoom -> only support the full size as the crop region
661df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return new ArrayList<>(Arrays.asList(unzoomedStreamCrop));
662df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
663df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
664df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Rect> zoomCropRectangles = new ArrayList<>(params.getMaxZoom() + 1);
665df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix scaleMatrix = new Matrix();
666df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF scaledRect = new RectF();
667df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
668df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        for (int zoom : params.getZoomRatios()) {
669df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            float shrinkRatio = ZOOM_RATIO_MULTIPLIER * 1.0f / zoom; // normalize to 1.0 and smaller
670df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
671df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // set scaledRect to unzoomedStreamCrop
672df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            ParamsUtils.convertRectF(unzoomedStreamCrop, /*out*/scaledRect);
673df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
674df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            scaleMatrix.setScale(
675df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    shrinkRatio, shrinkRatio,
676df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    activeArray.exactCenterX(),
677df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    activeArray.exactCenterY());
678df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
679df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            scaleMatrix.mapRect(scaledRect);
680df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
681df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect intRect = ParamsUtils.createRect(scaledRect);
682df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
683df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Round the rect corners towards the nearest integer values
684df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            zoomCropRectangles.add(intRect);
685df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
686df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
687df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return zoomCropRectangles;
688df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
689df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
690df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
691df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the largest possible zoom ratio (normalized to {@code 1.0f} and higher)
692df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * that the camera can support.
693df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
694df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>If the camera does not support zoom, it always returns {@code 1.0f}.</p>
695df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
696df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
697df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return normalized max zoom ratio, at least {@code 1.0f}
698df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
699df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static float getMaxZoomRatio(Camera.Parameters params) {
700df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (!params.isZoomSupported()) {
701df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return 1.0f; // no zoom
702df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
703df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
704df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Integer> zoomRatios = params.getZoomRatios(); // sorted smallest->largest
705df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        int zoom = zoomRatios.get(zoomRatios.size() - 1); // largest zoom ratio
706df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatio = zoom * 1.0f / ZOOM_RATIO_MULTIPLIER; // normalize to 1.0 and smaller
707df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
708df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return zoomRatio;
709df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
710df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
711df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
712df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Returns the component-wise zoom ratio (each greater or equal than {@code 1.0});
713df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * largest values means more zoom.
714df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
715df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
716df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param cropSize size of the crop/zoom
717df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
718df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return {@link SizeF} with width/height being the component-wise zoom ratio
719df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
720df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws NullPointerException if any of the args were {@code null}
721df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws IllegalArgumentException if any component of {@code cropSize} was {@code 0}
722df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
723df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static SizeF getZoomRatio(Size activeArraySize, Size cropSize) {
724df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArraySize, "activeArraySize must not be null");
725df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(cropSize, "cropSize must not be null");
726df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkArgumentPositive(cropSize.getWidth(), "cropSize.width must be positive");
727df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkArgumentPositive(cropSize.getHeight(), "cropSize.height must be positive");
728df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
729df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatioWidth = activeArraySize.getWidth() * 1.0f / cropSize.getWidth();
730df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatioHeight = activeArraySize.getHeight() * 1.0f / cropSize.getHeight();
731df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
732df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return new SizeF(zoomRatioWidth, zoomRatioHeight);
733df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
734df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
7357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
7367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Convert the user-specified crop region into zoom data; which can be used
7377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * to set the parameters to a specific zoom index, or to report back to the user what the
7387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * actual zoom was, or for other calculations requiring the current preview crop region.
7397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>None of the parameters are mutated.</p>
7417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
7437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param cropRegion the user-specified crop region
7447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param previewSize the current preview size (in pixels)
7457ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param params the current camera parameters (not mutated)
7467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the zoom index, and the effective/reported crop regions (relative to active array)
7487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
7497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static ZoomData convertScalerCropRegion(Rect activeArraySize, Rect
7507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            cropRegion, Size previewSize, Camera.Parameters params) {
7517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect activeArraySizeOnly = new Rect(
7527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*left*/0, /*top*/0,
7537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                activeArraySize.width(), activeArraySize.height());
7547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect userCropRegion = cropRegion;
7567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (userCropRegion == null) {
7587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            userCropRegion = activeArraySizeOnly;
7597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
761a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala        if (DEBUG) {
7627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, "convertScalerCropRegion - user crop region was " + userCropRegion);
7637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final Rect reportedCropRegion = new Rect();
7667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final Rect previewCropRegion = new Rect();
7677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
7687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewSize, userCropRegion,
7697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*out*/reportedCropRegion, /*out*/previewCropRegion);
7707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
771a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala        if (DEBUG) {
7727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
7737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "zoomIndex = " + zoomIdx +
7747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    ", reported crop region = " + reportedCropRegion +
7757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    ", preview crop region = " + previewCropRegion);
7767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
7797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
7807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
7827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Calculate the actual/effective/reported normalized rectangle data from a metering
7837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * rectangle.
7847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>If any of the rectangles are out-of-range of their intended bounding box,
7867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * the {@link #RECTANGLE_EMPTY empty rectangle} is substituted instead
7877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * (with a weight of {@code 0}).</p>
7887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>The metering rectangle is bound by the crop region (effective/reported respectively).
7907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * The metering {@link Camera.Area area} is bound by {@code [-1000, 1000]}.</p>
7917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>No parameters are mutated; returns the new metering data.</p>
7937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
7957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param meteringRect the user-specified metering rectangle
7967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
7977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the metering area, the reported/effective metering rectangles
7997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
8007ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static MeteringData convertMeteringRectangleToLegacy(
8017ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, MeteringRectangle meteringRect, ZoomData zoomData) {
8027ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewCrop = zoomData.previewCrop;
8037ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8047ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleW = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
8057ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewCrop.width();
8067ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleH = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
8077ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewCrop.height();
8087ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8097ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Matrix transform = new Matrix();
8107ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move the preview crop so that top,left is at (0,0), otherwise after scaling
8117ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // the corner bounds will be outside of [-1000, 1000]
8127ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.setTranslate(-previewCrop.left, -previewCrop.top);
8137ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Scale into [0, 2000] range about the center of the preview
8147ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postScale(scaleW, scaleH);
8157ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move so that top left of a typical rect is at [-1000, -1000]
8167ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postTranslate(/*dx*/NORMALIZED_RECTANGLE_MIN, /*dy*/NORMALIZED_RECTANGLE_MIN);
8177ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8187ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8197ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate the preview metering region (effective), and the camera1 api
8207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * normalized metering region.
8217ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8227ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect normalizedRegionUnbounded = ParamsUtils.mapRect(transform, meteringRect.getRect());
8237ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Try to intersect normalized area with [-1000, 1000] rectangle; otherwise
8267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * it's completely out of range
8277ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8287ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect normalizedIntersected = new Rect(normalizedRegionUnbounded);
8297ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8307ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Camera.Area meteringArea;
8317ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!normalizedIntersected.intersect(NORMALIZED_RECTANGLE_DEFAULT)) {
8327ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.w(TAG,
8337ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertMeteringRectangleToLegacy - metering rectangle too small, " +
8347ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "no metering will be done");
8357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            normalizedIntersected.set(RECTANGLE_EMPTY);
8367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            meteringArea = new Camera.Area(RECTANGLE_EMPTY,
8377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    MeteringRectangle.METERING_WEIGHT_DONT_CARE);
8387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        } else {
8397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            meteringArea = new Camera.Area(normalizedIntersected,
8407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    meteringRect.getMeteringWeight());
8417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate effective preview metering region
8457ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewMetering = meteringRect.getRect();
8477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!previewMetering.intersect(previewCrop)) {
8487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            previewMetering.set(RECTANGLE_EMPTY);
8497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate effective reported metering region
8537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * - Transform the calculated metering area back into active array space
8547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * - Clip it to be a subset of the reported crop region
8557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedMetering;
8577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        {
8587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Camera.Area normalizedAreaUnbounded = new Camera.Area(
8597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    normalizedRegionUnbounded, meteringRect.getMeteringWeight());
8607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            WeightedRectangle reportedMeteringRect = convertCameraAreaToActiveArrayRectangle(
8617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    activeArray, zoomData, normalizedAreaUnbounded, /*usePreviewCrop*/false);
8627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            reportedMetering = reportedMeteringRect.rect;
8637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
865a78791f22af6c6985d186494737468bb19b69540Eino-Ville Talvala        if (DEBUG) {
8667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, String.format(
8677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertMeteringRectangleToLegacy - activeArray = %s, meteringRect = %s, " +
8687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "previewCrop = %s, meteringArea = %s, previewMetering = %s, " +
8697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "reportedMetering = %s, normalizedRegionUnbounded = %s",
8707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    activeArray, meteringRect,
8717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    previewCrop, stringFromArea(meteringArea), previewMetering,
8727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    reportedMetering, normalizedRegionUnbounded));
8737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new MeteringData(meteringArea, previewMetering, reportedMetering);
8767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
8777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
8797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Convert the normalized camera area from [-1000, 1000] coordinate space
8807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * into the active array-based coordinate space.
8817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>Values out of range are clipped to be within the resulting (reported) crop
8837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * region. It is possible to have values larger than the preview crop.</p>
8847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>Weights out of range of [0, 1000] are clipped to be within the range.</p>
8867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
8887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
8897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param area the normalized camera area
8907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the weighed rectangle in active array coordinate space, with the weight
8927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
8937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
8947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, ZoomData zoomData, Camera.Area area) {
8957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, area,
8967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*usePreviewCrop*/true);
8977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
8987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8990a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    /**
9000a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * Convert an api1 face into an active-array based api2 face.
9010a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9020a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p>
9030a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9040a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param face a non-{@code null} api1 face
9050a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
9060a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
9070a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9080a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @return a non-{@code null} api2 face
9090a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9100a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @throws NullPointerException if the {@code face} was {@code null}
9110a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     */
9120a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray,
9130a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            ZoomData zoomData) {
9140a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        checkNotNull(face, "face must not be null");
9150a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9160a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Face api2Face;
9170a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9180a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1);
9190a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9200a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        WeightedRectangle faceRect =
9210a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
9220a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9230a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
924000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk        if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
925000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk                leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
926000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk                mouth.x != -2000 && mouth.y != -2000) {
9270a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9280a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9290a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9300a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9310a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9320a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9330a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9340a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth);
9350a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        } else {
9360a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            api2Face = faceRect.toFace();
9370a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        }
9380a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9390a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        return api2Face;
9400a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    }
9410a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9420a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    private static Point convertCameraPointToActiveArrayPoint(
9430a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) {
9440a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Rect pointedRect = new Rect(point.x, point.y, point.x, point.y);
9450a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Camera.Area pointedArea = new Area(pointedRect, /*weight*/1);
9460a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9470a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        WeightedRectangle adjustedRect =
9480a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                convertCameraAreaToActiveArrayRectangle(activeArray,
9490a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                        zoomData, pointedArea, usePreviewCrop);
9500a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9510a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top);
9520a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9530a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        return transformedPoint;
9540a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    }
9550a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    private static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
9577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) {
9587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewCrop = zoomData.previewCrop;
9597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedCrop = zoomData.reportedCrop;
9607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleW = previewCrop.width() * 1.0f /
9627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
9637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleH = previewCrop.height() * 1.0f /
9647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
9657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
9677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate the reported metering region from the non-intersected normalized region
9687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * by scaling and translating back into active array-relative coordinates.
9697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
9707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Matrix transform = new Matrix();
9717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move top left from (-1000, -1000) to (0, 0)
9737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.setTranslate(/*dx*/NORMALIZED_RECTANGLE_MAX, /*dy*/NORMALIZED_RECTANGLE_MAX);
9747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Scale from [0, 2000] back into the preview rectangle
9767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postScale(scaleW, scaleH);
9777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move the rect so that the [-1000,-1000] point ends up at the preview [left, top]
9797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postTranslate(previewCrop.left, previewCrop.top);
9807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect cropToIntersectAgainst = usePreviewCrop ? previewCrop : reportedCrop;
9827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Now apply the transformation backwards to get the reported metering region
9847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedMetering = ParamsUtils.mapRect(transform, area.rect);
9857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Intersect it with the crop region, to avoid reporting out-of-bounds
9867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // metering regions
9877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!reportedMetering.intersect(cropToIntersectAgainst)) {
9887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            reportedMetering.set(RECTANGLE_EMPTY);
9897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
9907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        int weight = area.weight;
9927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (weight < MeteringRectangle.METERING_WEIGHT_MIN) {
9937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.w(TAG,
9947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertCameraAreaToMeteringRectangle - rectangle "
9957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                            + stringFromArea(area) + " has too small weight, clip to 0");
9967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            weight = 0;
9977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
9987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new WeightedRectangle(reportedMetering, area.weight);
10007ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
10017ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
10027ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1003df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private ParameterUtils() {
1004df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        throw new AssertionError();
1005df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
1006df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin}
1007