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 17 package android.hardware.camera2.params; 18 19 import static com.android.internal.util.Preconditions.*; 20 21 import android.hardware.camera2.CameraMetadata; 22 import android.hardware.camera2.utils.HashCodeHelpers; 23 import android.util.Rational; 24 25 import java.util.Arrays; 26 27 /** 28 * Immutable class for describing a 3x3 matrix of {@link Rational} values in row-major order. 29 * 30 * <p>This matrix maps a transform from one color space to another. For the particular color space 31 * source and target, see the appropriate camera metadata documentation for the key that provides 32 * this value.</p> 33 * 34 * @see CameraMetadata 35 */ 36 public final class ColorSpaceTransform { 37 38 /** The number of rows in this matrix. */ 39 private static final int ROWS = 3; 40 41 /** The number of columns in this matrix. */ 42 private static final int COLUMNS = 3; 43 44 /** The number of total Rational elements in this matrix. */ 45 private static final int COUNT = ROWS * COLUMNS; 46 47 /** Number of int elements in a rational. */ 48 private static final int RATIONAL_SIZE = 2; 49 50 /** Numerator offset inside a rational (pair). */ 51 private static final int OFFSET_NUMERATOR = 0; 52 53 /** Denominator offset inside a rational (pair). */ 54 private static final int OFFSET_DENOMINATOR = 1; 55 56 /** Number of int elements in this matrix. */ 57 private static final int COUNT_INT = ROWS * COLUMNS * RATIONAL_SIZE; 58 59 /** 60 * Create a new immutable {@link ColorSpaceTransform} instance from a {@link Rational} array. 61 * 62 * <p>The elements must be stored in a row-major order.</p> 63 * 64 * @param elements An array of {@code 9} elements 65 * 66 * @throws IllegalArgumentException 67 * if the count of {@code elements} is not {@code 9} 68 * @throws NullPointerException 69 * if {@code elements} or any sub-element is {@code null} 70 */ ColorSpaceTransform(Rational[] elements)71 public ColorSpaceTransform(Rational[] elements) { 72 73 checkNotNull(elements, "elements must not be null"); 74 if (elements.length != COUNT) { 75 throw new IllegalArgumentException("elements must be " + COUNT + " length"); 76 } 77 78 mElements = new int[COUNT_INT]; 79 80 for (int i = 0; i < elements.length; ++i) { 81 checkNotNull(elements, "element[" + i + "] must not be null"); 82 mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator(); 83 mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator(); 84 } 85 } 86 87 /** 88 * Create a new immutable {@link ColorSpaceTransform} instance from an {@code int} array. 89 * 90 * <p>The elements must be stored in a row-major order. Each rational is stored 91 * contiguously as a {@code (numerator, denominator)} pair.</p> 92 * 93 * <p>In particular:<pre>{@code 94 * int[] elements = new int[ 95 * N11, D11, N12, D12, N13, D13, 96 * N21, D21, N22, D22, N23, D23, 97 * N31, D31, N32, D32, N33, D33 98 * ]; 99 * 100 * new ColorSpaceTransform(elements)}</pre> 101 * 102 * where {@code Nij} and {@code Dij} is the numerator and denominator for row {@code i} and 103 * column {@code j}.</p> 104 * 105 * @param elements An array of {@code 18} elements 106 * 107 * @throws IllegalArgumentException 108 * if the count of {@code elements} is not {@code 18} 109 * @throws NullPointerException 110 * if {@code elements} is {@code null} 111 */ ColorSpaceTransform(int[] elements)112 public ColorSpaceTransform(int[] elements) { 113 checkNotNull(elements, "elements must not be null"); 114 if (elements.length != COUNT_INT) { 115 throw new IllegalArgumentException("elements must be " + COUNT_INT + " length"); 116 } 117 118 for (int i = 0; i < elements.length; ++i) { 119 checkNotNull(elements, "element " + i + " must not be null"); 120 } 121 122 mElements = Arrays.copyOf(elements, elements.length); 123 } 124 125 /** 126 * Get an element of this matrix by its row and column. 127 * 128 * <p>The rows must be within the range [0, 3), 129 * and the column must be within the range [0, 3).</p> 130 * 131 * @return element (non-{@code null}) 132 * 133 * @throws IllegalArgumentException if column or row was out of range 134 */ getElement(int column, int row)135 public Rational getElement(int column, int row) { 136 if (column < 0 || column >= COLUMNS) { 137 throw new IllegalArgumentException("column out of range"); 138 } else if (row < 0 || row >= ROWS) { 139 throw new IllegalArgumentException("row out of range"); 140 } 141 142 int numerator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_NUMERATOR]; 143 int denominator = mElements[(row * COLUMNS + column) * RATIONAL_SIZE + OFFSET_DENOMINATOR]; 144 145 return new Rational(numerator, denominator); 146 } 147 148 /** 149 * Copy the {@link Rational} elements in row-major order from this matrix into the destination. 150 * 151 * @param destination 152 * an array big enough to hold at least {@code 9} elements after the 153 * {@code offset} 154 * @param offset 155 * a non-negative offset into the array 156 * @throws NullPointerException 157 * If {@code destination} was {@code null} 158 * @throws ArrayIndexOutOfBoundsException 159 * If there's not enough room to write the elements at the specified destination and 160 * offset. 161 */ copyElements(Rational[] destination, int offset)162 public void copyElements(Rational[] destination, int offset) { 163 checkArgumentNonnegative(offset, "offset must not be negative"); 164 checkNotNull(destination, "destination must not be null"); 165 if (destination.length - offset < COUNT) { 166 throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); 167 } 168 169 for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) { 170 int numerator = mElements[j + OFFSET_NUMERATOR]; 171 int denominator = mElements[j + OFFSET_DENOMINATOR]; 172 173 destination[i + offset] = new Rational(numerator, denominator); 174 } 175 } 176 177 /** 178 * Copy the {@link Rational} elements in row-major order from this matrix into the destination. 179 * 180 * <p>Each element is stored as a contiguous rational packed as a 181 * {@code (numerator, denominator)} pair of ints, identical to the 182 * {@link ColorSpaceTransform#ColorSpaceTransform(int[]) constructor}.</p> 183 * 184 * @param destination 185 * an array big enough to hold at least {@code 18} elements after the 186 * {@code offset} 187 * @param offset 188 * a non-negative offset into the array 189 * @throws NullPointerException 190 * If {@code destination} was {@code null} 191 * @throws ArrayIndexOutOfBoundsException 192 * If there's not enough room to write the elements at the specified destination and 193 * offset. 194 * 195 * @see ColorSpaceTransform#ColorSpaceTransform(int[]) 196 */ copyElements(int[] destination, int offset)197 public void copyElements(int[] destination, int offset) { 198 checkArgumentNonnegative(offset, "offset must not be negative"); 199 checkNotNull(destination, "destination must not be null"); 200 if (destination.length - offset < COUNT_INT) { 201 throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); 202 } 203 204 // Manual copy faster than System#arraycopy for very small loops 205 for (int i = 0; i < COUNT_INT; ++i) { 206 destination[i + offset] = mElements[i]; 207 } 208 } 209 210 /** 211 * Check if this {@link ColorSpaceTransform} is equal to another {@link ColorSpaceTransform}. 212 * 213 * <p>Two color space transforms are equal if and only if all of their elements are 214 * {@link Object#equals equal}.</p> 215 * 216 * @return {@code true} if the objects were equal, {@code false} otherwise 217 */ 218 @Override equals(final Object obj)219 public boolean equals(final Object obj) { 220 if (obj == null) { 221 return false; 222 } 223 if (this == obj) { 224 return true; 225 } 226 if (obj instanceof ColorSpaceTransform) { 227 final ColorSpaceTransform other = (ColorSpaceTransform) obj; 228 for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) { 229 int numerator = mElements[j + OFFSET_NUMERATOR]; 230 int denominator = mElements[j + OFFSET_DENOMINATOR]; 231 int numeratorOther = other.mElements[j + OFFSET_NUMERATOR]; 232 int denominatorOther = other.mElements[j + OFFSET_DENOMINATOR]; 233 Rational r = new Rational(numerator, denominator); 234 Rational rOther = new Rational(numeratorOther, denominatorOther); 235 if (!r.equals(rOther)) { 236 return false; 237 } 238 } 239 return true; 240 } 241 return false; 242 } 243 244 /** 245 * {@inheritDoc} 246 */ 247 @Override hashCode()248 public int hashCode() { 249 return HashCodeHelpers.hashCode(mElements); 250 } 251 252 /** 253 * Return the color space transform as a string representation. 254 * 255 * <p> Example: 256 * {@code "ColorSpaceTransform([1/1, 0/1, 0/1], [0/1, 1/1, 0/1], [0/1, 0/1, 1/1])"} is an 257 * identity transform. Elements are printed in row major order. </p> 258 * 259 * @return string representation of color space transform 260 */ 261 @Override toString()262 public String toString() { 263 return String.format("ColorSpaceTransform%s", toShortString()); 264 } 265 266 /** 267 * Return the color space transform as a compact string representation. 268 * 269 * <p> Example: 270 * {@code "([1/1, 0/1, 0/1], [0/1, 1/1, 0/1], [0/1, 0/1, 1/1])"} is an identity transform. 271 * Elements are printed in row major order. </p> 272 * 273 * @return compact string representation of color space transform 274 */ toShortString()275 private String toShortString() { 276 StringBuilder sb = new StringBuilder("("); 277 for (int row = 0, i = 0; row < ROWS; row++) { 278 sb.append("["); 279 for (int col = 0; col < COLUMNS; col++, i += RATIONAL_SIZE) { 280 int numerator = mElements[i + OFFSET_NUMERATOR]; 281 int denominator = mElements[i + OFFSET_DENOMINATOR]; 282 sb.append(numerator); 283 sb.append("/"); 284 sb.append(denominator); 285 if (col < COLUMNS - 1) { 286 sb.append(", "); 287 } 288 } 289 sb.append("]"); 290 if (row < ROWS - 1) { 291 sb.append(", "); 292 } 293 } 294 sb.append(")"); 295 return sb.toString(); 296 } 297 298 private final int[] mElements; 299 } 300