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