1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.hardware.camera2.utils;
18
19import android.graphics.Matrix;
20import android.graphics.Rect;
21import android.graphics.RectF;
22import android.hardware.camera2.CaptureRequest;
23import android.util.Rational;
24import android.util.Size;
25
26import static com.android.internal.util.Preconditions.*;
27
28/**
29 * Various assortment of params utilities.
30 */
31public class ParamsUtils {
32
33    /** Arbitrary denominator used to estimate floats as rationals */
34    private static final int RATIONAL_DENOMINATOR = 1000000; // 1million
35
36    /**
37     * Create a {@link Rect} from a {@code Size} by creating a new rectangle with
38     * left, top = {@code (0, 0)} and right, bottom = {@code (width, height)}
39     *
40     * @param size a non-{@code null} size
41     *
42     * @return a {@code non-null} rectangle
43     *
44     * @throws NullPointerException if {@code size} was {@code null}
45     */
46    public static Rect createRect(Size size) {
47        checkNotNull(size, "size must not be null");
48
49        return new Rect(/*left*/0, /*top*/0, size.getWidth(), size.getHeight());
50    }
51
52    /**
53     * Create a {@link Rect} from a {@code RectF} by creating a new rectangle with
54     * each corner (left, top, right, bottom) rounded towards the nearest integer bounding box.
55     *
56     * <p>In particular (left, top) is floored, and (right, bottom) is ceiled.</p>
57     *
58     * @param size a non-{@code null} rect
59     *
60     * @return a {@code non-null} rectangle
61     *
62     * @throws NullPointerException if {@code rect} was {@code null}
63     */
64    public static Rect createRect(RectF rect) {
65        checkNotNull(rect, "rect must not be null");
66
67        Rect r = new Rect();
68        rect.roundOut(r);
69
70        return r;
71    }
72
73    /**
74     * Map the rectangle in {@code rect} with the transform in {@code transform} into
75     * a new rectangle, with each corner (left, top, right, bottom) rounded towards the nearest
76     * integer bounding box.
77     *
78     * <p>None of the arguments are mutated.</p>
79     *
80     * @param transform a non-{@code null} transformation matrix
81     * @param rect a non-{@code null} rectangle
82     * @return a new rectangle that was transformed by {@code transform}
83     *
84     * @throws NullPointerException if any of the args were {@code null}
85     */
86    public static Rect mapRect(Matrix transform, Rect rect) {
87        checkNotNull(transform, "transform must not be null");
88        checkNotNull(rect, "rect must not be null");
89
90        RectF rectF = new RectF(rect);
91        transform.mapRect(rectF);
92        return createRect(rectF);
93    }
94
95    /**
96     * Create a {@link Size} from a {@code Rect} by creating a new size whose width
97     * and height are the same as the rectangle's width and heights.
98     *
99     * @param rect a non-{@code null} rectangle
100     *
101     * @return a {@code non-null} size
102     *
103     * @throws NullPointerException if {@code rect} was {@code null}
104     */
105    public static Size createSize(Rect rect) {
106        checkNotNull(rect, "rect must not be null");
107
108        return new Size(rect.width(), rect.height());
109    }
110
111    /**
112     * Create a {@link Rational} value by approximating the float value as a rational.
113     *
114     * <p>Floating points too large to be represented as an integer will be converted to
115     * to {@link Integer#MAX_VALUE}; floating points too small to be represented as an integer
116     * will be converted to {@link Integer#MIN_VALUE}.</p>
117     *
118     * @param value a floating point value
119     * @return the rational representation of the float
120     */
121    public static Rational createRational(float value) {
122        if (Float.isNaN(value)) {
123            return Rational.NaN;
124        } else if (value == Float.POSITIVE_INFINITY) {
125            return Rational.POSITIVE_INFINITY;
126        } else if (value == Float.NEGATIVE_INFINITY) {
127            return Rational.NEGATIVE_INFINITY;
128        } else if (value == 0.0f) {
129            return Rational.ZERO;
130        }
131
132        // normal finite value: approximate it
133
134        /*
135         * Start out trying to approximate with denominator = 1million,
136         * but if the numerator doesn't fit into an Int then keep making the denominator
137         * smaller until it does.
138         */
139        int den = RATIONAL_DENOMINATOR;
140        float numF;
141        do {
142            numF = value * den;
143
144            if ((numF > Integer.MIN_VALUE && numF < Integer.MAX_VALUE) || (den == 1)) {
145                break;
146            }
147
148            den /= 10;
149        } while (true);
150
151        /*
152         *  By float -> int narrowing conversion in JLS 5.1.3, this will automatically become
153         *  MIN_VALUE or MAX_VALUE if numF is too small/large to be represented by an integer
154         */
155        int num = (int) numF;
156
157        return new Rational(num, den);
158     }
159
160    /**
161     * Convert an integral rectangle ({@code source}) to a floating point rectangle
162     * ({@code destination}) in-place.
163     *
164     * @param source the originating integer rectangle will be read from here
165     * @param destination the resulting floating point rectangle will be written out to here
166     *
167     * @throws NullPointerException if {@code rect} was {@code null}
168     */
169    public static void convertRectF(Rect source, RectF destination) {
170        checkNotNull(source, "source must not be null");
171        checkNotNull(destination, "destination must not be null");
172
173        destination.left = source.left;
174        destination.right = source.right;
175        destination.bottom = source.bottom;
176        destination.top = source.top;
177    }
178
179    /**
180     * Return the value set by the key, or the {@code defaultValue} if no value was set.
181     *
182     * @throws NullPointerException if any of the args were {@code null}
183     */
184    public static <T> T getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue) {
185        checkNotNull(r, "r must not be null");
186        checkNotNull(key, "key must not be null");
187        checkNotNull(defaultValue, "defaultValue must not be null");
188
189        T value = r.get(key);
190        if (value == null) {
191            return defaultValue;
192        } else {
193            return value;
194        }
195    }
196
197    private ParamsUtils() {
198        throw new AssertionError();
199    }
200}
201