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