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