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 android.annotation.NonNull; 20 import android.annotation.Nullable; 21 22 import java.util.Arrays; 23 import java.util.Objects; 24 25 /** 26 * Immutable class to store a 4-element vector of integers corresponding to a 2x2 pattern 27 * of color channel offsets used for the black level offsets of each color channel. 28 * 29 * For a camera device with 30 * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME 31 * MONOCHROME} capability, all 4 elements of the pattern will have the same value. 32 */ 33 public final class BlackLevelPattern { 34 35 /** 36 * The number of offsets in this vector. 37 */ 38 public static final int COUNT = 4; 39 40 /** 41 * Create a new {@link BlackLevelPattern} from a given offset array. 42 * 43 * <p>The given offset array must contain offsets for each color channel in 44 * a 2x2 pattern corresponding to the color filter arrangement. Offsets are 45 * given in row-column scan order.</p> 46 * 47 * <p>This constructor is public to allow for easier application testing by 48 * creating custom object instances. It's not necessary to construct these 49 * objects during normal use of the camera API.</p> 50 * 51 * @param offsets an array containing a 2x2 pattern of offsets. 52 * 53 * @throws IllegalArgumentException if the given array has an incorrect length. 54 * @throws NullPointerException if the given array is null. 55 */ BlackLevelPattern(@onNull int[] offsets)56 public BlackLevelPattern(@NonNull int[] offsets) { 57 if (offsets == null) { 58 throw new NullPointerException("Null offsets array passed to constructor"); 59 } 60 if (offsets.length < COUNT) { 61 throw new IllegalArgumentException("Invalid offsets array length"); 62 } 63 mCfaOffsets = Arrays.copyOf(offsets, COUNT); 64 } 65 66 /** 67 * Return the color channel offset for a given index into the array of raw pixel values. 68 * 69 * @param column the column index in the the raw pixel array. 70 * @param row the row index in the raw pixel array. 71 * @return a color channel offset. 72 * 73 * @throws IllegalArgumentException if a column or row given is negative. 74 */ getOffsetForIndex(int column, int row)75 public int getOffsetForIndex(int column, int row) { 76 if (row < 0 || column < 0) { 77 throw new IllegalArgumentException("column, row arguments must be positive"); 78 } 79 return mCfaOffsets[((row & 1) << 1) | (column & 1)]; 80 } 81 82 /** 83 * Copy the ColorChannel offsets into the destination vector. 84 * 85 * <p>Offsets are given in row-column scan order for a given 2x2 color pattern.</p> 86 * 87 * @param destination an array big enough to hold at least {@value #COUNT} elements after the 88 * {@code offset} 89 * @param offset a non-negative offset into the array 90 * 91 * @throws IllegalArgumentException if the offset is invalid. 92 * @throws ArrayIndexOutOfBoundsException if the destination vector is too small. 93 * @throws NullPointerException if the destination is null. 94 */ copyTo(int[] destination, int offset)95 public void copyTo(int[] destination, int offset) { 96 Objects.requireNonNull(destination, "destination must not be null"); 97 if (offset < 0) { 98 throw new IllegalArgumentException("Null offset passed to copyTo"); 99 } 100 if (destination.length - offset < COUNT) { 101 throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); 102 } 103 for (int i = 0; i < COUNT; ++i) { 104 destination[offset + i] = mCfaOffsets[i]; 105 } 106 } 107 108 /** 109 * Check if this {@link BlackLevelPattern} is equal to another {@link BlackLevelPattern}. 110 * 111 * <p>Two vectors are only equal if and only if each of the respective elements is equal.</p> 112 * 113 * @return {@code true} if the objects were equal, {@code false} otherwise 114 */ 115 @Override equals(@ullable Object obj)116 public boolean equals(@Nullable Object obj) { 117 if (obj == null) { 118 return false; 119 } else if (this == obj) { 120 return true; 121 } else if (obj instanceof BlackLevelPattern) { 122 final BlackLevelPattern other = (BlackLevelPattern) obj; 123 return Arrays.equals(other.mCfaOffsets, mCfaOffsets); 124 } 125 return false; 126 } 127 128 /** 129 * {@inheritDoc} 130 */ 131 @Override hashCode()132 public int hashCode() { 133 return Arrays.hashCode(mCfaOffsets); 134 } 135 136 /** 137 * Return this {@link BlackLevelPattern} as a string representation. 138 * 139 * <p> {@code "BlackLevelPattern([%d, %d], [%d, %d])"}, where each {@code %d} represents one 140 * black level offset of a color channel. The values are in the same order as channels listed 141 * for the CFA layout key (see 142 * {@link android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT}). 143 * </p> 144 * 145 * <p>A {@link 146 * android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME 147 * MONOCHROME} camera only has one channel. As a result, the returned string will contain 4 148 * identical values. 149 * </p> 150 * 151 * @return string representation of {@link BlackLevelPattern} 152 * 153 * @see android.hardware.camera2.CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT 154 */ 155 @Override toString()156 public String toString() { 157 return String.format("BlackLevelPattern([%d, %d], [%d, %d])", mCfaOffsets[0], 158 mCfaOffsets[1], mCfaOffsets[2], mCfaOffsets[3]); 159 } 160 161 private final int[] mCfaOffsets; 162 } 163