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