/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.camera2.params; import static com.android.internal.util.Preconditions.*; import static android.hardware.camera2.params.RggbChannelVector.*; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.utils.HashCodeHelpers; import java.util.Arrays; /** * Immutable class for describing a {@code 4 x N x M} lens shading map of floats. * * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP */ public final class LensShadingMap { /** * The smallest gain factor in this map. * *

All values in this map will be at least this large.

*/ public static final float MINIMUM_GAIN_FACTOR = 1.0f; /** * Create a new immutable LensShadingMap instance. * *

The elements must be stored in a row-major order (fully packed).

* *

This constructor takes over the array; do not write to the array afterwards.

* * @param elements * An array of elements whose length is * {@code RggbChannelVector.COUNT * rows * columns} * * @throws IllegalArgumentException * if the {@code elements} array length is invalid, * if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR}, * or if rows or columns is not positive * @throws NullPointerException * if {@code elements} is {@code null} * * @hide */ public LensShadingMap(final float[] elements, final int rows, final int columns) { mRows = checkArgumentPositive(rows, "rows must be positive"); mColumns = checkArgumentPositive(columns, "columns must be positive"); mElements = checkNotNull(elements, "elements must not be null"); if (elements.length != getGainFactorCount()) { throw new IllegalArgumentException("elements must be " + getGainFactorCount() + " length, received " + elements.length); } // Every element must be finite and >= 1.0f checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements"); } /** * Get the number of rows in this map. */ public int getRowCount() { return mRows; } /** * Get the number of columns in this map. */ public int getColumnCount() { return mColumns; } /** * Get the total number of gain factors in this map. * *

A single gain factor contains exactly one color channel. * Use with {@link #copyGainFactors} to allocate a large-enough array.

*/ public int getGainFactorCount() { return mRows * mColumns * COUNT; } /** * Get a single color channel gain factor from this lens shading map by its row and column. * *

The rows must be within the range [0, {@link #getRowCount}), * the column must be within the range [0, {@link #getColumnCount}), * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).

* *

The channel order is {@code [R, Geven, Godd, B]}, where * {@code Geven} is the green channel for the even rows of a Bayer pattern, and * {@code Godd} is the odd rows. *

* * @param colorChannel color channel from {@code [R, Geven, Godd, B]} * @param column within the range [0, {@link #getColumnCount}) * @param row within the range [0, {@link #getRowCount}) * * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR} * * @throws IllegalArgumentException if any of the parameters was out of range * * @see #RED * @see #GREEN_EVEN * @see #GREEN_ODD * @see #BLUE * @see #getRowCount * @see #getColumnCount */ public float getGainFactor(final int colorChannel, final int column, final int row) { if (colorChannel < 0 || colorChannel > COUNT) { throw new IllegalArgumentException("colorChannel out of range"); } else if (column < 0 || column >= mColumns) { throw new IllegalArgumentException("column out of range"); } else if (row < 0 || row >= mRows) { throw new IllegalArgumentException("row out of range"); } return mElements[colorChannel + (row * mColumns + column) * COUNT ]; } /** * Get a gain factor vector from this lens shading map by its row and column. * *

The rows must be within the range [0, {@link #getRowCount}), * the column must be within the range [0, {@link #getColumnCount}).

* * @param column within the range [0, {@link #getColumnCount}) * @param row within the range [0, {@link #getRowCount}) * * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR} * * @throws IllegalArgumentException if any of the parameters was out of range * * @see #getRowCount * @see #getColumnCount */ public RggbChannelVector getGainFactorVector(final int column, final int row) { if (column < 0 || column >= mColumns) { throw new IllegalArgumentException("column out of range"); } else if (row < 0 || row >= mRows) { throw new IllegalArgumentException("row out of range"); } final int offset = (row * mColumns + column) * COUNT; final float red = mElements[RED + offset]; final float greenEven = mElements[GREEN_EVEN + offset]; final float greenOdd = mElements[GREEN_ODD + offset]; final float blue = mElements[BLUE + offset]; return new RggbChannelVector(red, greenEven, greenOdd, blue); } /** * Copy all gain factors in row-major order from this lens shading map into the destination. * *

Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.

* * @param destination * an array big enough to hold at least {@link RggbChannelVector#COUNT} * elements after the {@code offset} * @param offset * a non-negative offset into the array * @throws NullPointerException * If {@code destination} was {@code null} * @throws IllegalArgumentException * If offset was negative * @throws ArrayIndexOutOfBoundsException * If there's not enough room to write the elements at the specified destination and * offset. * * @see CaptureResult#STATISTICS_LENS_SHADING_MAP */ public void copyGainFactors(final float[] destination, final int offset) { checkArgumentNonnegative(offset, "offset must not be negative"); checkNotNull(destination, "destination must not be null"); if (destination.length + offset < getGainFactorCount()) { throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); } System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount()); } /** * Check if this LensShadingMap is equal to another LensShadingMap. * *

Two lens shading maps are equal if and only if they have the same rows/columns, * and all of their elements are {@link Object#equals equal}.

* * @return {@code true} if the objects were equal, {@code false} otherwise */ @Override public boolean equals(final Object obj) { if (obj == null) { return false; } if (this == obj) { return true; } if (obj instanceof LensShadingMap) { final LensShadingMap other = (LensShadingMap) obj; return mRows == other.mRows && mColumns == other.mColumns && Arrays.equals(mElements, other.mElements); } return false; } /** * {@inheritDoc} */ @Override public int hashCode() { int elemsHash = HashCodeHelpers.hashCode(mElements); return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash); } /** * Return the LensShadingMap as a string representation. * *

{@code "LensShadingMap{R:([%f, %f, ... %f], ... [%f, %f, ... %f]), G_even:([%f, %f, ... * %f], ... [%f, %f, ... %f]), G_odd:([%f, %f, ... %f], ... [%f, %f, ... %f]), B:([%f, %f, ... * %f], ... [%f, %f, ... %f])}"}, * where each {@code %f} represents one gain factor and each {@code [%f, %f, ... %f]} represents * a row of the lens shading map

* * @return string representation of {@link LensShadingMap} */ @Override public String toString() { StringBuilder str = new StringBuilder(); str.append("LensShadingMap{"); final String channelPrefix[] = {"R:(", "G_even:(", "G_odd:(", "B:("}; for (int ch = 0; ch < COUNT; ch++) { str.append(channelPrefix[ch]); for (int r = 0; r < mRows; r++) { str.append("["); for (int c = 0; c < mColumns; c++) { float gain = getGainFactor(ch, c, r); str.append(gain); if (c < mColumns - 1) { str.append(", "); } } str.append("]"); if (r < mRows - 1) { str.append(", "); } } str.append(")"); if (ch < COUNT - 1) { str.append(", "); } } str.append("}"); return str.toString(); } private final int mRows; private final int mColumns; private final float[] mElements; }