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.legacy.ParameterUtils.MeteringData;
267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.camera2.legacy.ParameterUtils.ZoomData;
277ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.camera2.params.Face;
287ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkinimport android.hardware.camera2.params.MeteringRectangle;
29df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.ListUtils;
30df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.ParamsUtils;
31df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.hardware.camera2.utils.SizeAreaComparator;
32df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.Size;
33df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.SizeF;
34df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
35df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport android.util.Log;
36df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
37df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.ArrayList;
38df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.Arrays;
39df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport java.util.List;
40df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
41df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinimport static com.android.internal.util.Preconditions.*;
42df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
43df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin/**
44df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin * Various utilities for dealing with camera API1 parameters.
45df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin */
460a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin@SuppressWarnings("deprecation")
47df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkinpublic class ParameterUtils {
487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Upper/left minimal point of a normalized rectangle */
497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final int NORMALIZED_RECTANGLE_MIN = -1000;
507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Lower/right maximal point of a normalized rectangle */
517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final int NORMALIZED_RECTANGLE_MAX = 1000;
527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** The default normalized rectangle spans the entire size of the preview viewport */
537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Rect NORMALIZED_RECTANGLE_DEFAULT = new Rect(
547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MIN,
557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MIN,
567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MAX,
577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            NORMALIZED_RECTANGLE_MAX);
587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** The default normalized area uses the default normalized rectangle with a weight=1 */
597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Camera.Area CAMERA_AREA_DEFAULT =
607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            new Camera.Area(new Rect(NORMALIZED_RECTANGLE_DEFAULT),
617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                            /*weight*/1);
627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /** Empty rectangle {@code 0x0+0,0} */
637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static final Rect RECTANGLE_EMPTY =
647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            new Rect(/*left*/0, /*top*/0, /*right*/0, /*bottom*/0);
657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
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";
228df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
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
401df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (VERBOSE) {
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
421df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (VERBOSE) {
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;
503df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (aspectRatioPreview < aspectRatioArray) {
504df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new width must be smaller than the height, so scale the width by AR
505df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = activeArray.height();
506df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = cropH * aspectRatioPreview;
507df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else {
508df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new height must be smaller (or equal) than the width, so scale the height by AR
509df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = activeArray.width();
510df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = cropW / aspectRatioPreview;
511df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
512df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
513df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix translateMatrix = new Matrix();
514df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF cropRect = new RectF(/*left*/0, /*top*/0, cropW, cropH);
515df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
516df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Now center the crop rectangle so its center is in the center of the active array
517df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.setTranslate(activeArray.exactCenterX(), activeArray.exactCenterY());
518df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());
519df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
520df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.mapRect(/*inout*/cropRect);
521df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
522df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Round the rect corners towards the nearest integer values
523df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return ParamsUtils.createRect(cropRect);
524df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
525df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
526df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
527df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Shrink the {@code shrinkTarget} rectangle to snugly fit inside of {@code reference};
528df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * the aspect ratio of {@code shrinkTarget} will change to be the same aspect ratio as
529df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * {@code reference}.
530df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
531df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>At most a single dimension will scale (down). Both dimensions will never be scaled.</p>
532df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
533df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param reference the rectangle whose aspect ratio will be used as the new aspect ratio
534df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param shrinkTarget the rectangle which will be scaled down to have a new aspect ratio
535df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
536df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a new rectangle, a subset of {@code shrinkTarget},
537df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *          whose aspect ratio will match that of {@code reference}
538df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
539df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static Rect shrinkToSameAspectRatioCentered(Rect reference, Rect shrinkTarget) {
540df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioReference = reference.width() * 1.0f / reference.height();
541df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float aspectRatioShrinkTarget = shrinkTarget.width() * 1.0f / shrinkTarget.height();
542df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
543df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float cropH, cropW;
544df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (aspectRatioShrinkTarget < aspectRatioReference) {
545df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new width must be smaller than the height, so scale the width by AR
546df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = reference.height();
547df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = cropH * aspectRatioShrinkTarget;
548df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        } else {
549df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // The new height must be smaller (or equal) than the width, so scale the height by AR
550df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropW = reference.width();
551df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            cropH = cropW / aspectRatioShrinkTarget;
552df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
553df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
554df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix translateMatrix = new Matrix();
555df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF shrunkRect = new RectF(shrinkTarget);
556df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
557df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // Scale the rectangle down, but keep its center in the same place as before
558df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.setScale(cropW / reference.width(), cropH / reference.height(),
559df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                shrinkTarget.exactCenterX(), shrinkTarget.exactCenterY());
560df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
561df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        translateMatrix.mapRect(/*inout*/shrunkRect);
562df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
563df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return ParamsUtils.createRect(shrunkRect);
564df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
565df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
566df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
567df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera that will be reported
568df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * via a {@code CaptureResult} when a zoom is requested.
569df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
570df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>These crops ignores the underlying preview buffer size, and will always be reported
571df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * the same values regardless of what configuration of outputs is used.</p>
572df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
573df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
574df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
575df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
576df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
577df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
578df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
579df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
580df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
581df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
582df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
583df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
584df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
585df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
586df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
587df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
588df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static List<Rect> getAvailableZoomCropRectangles(
589df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Camera.Parameters params, Rect activeArray) {
590df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
591df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
592df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
593df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return getAvailableCropRectangles(params, activeArray, ParamsUtils.createSize(activeArray));
594df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
595df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
596df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
597df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera.
598df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
599df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>This is the effective (real) crop that is applied by the camera api1 device
600df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * when projecting the zoom onto the intermediate preview buffer. Use this when
601df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * deciding which zoom ratio to apply.</p>
602df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
603df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
604df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
605df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
606df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
607df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
608df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
609df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
610df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
611df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
612df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
613df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
614df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
615df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
616df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
617df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
618df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static List<Rect> getAvailablePreviewZoomCropRectangles(Camera.Parameters params,
619df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect activeArray, Size previewSize) {
620df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
621df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
622df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(previewSize, "previewSize must not be null");
623df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
624df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return getAvailableCropRectangles(params, activeArray, previewSize);
625df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
626df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
627df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
628df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the available 'crop' (zoom) rectangles for this camera.
629df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
630df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>When zoom is supported, this will return a list of {@code 1 + #getMaxZoom} size,
631df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * where each crop rectangle corresponds to a zoom ratio (and is centered at the middle).</p>
632df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
633df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>Each crop rectangle is changed to have the same aspect ratio as {@code streamSize},
634df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * by shrinking the rectangle if necessary.</p>
635df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
636df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>To get the reported crop region when applying a zoom to the sensor, use {@code streamSize}
637df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * = {@code activeArray size}.</p>
638df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
639df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
640df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArray active array dimensions, in sensor space
641df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param streamSize stream size dimensions, in pixels
642df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
643df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return a list of available zoom rectangles, sorted from least zoomed to most zoomed
644df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
645df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static List<Rect> getAvailableCropRectangles(Camera.Parameters params,
646df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect activeArray, Size streamSize) {
647df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(params, "params must not be null");
648df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArray, "activeArray must not be null");
649df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(streamSize, "streamSize must not be null");
650df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
651df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // TODO: change all uses of Rect activeArray to Size activeArray,
652df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        // since we want the crop to be active-array relative, not pixel-array relative
653df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
654df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Rect unzoomedStreamCrop = getPreviewCropRectangleUnzoomed(activeArray, streamSize);
655df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
656df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (!params.isZoomSupported()) {
657df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Trivial case: No zoom -> only support the full size as the crop region
658df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return new ArrayList<>(Arrays.asList(unzoomedStreamCrop));
659df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
660df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
661df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Rect> zoomCropRectangles = new ArrayList<>(params.getMaxZoom() + 1);
662df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        Matrix scaleMatrix = new Matrix();
663df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        RectF scaledRect = new RectF();
664df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
665df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        for (int zoom : params.getZoomRatios()) {
666df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            float shrinkRatio = ZOOM_RATIO_MULTIPLIER * 1.0f / zoom; // normalize to 1.0 and smaller
667df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
668df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // set scaledRect to unzoomedStreamCrop
669df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            ParamsUtils.convertRectF(unzoomedStreamCrop, /*out*/scaledRect);
670df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
671df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            scaleMatrix.setScale(
672df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    shrinkRatio, shrinkRatio,
673df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    activeArray.exactCenterX(),
674df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin                    activeArray.exactCenterY());
675df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
676df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            scaleMatrix.mapRect(scaledRect);
677df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
678df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            Rect intRect = ParamsUtils.createRect(scaledRect);
679df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
680df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            // Round the rect corners towards the nearest integer values
681df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            zoomCropRectangles.add(intRect);
682df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
683df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
684df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return zoomCropRectangles;
685df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
686df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
687df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
688df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Get the largest possible zoom ratio (normalized to {@code 1.0f} and higher)
689df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * that the camera can support.
690df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
691df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * <p>If the camera does not support zoom, it always returns {@code 1.0f}.</p>
692df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
693df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param params non-{@code null} camera api1 parameters
694df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return normalized max zoom ratio, at least {@code 1.0f}
695df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
696df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    public static float getMaxZoomRatio(Camera.Parameters params) {
697df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        if (!params.isZoomSupported()) {
698df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin            return 1.0f; // no zoom
699df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        }
700df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
701df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        List<Integer> zoomRatios = params.getZoomRatios(); // sorted smallest->largest
702df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        int zoom = zoomRatios.get(zoomRatios.size() - 1); // largest zoom ratio
703df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatio = zoom * 1.0f / ZOOM_RATIO_MULTIPLIER; // normalize to 1.0 and smaller
704df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
705df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return zoomRatio;
706df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
707df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
708df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    /**
709df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * Returns the component-wise zoom ratio (each greater or equal than {@code 1.0});
710df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * largest values means more zoom.
711df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
712df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
713df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @param cropSize size of the crop/zoom
714df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
715df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @return {@link SizeF} with width/height being the component-wise zoom ratio
716df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     *
717df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws NullPointerException if any of the args were {@code null}
718df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     * @throws IllegalArgumentException if any component of {@code cropSize} was {@code 0}
719df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin     */
720df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private static SizeF getZoomRatio(Size activeArraySize, Size cropSize) {
721df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(activeArraySize, "activeArraySize must not be null");
722df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkNotNull(cropSize, "cropSize must not be null");
723df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkArgumentPositive(cropSize.getWidth(), "cropSize.width must be positive");
724df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        checkArgumentPositive(cropSize.getHeight(), "cropSize.height must be positive");
725df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
726df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatioWidth = activeArraySize.getWidth() * 1.0f / cropSize.getWidth();
727df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        float zoomRatioHeight = activeArraySize.getHeight() * 1.0f / cropSize.getHeight();
728df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
729df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        return new SizeF(zoomRatioWidth, zoomRatioHeight);
730df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
731df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin
7327ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
7337ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Convert the user-specified crop region into zoom data; which can be used
7347ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * to set the parameters to a specific zoom index, or to report back to the user what the
7357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * actual zoom was, or for other calculations requiring the current preview crop region.
7367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>None of the parameters are mutated.</p>
7387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
7407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param cropRegion the user-specified crop region
7417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param previewSize the current preview size (in pixels)
7427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param params the current camera parameters (not mutated)
7437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the zoom index, and the effective/reported crop regions (relative to active array)
7457ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
7467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static ZoomData convertScalerCropRegion(Rect activeArraySize, Rect
7477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            cropRegion, Size previewSize, Camera.Parameters params) {
7487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect activeArraySizeOnly = new Rect(
7497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*left*/0, /*top*/0,
7507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                activeArraySize.width(), activeArraySize.height());
7517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect userCropRegion = cropRegion;
7537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (userCropRegion == null) {
7557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            userCropRegion = activeArraySizeOnly;
7567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (VERBOSE) {
7597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, "convertScalerCropRegion - user crop region was " + userCropRegion);
7607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final Rect reportedCropRegion = new Rect();
7637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final Rect previewCropRegion = new Rect();
7647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        final int zoomIdx = ParameterUtils.getClosestAvailableZoomCrop(params, activeArraySizeOnly,
7657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewSize, userCropRegion,
7667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*out*/reportedCropRegion, /*out*/previewCropRegion);
7677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (VERBOSE) {
7697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, "convertScalerCropRegion - zoom calculated to: " +
7707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "zoomIndex = " + zoomIdx +
7717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    ", reported crop region = " + reportedCropRegion +
7727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    ", preview crop region = " + previewCropRegion);
7737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
7747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new ZoomData(zoomIdx, previewCropRegion, reportedCropRegion);
7767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
7777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
7787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
7797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Calculate the actual/effective/reported normalized rectangle data from a metering
7807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * rectangle.
7817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>If any of the rectangles are out-of-range of their intended bounding box,
7837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * the {@link #RECTANGLE_EMPTY empty rectangle} is substituted instead
7847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * (with a weight of {@code 0}).</p>
7857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>The metering rectangle is bound by the crop region (effective/reported respectively).
7877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * The metering {@link Camera.Area area} is bound by {@code [-1000, 1000]}.</p>
7887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>No parameters are mutated; returns the new metering data.</p>
7907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
7927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param meteringRect the user-specified metering rectangle
7937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
7947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
7957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the metering area, the reported/effective metering rectangles
7967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
7977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static MeteringData convertMeteringRectangleToLegacy(
7987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, MeteringRectangle meteringRect, ZoomData zoomData) {
7997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewCrop = zoomData.previewCrop;
8007ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8017ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleW = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
8027ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewCrop.width();
8037ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleH = (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN) * 1.0f /
8047ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                previewCrop.height();
8057ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8067ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Matrix transform = new Matrix();
8077ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move the preview crop so that top,left is at (0,0), otherwise after scaling
8087ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // the corner bounds will be outside of [-1000, 1000]
8097ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.setTranslate(-previewCrop.left, -previewCrop.top);
8107ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Scale into [0, 2000] range about the center of the preview
8117ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postScale(scaleW, scaleH);
8127ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move so that top left of a typical rect is at [-1000, -1000]
8137ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postTranslate(/*dx*/NORMALIZED_RECTANGLE_MIN, /*dy*/NORMALIZED_RECTANGLE_MIN);
8147ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8157ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8167ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate the preview metering region (effective), and the camera1 api
8177ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * normalized metering region.
8187ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8197ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect normalizedRegionUnbounded = ParamsUtils.mapRect(transform, meteringRect.getRect());
8207ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8217ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8227ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Try to intersect normalized area with [-1000, 1000] rectangle; otherwise
8237ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * it's completely out of range
8247ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8257ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect normalizedIntersected = new Rect(normalizedRegionUnbounded);
8267ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8277ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Camera.Area meteringArea;
8287ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!normalizedIntersected.intersect(NORMALIZED_RECTANGLE_DEFAULT)) {
8297ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.w(TAG,
8307ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertMeteringRectangleToLegacy - metering rectangle too small, " +
8317ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "no metering will be done");
8327ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            normalizedIntersected.set(RECTANGLE_EMPTY);
8337ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            meteringArea = new Camera.Area(RECTANGLE_EMPTY,
8347ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    MeteringRectangle.METERING_WEIGHT_DONT_CARE);
8357ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        } else {
8367ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            meteringArea = new Camera.Area(normalizedIntersected,
8377ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    meteringRect.getMeteringWeight());
8387ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8397ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8407ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8417ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate effective preview metering region
8427ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8437ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewMetering = meteringRect.getRect();
8447ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!previewMetering.intersect(previewCrop)) {
8457ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            previewMetering.set(RECTANGLE_EMPTY);
8467ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8477ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8487ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
8497ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate effective reported metering region
8507ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * - Transform the calculated metering area back into active array space
8517ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * - Clip it to be a subset of the reported crop region
8527ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
8537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedMetering;
8547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        {
8557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Camera.Area normalizedAreaUnbounded = new Camera.Area(
8567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    normalizedRegionUnbounded, meteringRect.getMeteringWeight());
8577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            WeightedRectangle reportedMeteringRect = convertCameraAreaToActiveArrayRectangle(
8587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    activeArray, zoomData, normalizedAreaUnbounded, /*usePreviewCrop*/false);
8597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            reportedMetering = reportedMeteringRect.rect;
8607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (VERBOSE) {
8637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.v(TAG, String.format(
8647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertMeteringRectangleToLegacy - activeArray = %s, meteringRect = %s, " +
8657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "previewCrop = %s, meteringArea = %s, previewMetering = %s, " +
8667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "reportedMetering = %s, normalizedRegionUnbounded = %s",
8677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    activeArray, meteringRect,
8687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    previewCrop, stringFromArea(meteringArea), previewMetering,
8697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    reportedMetering, normalizedRegionUnbounded));
8707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
8717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new MeteringData(meteringArea, previewMetering, reportedMetering);
8737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
8747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    /**
8767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * Convert the normalized camera area from [-1000, 1000] coordinate space
8777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * into the active array-based coordinate space.
8787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>Values out of range are clipped to be within the resulting (reported) crop
8807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * region. It is possible to have values larger than the preview crop.</p>
8817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * <p>Weights out of range of [0, 1000] are clipped to be within the range.</p>
8837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
8857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
8867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @param area the normalized camera area
8877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     *
8887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     * @return the weighed rectangle in active array coordinate space, with the weight
8897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin     */
8907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    public static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
8917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, ZoomData zoomData, Camera.Area area) {
8927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, area,
8937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                /*usePreviewCrop*/true);
8947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
8957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
8960a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    /**
8970a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * Convert an api1 face into an active-array based api2 face.
8980a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
8990a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p>
9000a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9010a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param face a non-{@code null} api1 face
9020a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param activeArraySize active array size of the sensor (e.g. max jpeg size)
9030a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @param zoomData the calculated zoom data corresponding to this request
9040a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9050a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @return a non-{@code null} api2 face
9060a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     *
9070a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     * @throws NullPointerException if the {@code face} was {@code null}
9080a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin     */
9090a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray,
9100a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            ZoomData zoomData) {
9110a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        checkNotNull(face, "face must not be null");
9120a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9130a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Face api2Face;
9140a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9150a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1);
9160a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9170a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        WeightedRectangle faceRect =
9180a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea);
9190a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9200a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth;
921000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk        if (leftEye != null && rightEye != null && mouth != null && leftEye.x != -2000 &&
922000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk                leftEye.y != -2000 && rightEye.x != -2000 && rightEye.y != -2000 &&
923000973a163d7400a8f37b7b472acfa6144b0621bRuben Brunk                mouth.x != -2000 && mouth.y != -2000) {
9240a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9250a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9260a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9270a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9280a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData,
9290a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                    leftEye, /*usePreviewCrop*/true);
9300a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9310a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth);
9320a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        } else {
9330a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            api2Face = faceRect.toFace();
9340a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        }
9350a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9360a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        return api2Face;
9370a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    }
9380a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9390a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    private static Point convertCameraPointToActiveArrayPoint(
9400a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin            Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) {
9410a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Rect pointedRect = new Rect(point.x, point.y, point.x, point.y);
9420a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Camera.Area pointedArea = new Area(pointedRect, /*weight*/1);
9430a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9440a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        WeightedRectangle adjustedRect =
9450a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                convertCameraAreaToActiveArrayRectangle(activeArray,
9460a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin                        zoomData, pointedArea, usePreviewCrop);
9470a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9480a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top);
9490a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9500a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin        return transformedPoint;
9510a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin    }
9520a1ef4dbf39aa3dfae1a91daf972ae3457ce27feIgor Murashkin
9537ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    private static WeightedRectangle convertCameraAreaToActiveArrayRectangle(
9547ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) {
9557ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect previewCrop = zoomData.previewCrop;
9567ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedCrop = zoomData.reportedCrop;
9577ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9587ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleW = previewCrop.width() * 1.0f /
9597ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
9607ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        float scaleH = previewCrop.height() * 1.0f /
9617ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                (NORMALIZED_RECTANGLE_MAX - NORMALIZED_RECTANGLE_MIN);
9627ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9637ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        /*
9647ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * Calculate the reported metering region from the non-intersected normalized region
9657ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         * by scaling and translating back into active array-relative coordinates.
9667ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin         */
9677ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Matrix transform = new Matrix();
9687ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9697ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move top left from (-1000, -1000) to (0, 0)
9707ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.setTranslate(/*dx*/NORMALIZED_RECTANGLE_MAX, /*dy*/NORMALIZED_RECTANGLE_MAX);
9717ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9727ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Scale from [0, 2000] back into the preview rectangle
9737ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postScale(scaleW, scaleH);
9747ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9757ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Move the rect so that the [-1000,-1000] point ends up at the preview [left, top]
9767ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        transform.postTranslate(previewCrop.left, previewCrop.top);
9777ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9787ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect cropToIntersectAgainst = usePreviewCrop ? previewCrop : reportedCrop;
9797ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9807ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Now apply the transformation backwards to get the reported metering region
9817ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        Rect reportedMetering = ParamsUtils.mapRect(transform, area.rect);
9827ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // Intersect it with the crop region, to avoid reporting out-of-bounds
9837ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        // metering regions
9847ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (!reportedMetering.intersect(cropToIntersectAgainst)) {
9857ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            reportedMetering.set(RECTANGLE_EMPTY);
9867ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
9877ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9887ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        int weight = area.weight;
9897ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        if (weight < MeteringRectangle.METERING_WEIGHT_MIN) {
9907ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            Log.w(TAG,
9917ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                    "convertCameraAreaToMeteringRectangle - rectangle "
9927ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin                            + stringFromArea(area) + " has too small weight, clip to 0");
9937ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin            weight = 0;
9947ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        }
9957ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9967ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin        return new WeightedRectangle(reportedMetering, area.weight);
9977ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin    }
9987ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
9997ee78d1ee3ee068897b9313af2ed6446675c1be0Igor Murashkin
1000df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    private ParameterUtils() {
1001df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin        throw new AssertionError();
1002df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin    }
1003df6242e374b81e802a38cb891477f05d3e4b3cbcIgor Murashkin}
1004