Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add image size to camera response #115

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage
import java.io.ByteArrayOutputStream
import java.util.concurrent.Executors

Expand All @@ -54,7 +55,7 @@ actual fun PeekabooCamera(
captureIcon: @Composable (onClick: () -> Unit) -> Unit,
convertIcon: @Composable (onClick: () -> Unit) -> Unit,
progressIndicator: @Composable () -> Unit,
onCapture: (byteArray: ByteArray?) -> Unit,
onCapture: (image: PeekabooCameraImage?) -> Unit,
onFrame: ((frame: ByteArray) -> Unit)?,
permissionDeniedContent: @Composable () -> Unit,
) {
Expand Down Expand Up @@ -219,16 +220,24 @@ private fun CameraWithGrantedPermission(
}

class ImageCaptureCallback(
private val onCapture: (byteArray: ByteArray?) -> Unit,
private val onCapture: (image: PeekabooCameraImage?) -> Unit,
private val stopCapturing: () -> Unit,
) : OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val imageBytes = image.toByteArray()
onCapture(imageBytes)
val peekabooImage = image.toPeekabooCameraImage()
onCapture(peekabooImage)
stopCapturing()
}
}

private fun ImageProxy.toPeekabooCameraImage(): PeekabooCameraImage {
return PeekabooCameraImage(
image = this.toByteArray(),
height = height,
width = width
)
}

