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.*;
2094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
2194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinimport android.graphics.PointF;
2272f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport android.hardware.camera2.CameraCharacteristics;
2372f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport android.hardware.camera2.CameraDevice;
2472f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport android.hardware.camera2.CameraMetadata;
2572f9f0a96e4476ef231d5001cb30521ad4ce5b1eIgor Murashkinimport android.hardware.camera2.CaptureRequest;
263c40a046cf0ea7b6af01ec93e5276eccb3234bfeIgor Murashkinimport android.hardware.camera2.utils.HashCodeHelpers;
2794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
2894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinimport java.util.Arrays;
2994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
3094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin/**
3194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * Immutable class for describing a {@code 2 x M x 3} tonemap curve of floats.
3294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
3394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * <p>This defines red, green, and blue curves that the {@link CameraDevice} will
3494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * use as the tonemapping/contrast/gamma curve when {@link CaptureRequest#TONEMAP_MODE} is
3594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * set to {@link CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE}.</p>
3694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
3794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * <p>The total number of points {@code (Pin, Pout)} for each color channel can be no more than
3894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.</p>
3994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
4094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * <p>The coordinate system for each point is within the inclusive range
4194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
4294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin *
4394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * @see CaptureRequest#TONEMAP_CURVE_BLUE
4494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * @see CaptureRequest#TONEMAP_CURVE_GREEN
4594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * @see CaptureRequest#TONEMAP_CURVE_RED
4694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * @see CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE
4794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS
4894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin */
4994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkinpublic final class TonemapCurve {
5094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
5194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Lower bound tonemap value corresponding to pure black for a single color channel.
5294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
5394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final float LEVEL_BLACK = 0.0f;
5494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
5594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
5694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Upper bound tonemap value corresponding to a pure white for a single color channel.
5794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
5894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final float LEVEL_WHITE = 1.0f;
5994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
6094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
6194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Number of elements in a {@code (Pin, Pout)} point;
6294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
6394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final int POINT_SIZE = 2;
6494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
6594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
6694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Index of the red color channel curve.
6794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
6894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final int CHANNEL_RED = 0;
6994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
7094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Index of the green color channel curve.
7194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
7294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final int CHANNEL_GREEN = 1;
7394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
7494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Index of the blue color channel curve.
7594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
7694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public static final int CHANNEL_BLUE = 2;
7794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
7894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
7994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Create a new immutable TonemapCurve instance.
8094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
8183a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     * <p>Values are stored as a contiguous array of {@code (Pin, Pout)} points.</p>
8294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
8394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>All parameters may have independent length but should have at most
84b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements and
85b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * at least 2 * {@value #POINT_SIZE} elements.</p>
8694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
8794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>All sub-elements must be in the inclusive range of
8894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
8994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
9094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>This constructor copies the array contents and does not retain ownership of the array.</p>
9194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
9283a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     * @param red An array of elements whose length is divisible by {@value #POINT_SIZE}
9383a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     * @param green An array of elements whose length is divisible by {@value #POINT_SIZE}
9483a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     * @param blue An array of elements whose length is divisible by {@value #POINT_SIZE}
9594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
9694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException
9783a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     *            if any of input array length is invalid,
9883a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     *            or if any of the elements in the array are not in the range of
9983a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     *            [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}]
10094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws NullPointerException
10183a9e4d86407d627f3f6fbf8757d2a389097ab6fYin-Chia Yeh     *            if any of the parameters are {@code null}
10294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
10394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public TonemapCurve(float[] red, float[] green, float[] blue) {
10494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        // TODO: maxCurvePoints check?
10594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
10694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkNotNull(red, "red must not be null");
10794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkNotNull(green, "green must not be null");
10894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkNotNull(blue, "blue must not be null");
10994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
11094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentArrayLengthDivisibleBy(red, POINT_SIZE, "red");
11194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentArrayLengthDivisibleBy(green, POINT_SIZE, "green");
11294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentArrayLengthDivisibleBy(blue, POINT_SIZE, "blue");
11394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
114b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        checkArgumentArrayLengthNoLessThan(red, MIN_CURVE_LENGTH, "red");
115b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        checkArgumentArrayLengthNoLessThan(green, MIN_CURVE_LENGTH, "green");
116b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        checkArgumentArrayLengthNoLessThan(blue, MIN_CURVE_LENGTH, "blue");
117b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh
11894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArrayElementsInRange(red, LEVEL_BLACK, LEVEL_WHITE, "red");
11994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArrayElementsInRange(green, LEVEL_BLACK, LEVEL_WHITE, "green");
12094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArrayElementsInRange(blue, LEVEL_BLACK, LEVEL_WHITE, "blue");
12194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
12294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mRed = Arrays.copyOf(red, red.length);
12394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mGreen = Arrays.copyOf(green, green.length);
12494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mBlue = Arrays.copyOf(blue, blue.length);
12594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
12694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
12794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private static void checkArgumentArrayLengthDivisibleBy(float[] array,
12894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            int divisible, String arrayName) {
12994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (array.length % divisible != 0) {
13094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException(arrayName + " size must be divisible by "
13194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                    + divisible);
13294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
13394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
13494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
13594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private static int checkArgumentColorChannel(int colorChannel) {
13694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        switch (colorChannel) {
13794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_RED:
13894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_GREEN:
13994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_BLUE:
14094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                break;
14194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            default:
14294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                throw new IllegalArgumentException("colorChannel out of range");
14394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
14494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
14594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return colorChannel;
14694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
14794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
148b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    private static void checkArgumentArrayLengthNoLessThan(float[] array, int minLength,
149b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            String arrayName) {
150b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        if (array.length < minLength) {
151b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            throw new IllegalArgumentException(arrayName + " size must be at least "
152b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh                    + minLength);
153b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        }
154b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    }
155b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh
15694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
15794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get the number of points stored in this tonemap curve for the specified color channel.
15894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
15994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param colorChannel one of {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, {@link #CHANNEL_BLUE}
16094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return number of points stored in this tonemap for that color's curve (>= 0)
16194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
16294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException if {@code colorChannel} was out of range
16394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
16494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int getPointCount(int colorChannel) {
16594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentColorChannel(colorChannel);
16694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
16794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return getCurve(colorChannel).length / POINT_SIZE;
16894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
16994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
17094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
17194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Get the point for a color channel at a specified index.
17294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
17394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>The index must be at least 0 but no greater than {@link #getPointCount(int)} for
17494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * that {@code colorChannel}.</p>
17594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
17694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>All returned coordinates in the point are between the range of
17794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
17894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
17994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param colorChannel {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, or {@link #CHANNEL_BLUE}
18094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param index at least 0 but no greater than {@code getPointCount(colorChannel)}
18194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return the {@code (Pin, Pout)} pair mapping the tone for that index
18294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
18394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException if {@code colorChannel} or {@code index} was out of range
18494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
18594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #LEVEL_BLACK
18694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #LEVEL_WHITE
18794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
18894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public PointF getPoint(int colorChannel, int index) {
18994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentColorChannel(colorChannel);
19094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (index < 0 || index >= getPointCount(colorChannel)) {
19194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new IllegalArgumentException("index out of range");
19294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
19394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
19494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float[] curve = getCurve(colorChannel);
19594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
19694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float pIn = curve[index * POINT_SIZE + OFFSET_POINT_IN];
19794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        final float pOut = curve[index * POINT_SIZE + OFFSET_POINT_OUT];
19894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
19994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return new PointF(pIn, pOut);
20094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
20194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
20294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
20394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Copy the color curve for a single color channel from this tonemap curve into the destination.
20494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
20594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>
20694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <!--The output is encoded the same as in the constructor -->
20794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Values are stored as packed {@code (Pin, Pout}) points, and there are a total of
20894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@link #getPointCount} points for that respective channel.</p>
20994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
21094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>All returned coordinates are between the range of
21194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
21294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
21394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param destination
21494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          an array big enough to hold at least {@link #getPointCount} {@code *}
21594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          {@link #POINT_SIZE} elements after the {@code offset}
21694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @param offset
21794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          a non-negative offset into the array
21894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws NullPointerException
21994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If {@code destination} was {@code null}
22094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws IllegalArgumentException
22194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If offset was negative
22294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @throws ArrayIndexOutOfBoundsException
22394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          If there's not enough room to write the elements at the specified destination and
22494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *          offset.
22594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
22694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see CaptureRequest#TONEMAP_CURVE_BLUE
22794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see CaptureRequest#TONEMAP_CURVE_RED
22894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see CaptureRequest#TONEMAP_CURVE_GREEN
22994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #LEVEL_BLACK
23094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @see #LEVEL_WHITE
23194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
23294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public void copyColorCurve(int colorChannel, float[] destination,
23394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            int offset) {
23494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkArgumentNonnegative(offset, "offset must not be negative");
23594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        checkNotNull(destination, "destination must not be null");
23694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
23794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (destination.length + offset < getPointCount(colorChannel) * POINT_SIZE) {
23894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
23994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
24094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
24194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        float[] curve = getCurve(colorChannel);
24294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        System.arraycopy(curve, /*srcPos*/0, destination, offset, curve.length);
24394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
24494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
24594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
24694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * Check if this TonemapCurve is equal to another TonemapCurve.
24794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
24894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * <p>Two matrices are equal if and only if all of their elements are
24994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@link Object#equals equal}.</p>
25094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     *
25194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * @return {@code true} if the objects were equal, {@code false} otherwise
25294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
25394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    @Override
25494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public boolean equals(Object obj) {
25594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (obj == null) {
25694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return false;
25794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
25894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (this == obj) {
25994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return true;
26094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
26194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (obj instanceof TonemapCurve) {
26294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            final TonemapCurve other = (TonemapCurve) obj;
26394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return Arrays.equals(mRed, other.mRed) &&
26494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                    Arrays.equals(mGreen, other.mGreen) &&
26594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                    Arrays.equals(mBlue, other.mBlue);
26694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
26794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return false;
26894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
26994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
27094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    /**
27194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     * {@inheritDoc}
27294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin     */
27394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    @Override
27494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    public int hashCode() {
27594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        if (mHashCalculated) {
27694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            // Avoid re-calculating hash. Data is immutable so this is both legal and faster.
27794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            return mHashCode;
27894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
27994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
28094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mHashCode = HashCodeHelpers.hashCode(mRed, mGreen, mBlue);
28194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        mHashCalculated = true;
28294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
28394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        return mHashCode;
28494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
28594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
286b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    /**
287b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * Return the TonemapCurve as a string representation.
288b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     *
289b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * <p> {@code "TonemapCurve{R:[(%f, %f), (%f, %f) ... (%f, %f)], G:[(%f, %f), (%f, %f) ...
290b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * (%f, %f)], B:[(%f, %f), (%f, %f) ... (%f, %f)]}"},
291b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * where each {@code (%f, %f)} respectively represents one point of the corresponding
292b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * tonemap curve. </p>
293b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     *
294b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     * @return string representation of {@link TonemapCurve}
295b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh     */
296b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    @Override
297b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    public String toString() {
298b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        StringBuilder sb = new StringBuilder("TonemapCurve{");
299b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append("R:");
300b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append(curveToString(CHANNEL_RED));
301b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append(", G:");
302b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append(curveToString(CHANNEL_GREEN));
303b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append(", B:");
304b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append(curveToString(CHANNEL_BLUE));
305b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append("}");
306b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        return sb.toString();
307b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    }
308b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh
309b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    private String curveToString(int colorChannel) {
310b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        checkArgumentColorChannel(colorChannel);
311b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        StringBuilder sb = new StringBuilder("[");
312b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        float[] curve = getCurve(colorChannel);
313b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        int pointCount = curve.length / POINT_SIZE;
314b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        for (int i = 0, j = 0; i < pointCount; i++, j += 2) {
315b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            sb.append("(");
316b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            sb.append(curve[j]);
317b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            sb.append(", ");
318b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            sb.append(curve[j+1]);
319b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh            sb.append("), ");
320b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        }
321b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        // trim extra ", " at the end. Guaranteed to work because pointCount >= 2
322b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.setLength(sb.length() - 2);
323b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        sb.append("]");
324b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh        return sb.toString();
325b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    }
326b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh
32794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private float[] getCurve(int colorChannel) {
32894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        switch (colorChannel) {
32994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_RED:
33094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                return mRed;
33194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_GREEN:
33294814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                return mGreen;
33394814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            case CHANNEL_BLUE:
33494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                return mBlue;
33594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin            default:
33694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin                throw new AssertionError("colorChannel out of range");
33794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin        }
33894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    }
33994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
34094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final static int OFFSET_POINT_IN = 0;
34194814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final static int OFFSET_POINT_OUT = 1;
342b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    private final static int TONEMAP_MIN_CURVE_POINTS = 2;
343b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh    private final static int MIN_CURVE_LENGTH = TONEMAP_MIN_CURVE_POINTS * POINT_SIZE;
34494814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
34594814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final float[] mRed;
34694814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final float[] mGreen;
34794814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private final float[] mBlue;
34894814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin
34994814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private int mHashCode;
35094814218d2313a989a5a8969f633e3fc33e43071Igor Murashkin    private boolean mHashCalculated = false;
351b086421b41f1fbe0dfcd14cb38eed0acbddd81d0Yin-Chia Yeh}
352