1 /* 2 * Copyright 2023 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 androidx.camera.core; 18 19 import androidx.annotation.IntDef; 20 import androidx.annotation.RestrictTo; 21 22 import org.jspecify.annotations.NonNull; 23 24 import java.lang.annotation.Retention; 25 import java.lang.annotation.RetentionPolicy; 26 27 /** 28 * A representation of the dynamic range of an image. 29 * 30 * <p>The dynamic range specifies an encoding for how pixels will be displayed on screen along 31 * with the number of bits used to encode each pixel. In general, the encoding represents a set 32 * of operations applied to each pixel to expand the range of light and dark pixels on a 33 * specific screen. The bit depth represents the discrete number of steps those pixels can assume 34 * between the lightest and darkest pixels. 35 * 36 * <p>A category of dynamic ranges called high-dynamic range (HDR) are able to encode brighter 37 * highlights, darker shadows, and richer color. This class contains constants for specific HDR 38 * dynamic ranges, such as {@link DynamicRange#HLG_10_BIT}, but also unspecified HDR dynamic 39 * ranges, such as {@link DynamicRange#HDR_UNSPECIFIED_10_BIT}. When used with a camera API, such 40 * as {@link androidx.camera.video.VideoCapture.Builder#setDynamicRange(DynamicRange)}, these 41 * unspecified dynamic ranges will use device defaults as the HDR encoding. 42 * 43 * <p>The legacy behavior of most devices is to capture in standard dynamic range (SDR), which is 44 * represented by {@link DynamicRange#SDR}. This will be the default dynamic range encoding for 45 * most APIs taking dynamic range unless otherwise specified. 46 * 47 * @see androidx.camera.video.VideoCapture.Builder#setDynamicRange(DynamicRange) 48 */ 49 public final class DynamicRange { 50 /** 51 * An unspecified dynamic range encoding which allows the device to determine the underlying 52 * dynamic range encoding. 53 */ 54 public static final int ENCODING_UNSPECIFIED = 0; 55 56 /** Standard Dynamic Range (SDR) encoding. */ 57 public static final int ENCODING_SDR = 1; 58 59 //------------------------------------------------------------------------------// 60 // HDR Encodings // 61 //------------------------------------------------------------------------------// 62 /** 63 * An unspecified dynamic range encoding which allows the device to determine the 64 * underlying dynamic range encoding, limited to High Dynamic Range (HDR) encodings. 65 */ 66 public static final int ENCODING_HDR_UNSPECIFIED = 2; 67 /** Hybrid Log Gamma (HLG) dynamic range encoding. */ 68 public static final int ENCODING_HLG = ENCODING_HDR_UNSPECIFIED + 1; 69 /** HDR10 dynamic range encoding. */ 70 public static final int ENCODING_HDR10 = ENCODING_HDR_UNSPECIFIED + 2; 71 /** HDR10+ dynamic range encoding. */ 72 public static final int ENCODING_HDR10_PLUS = ENCODING_HDR_UNSPECIFIED + 3; 73 /** Dolby Vision dynamic range encoding. */ 74 public static final int ENCODING_DOLBY_VISION = ENCODING_HDR_UNSPECIFIED + 4; 75 //------------------------------------------------------------------------------// 76 77 /** Bit depth is unspecified and may be determined automatically by the device. */ 78 public static final int BIT_DEPTH_UNSPECIFIED = 0; 79 /** Eight-bit bit depth. */ 80 public static final int BIT_DEPTH_8_BIT = 8; 81 /** Ten-bit bit depth. */ 82 public static final int BIT_DEPTH_10_BIT = 10; 83 84 @RestrictTo(RestrictTo.Scope.LIBRARY) 85 @IntDef({ENCODING_UNSPECIFIED, ENCODING_SDR, ENCODING_HDR_UNSPECIFIED, ENCODING_HLG, 86 ENCODING_HDR10, 87 ENCODING_HDR10_PLUS, ENCODING_DOLBY_VISION}) 88 @Retention(RetentionPolicy.SOURCE) 89 public @interface DynamicRangeEncoding { 90 } 91 92 @RestrictTo(RestrictTo.Scope.LIBRARY) 93 @IntDef({BIT_DEPTH_UNSPECIFIED, BIT_DEPTH_8_BIT, BIT_DEPTH_10_BIT}) 94 @Retention(RetentionPolicy.SOURCE) 95 public @interface BitDepth { 96 } 97 98 //------------------------------------------------------------------------------// 99 // Pre-defined DynamicRanges // 100 //------------------------------------------------------------------------------// 101 /** 102 * A dynamic range with unspecified encoding and bit depth. 103 * 104 * <p>The dynamic range is unspecified and may defer to device defaults when used to select a 105 * dynamic range. 106 * 107 * <p>This dynamic range is composed of: 108 * <pre> 109 * Encoding: ENCODING_UNSPECIFIED 110 * Bit Depth: BIT_DEPTH_UNSPECIFIED 111 * </pre> 112 */ 113 public static final @NonNull DynamicRange UNSPECIFIED = new DynamicRange(ENCODING_UNSPECIFIED, 114 BIT_DEPTH_UNSPECIFIED); 115 116 /** 117 * A dynamic range representing 8-bit standard dynamic range (SDR). 118 * 119 * <p>This dynamic range is composed of: 120 * <pre> 121 * Encoding: ENCODING_SDR 122 * Bit Depth: BIT_DEPTH_8_BIT 123 * </pre> 124 */ 125 public static final @NonNull DynamicRange SDR = new DynamicRange(ENCODING_SDR, BIT_DEPTH_8_BIT); 126 127 /** 128 * A dynamic range representing 10-bit high dynamic range (HDR) with unspecified encoding. 129 * 130 * <p>The HDR encoding is unspecified, and may defer to device defaults 131 * when used to select a dynamic range. In this case, the dynamic range will be limited to 132 * 10-bit high dynamic ranges. 133 * 134 * <p>This dynamic range is composed of: 135 * <pre> 136 * Encoding: ENCODING_HDR_UNSPECIFIED 137 * Bit Depth: BIT_DEPTH_10_BIT 138 * </pre> 139 */ 140 public static final @NonNull DynamicRange HDR_UNSPECIFIED_10_BIT = 141 new DynamicRange(ENCODING_HDR_UNSPECIFIED, BIT_DEPTH_10_BIT); 142 143 /** 144 * A 10-bit high-dynamic range with HLG encoding. 145 * 146 * <p>This dynamic range is composed of: 147 * <pre> 148 * Encoding: ENCODING_HLG 149 * Bit Depth: BIT_DEPTH_10_BIT 150 * </pre> 151 */ 152 public static final @NonNull DynamicRange HLG_10_BIT = 153 new DynamicRange(ENCODING_HLG, BIT_DEPTH_10_BIT); 154 155 /** 156 * A 10-bit high-dynamic range with HDR10 encoding. 157 * 158 * <p>This dynamic range is composed of: 159 * <pre> 160 * Encoding: ENCODING_HDR10 161 * Bit Depth: BIT_DEPTH_10_BIT 162 * </pre> 163 */ 164 public static final @NonNull DynamicRange HDR10_10_BIT = new DynamicRange(ENCODING_HDR10, 165 BIT_DEPTH_10_BIT); 166 167 /** 168 * A 10-bit high-dynamic range with HDR10+ encoding. 169 * 170 * <p>This dynamic range is composed of: 171 * <pre> 172 * Encoding: ENCODING_HDR10_PLUS 173 * Bit Depth: BIT_DEPTH_10_BIT 174 * </pre> 175 */ 176 public static final @NonNull DynamicRange HDR10_PLUS_10_BIT = 177 new DynamicRange(ENCODING_HDR10_PLUS, BIT_DEPTH_10_BIT); 178 179 /** 180 * A 10-bit high-dynamic range with Dolby Vision encoding. 181 * 182 * <p>This dynamic range is composed of: 183 * <pre> 184 * Encoding: ENCODING_DOLBY_VISION 185 * Bit Depth: BIT_DEPTH_10_BIT 186 * </pre> 187 */ 188 public static final @NonNull DynamicRange DOLBY_VISION_10_BIT = 189 new DynamicRange(ENCODING_DOLBY_VISION, BIT_DEPTH_10_BIT); 190 191 /** 192 * An 8-bit high-dynamic range with Dolby Vision encoding. 193 * 194 * <p>This dynamic range is composed of: 195 * <pre> 196 * Encoding: ENCODING_DOLBY_VISION 197 * Bit Depth: BIT_DEPTH_8_BIT 198 * </pre> 199 */ 200 public static final @NonNull DynamicRange DOLBY_VISION_8_BIT = 201 new DynamicRange(ENCODING_DOLBY_VISION, BIT_DEPTH_8_BIT); 202 //------------------------------------------------------------------------------// 203 204 private final @DynamicRangeEncoding int mEncoding; 205 private final @BitDepth int mBitDepth; 206 207 /** 208 * Creates a dynamic range representation from a encoding and bit depth. 209 * 210 * <p>This constructor is left public for testing purposes. It does not do any verification that 211 * the provided arguments are a valid combination of encoding and bit depth. 212 * 213 * @param encoding The dynamic range encoding. 214 * @param bitDepth The bit depth. 215 */ DynamicRange( @ynamicRangeEncoding int encoding, @BitDepth int bitDepth)216 public DynamicRange( 217 @DynamicRangeEncoding int encoding, 218 @BitDepth int bitDepth) { 219 mEncoding = encoding; 220 mBitDepth = bitDepth; 221 } 222 223 /** 224 * Returns the dynamic range encoding. 225 * 226 * @return The dynamic range encoding. Possible values are {@link #ENCODING_SDR}, 227 * {@link #ENCODING_HLG}, {@link #ENCODING_HDR10}, {@link #ENCODING_HDR10_PLUS}, or 228 * {@link #ENCODING_DOLBY_VISION}. 229 */ 230 @DynamicRangeEncoding getEncoding()231 public int getEncoding() { 232 return mEncoding; 233 } 234 235 /** 236 * Returns the bit depth used by this dynamic range configuration. 237 * 238 * <p>Common values are {@link #BIT_DEPTH_8_BIT}, such as for {@link #ENCODING_SDR} or 239 * {@link #BIT_DEPTH_10_BIT}, such as for {@link #ENCODING_HDR10}. 240 * 241 * @return The bit depth. Possible values are {@link #BIT_DEPTH_8_BIT}, 242 * {@link #BIT_DEPTH_10_BIT}, or {@link #BIT_DEPTH_UNSPECIFIED}. 243 */ 244 @BitDepth getBitDepth()245 public int getBitDepth() { 246 return mBitDepth; 247 } 248 249 /** 250 * Returns {@code true} if both the encoding and bit depth are not unspecified types. 251 */ 252 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) isFullySpecified()253 public boolean isFullySpecified() { 254 return getEncoding() != ENCODING_UNSPECIFIED 255 && getEncoding() != ENCODING_HDR_UNSPECIFIED 256 && getBitDepth() != BIT_DEPTH_UNSPECIFIED; 257 } 258 259 /** 260 * Returns true if this dynamic range is fully specified, the encoding is one of the HDR 261 * encodings and bit depth is 10-bit. 262 * 263 * @see #isFullySpecified() 264 */ 265 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) is10BitHdr()266 public boolean is10BitHdr() { 267 return isFullySpecified() && getEncoding() != ENCODING_SDR 268 && getBitDepth() == BIT_DEPTH_10_BIT; 269 } 270 271 @Override toString()272 public @NonNull String toString() { 273 return "DynamicRange@" + Integer.toHexString(System.identityHashCode(this)) + "{" 274 + "encoding=" + getEncodingLabel(mEncoding) + ", " 275 + "bitDepth=" + mBitDepth 276 + "}"; 277 } 278 279 @Override equals(Object o)280 public boolean equals(Object o) { 281 if (o == this) { 282 return true; 283 } 284 if (o instanceof DynamicRange) { 285 DynamicRange that = (DynamicRange) o; 286 return this.mEncoding == that.getEncoding() 287 && this.mBitDepth == that.getBitDepth(); 288 } 289 return false; 290 } 291 292 @Override hashCode()293 public int hashCode() { 294 int hashCode = 1; 295 hashCode *= 1000003; 296 hashCode ^= mEncoding; 297 hashCode *= 1000003; 298 hashCode ^= mBitDepth; 299 return hashCode; 300 } 301 getEncodingLabel(@ynamicRangeEncoding int encoding)302 private static @NonNull String getEncodingLabel(@DynamicRangeEncoding int encoding) { 303 switch (encoding) { 304 case ENCODING_UNSPECIFIED: return "UNSPECIFIED"; 305 case ENCODING_SDR: return "SDR"; 306 case ENCODING_HDR_UNSPECIFIED: return "HDR_UNSPECIFIED"; 307 case ENCODING_HLG: return "HLG"; 308 case ENCODING_HDR10: return "HDR10"; 309 case ENCODING_HDR10_PLUS: return "HDR10_PLUS"; 310 case ENCODING_DOLBY_VISION: return "DOLBY_VISION"; 311 } 312 313 return "<Unknown>"; 314 } 315 } 316