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 static com.android.internal.util.Preconditions.*; 20 import static android.hardware.camera2.params.RggbChannelVector.*; 21 22 import android.hardware.camera2.CaptureResult; 23 import android.hardware.camera2.utils.HashCodeHelpers; 24 25 import java.util.Arrays; 26 27 /** 28 * Immutable class for describing a {@code 4 x N x M} lens shading map of floats. 29 * 30 * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP 31 */ 32 public final class LensShadingMap { 33 34 /** 35 * The smallest gain factor in this map. 36 * 37 * <p>All values in this map will be at least this large.</p> 38 */ 39 public static final float MINIMUM_GAIN_FACTOR = 1.0f; 40 41 /** 42 * Create a new immutable LensShadingMap instance. 43 * 44 * <p>The elements must be stored in a row-major order (fully packed).</p> 45 * 46 * <p>This constructor takes over the array; do not write to the array afterwards.</p> 47 * 48 * @param elements 49 * An array of elements whose length is 50 * {@code RggbChannelVector.COUNT * rows * columns} 51 * 52 * @throws IllegalArgumentException 53 * if the {@code elements} array length is invalid, 54 * if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR}, 55 * or if rows or columns is not positive 56 * @throws NullPointerException 57 * if {@code elements} is {@code null} 58 * 59 * @hide 60 */ LensShadingMap(final float[] elements, final int rows, final int columns)61 public LensShadingMap(final float[] elements, final int rows, final int columns) { 62 63 mRows = checkArgumentPositive(rows, "rows must be positive"); 64 mColumns = checkArgumentPositive(columns, "columns must be positive"); 65 mElements = checkNotNull(elements, "elements must not be null"); 66 67 if (elements.length != getGainFactorCount()) { 68 throw new IllegalArgumentException("elements must be " + getGainFactorCount() + 69 " length, received " + elements.length); 70 } 71 72 // Every element must be finite and >= 1.0f 73 checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements"); 74 } 75 76 /** 77 * Get the number of rows in this map. 78 */ getRowCount()79 public int getRowCount() { 80 return mRows; 81 } 82 83 /** 84 * Get the number of columns in this map. 85 */ getColumnCount()86 public int getColumnCount() { 87 return mColumns; 88 } 89 90 /** 91 * Get the total number of gain factors in this map. 92 * 93 * <p>A single gain factor contains exactly one color channel. 94 * Use with {@link #copyGainFactors} to allocate a large-enough array.</p> 95 */ getGainFactorCount()96 public int getGainFactorCount() { 97 return mRows * mColumns * COUNT; 98 } 99 100 /** 101 * Get a single color channel gain factor from this lens shading map by its row and column. 102 * 103 * <p>The rows must be within the range [0, {@link #getRowCount}), 104 * the column must be within the range [0, {@link #getColumnCount}), 105 * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).</p> 106 * 107 * <p>The channel order is {@code [R, Geven, Godd, B]}, where 108 * {@code Geven} is the green channel for the even rows of a Bayer pattern, and 109 * {@code Godd} is the odd rows. 110 * </p> 111 * 112 * @param colorChannel color channel from {@code [R, Geven, Godd, B]} 113 * @param column within the range [0, {@link #getColumnCount}) 114 * @param row within the range [0, {@link #getRowCount}) 115 * 116 * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR} 117 * 118 * @throws IllegalArgumentException if any of the parameters was out of range 119 * 120 * @see #RED 121 * @see #GREEN_EVEN 122 * @see #GREEN_ODD 123 * @see #BLUE 124 * @see #getRowCount 125 * @see #getColumnCount 126 */ getGainFactor(final int colorChannel, final int column, final int row)127 public float getGainFactor(final int colorChannel, final int column, final int row) { 128 if (colorChannel < 0 || colorChannel > COUNT) { 129 throw new IllegalArgumentException("colorChannel out of range"); 130 } else if (column < 0 || column >= mColumns) { 131 throw new IllegalArgumentException("column out of range"); 132 } else if (row < 0 || row >= mRows) { 133 throw new IllegalArgumentException("row out of range"); 134 } 135 136 return mElements[colorChannel + (row * mColumns + column) * COUNT ]; 137 } 138 139 /** 140 * Get a gain factor vector from this lens shading map by its row and column. 141 * 142 * <p>The rows must be within the range [0, {@link #getRowCount}), 143 * the column must be within the range [0, {@link #getColumnCount}).</p> 144 * 145 * @param column within the range [0, {@link #getColumnCount}) 146 * @param row within the range [0, {@link #getRowCount}) 147 * 148 * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR} 149 * 150 * @throws IllegalArgumentException if any of the parameters was out of range 151 * 152 * @see #getRowCount 153 * @see #getColumnCount 154 */ getGainFactorVector(final int column, final int row)155 public RggbChannelVector getGainFactorVector(final int column, final int row) { 156 if (column < 0 || column >= mColumns) { 157 throw new IllegalArgumentException("column out of range"); 158 } else if (row < 0 || row >= mRows) { 159 throw new IllegalArgumentException("row out of range"); 160 } 161 162 final int offset = (row * mColumns + column) * COUNT; 163 164 final float red = 165 mElements[RED + offset]; 166 final float greenEven = 167 mElements[GREEN_EVEN + offset]; 168 final float greenOdd = 169 mElements[GREEN_ODD + offset]; 170 final float blue = 171 mElements[BLUE + offset]; 172 173 return new RggbChannelVector(red, greenEven, greenOdd, blue); 174 } 175 176 /** 177 * Copy all gain factors in row-major order from this lens shading map into the destination. 178 * 179 * <p>Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.</p> 180 * 181 * @param destination 182 * an array big enough to hold at least {@link RggbChannelVector#COUNT} 183 * elements after the {@code offset} 184 * @param offset 185 * a non-negative offset into the array 186 * @throws NullPointerException 187 * If {@code destination} was {@code null} 188 * @throws IllegalArgumentException 189 * If offset was negative 190 * @throws ArrayIndexOutOfBoundsException 191 * If there's not enough room to write the elements at the specified destination and 192 * offset. 193 * 194 * @see CaptureResult#STATISTICS_LENS_SHADING_MAP 195 */ copyGainFactors(final float[] destination, final int offset)196 public void copyGainFactors(final float[] destination, final int offset) { 197 checkArgumentNonnegative(offset, "offset must not be negative"); 198 checkNotNull(destination, "destination must not be null"); 199 if (destination.length + offset < getGainFactorCount()) { 200 throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); 201 } 202 203 System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount()); 204 } 205 206 /** 207 * Check if this LensShadingMap is equal to another LensShadingMap. 208 * 209 * <p>Two lens shading maps are equal if and only if they have the same rows/columns, 210 * and all of their elements are {@link Object#equals equal}.</p> 211 * 212 * @return {@code true} if the objects were equal, {@code false} otherwise 213 */ 214 @Override equals(final Object obj)215 public boolean equals(final Object obj) { 216 if (obj == null) { 217 return false; 218 } 219 if (this == obj) { 220 return true; 221 } 222 if (obj instanceof LensShadingMap) { 223 final LensShadingMap other = (LensShadingMap) obj; 224 return mRows == other.mRows 225 && mColumns == other.mColumns 226 && Arrays.equals(mElements, other.mElements); 227 } 228 return false; 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override hashCode()235 public int hashCode() { 236 int elemsHash = HashCodeHelpers.hashCode(mElements); 237 return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash); 238 } 239 240 /** 241 * Return the LensShadingMap as a string representation. 242 * 243 * <p> {@code "LensShadingMap{R:([%f, %f, ... %f], ... [%f, %f, ... %f]), G_even:([%f, %f, ... 244 * %f], ... [%f, %f, ... %f]), G_odd:([%f, %f, ... %f], ... [%f, %f, ... %f]), B:([%f, %f, ... 245 * %f], ... [%f, %f, ... %f])}"}, 246 * where each {@code %f} represents one gain factor and each {@code [%f, %f, ... %f]} represents 247 * a row of the lens shading map</p> 248 * 249 * @return string representation of {@link LensShadingMap} 250 */ 251 @Override toString()252 public String toString() { 253 StringBuilder str = new StringBuilder(); 254 str.append("LensShadingMap{"); 255 256 final String channelPrefix[] = {"R:(", "G_even:(", "G_odd:(", "B:("}; 257 258 for (int ch = 0; ch < COUNT; ch++) { 259 str.append(channelPrefix[ch]); 260 261 for (int r = 0; r < mRows; r++) { 262 str.append("["); 263 for (int c = 0; c < mColumns; c++) { 264 float gain = getGainFactor(ch, c, r); 265 str.append(gain); 266 if (c < mColumns - 1) { 267 str.append(", "); 268 } 269 } 270 str.append("]"); 271 if (r < mRows - 1) { 272 str.append(", "); 273 } 274 } 275 276 str.append(")"); 277 if (ch < COUNT - 1) { 278 str.append(", "); 279 } 280 } 281 282 str.append("}"); 283 return str.toString(); 284 } 285 286 private final int mRows; 287 private final int mColumns; 288 private final float[] mElements; 289 } 290