TonemapCurve.java revision 83a9e4d86407d627f3f6fbf8757d2a389097ab6f
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.params; 18 19import static com.android.internal.util.Preconditions.*; 20 21import android.graphics.PointF; 22import android.hardware.camera2.CameraCharacteristics; 23import android.hardware.camera2.CameraDevice; 24import android.hardware.camera2.CameraMetadata; 25import android.hardware.camera2.CaptureRequest; 26import android.hardware.camera2.utils.HashCodeHelpers; 27 28import java.util.Arrays; 29 30/** 31 * Immutable class for describing a {@code 2 x M x 3} tonemap curve of floats. 32 * 33 * <p>This defines red, green, and blue curves that the {@link CameraDevice} will 34 * use as the tonemapping/contrast/gamma curve when {@link CaptureRequest#TONEMAP_MODE} is 35 * set to {@link CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE}.</p> 36 * 37 * <p>The total number of points {@code (Pin, Pout)} for each color channel can be no more than 38 * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.</p> 39 * 40 * <p>The coordinate system for each point is within the inclusive range 41 * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p> 42 * 43 * @see CaptureRequest#TONEMAP_CURVE_BLUE 44 * @see CaptureRequest#TONEMAP_CURVE_GREEN 45 * @see CaptureRequest#TONEMAP_CURVE_RED 46 * @see CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE 47 * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS 48 */ 49public final class TonemapCurve { 50 /** 51 * Lower bound tonemap value corresponding to pure black for a single color channel. 52 */ 53 public static final float LEVEL_BLACK = 0.0f; 54 55 /** 56 * Upper bound tonemap value corresponding to a pure white for a single color channel. 57 */ 58 public static final float LEVEL_WHITE = 1.0f; 59 60 /** 61 * Number of elements in a {@code (Pin, Pout)} point; 62 */ 63 public static final int POINT_SIZE = 2; 64 65 /** 66 * Index of the red color channel curve. 67 */ 68 public static final int CHANNEL_RED = 0; 69 /** 70 * Index of the green color channel curve. 71 */ 72 public static final int CHANNEL_GREEN = 1; 73 /** 74 * Index of the blue color channel curve. 75 */ 76 public static final int CHANNEL_BLUE = 2; 77 78 /** 79 * Create a new immutable TonemapCurve instance. 80 * 81 * <p>Values are stored as a contiguous array of {@code (Pin, Pout)} points.</p> 82 * 83 * <p>All parameters may have independent length but should have at most 84 * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements.</p> 85 * 86 * <p>All sub-elements must be in the inclusive range of 87 * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p> 88 * 89 * <p>This constructor copies the array contents and does not retain ownership of the array.</p> 90 * 91 * @param red An array of elements whose length is divisible by {@value #POINT_SIZE} 92 * @param green An array of elements whose length is divisible by {@value #POINT_SIZE} 93 * @param blue An array of elements whose length is divisible by {@value #POINT_SIZE} 94 * 95 * @throws IllegalArgumentException 96 * if any of input array length is invalid, 97 * or if any of the elements in the array are not in the range of 98 * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}] 99 * @throws NullPointerException 100 * if any of the parameters are {@code null} 101 */ 102 public TonemapCurve(float[] red, float[] green, float[] blue) { 103 // TODO: maxCurvePoints check? 104 105 checkNotNull(red, "red must not be null"); 106 checkNotNull(green, "green must not be null"); 107 checkNotNull(blue, "blue must not be null"); 108 109 checkArgumentArrayLengthDivisibleBy(red, POINT_SIZE, "red"); 110 checkArgumentArrayLengthDivisibleBy(green, POINT_SIZE, "green"); 111 checkArgumentArrayLengthDivisibleBy(blue, POINT_SIZE, "blue"); 112 113 checkArrayElementsInRange(red, LEVEL_BLACK, LEVEL_WHITE, "red"); 114 checkArrayElementsInRange(green, LEVEL_BLACK, LEVEL_WHITE, "green"); 115 checkArrayElementsInRange(blue, LEVEL_BLACK, LEVEL_WHITE, "blue"); 116 117 mRed = Arrays.copyOf(red, red.length); 118 mGreen = Arrays.copyOf(green, green.length); 119 mBlue = Arrays.copyOf(blue, blue.length); 120 } 121 122 private static void checkArgumentArrayLengthDivisibleBy(float[] array, 123 int divisible, String arrayName) { 124 if (array.length % divisible != 0) { 125 throw new IllegalArgumentException(arrayName + " size must be divisible by " 126 + divisible); 127 } 128 } 129 130 private static int checkArgumentColorChannel(int colorChannel) { 131 switch (colorChannel) { 132 case CHANNEL_RED: 133 case CHANNEL_GREEN: 134 case CHANNEL_BLUE: 135 break; 136 default: 137 throw new IllegalArgumentException("colorChannel out of range"); 138 } 139 140 return colorChannel; 141 } 142 143 /** 144 * Get the number of points stored in this tonemap curve for the specified color channel. 145 * 146 * @param colorChannel one of {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, {@link #CHANNEL_BLUE} 147 * @return number of points stored in this tonemap for that color's curve (>= 0) 148 * 149 * @throws IllegalArgumentException if {@code colorChannel} was out of range 150 */ 151 public int getPointCount(int colorChannel) { 152 checkArgumentColorChannel(colorChannel); 153 154 return getCurve(colorChannel).length / POINT_SIZE; 155 } 156 157 /** 158 * Get the point for a color channel at a specified index. 159 * 160 * <p>The index must be at least 0 but no greater than {@link #getPointCount(int)} for 161 * that {@code colorChannel}.</p> 162 * 163 * <p>All returned coordinates in the point are between the range of 164 * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p> 165 * 166 * @param colorChannel {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, or {@link #CHANNEL_BLUE} 167 * @param index at least 0 but no greater than {@code getPointCount(colorChannel)} 168 * @return the {@code (Pin, Pout)} pair mapping the tone for that index 169 * 170 * @throws IllegalArgumentException if {@code colorChannel} or {@code index} was out of range 171 * 172 * @see #LEVEL_BLACK 173 * @see #LEVEL_WHITE 174 */ 175 public PointF getPoint(int colorChannel, int index) { 176 checkArgumentColorChannel(colorChannel); 177 if (index < 0 || index >= getPointCount(colorChannel)) { 178 throw new IllegalArgumentException("index out of range"); 179 } 180 181 final float[] curve = getCurve(colorChannel); 182 183 final float pIn = curve[index * POINT_SIZE + OFFSET_POINT_IN]; 184 final float pOut = curve[index * POINT_SIZE + OFFSET_POINT_OUT]; 185 186 return new PointF(pIn, pOut); 187 } 188 189 /** 190 * Copy the color curve for a single color channel from this tonemap curve into the destination. 191 * 192 * <p> 193 * <!--The output is encoded the same as in the constructor --> 194 * Values are stored as packed {@code (Pin, Pout}) points, and there are a total of 195 * {@link #getPointCount} points for that respective channel.</p> 196 * 197 * <p>All returned coordinates are between the range of 198 * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p> 199 * 200 * @param destination 201 * an array big enough to hold at least {@link #getPointCount} {@code *} 202 * {@link #POINT_SIZE} elements after the {@code offset} 203 * @param offset 204 * a non-negative offset into the array 205 * @throws NullPointerException 206 * If {@code destination} was {@code null} 207 * @throws IllegalArgumentException 208 * If offset was negative 209 * @throws ArrayIndexOutOfBoundsException 210 * If there's not enough room to write the elements at the specified destination and 211 * offset. 212 * 213 * @see CaptureRequest#TONEMAP_CURVE_BLUE 214 * @see CaptureRequest#TONEMAP_CURVE_RED 215 * @see CaptureRequest#TONEMAP_CURVE_GREEN 216 * @see #LEVEL_BLACK 217 * @see #LEVEL_WHITE 218 */ 219 public void copyColorCurve(int colorChannel, float[] destination, 220 int offset) { 221 checkArgumentNonnegative(offset, "offset must not be negative"); 222 checkNotNull(destination, "destination must not be null"); 223 224 if (destination.length + offset < getPointCount(colorChannel) * POINT_SIZE) { 225 throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); 226 } 227 228 float[] curve = getCurve(colorChannel); 229 System.arraycopy(curve, /*srcPos*/0, destination, offset, curve.length); 230 } 231 232 /** 233 * Check if this TonemapCurve is equal to another TonemapCurve. 234 * 235 * <p>Two matrices are equal if and only if all of their elements are 236 * {@link Object#equals equal}.</p> 237 * 238 * @return {@code true} if the objects were equal, {@code false} otherwise 239 */ 240 @Override 241 public boolean equals(Object obj) { 242 if (obj == null) { 243 return false; 244 } 245 if (this == obj) { 246 return true; 247 } 248 if (obj instanceof TonemapCurve) { 249 final TonemapCurve other = (TonemapCurve) obj; 250 return Arrays.equals(mRed, other.mRed) && 251 Arrays.equals(mGreen, other.mGreen) && 252 Arrays.equals(mBlue, other.mBlue); 253 } 254 return false; 255 } 256 257 /** 258 * {@inheritDoc} 259 */ 260 @Override 261 public int hashCode() { 262 if (mHashCalculated) { 263 // Avoid re-calculating hash. Data is immutable so this is both legal and faster. 264 return mHashCode; 265 } 266 267 mHashCode = HashCodeHelpers.hashCode(mRed, mGreen, mBlue); 268 mHashCalculated = true; 269 270 return mHashCode; 271 } 272 273 private float[] getCurve(int colorChannel) { 274 switch (colorChannel) { 275 case CHANNEL_RED: 276 return mRed; 277 case CHANNEL_GREEN: 278 return mGreen; 279 case CHANNEL_BLUE: 280 return mBlue; 281 default: 282 throw new AssertionError("colorChannel out of range"); 283 } 284 } 285 286 private final static int OFFSET_POINT_IN = 0; 287 private final static int OFFSET_POINT_OUT = 1; 288 289 private final float[] mRed; 290 private final float[] mGreen; 291 private final float[] mBlue; 292 293 private int mHashCode; 294 private boolean mHashCalculated = false; 295}; 296