1 /* <lambda>null2 * Copyright 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 androidx.camera.integration.extensions.utils 18 19 import android.content.Context 20 import android.graphics.ImageFormat 21 import android.graphics.Point 22 import android.graphics.SurfaceTexture 23 import android.hardware.camera2.CameraCharacteristics 24 import android.hardware.camera2.CameraExtensionCharacteristics 25 import android.hardware.camera2.CameraManager 26 import android.os.Build 27 import android.util.DisplayMetrics 28 import android.util.Log 29 import android.util.Size 30 import androidx.annotation.RequiresApi 31 import androidx.camera.integration.extensions.EXTENSION_MODE_NONE 32 import java.util.stream.Collectors 33 34 private const val TAG = "Camera2ExtensionsUtil" 35 36 /** Util functions for Camera2 Extensions implementation */ 37 object Camera2ExtensionsUtil { 38 39 /** Camera2 extension modes */ 40 @Suppress("DEPRECATION") // EXTENSION_BEAUTY 41 @RequiresApi(31) 42 @JvmStatic 43 val AVAILABLE_CAMERA2_EXTENSION_MODES = 44 arrayOf( 45 CameraExtensionCharacteristics.EXTENSION_AUTOMATIC, 46 CameraExtensionCharacteristics.EXTENSION_BEAUTY, 47 CameraExtensionCharacteristics.EXTENSION_BOKEH, 48 CameraExtensionCharacteristics.EXTENSION_HDR, 49 CameraExtensionCharacteristics.EXTENSION_NIGHT, 50 ) 51 52 /** Converts extension mode from integer to string. */ 53 @Suppress("DEPRECATION") // EXTENSION_BEAUTY 54 @RequiresApi(31) 55 @JvmStatic 56 fun getCamera2ExtensionModeStringFromId(extension: Int): String = 57 when (extension) { 58 EXTENSION_MODE_NONE -> "None" 59 CameraExtensionCharacteristics.EXTENSION_HDR -> "HDR" 60 CameraExtensionCharacteristics.EXTENSION_NIGHT -> "NIGHT" 61 CameraExtensionCharacteristics.EXTENSION_BOKEH -> "BOKEH" 62 CameraExtensionCharacteristics.EXTENSION_BEAUTY -> "FACE RETOUCH" 63 CameraExtensionCharacteristics.EXTENSION_AUTOMATIC -> "AUTO" 64 else -> throw IllegalArgumentException("Invalid extension mode id!") 65 } 66 67 @Suppress("DEPRECATION") // EXTENSION_BEAUTY 68 @RequiresApi(31) 69 @JvmStatic 70 fun getCamera2ExtensionModeIdFromString(mode: String): Int = 71 when (mode) { 72 "None" -> EXTENSION_MODE_NONE 73 "HDR" -> CameraExtensionCharacteristics.EXTENSION_HDR 74 "NIGHT" -> CameraExtensionCharacteristics.EXTENSION_NIGHT 75 "BOKEH" -> CameraExtensionCharacteristics.EXTENSION_BOKEH 76 "FACE RETOUCH" -> CameraExtensionCharacteristics.EXTENSION_BEAUTY 77 "AUTO" -> CameraExtensionCharacteristics.EXTENSION_AUTOMATIC 78 else -> throw IllegalArgumentException("Invalid extension mode string!") 79 } 80 81 /** Gets the first camera id of the specified lens facing. */ 82 @JvmStatic 83 fun getLensFacingCameraId(cameraManager: CameraManager, lensFacing: Int): String { 84 cameraManager.cameraIdList.forEach { cameraId -> 85 val characteristics = cameraManager.getCameraCharacteristics(cameraId) 86 if (characteristics[CameraCharacteristics.LENS_FACING] == lensFacing) { 87 characteristics[CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES]?.let { 88 if ( 89 it.contains( 90 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE 91 ) 92 ) { 93 return cameraId 94 } 95 } 96 } 97 } 98 99 throw IllegalArgumentException("Can't find camera of lens facing $lensFacing") 100 } 101 102 @RequiresApi(31) 103 @JvmStatic 104 fun isCamera2ExtensionModeSupported( 105 context: Context, 106 cameraId: String, 107 extensionMode: Int 108 ): Boolean { 109 val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager 110 val extensionCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId) 111 return extensionCharacteristics.supportedExtensions.contains(extensionMode) 112 } 113 114 /** 115 * Picks a preview resolution that is both close/same as the display size and supported by 116 * camera and extensions. 117 */ 118 @RequiresApi(Build.VERSION_CODES.S) 119 @JvmStatic 120 fun pickPreviewResolution( 121 cameraManager: CameraManager, 122 cameraId: String, 123 displayMetrics: DisplayMetrics, 124 extensionMode: Int 125 ): Size? { 126 val characteristics = cameraManager.getCameraCharacteristics(cameraId) 127 val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) 128 val textureSizes = map!!.getOutputSizes(SurfaceTexture::class.java) 129 val displaySize = Point() 130 displaySize.x = displayMetrics.widthPixels 131 displaySize.y = displayMetrics.heightPixels 132 if (displaySize.x < displaySize.y) { 133 displaySize.x = displayMetrics.heightPixels 134 displaySize.y = displayMetrics.widthPixels 135 } 136 val displayArRatio = displaySize.x.toFloat() / displaySize.y 137 val previewSizes = ArrayList<Size>() 138 var previewSize: Size? = null 139 var currentDistance = Int.MAX_VALUE 140 for (sz in textureSizes) { 141 val arRatio = sz.width.toFloat() / sz.height 142 if (Math.abs(arRatio - displayArRatio) <= .2f) { 143 previewSizes.add(sz) 144 } 145 val distance = Math.abs(sz.width * sz.height - displaySize.x * displaySize.y) 146 if (currentDistance > distance) { 147 currentDistance = distance 148 previewSize = sz 149 } 150 } 151 152 if (previewSizes.isEmpty()) { 153 previewSize?.let { previewSizes.add(it) } 154 ?: throw IllegalStateException("No preview size was found") 155 } else { 156 previewSize = previewSizes[0] 157 } 158 159 if (extensionMode == EXTENSION_MODE_NONE) { 160 for (sz in previewSizes) { 161 val distance = Math.abs(sz.width * sz.height - displaySize.x * displaySize.y) 162 if (currentDistance > distance) { 163 currentDistance = distance 164 previewSize = sz 165 } 166 } 167 return previewSize 168 } 169 170 val extensionCharacteristics = cameraManager.getCameraExtensionCharacteristics(cameraId) 171 val extensionSizes = 172 extensionCharacteristics.getExtensionSupportedSizes( 173 extensionMode, 174 SurfaceTexture::class.java 175 ) 176 if (extensionSizes.isEmpty()) { 177 return null 178 } 179 180 previewSize = extensionSizes[0] 181 val supportedPreviewSizes = 182 previewSizes 183 .stream() 184 .distinct() 185 .filter { o: Size -> extensionSizes.contains(o) } 186 .collect(Collectors.toList()) 187 if (supportedPreviewSizes.isNotEmpty()) { 188 currentDistance = Int.MAX_VALUE 189 for (sz in supportedPreviewSizes) { 190 val distance = Math.abs(sz.width * sz.height - displaySize.x * displaySize.y) 191 if (currentDistance > distance) { 192 currentDistance = distance 193 previewSize = sz 194 } 195 } 196 } else { 197 Log.w( 198 TAG, 199 "No overlap between supported camera and extensions preview sizes using" + 200 " first available!" 201 ) 202 } 203 204 return previewSize 205 } 206 207 /** Picks a resolution for still image capture. */ 208 @RequiresApi(Build.VERSION_CODES.S) 209 @JvmStatic 210 fun pickStillImageResolution( 211 cameraCharacteristics: CameraCharacteristics, 212 extensionCharacteristics: CameraExtensionCharacteristics, 213 extensionMode: Int 214 ): Pair<Size, Int> { 215 val yuvColorEncodingSystemSizes = 216 if (extensionMode == EXTENSION_MODE_NONE) { 217 cameraCharacteristics 218 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! 219 .getOutputSizes(ImageFormat.YUV_420_888) 220 .toList() 221 } else { 222 extensionCharacteristics.getExtensionSupportedSizes( 223 extensionMode, 224 ImageFormat.YUV_420_888 225 ) 226 } 227 val jpegSizes = 228 if (extensionMode == EXTENSION_MODE_NONE) { 229 cameraCharacteristics 230 .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! 231 .getOutputSizes(ImageFormat.JPEG) 232 .toList() 233 } else { 234 extensionCharacteristics.getExtensionSupportedSizes(extensionMode, ImageFormat.JPEG) 235 } 236 val stillFormat = if (jpegSizes.isEmpty()) ImageFormat.YUV_420_888 else ImageFormat.JPEG 237 val stillCaptureSize = 238 if (jpegSizes.isEmpty()) yuvColorEncodingSystemSizes[0] else jpegSizes[0] 239 240 return Pair(stillCaptureSize, stillFormat) 241 } 242 } 243