| /* |
| * 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 android.hardware.camera2.CameraMetadata; |
| import android.hardware.camera2.utils.HashCodeHelpers; |
| import android.util.Rational; |
| |
| import java.util.Arrays; |
| |
| /** |
| * Immutable class for describing a 3x3 matrix of {@link Rational} values in row-major order. |
| * |
| * <p>This matrix maps a transform from one color space to another. For the particular color space |
| * source and target, see the appropriate camera metadata documentation for the key that provides |
| * this value.</p> |
| * |
| * @see CameraMetadata |
| */ |
| public final class ColorSpaceTransform { |
| |
| /** The number of rows in this matrix. */ |
| private static final int ROWS = 3; |
| |
| /** The number of columns in this matrix. */ |
| private static final int COLUMNS = 3; |
| |
| /** The number of total Rational elements in this matrix. */ |
| private static final int COUNT = ROWS * COLUMNS; |
| |
| /** Number of int elements in a rational. */ |
| private static final int RATIONAL_SIZE = 2; |
| |
| /** Numerator offset inside a rational (pair). */ |
| private static final int OFFSET_NUMERATOR = 0; |
| |
| /** Denominator offset inside a rational (pair). */ |
| private static final int OFFSET_DENOMINATOR = 1; |
| |
| /** Number of int elements in this matrix. */ |
| private static final int COUNT_INT = ROWS * COLUMNS * RATIONAL_SIZE; |
| |
| /** |
| * Create a new immutable {@link ColorSpaceTransform} instance from a {@link Rational} array. |
| * |
| * <p>The elements must be stored in a row-major order.</p> |
| * |
| * @param elements An array of {@code 9} elements |
| * |
| * @throws IllegalArgumentException |
| * if the count of {@code elements} is not {@code 9} |
| * @throws NullPointerException |
| * if {@code elements} or any sub-element is {@code null} |
| */ |
| public ColorSpaceTransform(Rational[] elements) { |
| |
| checkNotNull(elements, "elements must not be null"); |
| if (elements.length != COUNT) { |
| throw new IllegalArgumentException("elements must be " + COUNT + " length"); |
| } |
| |
| mElements = new int[COUNT_INT]; |
| |
| for (int i = 0; i < elements.length; ++i) { |
| checkNotNull(elements, "element[" + i + "] must not be null"); |
| mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator(); |
| mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator(); |
| } |
| } |
| |
| /** |
| * Create a new immutable {@link ColorSpaceTransform} instance from an {@code int} array. |
| * |
| * <p>The elements must be stored in a row-major order. Each rational is stored |
| * contiguously as a {@code (numerator, denominator)} pair.</p> |
| * |
| * <p>In particular:<pre>{@code |
| * int[] elements = new int[ |
| * N11, D11, N12, D12, N13, D13, |
| * N21, D21, N22, D22, N23, D23, |
| * N31, D31, N32, D32, N33, D33 |
| * ]; |
| * |
| * new ColorSpaceTransform(elements)}</pre> |
| * |
| * where {@code Nij} and {@code Dij} is the numerator and denominator for row {@code i} and |
| * column {@code j}.</p> |
| * |
| * @param elements An array of {@code 18} elements |
| * |
| * @throws IllegalArgumentException |
| * if the count of {@code elements} is not {@code 18} |
| * @throws NullPointerException |
| * if {@code elements} is {@code null} |
| */ |
| public ColorSpaceTransform(int[] elements) { |
| checkNotNull(elements, "elements must not be null"); |
| if (elements.length != COUNT_INT) { |
| throw new IllegalArgumentException("elements must be " + COUNT_INT + " length"); |
| } |
| |
| for (int i = 0; i < elements.length; ++i) { |
| checkNotNull(elements, "element " + i + " must not be null"); |
| } |
| |
| mElements = Arrays.copyOf(elements, elements.length); |
| } |
| |
| /** |
| * Get an element of this matrix by its row and column. |
| * |
| * <p>The rows must be within the range [0, 3), |
| * and the column must be within the range [0, 3).</p> |
| * |
| * @return element (non-{@code null}) |
| * |
| * @throws IllegalArgumentException if column or row was out of range |
| */ |
| public Rational getElement(int column, int row) { |
| if (column < 0 || column >= COLUMNS) { |
| throw new IllegalArgumentException("column out of range"); |
| } else if (row < 0 || row >= ROWS) { |
| throw new IllegalArgumentException("row out of range"); |
| } |
| |
| int numerator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_NUMERATOR]; |
| int denominator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_DENOMINATOR]; |
| |
| return new Rational(numerator, denominator); |
| } |
| |
| /** |
| * Copy the {@link Rational} elements in row-major order from this matrix into the destination. |
| * |
| * @param destination |
| * an array big enough to hold at least {@code 9} elements after the |
| * {@code offset} |
| * @param offset |
| * a non-negative offset into the array |
| * @throws NullPointerException |
| * If {@code destination} was {@code null} |
| * @throws ArrayIndexOutOfBoundsException |
| * If there's not enough room to write the elements at the specified destination and |
| * offset. |
| */ |
| public void copyElements(Rational[] destination, int offset) { |
| checkArgumentNonnegative(offset, "offset must not be negative"); |
| checkNotNull(destination, "destination must not be null"); |
| if (destination.length - offset < COUNT) { |
| throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); |
| } |
| |
| for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) { |
| int numerator = mElements[j + OFFSET_NUMERATOR]; |
| int denominator = mElements[j + OFFSET_DENOMINATOR]; |
| |
| destination[i + offset] = new Rational(numerator, denominator); |
| } |
| } |
| |
| /** |
| * Copy the {@link Rational} elements in row-major order from this matrix into the destination. |
| * |
| * <p>Each element is stored as a contiguous rational packed as a |
| * {@code (numerator, denominator)} pair of ints, identical to the |
| * {@link ColorSpaceTransform#ColorSpaceTransform(int[]) constructor}.</p> |
| * |
| * @param destination |
| * an array big enough to hold at least {@code 18} elements after the |
| * {@code offset} |
| * @param offset |
| * a non-negative offset into the array |
| * @throws NullPointerException |
| * If {@code destination} was {@code null} |
| * @throws ArrayIndexOutOfBoundsException |
| * If there's not enough room to write the elements at the specified destination and |
| * offset. |
| * |
| * @see ColorSpaceTransform#ColorSpaceTransform(int[]) |
| */ |
| public void copyElements(int[] destination, int offset) { |
| checkArgumentNonnegative(offset, "offset must not be negative"); |
| checkNotNull(destination, "destination must not be null"); |
| if (destination.length - offset < COUNT_INT) { |
| throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); |
| } |
| |
| // Manual copy faster than System#arraycopy for very small loops |
| for (int i = 0; i < COUNT_INT; ++i) { |
| destination[i + offset] = mElements[i]; |
| } |
| } |
| |
| /** |
| * Check if this {@link ColorSpaceTransform} is equal to another {@link ColorSpaceTransform}. |
| * |
| * <p>Two color space transforms are equal if and only if all of their elements are |
| * {@link Object#equals equal}.</p> |
| * |
| * @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 ColorSpaceTransform) { |
| final ColorSpaceTransform other = (ColorSpaceTransform) obj; |
| for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) { |
| int numerator = mElements[j + OFFSET_NUMERATOR]; |
| int denominator = mElements[j + OFFSET_DENOMINATOR]; |
| int numeratorOther = other.mElements[j + OFFSET_NUMERATOR]; |
| int denominatorOther = other.mElements[j + OFFSET_DENOMINATOR]; |
| Rational r = new Rational(numerator, denominator); |
| Rational rOther = new Rational(numeratorOther, denominatorOther); |
| if (!r.equals(rOther)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public int hashCode() { |
| return HashCodeHelpers.hashCode(mElements); |
| } |
| |
| /** |
| * Return the color space transform as a string representation. |
| * |
| * <p> Example: |
| * {@code "ColorSpaceTransform([1/1, 0/1, 0/1], [0/1, 1/1, 0/1], [0/1, 0/1, 1/1])"} is an |
| * identity transform. Elements are printed in row major order. </p> |
| * |
| * @return string representation of color space transform |
| */ |
| @Override |
| public String toString() { |
| return String.format("ColorSpaceTransform%s", toShortString()); |
| } |
| |
| /** |
| * Return the color space transform as a compact string representation. |
| * |
| * <p> Example: |
| * {@code "([1/1, 0/1, 0/1], [0/1, 1/1, 0/1], [0/1, 0/1, 1/1])"} is an identity transform. |
| * Elements are printed in row major order. </p> |
| * |
| * @return compact string representation of color space transform |
| */ |
| private String toShortString() { |
| StringBuilder sb = new StringBuilder("("); |
| for (int row = 0, i = 0; row < ROWS; row++) { |
| sb.append("["); |
| for (int col = 0; col < COLUMNS; col++, i += RATIONAL_SIZE) { |
| int numerator = mElements[i + OFFSET_NUMERATOR]; |
| int denominator = mElements[i + OFFSET_DENOMINATOR]; |
| sb.append(numerator); |
| sb.append("/"); |
| sb.append(denominator); |
| if (col < COLUMNS - 1) { |
| sb.append(", "); |
| } |
| } |
| sb.append("]"); |
| if (row < ROWS - 1) { |
| sb.append(", "); |
| } |
| } |
| sb.append(")"); |
| return sb.toString(); |
| } |
| |
| private final int[] mElements; |
| } |