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