private fun ImageProxy.toByteArray(): ByteArray {
val rotationDegrees = imageInfo.rotationDegrees
val bitmap = toBitmap()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage

@Stable
actual class PeekabooCameraState(
cameraMode: CameraMode,
internal var onFrame: ((frame: ByteArray) -> Unit)?,
internal var onCapture: (ByteArray?) -> Unit,
internal var onCapture: (PeekabooCameraImage?) -> Unit,
) {
actual var isCameraReady: Boolean by mutableStateOf(false)

Expand All @@ -50,7 +51,7 @@ actual class PeekabooCameraState(
isCapturing = false
}

internal fun onCapture(image: ByteArray?) {
internal fun onCapture(image: PeekabooCameraImage?) {
onCapture.invoke(image)
}

Expand All @@ -61,7 +62,7 @@ actual class PeekabooCameraState(
companion object {
fun saver(
onFrame: ((frame: ByteArray) -> Unit)?,
onCapture: (ByteArray?) -> Unit,
onCapture: (PeekabooCameraImage?) -> Unit,
): Saver<PeekabooCameraState, Int> {
return Saver(
save = {
Expand All @@ -83,7 +84,7 @@ actual class PeekabooCameraState(
actual fun rememberPeekabooCameraState(
initialCameraMode: CameraMode,
onFrame: ((frame: ByteArray) -> Unit)?,
onCapture: (ByteArray?) -> Unit,
onCapture: (PeekabooCameraImage?) -> Unit,
): PeekabooCameraState {
return rememberSaveable(
saver = PeekabooCameraState.saver(onFrame, onCapture),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package com.preat.peekaboo.ui.camera

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage

/**
* `PeekabooCamera` is a composable function that provides a customizable camera UI within a Compose Multiplatform application.
Expand Down Expand Up @@ -44,7 +45,7 @@ expect fun PeekabooCamera(
captureIcon: @Composable (onClick: () -> Unit) -> Unit,
convertIcon: @Composable (onClick: () -> Unit) -> Unit = {},
progressIndicator: @Composable () -> Unit = {},
onCapture: (byteArray: ByteArray?) -> Unit,
onCapture: (image: PeekabooCameraImage?) -> Unit,
onFrame: ((frame: ByteArray) -> Unit)? = null,
permissionDeniedContent: @Composable () -> Unit = {},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.preat.peekaboo.ui.camera
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.remember
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage

/**
* Create and [remember] a [PeekabooCameraState]
Expand All @@ -28,7 +29,7 @@ import androidx.compose.runtime.remember
expect fun rememberPeekabooCameraState(
initialCameraMode: CameraMode = CameraMode.Back,
onFrame: ((frame: ByteArray) -> Unit)? = null,
onCapture: (ByteArray?) -> Unit,
onCapture: (PeekabooCameraImage?) -> Unit,
): PeekabooCameraState

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.preat.peekaboo.ui.camera.model

data class PeekabooCameraImage(
val image: ByteArray,
val height: Int,
val width: Int
)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.interop.UIKitView
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
Expand Down Expand Up @@ -186,7 +187,7 @@ actual fun PeekabooCamera(
captureIcon: @Composable (onClick: () -> Unit) -> Unit,
convertIcon: @Composable (onClick: () -> Unit) -> Unit,
progressIndicator: @Composable () -> Unit,
onCapture: (byteArray: ByteArray?) -> Unit,
onCapture: (image: PeekabooCameraImage?) -> Unit,
onFrame: ((frame: ByteArray) -> Unit)?,
permissionDeniedContent: @Composable () -> Unit,
) {
Expand Down Expand Up @@ -236,7 +237,7 @@ private fun BoxScope.AuthorizedCamera(
captureIcon: @Composable (onClick: () -> Unit) -> Unit,
convertIcon: @Composable (onClick: () -> Unit) -> Unit,
progressIndicator: @Composable () -> Unit,
onCapture: (byteArray: ByteArray?) -> Unit,
onCapture: (image: PeekabooCameraImage?) -> Unit,
) {
var cameraReady by remember { mutableStateOf(false) }
val camera: AVCaptureDevice? =
Expand Down Expand Up @@ -327,7 +328,7 @@ private fun BoxScope.RealDeviceCamera(
captureIcon: @Composable (onClick: () -> Unit) -> Unit,
convertIcon: @Composable (onClick: () -> Unit) -> Unit,
progressIndicator: @Composable () -> Unit,
onCapture: (byteArray: ByteArray?) -> Unit,
onCapture: (image: PeekabooCameraImage?) -> Unit,
) {
var isFrontCamera by remember { mutableStateOf(camera.position == AVCaptureDevicePositionFront) }
val capturePhotoOutput = remember { AVCapturePhotoOutput() }
Expand Down Expand Up @@ -751,7 +752,7 @@ class CameraFrameAnalyzerDelegate(

class PhotoCaptureDelegate(
private val onCaptureEnd: () -> Unit,
private val onCapture: (byteArray: ByteArray?) -> Unit,
private val onCapture: (image: PeekabooCameraImage?) -> Unit,
) : NSObject(), AVCapturePhotoCaptureDelegateProtocol {
@OptIn(ExperimentalForeignApi::class)
override fun captureOutput(
Expand Down Expand Up @@ -788,6 +789,18 @@ class PhotoCaptureDelegate(
}
}

private fun UIImage.toPeekabooCameraImage(): PeekabooCameraImage? {
return UIImagePNGRepresentation(this)?.let { imageData ->
val (width, height) = this.size.useContents { width to height }

PeekabooCameraImage(
image = imageData.toByteArray(),
height = height.roundToInt(),
width = width.roundToInt()
)
}
}

@OptIn(ExperimentalForeignApi::class)
private inline fun NSData.toByteArray(): ByteArray {
val size = length.toInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.preat.peekaboo.ui.camera.model.PeekabooCameraImage

@Stable
actual class PeekabooCameraState(
cameraMode: CameraMode,
internal var onFrame: ((frame: ByteArray) -> Unit)?,
internal var onCapture: (ByteArray?) -> Unit,
internal var onCapture: (PeekabooCameraImage?) -> Unit,
) {
actual var isCameraReady: Boolean by mutableStateOf(false)

Expand All @@ -50,7 +51,7 @@ actual class PeekabooCameraState(
isCapturing = false
}

internal fun onCapture(image: ByteArray?) {
internal fun onCapture(image: PeekabooCameraImage?) {
onCapture.invoke(image)
}

Expand All @@ -61,7 +62,7 @@ actual class PeekabooCameraState(
companion object {
fun saver(
onFrame: ((frame: ByteArray) -> Unit)?,
onCapture: (ByteArray?) -> Unit,
onCapture: (PeekabooCameraImage?) -> Unit,
): Saver<PeekabooCameraState, Int> {
return Saver(
save = {
Expand All @@ -83,7 +84,7 @@ actual class PeekabooCameraState(
actual fun rememberPeekabooCameraState(
initialCameraMode: CameraMode,
onFrame: ((frame: ByteArray) -> Unit)?,
onCapture: (ByteArray?) -> Unit,
onCapture: (PeekabooCameraImage?) -> Unit,
): PeekabooCameraState {
return rememberSaveable(
saver = PeekabooCameraState.saver(onFrame, onCapture),
Expand Down