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 
17 package android.hardware.camera2.utils;
18 
19 import android.graphics.Matrix;
20 import android.graphics.Rect;
21 import android.graphics.RectF;
22 import android.hardware.camera2.CaptureRequest;
23 import android.util.Rational;
24 import android.util.Size;
25 
26 import static com.android.internal.util.Preconditions.*;
27 
28 /**
29  * Various assortment of params utilities.
30  */
31 public 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      */
createRect(Size size)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      */
createRect(RectF rect)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      */
mapRect(Matrix transform, Rect rect)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      */
createSize(Rect rect)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      */
createRational(float value)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      */
convertRectF(Rect source, RectF destination)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      */
getOrDefault(CaptureRequest r, CaptureRequest.Key<T> key, T defaultValue)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 
ParamsUtils()197     private ParamsUtils() {
198         throw new AssertionError();
199     }
200 }
201