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.params;
18 
19 import android.util.Size;
20 import static com.android.internal.util.Preconditions.*;
21 
22 import android.graphics.Point;
23 import android.graphics.Rect;
24 import android.hardware.camera2.CameraCharacteristics;
25 import android.hardware.camera2.CaptureRequest;
26 import android.hardware.camera2.utils.HashCodeHelpers;
27 
28 /**
29  * An immutable class to represent a rectangle {@code (x, y, width, height)} with an additional
30  * weight component.
31  * <p>
32  * The rectangle is defined to be inclusive of the specified coordinates.
33  * </p>
34  * <p>
35  * When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
36  * array, with {@code (0,0)} being the top-left pixel in the
37  * {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
38  * {@code (android.sensor.info.activeArraySize.width - 1,
39  * android.sensor.info.activeArraySize.height - 1)} being the bottom-right pixel in the active pixel
40  * array.
41  * </p>
42  * <p>
43  * The weight must range from {@value #METERING_WEIGHT_MIN} to {@value #METERING_WEIGHT_MAX}
44  * inclusively, and represents a weight for every pixel in the area. This means that a large
45  * metering area with the same weight as a smaller area will have more effect in the metering
46  * result. Metering areas can partially overlap and the camera device will add the weights in the
47  * overlap rectangle.
48  * </p>
49  * <p>
50  * If all rectangles have 0 weight, then no specific metering area needs to be used by the camera
51  * device. If the metering rectangle is outside the used android.scaler.cropRegion returned in
52  * capture result metadata, the camera device will ignore the sections outside the rectangle and
53  * output the used sections in the result metadata.
54  * </p>
55  */
56 public final class MeteringRectangle {
57     /**
58      * The minimum value of valid metering weight.
59      */
60     public static final int METERING_WEIGHT_MIN = 0;
61 
62     /**
63      * The maximum value of valid metering weight.
64      */
65     public static final int METERING_WEIGHT_MAX = 1000;
66 
67     /**
68      * Weights set to this value will cause the camera device to ignore this rectangle.
69      * If all metering rectangles are weighed with 0, the camera device will choose its own metering
70      * rectangles.
71      */
72     public static final int METERING_WEIGHT_DONT_CARE = 0;
73 
74     private final int mX;
75     private final int mY;
76     private final int mWidth;
77     private final int mHeight;
78     private final int mWeight;
79 
80     /**
81      * Create a new metering rectangle.
82      *
83      * @param x coordinate >= 0
84      * @param y coordinate >= 0
85      * @param width width >= 0
86      * @param height height >= 0
87      * @param meteringWeight weight between {@value #METERING_WEIGHT_MIN} and
88      *        {@value #METERING_WEIGHT_MAX} inclusively
89      * @throws IllegalArgumentException if any of the parameters were negative
90      */
MeteringRectangle(int x, int y, int width, int height, int meteringWeight)91     public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
92         mX = checkArgumentNonnegative(x, "x must be nonnegative");
93         mY = checkArgumentNonnegative(y, "y must be nonnegative");
94         mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
95         mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
96         mWeight = checkArgumentInRange(
97                 meteringWeight, METERING_WEIGHT_MIN, METERING_WEIGHT_MAX, "meteringWeight");
98     }
99 
100     /**
101      * Create a new metering rectangle.
102      *
103      * <p>The point {@code xy}'s data is copied; the reference is not retained.</p>
104      *
105      * @param xy a non-{@code null} {@link Point} with both x,y >= 0
106      * @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
107      * @param meteringWeight weight >= 0
108      *
109      * @throws IllegalArgumentException if any of the parameters were negative
110      * @throws NullPointerException if any of the arguments were null
111      */
MeteringRectangle(Point xy, Size dimensions, int meteringWeight)112     public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
113         checkNotNull(xy, "xy must not be null");
114         checkNotNull(dimensions, "dimensions must not be null");
115 
116         mX = checkArgumentNonnegative(xy.x, "x must be nonnegative");
117         mY = checkArgumentNonnegative(xy.y, "y must be nonnegative");
118         mWidth = checkArgumentNonnegative(dimensions.getWidth(), "width must be nonnegative");
119         mHeight = checkArgumentNonnegative(dimensions.getHeight(), "height must be nonnegative");
120         mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
121     }
122 
123     /**
124      * Create a new metering rectangle.
125      *
126      * <p>The rectangle data is copied; the reference is not retained.</p>
127      *
128      * @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
129      * @param meteringWeight weight >= 0
130      *
131      * @throws IllegalArgumentException if any of the parameters were negative
132      * @throws NullPointerException if any of the arguments were null
133      */
MeteringRectangle(Rect rect, int meteringWeight)134     public MeteringRectangle(Rect rect, int meteringWeight) {
135         checkNotNull(rect, "rect must not be null");
136 
137         mX = checkArgumentNonnegative(rect.left, "rect.left must be nonnegative");
138         mY = checkArgumentNonnegative(rect.top, "rect.top must be nonnegative");
139         mWidth = checkArgumentNonnegative(rect.width(), "rect.width must be nonnegative");
140         mHeight = checkArgumentNonnegative(rect.height(), "rect.height must be nonnegative");
141         mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
142     }
143 
144     /**
145      * Return the X coordinate of the left side of the rectangle.
146      *
147      * @return x coordinate >= 0
148      */
getX()149     public int getX() {
150         return mX;
151     }
152 
153     /**
154      * Return the Y coordinate of the upper side of the rectangle.
155      *
156      * @return y coordinate >= 0
157      */
getY()158     public int getY() {
159         return mY;
160     }
161 
162     /**
163      * Return the width of the rectangle.
164      *
165      * @return width >= 0
166      */
getWidth()167     public int getWidth() {
168         return mWidth;
169     }
170 
171     /**
172      * Return the height of the rectangle.
173      *
174      * @return height >= 0
175      */
getHeight()176     public int getHeight() {
177         return mHeight;
178     }
179 
180     /**
181      * Return the metering weight of the rectangle.
182      *
183      * @return weight >= 0
184      */
getMeteringWeight()185     public int getMeteringWeight() {
186         return mWeight;
187     }
188 
189     /**
190      * Convenience method to create the upper-left (X,Y) coordinate as a {@link Point}.
191      *
192      * @return a new {@code (x,y)} {@link Point} with both x,y >= 0
193      */
getUpperLeftPoint()194     public Point getUpperLeftPoint() {
195         return new Point(mX, mY);
196     }
197 
198     /**
199      * Convenience method to create the size from this metering rectangle.
200      *
201      * <p>This strips away the X,Y,weight from the rectangle.</p>
202      *
203      * @return a new {@link Size} with non-negative width and height
204      */
getSize()205     public Size getSize() {
206         return new Size(mWidth, mHeight);
207     }
208 
209     /**
210      * Convenience method to create a {@link Rect} from this metering rectangle.
211      *
212      * <p>This strips away the weight from the rectangle.</p>
213      *
214      * @return a new {@link Rect} with non-negative x1, y1, x2, y2
215      */
getRect()216     public Rect getRect() {
217         return new Rect(mX, mY, mX + mWidth, mY + mHeight);
218     }
219 
220     /**
221      * {@inheritDoc}
222      */
223     @Override
equals(final Object other)224     public boolean equals(final Object other) {
225         return other instanceof MeteringRectangle && equals((MeteringRectangle)other);
226     }
227 
228     /**
229      * Compare two metering rectangles to see if they are equal.
230      *
231      * Two weighted rectangles are only considered equal if each of their components
232      * (x, y, width, height, weight) is respectively equal.
233      *
234      * @param other Another MeteringRectangle
235      *
236      * @return {@code true} if the metering rectangles are equal, {@code false} otherwise
237      */
equals(final MeteringRectangle other)238     public boolean equals(final MeteringRectangle other) {
239         if (other == null) {
240             return false;
241         }
242 
243         return (mX == other.mX
244                 && mY == other.mY
245                 && mWidth == other.mWidth
246                 && mHeight == other.mHeight
247                 && mWeight == other.mWeight);
248     }
249 
250     /**
251      * {@inheritDoc}
252      */
253     @Override
hashCode()254     public int hashCode() {
255         return HashCodeHelpers.hashCode(mX, mY, mWidth, mHeight, mWeight);
256     }
257 
258     /**
259      * Return the metering rectangle as a string representation
260      * {@code "(x:%d, y:%d, w:%d, h:%d, wt:%d)"} where each {@code %d} respectively represents
261      * the x, y, width, height, and weight points.
262      *
263      * @return string representation of the metering rectangle
264      */
265     @Override
toString()266     public String toString() {
267         return String.format("(x:%d, y:%d, w:%d, h:%d, wt:%d)", mX, mY, mWidth, mHeight, mWeight);
268     }
269 }
270