194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin/*
294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * Copyright (C) 2014 The Android Open Source Project
394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * Licensed under the Apache License, Version 2.0 (the "License");
594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * you may not use this file except in compliance with the License.
694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * You may obtain a copy of the License at
794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *      http://www.apache.org/licenses/LICENSE-2.0
994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
1094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * Unless required by applicable law or agreed to in writing, software
1194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * distributed under the License is distributed on an "AS IS" BASIS,
1294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * See the License for the specific language governing permissions and
1494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * limitations under the License.
1594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin */
1694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
1772f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinpackage android.hardware.camera2.params;
1894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
1994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinimport static com.android.internal.util.Preconditions.*;
2072f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport static android.hardware.camera2.params.RggbChannelVector.*;
2194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
2272f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport android.hardware.camera2.CaptureResult;
233c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport android.hardware.camera2.utils.HashCodeHelpers;
2494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
2594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinimport java.util.Arrays;
2694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
2794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin/**
2894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * Immutable class for describing a {@code 4 x N x M} lens shading map of floats.
2994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
30574936894d3044445a272b39f2d925af40ece5d8Ruben Brunk * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
3194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin */
3294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinpublic final class LensShadingMap {
3394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
3494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
3594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * The smallest gain factor in this map.
3694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
3794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>All values in this map will be at least this large.</p>
3894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
3994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final float MINIMUM_GAIN_FACTOR = 1.0f;
4094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
4194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
4294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Create a new immutable LensShadingMap instance.
4394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
4494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>The elements must be stored in a row-major order (fully packed).</p>
4594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
4694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
4794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
4894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param elements
4994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          An array of elements whose length is
5094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          {@code RggbChannelVector.COUNT * rows * columns}
5194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
5294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException
5394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *            if the {@code elements} array length is invalid,
5494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *            if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR},
5594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *            or if rows or columns is not positive
5694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws NullPointerException
5794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *            if {@code elements} is {@code null}
5894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
5994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @hide
6094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
6194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public LensShadingMap(final float[] elements, final int rows, final int columns) {
6294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
6394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mRows = checkArgumentPositive(rows, "rows must be positive");
64574936894d3044445a272b39f2d925af40ece5d8Ruben Brunk        mColumns = checkArgumentPositive(columns, "columns must be positive");
6594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mElements = checkNotNull(elements, "elements must not be null");
6694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
6794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (elements.length != getGainFactorCount()) {
6894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("elements must be " + getGainFactorCount() +
69574936894d3044445a272b39f2d925af40ece5d8Ruben Brunk                    " length, received " + elements.length);
7094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
7194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
7294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        // Every element must be finite and >= 1.0f
7394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements");
7494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
7594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
7694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
7794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get the number of rows in this map.
7894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
7994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int getRowCount() {
8094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return mRows;
8194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
8294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
8394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
8494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get the number of columns in this map.
8594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
8694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int getColumnCount() {
8794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return mColumns;
8894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
8994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
9094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
9194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get the total number of gain factors in this map.
9294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
9394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>A single gain factor contains exactly one color channel.
9494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Use with {@link #copyGainFactors} to allocate a large-enough array.</p>
9594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
9694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int getGainFactorCount() {
9794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return mRows * mColumns * COUNT;
9894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
9994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
10094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
10194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get a single color channel gain factor from this lens shading map by its row and column.
10294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
10394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>The rows must be within the range [0, {@link #getRowCount}),
10494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * the column must be within the range [0, {@link #getColumnCount}),
10594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).</p>
10694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
10794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>The channel order is {@code [R, Geven, Godd, B]}, where
10894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@code Geven} is the green channel for the even rows of a Bayer pattern, and
10994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@code Godd} is the odd rows.
11094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * </p>
11194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
11294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param colorChannel color channel from {@code [R, Geven, Godd, B]}
11394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param column within the range [0, {@link #getColumnCount})
11494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param row within the range [0, {@link #getRowCount})
11594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
11694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR}
11794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
11894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException if any of the parameters was out of range
11994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
12094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #RED
12194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #GREEN_EVEN
12294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #GREEN_ODD
12394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #BLUE
12494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #getRowCount
12594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #getColumnCount
12694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
12794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public float getGainFactor(final int colorChannel, final int column, final int row) {
12894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (colorChannel < 0 || colorChannel > COUNT) {
12994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("colorChannel out of range");
13094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        } else if (column < 0 || column >= mColumns) {
13194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("column out of range");
13294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        } else if (row < 0 || row >= mRows) {
13394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("row out of range");
13494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
13594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
13694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return mElements[colorChannel + (row * mColumns +  column) * COUNT ];
13794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
13894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
13994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
14094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get a gain factor vector from this lens shading map by its row and column.
14194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
14294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>The rows must be within the range [0, {@link #getRowCount}),
14394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * the column must be within the range [0, {@link #getColumnCount}).</p>
14494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
14594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param column within the range [0, {@link #getColumnCount})
14694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param row within the range [0, {@link #getRowCount})
14794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
14894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR}
14994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
15094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException if any of the parameters was out of range
15194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
15294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #getRowCount
15394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #getColumnCount
15494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
15594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public RggbChannelVector getGainFactorVector(final int column, final int row) {
15694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (column < 0 || column >= mColumns) {
15794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("column out of range");
15894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        } else if (row < 0 || row >= mRows) {
15994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("row out of range");
16094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
16194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
16294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final int offset = (row * mColumns +  column) * COUNT;
16394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
16494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float red =
16594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                mElements[RED + offset];
16694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float greenEven =
16794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                mElements[GREEN_EVEN + offset];
16894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float greenOdd =
16994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                mElements[GREEN_ODD + offset];
17094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float blue =
17194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                mElements[BLUE + offset];
17294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
17394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return new RggbChannelVector(red, greenEven, greenOdd, blue);
17494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
17594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
17694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
17794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Copy all gain factors in row-major order from this lens shading map into the destination.
17894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
17994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.</p>
18094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
18194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param destination
18294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          an array big enough to hold at least {@link RggbChannelVector#COUNT}
18394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          elements after the {@code offset}
18494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param offset
18594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          a non-negative offset into the array
18694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws NullPointerException
18794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If {@code destination} was {@code null}
18894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException
18994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If offset was negative
19094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws ArrayIndexOutOfBoundsException
19194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If there's not enough room to write the elements at the specified destination and
19294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          offset.
19394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
19494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see CaptureResult#STATISTICS_LENS_SHADING_MAP
19594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
19694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public void copyGainFactors(final float[] destination, final int offset) {
19794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentNonnegative(offset, "offset must not be negative");
19894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkNotNull(destination, "destination must not be null");
19994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (destination.length + offset < getGainFactorCount()) {
20094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
20194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
20294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
20394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount());
20494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
20594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
20694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
20794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Check if this LensShadingMap is equal to another LensShadingMap.
20894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
20994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>Two lens shading maps are equal if and only if they have the same rows/columns,
21094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * and all of their elements are {@link Object#equals equal}.</p>
21194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
21294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return {@code true} if the objects were equal, {@code false} otherwise
21394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
21494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    @Override
21594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public boolean equals(final Object obj) {
21694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (obj == null) {
21794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return false;
21894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
21994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (this == obj) {
22094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return true;
22194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
22294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (obj instanceof LensShadingMap) {
22394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            final LensShadingMap other = (LensShadingMap) obj;
22494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return mRows == other.mRows
22594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                    && mColumns == other.mColumns
22694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                    && Arrays.equals(mElements, other.mElements);
22794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
22894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return false;
22994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
23094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
23194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
23294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@inheritDoc}
23394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
23494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    @Override
23594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int hashCode() {
23694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        int elemsHash = HashCodeHelpers.hashCode(mElements);
23794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash);
23894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
23994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
24018be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh    /**
24118be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     * Return the LensShadingMap as a string representation.
24218be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     *
24318be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     * <p> {@code "LensShadingMap{R:([%f, %f, ... %f], ... [%f, %f, ... %f]), G_even:([%f, %f, ...
24418be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     *  %f], ... [%f, %f, ... %f]), G_odd:([%f, %f, ... %f], ... [%f, %f, ... %f]), B:([%f, %f, ...
24518be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     *  %f], ... [%f, %f, ... %f])}"},
24618be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     * where each {@code %f} represents one gain factor and each {@code [%f, %f, ... %f]} represents
24718be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     * a row of the lens shading map</p>
24818be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     *
24918be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     * @return string representation of {@link LensShadingMap}
25018be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh     */
25118be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh    @Override
25218be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh    public String toString() {
25318be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        StringBuilder str = new StringBuilder();
25418be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        str.append("LensShadingMap{");
25518be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh
25618be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        final String channelPrefix[] = {"R:(", "G_even:(", "G_odd:(", "B:("};
25718be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh
25818be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        for (int ch = 0; ch < COUNT; ch++) {
25918be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            str.append(channelPrefix[ch]);
26018be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh
26118be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            for (int r = 0; r < mRows; r++) {
26218be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                str.append("[");
26318be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                for (int c = 0; c < mColumns; c++) {
26418be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                    float gain = getGainFactor(ch, c, r);
26518be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                    str.append(gain);
26618be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                    if (c < mColumns - 1) {
26718be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                        str.append(", ");
26818be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                    }
26918be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                }
27018be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                str.append("]");
27118be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                if (r < mRows - 1) {
27218be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                    str.append(", ");
27318be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                }
27418be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            }
27518be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh
27618be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            str.append(")");
27718be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            if (ch < COUNT - 1) {
27818be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh                str.append(", ");
27918be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh            }
28018be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        }
28118be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh
28218be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        str.append("}");
28318be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh        return str.toString();
28418be00f77ab6d534d378f09bd254ad397ac10a7aYin-Chia Yeh    }
28594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
28694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final int mRows;
28794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final int mColumns;
28894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final float[] mElements;
289574936894d3044445a272b39f2d925af40ece5d8Ruben Brunk}
290