Skip to content

Commit

Permalink
Improve size selection logic and fallback mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
Priyank Vasa authored and pvasa committed Aug 21, 2019
1 parent 08f0b0a commit dbd57a9
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 49 deletions.
22 changes: 9 additions & 13 deletions CameraEx/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,21 +1,17 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
## For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
#Tue Aug 20 17:55:29 EDT 2019
kotlin.code.style=official
android.enableJetifier=true
org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
android.useAndroidX=true
4 changes: 2 additions & 2 deletions CameraEx/sampleApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ dependencies {
implementation(fileTree(mapOf("include" to arrayOf("*.jar"), "dir" to "libs")))

// CameraViewEx
// implementation("com.priyankvasa.android:cameraview-ex:3.5.2-alpha")
implementation("com.priyankvasa.android:cameraview-ex:3.5.2-alpha-debug")
// implementation("com.priyankvasa.android:cameraview-ex:3.5.3-alpha")
implementation("com.priyankvasa.android:cameraview-ex:3.5.3-alpha-debug")

// Kotlin
implementation(Config.Libs.kotlinStdLibJdk8)
Expand Down
3 changes: 2 additions & 1 deletion CameraEx/sampleApp/src/main/res/layout/fragment_camera.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:keepScreenOn="true"
app:aspectRatio="3:4"
app:aspectRatio="4:3"
app:autoFocus="continuous_picture"
app:awb="auto"
app:cameraMode="single_capture|video_capture|continuous_frame"
Expand All @@ -111,6 +111,7 @@
app:outputFormat="jpeg"
app:pinchToZoom="true"
app:shutter="short_time"
app:singleCaptureSize="W1440,H1080"
app:touchToFocus="true"
app:zsl="false" />

Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ object Config {

private const val majorVersion = 3
private const val minorVersion = 5
private const val patchVersion = 3
private const val patchVersion = 4
private const val versionClassifier = "alpha"

val versionName: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import android.support.v4.util.SparseArrayCompat
import android.util.SparseIntArray
import android.view.SurfaceHolder
import com.priyankvasa.android.cameraviewex.exif.ExifInterface
import com.priyankvasa.android.cameraviewex.extension.chooseOptimalPreviewSize
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand Down Expand Up @@ -79,7 +78,7 @@ internal class Camera1(
runCatching { Camera.getCameraInfo(id, info) }.getOrNull() != null &&
info.facing == internalFacing
}
.mapTo(TreeSet<String>()) { Integer.toString(it) }
.mapTo(TreeSet<String>()) { it.toString() }

private val isPictureCaptureInProgress: AtomicBoolean by lazy { AtomicBoolean(false) }

Expand Down Expand Up @@ -554,19 +553,23 @@ internal class Camera1(
if (!preview.isReady) return

val previewSize: Size =
config.continuousFrameSize.value
.takeIf { it != Size.Invalid && previewSizes.containsSize(Size(it.longerEdge, it.shorterEdge)) }
?: previewSizes.sizes(config.sensorAspectRatio)
.runCatching { chooseOptimalPreviewSize(preview.width, preview.height) }
.getOrElse {
listener.onCameraError(CameraViewException("No supported preview size available. This camera device (id $cameraId) is not supported.", it))
return
}
previewSizes.chooseOptimalSize(
config.continuousFrameSize.value,
config.sensorAspectRatio
)
?: run {
listener.onCameraError(
CameraViewException("No supported preview size available. This camera device (id $cameraId) is not supported."),
ErrorLevel.Error
)
return
}

val pictureSize: Size =
config.singleCaptureSize.value
.takeIf { it != Size.Invalid && pictureSizes.containsSize(Size(it.longerEdge, it.shorterEdge)) }
?: pictureSizes.sizes(config.sensorAspectRatio).lastOrNull()
pictureSizes.chooseOptimalSize(
config.singleCaptureSize.value,
config.sensorAspectRatio
)
?: previewSize

if (showingPreview) stopPreview()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import android.renderscript.RenderScript
import android.util.SparseIntArray
import android.view.Surface
import com.priyankvasa.android.cameraviewex.exif.ExifInterface
import com.priyankvasa.android.cameraviewex.extension.chooseOptimalPreviewSize
import com.priyankvasa.android.cameraviewex.extension.chooseOptimalSize
import com.priyankvasa.android.cameraviewex.extension.cropHeight
import com.priyankvasa.android.cameraviewex.extension.cropWidth
import com.priyankvasa.android.cameraviewex.extension.isAfSupported
Expand Down Expand Up @@ -1046,8 +1046,10 @@ internal open class Camera2(
continuousFrameReader?.close()

val (width: Int, height: Int) =
config.continuousFrameSize.value
.takeIf { it != Size.Invalid && previewSizes.containsSize(Size(it.longerEdge, it.shorterEdge)) }
previewSizes.chooseOptimalSize(
config.continuousFrameSize.value,
config.sensorAspectRatio
)
?: Size(preview.width, preview.height)

continuousFrameReader =
Expand All @@ -1064,9 +1066,10 @@ internal open class Camera2(
singleCaptureReader?.close()

val (pictureWidth: Int, pictureHeight: Int) =
config.singleCaptureSize.value
.takeIf { it != Size.Invalid && pictureSizes.containsSize(Size(it.longerEdge, it.shorterEdge)) }
?: pictureSizes.sizes(config.sensorAspectRatio).lastOrNull()
pictureSizes.chooseOptimalSize(
config.singleCaptureSize.value,
config.sensorAspectRatio
)
?: run {
listener.onCameraError(
CameraViewException("No supported picture size found for this camera. Try reopening this camera or use another one."),
Expand Down Expand Up @@ -1101,7 +1104,7 @@ internal open class Camera2(

val (width: Int, height: Int) =
previewSizes.sizes(config.sensorAspectRatio)
.runCatching { chooseOptimalPreviewSize(preview.width, preview.height) }
.runCatching { chooseOptimalSize(preview.width, preview.height) }
.getOrElse {
listener.onCameraError(CameraViewException("No supported preview size available. This camera device (id $cameraId) is not supported.", it))
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.priyankvasa.android.cameraviewex

import android.support.v4.util.ArrayMap
import com.priyankvasa.android.cameraviewex.extension.chooseOptimalSize
import java.util.SortedSet
import java.util.TreeSet

Expand Down Expand Up @@ -84,5 +85,37 @@ internal class SizeMap {
*/
fun sizes(ratio: AspectRatio?): SortedSet<Size> = map[ratio] ?: sortedSetOf()

/**
* If [requestedSize] is [Size.Invalid], choose highest size from
* set of sizes with aspect ratio of [fallbackRatio].
*
* Else if [requestedSize] is valid, choose best size from set of sizes
* which has same aspect ratio as [requestedSize].
* Best case would be [requestedSize] itself.
*
* If this set is empty, choose best size from set of sizes
* with aspect ratio of [fallbackRatio]
*
* Even if this set is empty, return `null`.
*/
fun chooseOptimalSize(requestedSize: Size, fallbackRatio: AspectRatio): Size? {

val fallbackRatioSizes by lazy { sizes(fallbackRatio) }

return if (requestedSize == Size.Invalid) {
fallbackRatioSizes.lastOrNull()
} else {
runCatching {
sizes(AspectRatio.of(requestedSize))
.chooseOptimalSize(requestedSize.width, requestedSize.height)
}
.recoverCatching {
fallbackRatioSizes
.chooseOptimalSize(requestedSize.width, requestedSize.height)
}
.getOrNull()
}
}

fun clear() = map.clear()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,26 @@ import com.priyankvasa.android.cameraviewex.Size
import java.util.SortedSet

/**
* Chooses the optimal size based on [previewWidth] and [previewHeight].
* Chooses the optimal size based on [requestedWidth] and [requestedHeight].
*
* Find the longer and shorter edge from preview dimensions.
* Find the longer and shorter edge from provided dimensions.
* Choose smallest of sizes which has width >= longerEdge `AND` height >= shorterEdge.
* Best case would be a size with exact dimensions as [previewWidth] and [previewHeight].
* Best case would be a size with exact dimensions as [requestedWidth] and [requestedHeight].
* If none found then choose largest of sizes whose width < longerEdge `OR` height < shorterEdge.
*
* If set is empty return [Size.Invalid].
* If again found null, choose highest available size in the set.
*
* @return The picked optimal size.
* @throws UnsupportedOperationException when the receiver set is empty
*/
internal fun SortedSet<Size>.chooseOptimalPreviewSize(previewWidth: Int, previewHeight: Int): Size {
internal fun SortedSet<Size>.chooseOptimalSize(requestedWidth: Int, requestedHeight: Int): Size {

if (isEmpty()) throw UnsupportedOperationException("No preview sizes to choose from.")
if (isEmpty()) throw UnsupportedOperationException("No sizes to choose from.")

val (surfaceLonger: Int, surfaceShorter: Int) =
if (previewWidth > previewHeight) previewWidth to previewHeight
else previewHeight to previewWidth
if (requestedWidth > requestedHeight) requestedWidth to requestedHeight
else requestedHeight to requestedWidth

return firstOrNull { it.width >= surfaceLonger && it.height >= surfaceShorter }
?: lastOrNull { it.width < previewWidth || it.height < previewHeight }
?: lastOrNull { it.width < requestedWidth || it.height < requestedHeight }
?: last()
}
}

0 comments on commit dbd57a9

Please sign in to comment.