1 /*
2  * Copyright 2019 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.impl;
18 
19 import static java.util.Objects.requireNonNull;
20 
21 import android.graphics.ImageFormat;
22 import android.util.Pair;
23 import android.util.Size;
24 import android.view.Surface;
25 
26 import androidx.annotation.IntDef;
27 import androidx.camera.core.AspectRatio;
28 import androidx.camera.core.MirrorMode;
29 import androidx.camera.core.resolutionselector.ResolutionSelector;
30 
31 import org.jspecify.annotations.NonNull;
32 import org.jspecify.annotations.Nullable;
33 
34 import java.lang.annotation.Retention;
35 import java.lang.annotation.RetentionPolicy;
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * Configuration containing options for configuring the output image data of a pipeline.
41  */
42 public interface ImageOutputConfig extends ReadableConfig {
43     /**
44      * Invalid integer rotation.
45      */
46     int INVALID_ROTATION = -1;
47 
48     /**
49      * Rotation not specified.
50      */
51     int ROTATION_NOT_SPECIFIED = -1;
52 
53     // Option Declarations:
54     // *********************************************************************************************
55 
56     /**
57      * Option: camerax.core.imageOutput.targetAspectRatio
58      */
59     Option<Integer> OPTION_TARGET_ASPECT_RATIO =
60             Option.create("camerax.core.imageOutput.targetAspectRatio", AspectRatio.class);
61 
62     /**
63      * Option: camerax.core.imageOutput.targetRotation
64      */
65     Option<Integer> OPTION_TARGET_ROTATION =
66             Option.create("camerax.core.imageOutput.targetRotation", int.class);
67 
68     /**
69      * Option: camerax.core.imageOutput.appTargetRotation
70      */
71     Option<Integer> OPTION_APP_TARGET_ROTATION =
72             Option.create("camerax.core.imageOutput.appTargetRotation", int.class);
73 
74     /**
75      * Option: camerax.core.imageOutput.mirrorMode
76      */
77     Option<Integer> OPTION_MIRROR_MODE =
78             Option.create("camerax.core.imageOutput.mirrorMode", int.class);
79 
80     /**
81      * Option: camerax.core.imageOutput.targetResolution
82      */
83     Option<Size> OPTION_TARGET_RESOLUTION =
84             Option.create("camerax.core.imageOutput.targetResolution", Size.class);
85     /**
86      * Option: camerax.core.imageOutput.defaultResolution
87      */
88     Option<Size> OPTION_DEFAULT_RESOLUTION =
89             Option.create("camerax.core.imageOutput.defaultResolution", Size.class);
90     /**
91      * Option: camerax.core.imageOutput.maxResolution
92      */
93     Option<Size> OPTION_MAX_RESOLUTION =
94             Option.create("camerax.core.imageOutput.maxResolution", Size.class);
95     /**
96      * Option: camerax.core.imageOutput.supportedResolutions
97      */
98     Option<List<Pair<Integer, Size[]>>> OPTION_SUPPORTED_RESOLUTIONS =
99             Option.create("camerax.core.imageOutput.supportedResolutions", List.class);
100 
101     /**
102      * Option: camerax.core.imageOutput.resolutionSelector
103      */
104     Option<ResolutionSelector> OPTION_RESOLUTION_SELECTOR =
105             Option.create("camerax.core.imageOutput.resolutionSelector", ResolutionSelector.class);
106 
107     /**
108      * Option: camerax.core.imageOutput.customOrderedResolutions
109      */
110     Option<List<Size>> OPTION_CUSTOM_ORDERED_RESOLUTIONS =
111             Option.create("camerax.core.imageOutput.customOrderedResolutions", List.class);
112 
113     // *********************************************************************************************
114 
115     /**
116      * Verifies whether the aspect ratio of the target intending to use images from this
117      * configuration is set.
118      *
119      * @return true is the value exists in this configuration, false otherwise.
120      */
hasTargetAspectRatio()121     default boolean hasTargetAspectRatio() {
122         return containsOption(OPTION_TARGET_ASPECT_RATIO);
123     }
124 
125     /**
126      * Retrieves the aspect ratio of the target intending to use images from this configuration.
127      *
128      * @return The stored value, if it exists in this configuration.
129      * @throws IllegalArgumentException if the option does not exist in this configuration.
130      */
131     @AspectRatio.Ratio
getTargetAspectRatio()132     default int getTargetAspectRatio() {
133         return retrieveOption(OPTION_TARGET_ASPECT_RATIO);
134     }
135 
136     /**
137      * Retrieves the rotation of the target intending to use images from this configuration.
138      *
139      * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
140      * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}. Rotation values are relative to
141      * the device's "natural" rotation, {@link Surface#ROTATION_0}.
142      *
143      * @param valueIfMissing The value to return if this configuration option has not been set.
144      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
145      * configuration.
146      */
147     @RotationValue
getTargetRotation(int valueIfMissing)148     default int getTargetRotation(int valueIfMissing) {
149         return retrieveOption(OPTION_TARGET_ROTATION, valueIfMissing);
150     }
151 
152     /**
153      * Retrieves the target rotation set by app explicitly.
154      *
155      * @param valueIfMissing The value to return if this configuration option has not been set.
156      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
157      * configuration.
158      */
159     @RotationValue
getAppTargetRotation(int valueIfMissing)160     default int getAppTargetRotation(int valueIfMissing) {
161         return retrieveOption(OPTION_APP_TARGET_ROTATION, valueIfMissing);
162     }
163 
164     /**
165      * Retrieves the rotation of the target intending to use images from this configuration.
166      *
167      * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link Surface#ROTATION_90},
168      * {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}. Rotation values are relative to
169      * the device's "natural" rotation, {@link Surface#ROTATION_0}.
170      *
171      * @return The stored value, if it exists in this configuration.
172      * @throws IllegalArgumentException if the option does not exist in this configuration.
173      */
174     @RotationValue
getTargetRotation()175     default int getTargetRotation() {
176         return retrieveOption(OPTION_TARGET_ROTATION);
177     }
178 
179     /**
180      * Retrieves the resolution of the target intending to use from this configuration.
181      *
182      * @param valueIfMissing The value to return if this configuration option has not been set.
183      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
184      * configuration.
185      */
getTargetResolution(@ullable Size valueIfMissing)186     default @Nullable Size getTargetResolution(@Nullable Size valueIfMissing) {
187         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION, valueIfMissing);
188     }
189 
190     /**
191      * Retrieves the mirror mode of the target intending to use from this configuration.
192      *
193      * @param valueIfMissing The value to return if this configuration option has not been set.
194      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in
195      * this configuration.
196      */
197     @MirrorMode.Mirror
getMirrorMode(int valueIfMissing)198     default int getMirrorMode(int valueIfMissing) {
199         return retrieveOption(ImageOutputConfig.OPTION_MIRROR_MODE, valueIfMissing);
200     }
201 
202     /**
203      * Retrieves the resolution of the target intending to use from this configuration.
204      *
205      * @return The stored value, if it exists in this configuration.
206      * @throws IllegalArgumentException if the option does not exist in this configuration.
207      */
getTargetResolution()208     default @NonNull Size getTargetResolution() {
209         return retrieveOption(ImageOutputConfig.OPTION_TARGET_RESOLUTION);
210     }
211 
212     /**
213      * Retrieves the default resolution of the target intending to use from this configuration.
214      *
215      * @param valueIfMissing The value to return if this configuration option has not been set.
216      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
217      * configuration.
218      */
getDefaultResolution(@ullable Size valueIfMissing)219     default @Nullable Size getDefaultResolution(@Nullable Size valueIfMissing) {
220         return retrieveOption(OPTION_DEFAULT_RESOLUTION, valueIfMissing);
221     }
222 
223     /**
224      * Retrieves the default resolution of the target intending to use from this configuration.
225      *
226      * @return The stored value, if it exists in this configuration.
227      * @throws IllegalArgumentException if the option does not exist in this configuration.
228      */
getDefaultResolution()229     default @NonNull Size getDefaultResolution() {
230         return retrieveOption(OPTION_DEFAULT_RESOLUTION);
231     }
232 
233     /**
234      * Retrieves the max resolution limitation of the target intending to use from this
235      * configuration.
236      *
237      * @param valueIfMissing The value to return if this configuration option has not been set.
238      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
239      * configuration.
240      */
getMaxResolution(@ullable Size valueIfMissing)241     default @Nullable Size getMaxResolution(@Nullable Size valueIfMissing) {
242         return retrieveOption(OPTION_MAX_RESOLUTION, valueIfMissing);
243     }
244 
245     /**
246      * Retrieves the max resolution limitation of the target intending to use from this
247      * configuration.
248      *
249      * @return The stored value, if it exists in this configuration.
250      * @throws IllegalArgumentException if the option does not exist in this configuration.
251      */
getMaxResolution()252     default @NonNull Size getMaxResolution() {
253         return retrieveOption(OPTION_MAX_RESOLUTION);
254     }
255 
256     /**
257      * Retrieves the supported resolutions can be used by the target from this configuration.
258      *
259      * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
260      * {@link Size} array should be subset of the complete supported sizes list for the camera
261      * device.
262      *
263      * @param valueIfMissing The value to return if this configuration option has not been set.
264      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
265      * configuration.
266      */
getSupportedResolutions( @ullable List<Pair<Integer, Size[]>> valueIfMissing)267     default @Nullable List<Pair<Integer, Size[]>> getSupportedResolutions(
268             @Nullable List<Pair<Integer, Size[]>> valueIfMissing) {
269         return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS, valueIfMissing);
270     }
271 
272     /**
273      * Retrieves the resolution selector can be used by the target from this configuration.
274      *
275      * @param valueIfMissing The value to return if this configuration option has not been set.
276      * @return The stored value or <code>valueIfMissing</code> if the value does not exist in this
277      * configuration.
278      */
getResolutionSelector( @ullable ResolutionSelector valueIfMissing)279     default @Nullable ResolutionSelector getResolutionSelector(
280             @Nullable ResolutionSelector valueIfMissing) {
281         return retrieveOption(OPTION_RESOLUTION_SELECTOR, valueIfMissing);
282     }
283 
284     /**
285      * Retrieves the resolution selector can be used by the target from this configuration.
286      *
287      * @return The stored value, if it exists in this configuration.
288      * @throws IllegalArgumentException if the option does not exist in this configuration.
289      */
getResolutionSelector()290     default @NonNull ResolutionSelector getResolutionSelector() {
291         return retrieveOption(OPTION_RESOLUTION_SELECTOR);
292     }
293 
294     /**
295      * Retrieves the supported resolutions can be used by the target from this configuration.
296      *
297      * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The returned
298      * {@link Size} array should be subset of the complete supported sizes list for the camera
299      * device.
300      *
301      * @return The stored value, if it exists in this configuration.
302      * @throws IllegalArgumentException if the option does not exist in this configuration.
303      */
getSupportedResolutions()304     default @NonNull List<Pair<Integer, Size[]>> getSupportedResolutions() {
305         return retrieveOption(OPTION_SUPPORTED_RESOLUTIONS);
306     }
307 
308     /**
309      * Retrieves the custom ordered resolutions can be used by the target from this configuration.
310      *
311      * <p>The returned {@link Size} list contains preferred priority from high to low and should
312      * be the subset of the supported sizes for the camera device.
313      *
314      * @return The stored value, if it exists in this configuration.
315      * @throws IllegalArgumentException if the option does not exist in this configuration.
316      */
getCustomOrderedResolutions(@ullable List<Size> valueIfMissing)317     default @Nullable List<Size> getCustomOrderedResolutions(@Nullable List<Size> valueIfMissing) {
318         List<Size> list = retrieveOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS, valueIfMissing);
319         return list != null ? new ArrayList<>(list) : null;
320     }
321 
322     /**
323      * Retrieves the custom resolutions can be used by the target from this configuration.
324      *
325      * <p>The returned {@link Size} list contains preferred priority from high to low and should
326      * be the subset of the supported sizes for the camera device.
327      *
328      * @return The stored value, if it exists in this configuration.
329      * @throws IllegalArgumentException if the option does not exist in this configuration.
330      */
getCustomOrderedResolutions()331     default @NonNull List<Size> getCustomOrderedResolutions() {
332         return new ArrayList<>(requireNonNull(retrieveOption(OPTION_CUSTOM_ORDERED_RESOLUTIONS)));
333     }
334 
335     /**
336      * Checks whether the input config contains any conflicted settings.
337      *
338      * @param config to be validated.
339      * @throws IllegalArgumentException if both the target aspect ratio and the target resolution
340      * settings are contained in the config, or if either the target aspect ratio or the target
341      * resolution is contained when a resolution selector has been set in the config.
342      */
validateConfig(@onNull ImageOutputConfig config)343     static void validateConfig(@NonNull ImageOutputConfig config) {
344         boolean hasTargetAspectRatio = config.hasTargetAspectRatio();
345         boolean hasTargetResolution = config.getTargetResolution(null) != null;
346 
347         // Case 1. Error at runtime for using both setTargetResolution and setTargetAspectRatio on
348         // the same config.
349         if (hasTargetAspectRatio && hasTargetResolution) {
350             throw new IllegalArgumentException(
351                     "Cannot use both setTargetResolution and setTargetAspectRatio on the same "
352                             + "config.");
353         }
354 
355         ResolutionSelector resolutionSelector = config.getResolutionSelector(null);
356 
357         if (resolutionSelector != null) {
358             // Case 2. Error at runtime for using setTargetResolution or setTargetAspectRatio
359             // with setResolutionSelector on the same config.
360             if (hasTargetAspectRatio || hasTargetResolution) {
361                 throw new IllegalArgumentException(
362                         "Cannot use setTargetResolution or setTargetAspectRatio with "
363                                 + "setResolutionSelector on the same config.");
364             }
365         }
366     }
367 
368     /**
369      * Builder for a {@link ImageOutputConfig}.
370      *
371      * @param <B> The top level builder type for which this builder is composed with.
372      */
373     interface Builder<B> {
374 
375         /**
376          * Sets the aspect ratio of the intended target for images from this configuration.
377          *
378          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
379          * use case.
380          *
381          * @param aspectRatio A {@link AspectRatio} representing the ratio of the
382          *                    target's width and height.
383          * @return The current Builder.
384          */
setTargetAspectRatio(@spectRatio.Ratio int aspectRatio)385         @NonNull B setTargetAspectRatio(@AspectRatio.Ratio int aspectRatio);
386 
387         /**
388          * Sets the rotation of the intended target for images from this configuration.
389          *
390          * <p>This is one of four valid values: {@link Surface#ROTATION_0}, {@link
391          * Surface#ROTATION_90}, {@link Surface#ROTATION_180}, {@link Surface#ROTATION_270}.
392          * Rotation values are relative to the "natural" rotation, {@link Surface#ROTATION_0}.
393          *
394          * @param rotation The rotation of the intended target.
395          * @return The current Builder.
396          */
setTargetRotation(@otationValue int rotation)397         @NonNull B setTargetRotation(@RotationValue int rotation);
398 
399         /**
400          * Sets the mirror mode of the intended target for images from this configuration.
401          *
402          * <p>Valid values include: {@link MirrorMode#MIRROR_MODE_OFF},
403          * {@link MirrorMode#MIRROR_MODE_ON} and {@link MirrorMode#MIRROR_MODE_ON_FRONT_ONLY}.
404          *
405          * @param mirrorMode The mirror mode of the intended target.
406          * @return The current Builder.
407          */
setMirrorMode(@irrorMode.Mirror int mirrorMode)408         @NonNull B setMirrorMode(@MirrorMode.Mirror int mirrorMode);
409 
410         /**
411          * Sets the resolution of the intended target from this configuration.
412          *
413          * <p>It is not allowed to set both target aspect ratio and target resolution on the same
414          * use case.
415          *
416          * <p>The target aspect ratio will also be set the same as the aspect ratio of the provided
417          * {@link Size}. Make sure to set the target resolution with the correct orientation.
418          *
419          * @param resolution The target resolution to choose from supported output sizes list.
420          * @return The current Builder.
421          */
setTargetResolution(@onNull Size resolution)422         @NonNull B setTargetResolution(@NonNull Size resolution);
423 
424         /**
425          * Sets the default resolution of the intended target from this configuration.
426          *
427          * @param resolution The default resolution to choose from supported output sizes list.
428          * @return The current Builder.
429          */
setDefaultResolution(@onNull Size resolution)430         @NonNull B setDefaultResolution(@NonNull Size resolution);
431 
432         /**
433          * Sets the max resolution limitation of the intended target from this configuration.
434          *
435          * @param resolution The max resolution limitation to choose from supported output sizes
436          *                   list.
437          * @return The current Builder.
438          */
setMaxResolution(@onNull Size resolution)439         @NonNull B setMaxResolution(@NonNull Size resolution);
440 
441         /**
442          * Sets the supported resolutions can be used by target from this configuration.
443          *
444          * <p>Pair list is composed with {@link ImageFormat} and {@link Size} array. The
445          * {@link Size} array should be subset of the complete supported sizes list for the camera
446          * device.
447          *
448          * @param resolutionsList The resolutions can be supported for image formats.
449          * @return The current Builder.
450          */
setSupportedResolutions(@onNull List<Pair<Integer, Size[]>> resolutionsList)451         @NonNull B setSupportedResolutions(@NonNull List<Pair<Integer, Size[]>> resolutionsList);
452 
453         /**
454          * Sets the custom resolutions can be used by target from this configuration.
455          *
456          * <p>The {@link Size} list should contain preferred priority from high to low and should
457          * be the subset of the supported sizes for the camera device. The list will be fully
458          * respected, meaning it will not be sorted or filtered by other configurations such as
459          * max/default/target/supported resolutions.
460          *
461          * @param resolutionsList The resolutions can be supported for this image config.
462          * @return The current Builder.
463          */
setCustomOrderedResolutions(@onNull List<Size> resolutionsList)464         @NonNull B setCustomOrderedResolutions(@NonNull List<Size> resolutionsList);
465 
466         /**
467          * Sets the resolution selector can be used by target from this configuration.
468          *
469          * @param resolutionSelector The resolution selector to select a preferred resolution.
470          * @return The current Builder.
471          */
setResolutionSelector(@onNull ResolutionSelector resolutionSelector)472         @NonNull B setResolutionSelector(@NonNull ResolutionSelector resolutionSelector);
473     }
474 
475     /**
476      * Valid integer rotation values.
477      */
478     @IntDef({Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270})
479     @Retention(RetentionPolicy.SOURCE)
480     @interface RotationValue {
481     }
482 
483     /**
484      * Valid integer rotation values including option indicating value not specified.
485      */
486     @IntDef({ROTATION_NOT_SPECIFIED, Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_180,
487             Surface.ROTATION_270})
488     @Retention(RetentionPolicy.SOURCE)
489     @interface OptionalRotationValue {
490     }
491 
492     /**
493      * Valid integer rotation degrees values.
494      */
495     @IntDef({0, 90, 180, 270})
496     @Retention(RetentionPolicy.SOURCE)
497     @interface RotationDegreesValue {
498     }
499 }
500