Skip to content

Commit

Permalink
Merge pull request #55 from onseok/feature/resize-based-on-image-size
Browse files Browse the repository at this point in the history
[feature/resize-based-on-image-size] Implement Image Resizing Based on Image Size Threshold
  • Loading branch information
onseok authored Jan 27, 2024
2 parents b1677a0 + 38fa50e commit 5019698
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 35 deletions.
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# peekaboo
[![Kotlin](https://img.shields.io/badge/kotlin-1.9.21-blue.svg?logo=kotlin)](http://kotlinlang.org)
[![Compose Multiplatform](https://img.shields.io/badge/Compose%20Multiplatform-v1.5.11-blue)](https://github.com/JetBrains/compose-multiplatform)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.team-preat/peekaboo-image-picker?color=orange)](https://search.maven.org/search?q=g:io.github.team-preat)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.onseok/peekaboo-image-picker?color=orange)](https://search.maven.org/search?q=g:io.github.onseok)
[![Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)

[![Build](https://github.com/team-preat/peekaboo/actions/workflows/ci_check.yml/badge.svg)](https://github.com/team-preat/peekaboo/actions/workflows/ci_check.yml)
[![Build](https://github.com/onseok/peekaboo/actions/workflows/ci_check.yml/badge.svg)](https://github.com/onseok/peekaboo/actions/workflows/ci_check.yml)
![badge-android](http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat)
![badge-ios](http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat)
[![Featured in androidweekly.net](https://img.shields.io/badge/Featured%20in%20androidweekly.net-Issue%20%23601-4998C2)](https://androidweekly.net/issues/issue-601)
Expand Down Expand Up @@ -32,10 +32,10 @@ In your `commonMain` configuration, add the desired dependency, either **`peekab
commonMain {
dependencies {
// peekaboo-ui
implementation("io.github.team-preat:peekaboo-ui:$latest_version")
implementation("io.github.onseok:peekaboo-ui:$latest_version")

// peekaboo-image-picker
implementation("io.github.team-preat:peekaboo-image-picker:$latest_version")
implementation("io.github.onseok:peekaboo-image-picker:$latest_version")
}
}
```
Expand All @@ -47,11 +47,11 @@ First, define the version in `libs.versions.toml`:

```toml
[versions]
peekaboo = "0.4.2"
peekaboo = "0.4.3"

[libraries]
peekaboo-ui = { module = "io.github.team-preat:peekaboo-ui", version.ref = "peekaboo" }
peekaboo-image-picker = { module = "io.github.team-preat:peekaboo-image-picker", version.ref = "peekaboo" }
peekaboo-ui = { module = "io.github.onseok:peekaboo-ui", version.ref = "peekaboo" }
peekaboo-image-picker = { module = "io.github.onseok:peekaboo-image-picker", version.ref = "peekaboo" }
```

Then, in your `commonMain` configuration, reference the defined version:
Expand Down Expand Up @@ -204,17 +204,22 @@ Button(
<br/>

## Image Resizing Options
`peekaboo` offers customizable resizing options for both single and multiple image selections. <br/>
`peekaboo` offers customizable resizing options for both single and multiple image selections, along with a new feature to resize images only if they exceed a certain file size. <br/>
This feature allows you to resize the selected images to specific dimensions, optimizing them for your application's requirements and enhancing performance.

- The default resizing dimensions are set to `800 x 800` pixels.
- You can customize the resizing dimensions according to your needs.
- The default threshold for resizing is set to `1MB`, meaning images larger than this size will be resized.
- You can customize the resizing dimensions and threshold according to your needs.

### Usage
Set the `resizeOptions` parameter in `rememberImagePickerLauncher` with your desired dimensions:
Set the `resizeOptions` parameter in `rememberImagePickerLauncher` with your desired dimensions and threshold:

```kotlin
val resizeOptions = ResizeOptions(width = 1200, height = 1200) // Custom dimensions
val resizeOptions = ResizeOptions(
width = 1200, // Custom width
height = 1200, // Custom height
resizeThresholdBytes = 2 * 1024 * 1024L // Custom threshold for 2MB
)
```

#### Single Image Selection with Resizing
Expand Down Expand Up @@ -299,7 +304,7 @@ If you'd like to contribute, please feel free to create a PR or open an issue.
<br/>

## Stargazers :star:
Support it by joining __[stargazers](https://github.com/TEAM-PREAT/peekaboo/stargazers)__ for this repository. :star: <br>
Support it by joining __[stargazers](https://github.com/onseok/peekaboo/stargazers)__ for this repository. :star: <br>

## License

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ plugins {
}

allprojects {
group = "io.github.team-preat"
version = "0.4.2"
group = "io.github.onseok"
version = "0.4.3"
}

nexusPublishing {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private fun pickSingleImage(
uri = uri,
width = resizeOptions.width,
height = resizeOptions.height,
resizeThresholdBytes = resizeOptions.resizeThresholdBytes,
filterOptions = filterOptions,
) { resizedImage ->
if (resizedImage != null) {
Expand Down Expand Up @@ -127,6 +128,7 @@ private fun pickMultipleImages(
uri = uri,
width = resizeOptions.width,
height = resizeOptions.height,
resizeThresholdBytes = resizeOptions.resizeThresholdBytes,
filterOptions = filterOptions,
) { resizedImage ->
resizedImage?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import android.graphics.ColorMatrixColorFilter
import android.graphics.Matrix
import android.graphics.Paint
import android.net.Uri
import android.provider.OpenableColumns
import androidx.exifinterface.media.ExifInterface
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -38,17 +39,50 @@ internal object PeekabooImageResizer {
uri: Uri,
width: Int,
height: Int,
resizeThresholdBytes: Long,
filterOptions: FilterOptions,
onResult: (ByteArray?) -> Unit,
) {
coroutineScope.launch(Dispatchers.Default) {
val byteArray = resizeImage(context, uri, width, height, filterOptions)
withContext(Dispatchers.Main) {
onResult(byteArray)
if (getImageSize(context, uri) > resizeThresholdBytes) {
val byteArray = resizeImage(context, uri, width, height, filterOptions)
withContext(Dispatchers.Main) {
onResult(byteArray)
}
} else {
withContext(Dispatchers.Main) {
onResult(getOriginalImageByteArray(context, uri))
}
}
}
}

private fun getImageSize(
context: Context,
uri: Uri,
): Int {
val cursor = context.contentResolver.query(uri, null, null, null, null)
val sizeIndex = cursor?.getColumnIndex(OpenableColumns.SIZE)
cursor?.moveToFirst()
val size = sizeIndex?.let { cursor.getInt(it) } ?: 0
cursor?.close()
return size
}

private fun getOriginalImageByteArray(
context: Context,
uri: Uri,
): ByteArray? {
return context.contentResolver.openInputStream(uri)?.use { inputStream ->
val bitmap = BitmapFactory.decodeStream(inputStream)
val rotatedBitmap = rotateImageIfRequired(context, bitmap, uri)

ByteArrayOutputStream().apply {
rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, this)
}.toByteArray()
}
}

private suspend fun resizeImage(
context: Context,
uri: Uri,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ package com.preat.peekaboo.image.picker
import androidx.compose.runtime.Composable
import kotlinx.coroutines.CoroutineScope

private const val DEFAULT_RESIZE_IMAGE_WIDTH = 800
private const val DEFAULT_RESIZE_IMAGE_HEIGHT = 800
private const val DEFAULT_RESIZE_THRESHOLD_BYTES = 1048576L // 1MB

@Composable
expect fun rememberImagePickerLauncher(
selectionMode: SelectionMode = SelectionMode.Single,
Expand All @@ -38,8 +42,9 @@ sealed class SelectionMode {
}

data class ResizeOptions(
val width: Int = 800,
val height: Int = 800,
val width: Int = DEFAULT_RESIZE_IMAGE_WIDTH,
val height: Int = DEFAULT_RESIZE_IMAGE_HEIGHT,
val resizeThresholdBytes: Long = DEFAULT_RESIZE_THRESHOLD_BYTES,
)

sealed interface FilterOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ actual fun rememberImagePickerLauncher(
image?.fitInto(
resizeOptions.width,
resizeOptions.height,
resizeOptions.resizeThresholdBytes,
filterOptions,
)
val bytes = resizedImage?.toByteArray()
Expand Down Expand Up @@ -132,27 +133,33 @@ private fun UIImage.toByteArray(): ByteArray {
private fun UIImage.fitInto(
maxWidth: Int,
maxHeight: Int,
resizeThresholdBytes: Long,
filterOptions: FilterOptions,
): UIImage {
val originalWidth = this.size.useContents { width }
val originalHeight = this.size.useContents { height }
val originalRatio = originalWidth / originalHeight

val targetRatio = maxWidth.toDouble() / maxHeight.toDouble()
val scale =
if (originalRatio > targetRatio) {
maxWidth.toDouble() / originalWidth
} else {
maxHeight.toDouble() / originalHeight
}
val imageData = this.toByteArray()
if (imageData.size > resizeThresholdBytes) {
val originalWidth = this.size.useContents { width }
val originalHeight = this.size.useContents { height }
val originalRatio = originalWidth / originalHeight

val targetRatio = maxWidth.toDouble() / maxHeight.toDouble()
val scale =
if (originalRatio > targetRatio) {
maxWidth.toDouble() / originalWidth
} else {
maxHeight.toDouble() / originalHeight
}

val newWidth = originalWidth * scale
val newHeight = originalHeight * scale
val newWidth = originalWidth * scale
val newHeight = originalHeight * scale

val targetSize = CGSizeMake(newWidth, newHeight)
val resizedImage = this.resize(targetSize)
val targetSize = CGSizeMake(newWidth, newHeight)
val resizedImage = this.resize(targetSize)

return applyFilterToUIImage(resizedImage, filterOptions)
return applyFilterToUIImage(resizedImage, filterOptions)
} else {
return this
}
}

@OptIn(ExperimentalForeignApi::class)
Expand Down

0 comments on commit 5019698

Please sign in to comment.