1 /*
2  * Copyright (C) 2022 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.TestApi;
21 import android.graphics.ColorSpace;
22 import android.graphics.ImageFormat;
23 import android.hardware.camera2.CameraMetadata;
24 import android.util.ArrayMap;
25 import android.util.ArraySet;
26 
27 import java.util.Map;
28 import java.util.Set;
29 
30 /**
31  * Immutable class with information about supported color space profiles.
32  *
33  * <p>An instance of this class can be queried by retrieving the value of
34  * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}.
35  * </p>
36  *
37  * <p>All camera devices supporting the
38  * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_COLOR_SPACE_PROFILES}
39  * capability must advertise the supported color space profiles in
40  * {@link #getSupportedColorSpaces}</p>
41  *
42  * @see SessionConfiguration#setColorSpace
43  */
44 public final class ColorSpaceProfiles {
45     /*
46      * @hide
47      */
48     public static final int UNSPECIFIED =
49             CameraMetadata.REQUEST_AVAILABLE_COLOR_SPACE_PROFILES_MAP_UNSPECIFIED;
50 
51     private final Map<ColorSpace.Named, Map<Integer, Set<Long>>> mProfileMap = new ArrayMap<>();
52 
53     /**
54      * Create a new immutable ColorSpaceProfiles instance.
55      *
56      * <p>This constructor takes over the array; do not write to the array afterwards.</p>
57      *
58      * <p>Do note that the constructor is available for testing purposes only!
59      * Camera clients must always retrieve the value of
60      * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_COLOR_SPACE_PROFILES}.
61      * for a given camera id in order to retrieve the device capabilities.</p>
62      *
63      * @param elements
64      *          An array of elements describing the map. It contains three elements per entry which
65      *          describe the supported color space profile value in the first element, a compatible
66      *          image format in the second, and in the third element a bitmap of compatible dynamic
67      *          range profiles (see {@link DynamicRangeProfiles#STANDARD} and others for the
68      *          individual bitmap components).
69      *
70      * @throws IllegalArgumentException
71      *            if the {@code elements} array length is invalid, not divisible by 3 or contains
72      *            invalid element values
73      * @throws NullPointerException
74      *            if {@code elements} is {@code null}
75      *
76      */
ColorSpaceProfiles(@onNull final long[] elements)77     public ColorSpaceProfiles(@NonNull final long[] elements) {
78         if ((elements.length % 3) != 0) {
79             throw new IllegalArgumentException("Color space profile map length "
80                     + elements.length + " is not divisible by 3!");
81         }
82 
83         for (int i = 0; i < elements.length; i += 3) {
84             int colorSpace = (int) elements[i];
85             checkProfileValue(colorSpace);
86             ColorSpace.Named namedColorSpace = ColorSpace.Named.values()[colorSpace];
87             int imageFormat = (int) elements[i + 1];
88             long dynamicRangeProfileBitmap = elements[i + 2];
89 
90             if (!mProfileMap.containsKey(namedColorSpace)) {
91                 ArrayMap<Integer, Set<Long>> imageFormatMap = new ArrayMap<>();
92                 mProfileMap.put(namedColorSpace, imageFormatMap);
93             }
94 
95             if (!mProfileMap.get(namedColorSpace).containsKey(imageFormat)) {
96                 ArraySet<Long> dynamicRangeProfiles = new ArraySet<>();
97                 mProfileMap.get(namedColorSpace).put(imageFormat, dynamicRangeProfiles);
98             }
99 
100             if (dynamicRangeProfileBitmap != 0) {
101                 for (long dynamicRangeProfile = DynamicRangeProfiles.STANDARD;
102                         dynamicRangeProfile < DynamicRangeProfiles.PUBLIC_MAX;
103                         dynamicRangeProfile <<= 1) {
104                     if ((dynamicRangeProfileBitmap & dynamicRangeProfile) != 0) {
105                         mProfileMap.get(namedColorSpace).get(imageFormat).add(dynamicRangeProfile);
106                     }
107                 }
108             }
109         }
110     }
111 
112     /**
113      * @hide
114      */
checkProfileValue(int colorSpace)115     public static void checkProfileValue(int colorSpace) {
116         boolean found = false;
117         for (ColorSpace.Named value : ColorSpace.Named.values()) {
118             if (colorSpace == value.ordinal()) {
119                 found = true;
120                 break;
121             }
122         }
123 
124         if (!found) {
125             throw new IllegalArgumentException("Unknown ColorSpace " + colorSpace);
126         }
127     }
128 
129     /**
130      * @hide
131      */
132     @TestApi
getProfileMap()133     public @NonNull Map<ColorSpace.Named, Map<Integer, Set<Long>>> getProfileMap() {
134         return mProfileMap;
135     }
136 
137     /**
138      * Return a list of color spaces that are compatible with an ImageFormat. If ImageFormat.UNKNOWN
139      * is provided, this function will return a set of all unique color spaces supported by the
140      * device, regardless of image format.
141      *
142      * Color spaces which are compatible with ImageFormat.PRIVATE are able to be used with
143      * SurfaceView, SurfaceTexture, MediaCodec and MediaRecorder.
144      *
145      * @return set of color spaces
146      * @see SessionConfiguration#setColorSpace
147      * @see ColorSpace.Named
148      */
getSupportedColorSpaces( @mageFormat.Format int imageFormat)149     public @NonNull Set<ColorSpace.Named> getSupportedColorSpaces(
150             @ImageFormat.Format int imageFormat) {
151         ArraySet<ColorSpace.Named> supportedColorSpaceProfiles = new ArraySet<>();
152         for (ColorSpace.Named colorSpace : mProfileMap.keySet()) {
153             if (imageFormat == ImageFormat.UNKNOWN) {
154                 supportedColorSpaceProfiles.add(colorSpace);
155             } else {
156                 Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
157                 if (imageFormatMap.containsKey(imageFormat)) {
158                     supportedColorSpaceProfiles.add(colorSpace);
159                 }
160             }
161         }
162         return supportedColorSpaceProfiles;
163     }
164 
165     /**
166      * Return a list of image formats that are compatible with a color space.
167      *
168      * Color spaces which are compatible with ImageFormat.PRIVATE are able to be used with
169      * SurfaceView, SurfaceTexture, MediaCodec and MediaRecorder.
170      *
171      * @return set of image formats
172      * @see SessionConfiguration#setColorSpace
173      * @see ColorSpace.Named
174      */
getSupportedImageFormatsForColorSpace( @onNull ColorSpace.Named colorSpace)175     public @NonNull Set<Integer> getSupportedImageFormatsForColorSpace(
176             @NonNull ColorSpace.Named colorSpace) {
177         Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
178         if (imageFormatMap == null) {
179             return new ArraySet<Integer>();
180         }
181 
182         return imageFormatMap.keySet();
183     }
184 
185     /**
186      * Return a list of dynamic range profiles that are compatible with a color space and
187      * ImageFormat. If ImageFormat.UNKNOWN is provided, this function will return a set of
188      * all unique dynamic range profiles supported by the device given a color space,
189      * regardless of image format.
190      *
191      * @return set of dynamic range profiles.
192      * @see OutputConfiguration#setDynamicRangeProfile
193      * @see SessionConfiguration#setColorSpace
194      * @see ColorSpace.Named
195      * @see DynamicRangeProfiles
196      */
getSupportedDynamicRangeProfiles(@onNull ColorSpace.Named colorSpace, @ImageFormat.Format int imageFormat)197     public @NonNull Set<Long> getSupportedDynamicRangeProfiles(@NonNull ColorSpace.Named colorSpace,
198             @ImageFormat.Format int imageFormat) {
199         Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
200         if (imageFormatMap == null) {
201             return new ArraySet<Long>();
202         }
203 
204         Set<Long> dynamicRangeProfiles = null;
205         if (imageFormat == ImageFormat.UNKNOWN) {
206             dynamicRangeProfiles = new ArraySet<>();
207             for (int supportedImageFormat : imageFormatMap.keySet()) {
208                 Set<Long> supportedDynamicRangeProfiles = imageFormatMap.get(
209                         supportedImageFormat);
210                 for (Long supportedDynamicRangeProfile : supportedDynamicRangeProfiles) {
211                     dynamicRangeProfiles.add(supportedDynamicRangeProfile);
212                 }
213             }
214         } else {
215             dynamicRangeProfiles = imageFormatMap.get(imageFormat);
216             if (dynamicRangeProfiles == null) {
217                 return new ArraySet<>();
218             }
219         }
220 
221         return dynamicRangeProfiles;
222     }
223 
224     /**
225      * Return a list of color spaces that are compatible with an ImageFormat and a dynamic range
226      * profile. If ImageFormat.UNKNOWN is provided, this function will return a set of all unique
227      * color spaces compatible with the given dynamic range profile, regardless of image format.
228      *
229      * @return set of color spaces
230      * @see SessionConfiguration#setColorSpace
231      * @see OutputConfiguration#setDynamicRangeProfile
232      * @see ColorSpace.Named
233      * @see DynamicRangeProfiles
234      */
getSupportedColorSpacesForDynamicRange( @mageFormat.Format int imageFormat, @DynamicRangeProfiles.Profile long dynamicRangeProfile)235     public @NonNull Set<ColorSpace.Named> getSupportedColorSpacesForDynamicRange(
236             @ImageFormat.Format int imageFormat,
237             @DynamicRangeProfiles.Profile long dynamicRangeProfile) {
238         ArraySet<ColorSpace.Named> supportedColorSpaceProfiles = new ArraySet<>();
239         for (ColorSpace.Named colorSpace : mProfileMap.keySet()) {
240             Map<Integer, Set<Long>> imageFormatMap = mProfileMap.get(colorSpace);
241             if (imageFormat == ImageFormat.UNKNOWN) {
242                 for (int supportedImageFormat : imageFormatMap.keySet()) {
243                     Set<Long> dynamicRangeProfiles = imageFormatMap.get(supportedImageFormat);
244                     if (dynamicRangeProfiles.contains(dynamicRangeProfile)) {
245                         supportedColorSpaceProfiles.add(colorSpace);
246                     }
247                 }
248             } else if (imageFormatMap.containsKey(imageFormat)) {
249                 Set<Long> dynamicRangeProfiles = imageFormatMap.get(imageFormat);
250                 if (dynamicRangeProfiles.contains(dynamicRangeProfile)) {
251                     supportedColorSpaceProfiles.add(colorSpace);
252                 }
253             }
254         }
255         return supportedColorSpaceProfiles;
256     }
257 }
258