1 /* 2 * Copyright 2024 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.FloatRange; 20 import androidx.camera.core.ConcurrentCamera.SingleCameraConfig; 21 import androidx.core.util.Pair; 22 23 import org.jspecify.annotations.NonNull; 24 25 /** 26 * Composition settings for dual concurrent camera. It includes alpha value for blending, 27 * offset in x, y coordinates, scale of width and height. The offset, and scale of width and height 28 * are specified in normalized device coordinates(NDCs). The offset is applied after scale. 29 * The origin of normalized device coordinates is at the center of the viewing volume. The positive 30 * X-axis extends to the right, the positive Y-axis extends upwards.The x, y values range from -1 31 * to 1. E.g. scale with {@code (0.5f, 0.5f)} and offset with {@code (0.5f, 0.5f)} is the 32 * bottom-right quadrant of the output device. 33 * 34 * <p>Composited dual camera frames preview and recording can be supported using 35 * {@link CompositionSettings} and {@link SingleCameraConfig}. The z-order of composition is 36 * determined by the order of camera configs to bind. Currently the background color will be black 37 * by default. The resolution of camera frames for preview and recording will be determined by 38 * resolution selection strategy configured for each use case and the scale of width and height set 39 * in {@link CompositionSettings}, so it is recommended to use 16:9 aspect ratio strategy for 40 * preview if 16:9 quality selector is configured for video capture. The mirroring and rotation of 41 * the camera frame will be applied after composition because both cameras are using the same use 42 * cases. 43 * 44 * <p>The following code snippet demonstrates how to display in Picture-in-Picture mode: 45 * <pre> 46 * 16 47 * -------------------------------- 48 * | c0 | 49 * | | 50 * | | 51 * | | 52 * | --------- | 9 53 * | | | | 54 * | | c1 | | 55 * | | | | 56 * | --------- | 57 * -------------------------------- 58 * c0: primary camera 59 * c1: secondary camera 60 * {@code 61 * ResolutionSelector resolutionSelector = new ResolutionSelector.Builder() 62 * .setAspectRatioStrategy( 63 * AspectRatioStrategy.RATIO_16_9_FALLBACK_AUTO_STRATEGY) 64 * .build(); 65 * Preview preview = new Preview.Builder() 66 * .setResolutionSelector(resolutionSelector) 67 * .build(); 68 * preview.setSurfaceProvider(mSinglePreviewView.getSurfaceProvider()); 69 * UseCaseGroup useCaseGroup = new UseCaseGroup.Builder() 70 * .addUseCase(preview) 71 * .addUseCase(mVideoCapture) 72 * .build(); 73 * SingleCameraConfig primary = new SingleCameraConfig( 74 * cameraSelectorPrimary, 75 * useCaseGroup, 76 * new CompositionSettings.Builder() 77 * .setAlpha(1.0f) 78 * .setOffset(0.0f, 0.0f) 79 * .setScale(1.0f, 1.0f) 80 * .build(), 81 * lifecycleOwner); 82 * SingleCameraConfig secondary = new SingleCameraConfig( 83 * cameraSelectorSecondary, 84 * useCaseGroup, 85 * new CompositionSettings.Builder() 86 * .setAlpha(1.0f) 87 * .setOffset(-0.3f, -0.4f) 88 * .setScale(0.3f, 0.3f) 89 * .build(), 90 * lifecycleOwner); 91 * cameraProvider.bindToLifecycle(ImmutableList.of(primary, secondary)); 92 * }</pre> 93 */ 94 public class CompositionSettings { 95 96 /** 97 * Default composition settings, which will display in full screen with no offset and scale. 98 */ 99 public static final CompositionSettings DEFAULT = new Builder() 100 .setAlpha(1.0f) 101 .setOffset(0.0f, 0.0f) 102 .setScale(1.0f, 1.0f) 103 .build(); 104 105 private final float mAlpha; 106 private final Pair<Float, Float> mOffset; 107 private final Pair<Float, Float> mScale; 108 CompositionSettings( float alpha, Pair<Float, Float> offset, Pair<Float, Float> scale)109 private CompositionSettings( 110 float alpha, 111 Pair<Float, Float> offset, 112 Pair<Float, Float> scale) { 113 mAlpha = alpha; 114 mOffset = offset; 115 mScale = scale; 116 } 117 118 /** 119 * Gets the alpha. 120 * 121 * @return alpha value. 122 */ getAlpha()123 public float getAlpha() { 124 return mAlpha; 125 } 126 127 /** 128 * Gets the offset. 129 * 130 * @return offset value. 131 */ getOffset()132 public @NonNull Pair<Float, Float> getOffset() { 133 return mOffset; 134 } 135 136 /** 137 * Gets the scale. Negative value means mirroring in X or Y direction. 138 * 139 * @return scale value. 140 */ getScale()141 public @NonNull Pair<Float, Float> getScale() { 142 return mScale; 143 } 144 145 /** A builder for {@link CompositionSettings} instances. */ 146 public static final class Builder { 147 private float mAlpha; 148 private Pair<Float, Float> mOffset; 149 private Pair<Float, Float> mScale; 150 151 /** 152 * Creates a new {@link Builder}. 153 * 154 * <p>The default alpha is 1.0f, the default offset is (0.0f, 0.0f), the default scale is 155 * (1.0f, 1.0f). 156 */ Builder()157 public Builder() { 158 mAlpha = 1.0f; 159 mOffset = Pair.create(0.0f, 0.0f); 160 mScale = Pair.create(1.0f, 1.0f); 161 } 162 163 /** 164 * Sets the alpha. 0 means fully transparent, 1 means fully opaque. 165 * 166 * @param alpha alpha value. 167 * @return Builder instance. 168 */ setAlpha(@loatRangefrom = 0, to = 1) float alpha)169 public @NonNull Builder setAlpha(@FloatRange(from = 0, to = 1) float alpha) { 170 mAlpha = alpha; 171 return this; 172 } 173 174 /** 175 * Sets the offset. 176 * 177 * @param offsetX offset X value. 178 * @param offsetY offset Y value. 179 * @return Builder instance. 180 */ setOffset( @loatRangefrom = -1, to = 1) float offsetX, @FloatRange(from = -1, to = 1) float offsetY)181 public @NonNull Builder setOffset( 182 @FloatRange(from = -1, to = 1) float offsetX, 183 @FloatRange(from = -1, to = 1) float offsetY) { 184 mOffset = Pair.create(offsetX, offsetY); 185 return this; 186 } 187 188 /** 189 * Sets the scale. 190 * 191 * @param scaleX scale X value. 192 * @param scaleY scale Y value. 193 * @return Builder instance. 194 */ setScale(float scaleX, float scaleY)195 public @NonNull Builder setScale(float scaleX, float scaleY) { 196 mScale = Pair.create(scaleX, scaleY); 197 return this; 198 } 199 200 /** 201 * Builds the {@link CompositionSettings}. 202 * 203 * @return {@link CompositionSettings}. 204 */ build()205 public @NonNull CompositionSettings build() { 206 return new CompositionSettings( 207 mAlpha, 208 mOffset, 209 mScale); 210 } 211 } 212 } 213