Skip to content

Commit

Permalink
Merge pull request #22 from RaedGhazal/feature/initial_size_configura…
Browse files Browse the repository at this point in the history
…tion

#17 Feature: configure initial frame size
  • Loading branch information
MoyuruAizawa authored Aug 20, 2024
2 parents 65050d6 + 7eb9a01 commit 53447ef
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.moyuru.cropify.Cropify
import io.moyuru.cropify.CropifyOption
import io.moyuru.cropify.CropifySize
import io.moyuru.cropify.rememberCropifyState
import io.moyuru.cropifysample.R
import io.moyuru.cropifysample.widget.AppBar
Expand All @@ -37,7 +38,7 @@ import io.moyuru.cropifysample.widget.ImagePreviewDialog
fun FileScreen(imageUri: Uri) {
val scaffoldState = rememberBottomSheetScaffoldState()
val cropifyState = rememberCropifyState()
var cropifyOption by remember { mutableStateOf(CropifyOption()) }
var cropifyOption by remember { mutableStateOf(CropifyOption(frameSize = CropifySize.PercentageSize(.9f))) }
var croppedImage by remember { mutableStateOf<ImageBitmap?>(null) }

croppedImage?.let { ImagePreviewDialog(bitmap = it) { croppedImage = null } }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.moyuru.cropify.AspectRatio
import io.moyuru.cropify.CropifySize
import io.moyuru.cropifysample.R

@Composable
fun AspectRatioPicker(
selectedAspectRatio: AspectRatio?,
selectedFixedAspectRatio: CropifySize.FixedAspectRatio?,
modifier: Modifier = Modifier,
onPicked: (AspectRatio?) -> Unit
onPicked: (CropifySize.FixedAspectRatio?) -> Unit
) {
Row(
verticalAlignment = Alignment.CenterVertically,
Expand All @@ -39,16 +39,16 @@ fun AspectRatioPicker(
3 to 4,
)
aspectRatioList.forEach { (x, y) ->
val aspectRatio = AspectRatio(x, y)
val fixedAspectRatio = CropifySize.FixedAspectRatio(x, y)
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(4.dp),
modifier = Modifier
.clickable { onPicked(aspectRatio) }
.clickable { onPicked(fixedAspectRatio) }
.padding(8.dp)
) {
Crossfade(
targetState = if (selectedAspectRatio == aspectRatio)
targetState = if (selectedFixedAspectRatio == fixedAspectRatio)
MaterialTheme.colorScheme.primary
else
MaterialTheme.colorScheme.onSurface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import io.moyuru.cropify.CropifySize
import io.moyuru.cropify.CropifyOption
import io.moyuru.cropifysample.R

Expand All @@ -32,8 +33,10 @@ fun CropifyOptionSelector(
valueRange = 1f..12f
)
TitleM(text = stringResource(id = R.string.frame_aspect_ratio))
AspectRatioPicker(selectedAspectRatio = option.frameAspectRatio) {
onOptionChanged(option.copy(frameAspectRatio = it))

val frameFixedAspectRatio = option.frameSize as? CropifySize.FixedAspectRatio
AspectRatioPicker(selectedFixedAspectRatio = frameFixedAspectRatio) {
onOptionChanged(option.copy(frameSize = it))
}

Space(dp = 16.dp)
Expand Down
6 changes: 0 additions & 6 deletions cropify/src/main/java/io/moyuru/cropify/AspectRatio.kt

This file was deleted.

26 changes: 18 additions & 8 deletions cropify/src/main/java/io/moyuru/cropify/Cropify.kt
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,18 @@ fun Cropify(
val density = LocalDensity.current
val tolerance = remember { density.run { 24.dp.toPx() } }
var touchRegion = remember<TouchRegion?> { null }
val frameFixedAspectRatio = option.frameSize as? CropifySize.FixedAspectRatio

BoxWithConstraints(
modifier = modifier
.pointerInput(bitmap, option.frameAspectRatio) {
.pointerInput(bitmap, frameFixedAspectRatio) {
detectDragGestures(
onDragStart = { touchRegion = detectTouchRegion(it, state.frameRect, tolerance) },
onDragEnd = { touchRegion = null }
) { change, dragAmount ->
touchRegion?.let {
when (it) {
is TouchRegion.Vertex -> state.scaleFrameRect(it, option.frameAspectRatio, dragAmount, tolerance * 2)
is TouchRegion.Vertex -> state.scaleFrameRect(it, frameFixedAspectRatio, dragAmount, tolerance * 2)
TouchRegion.Inside -> state.translateFrameRect(dragAmount)
}
change.consume()
Expand All @@ -106,10 +107,10 @@ fun Cropify(
onImageCropped(cropped)
}
}
LaunchedEffect(bitmap, option.frameAspectRatio, constraints) {
LaunchedEffect(bitmap, frameFixedAspectRatio, constraints) {
val canvasSize = Size(constraints.maxWidth.toFloat(), constraints.maxHeight.toFloat())
state.imageRect = calculateImagePosition(bitmap, canvasSize)
state.frameRect = calculateFrameRect(state.imageRect, canvasSize, option.frameAspectRatio)
state.frameRect = calculateFrameRect(state.imageRect, canvasSize, option.frameSize)
}
ImageCanvas(
bitmap = bitmap,
Expand Down Expand Up @@ -182,14 +183,23 @@ internal fun calculateImageSize(bitmap: ImageBitmap, canvasSize: Size): Size {
internal fun calculateFrameRect(
imageRect: Rect,
canvasSize: Size,
frameAspectRatio: AspectRatio?,
cropifySize: CropifySize?,
): Rect {
val shortSide = min(imageRect.width, imageRect.height)
return if (frameAspectRatio == null) {
return if (cropifySize == null) {
Rect(center = imageRect.center, radius = shortSide * 0.8f / 2)
} else {
val scale = shortSide / max(imageRect.width, imageRect.width * frameAspectRatio.value)
val size = Size(imageRect.width * scale * 0.8f, imageRect.width * scale * frameAspectRatio.value * 0.8f)
val size = when (cropifySize) {
is CropifySize.FixedAspectRatio -> {
val scale = shortSide / max(imageRect.width, imageRect.width * cropifySize.value)
Size(imageRect.width * scale * 0.8f, imageRect.width * scale * cropifySize.value * 0.8f)
}

is CropifySize.PercentageSize -> {
Size(imageRect.width * cropifySize.widthPercentage, imageRect.height * cropifySize.heightPercentage)
}
}

Rect(Offset((canvasSize.width - size.width) / 2, (canvasSize.height - size.height) / 2), size)
}
}
Expand Down
2 changes: 1 addition & 1 deletion cropify/src/main/java/io/moyuru/cropify/CropifyOption.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ data class CropifyOption(
val frameColor: Color = Color.White,
val frameAlpha: Float = 0.8f,
val frameWidth: Dp = 2.dp,
val frameAspectRatio: AspectRatio? = null,
val gridColor: Color = Color.White,
val gridAlpha: Float = 0.6f,
val gridWidth: Dp = 1.dp,
val maskColor: Color = Color.Black,
val maskAlpha: Float = 0.5f,
val backgroundColor: Color = Color.Black,
val frameSize: CropifySize? = null,
)
34 changes: 34 additions & 0 deletions cropify/src/main/java/io/moyuru/cropify/CropifySize.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.moyuru.cropify

import androidx.annotation.FloatRange

sealed interface CropifySize {

data class PercentageSize(
@FloatRange(0.0, 1.0, fromInclusive = false)
val widthPercentage: Float,
@FloatRange(0.0, 1.0, fromInclusive = false)
val heightPercentage: Float
) : CropifySize {

constructor(
@FloatRange(0.0, 1.0, fromInclusive = false)
percentage: Float
) : this(percentage, percentage)

init {
require(widthPercentage > 0f && widthPercentage <= 1f) { "widthPercentage must be more than 0f and less or equal to 1f" }
require(heightPercentage > 0f && heightPercentage <= 1f) { "heightPercentage must be more than 0f and less or equal to 1f" }
}

companion object {
val FullSize = PercentageSize(1f)
}

}

data class FixedAspectRatio(val value: Float) : CropifySize {
constructor(x: Int, y: Int) : this(y / x.toFloat())
constructor(x: Float, y: Float) : this(y / x)
}
}
4 changes: 2 additions & 2 deletions cropify/src/main/java/io/moyuru/cropify/CropifyState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ class CropifyState {

internal fun scaleFrameRect(
point: TouchRegion.Vertex,
aspectRatio: AspectRatio?,
fixedAspectRatio: CropifySize.FixedAspectRatio?,
amount: Offset,
minimumVertexDistance: Float
) {
frameRect = if (aspectRatio == null) scaleFlexibleRect(point, amount, minimumVertexDistance)
frameRect = if (fixedAspectRatio == null) scaleFlexibleRect(point, amount, minimumVertexDistance)
else scaleFixedAspectRatioRect(point, amount, minimumVertexDistance)
}

Expand Down

0 comments on commit 53447ef

Please sign in to comment.