You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tauri-plugins-workspace/plugins/camera/android/src/main/java/ImageUtils.kt

131 lines
4.0 KiB

// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
package app.tauri.camera
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Matrix
import android.net.Uri
import androidx.exifinterface.media.ExifInterface
import app.tauri.Logger
import java.io.IOException
import java.io.InputStream
import java.lang.Integer.min
import kotlin.math.roundToInt
object ImageUtils {
/**
* Resize an image to the given max width and max height. Constraint can be put
* on one dimension, or both. Resize will always preserve aspect ratio.
* @param bitmap
* @param desiredMaxWidth
* @param desiredMaxHeight
* @return a new, scaled Bitmap
*/
fun resize(bitmap: Bitmap, desiredMaxWidth: Int, desiredMaxHeight: Int): Bitmap {
return resizePreservingAspectRatio(bitmap, desiredMaxWidth, desiredMaxHeight)
}
/**
* Resize an image to the given max width and max height. Constraint can be put
* on one dimension, or both. Resize will always preserve aspect ratio.
* @param bitmap
* @param desiredMaxWidth
* @param desiredMaxHeight
* @return a new, scaled Bitmap
*/
private fun resizePreservingAspectRatio(
bitmap: Bitmap,
desiredMaxWidth: Int,
desiredMaxHeight: Int
): Bitmap {
val width = bitmap.width
val height = bitmap.height
// 0 is treated as 'no restriction'
val maxHeight = if (desiredMaxHeight == 0) height else desiredMaxHeight
val maxWidth = if (desiredMaxWidth == 0) width else desiredMaxWidth
// resize with preserved aspect ratio
var newWidth = min(width, maxWidth).toFloat()
var newHeight = height * newWidth / width
if (newHeight > maxHeight) {
newWidth = (width * maxHeight / height).toFloat()
newHeight = maxHeight.toFloat()
}
return Bitmap.createScaledBitmap(bitmap, newWidth.roundToInt(), newHeight.roundToInt(), false)
}
/**
* Transform an image with the given matrix
* @param bitmap
* @param matrix
* @return
*/
private fun transform(bitmap: Bitmap, matrix: Matrix): Bitmap {
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
}
/**
* Correct the orientation of an image by reading its exif information and rotating
* the appropriate amount for portrait mode
* @param bitmap
* @param imageUri
* @param exif
* @return
*/
@Throws(IOException::class)
fun correctOrientation(c: Context, bitmap: Bitmap, imageUri: Uri, exif: ExifWrapper): Bitmap {
val orientation = getOrientation(c, imageUri)
return if (orientation != 0) {
val matrix = Matrix()
matrix.postRotate(orientation.toFloat())
exif.resetOrientation()
transform(bitmap, matrix)
} else {
bitmap
}
}
@Throws(IOException::class)
private fun getOrientation(c: Context, imageUri: Uri): Int {
var result = 0
c.getContentResolver().openInputStream(imageUri).use { iStream ->
val exifInterface = ExifInterface(iStream!!)
val orientation: Int = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
if (orientation == ExifInterface.ORIENTATION_ROTATE_90) {
result = 90
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_180) {
result = 180
} else if (orientation == ExifInterface.ORIENTATION_ROTATE_270) {
result = 270
}
}
return result
}
fun getExifData(c: Context, bitmap: Bitmap?, imageUri: Uri): ExifWrapper {
var stream: InputStream? = null
try {
stream = c.getContentResolver().openInputStream(imageUri)
val exifInterface = ExifInterface(stream!!)
return ExifWrapper(exifInterface)
} catch (ex: IOException) {
Logger.error("Error loading exif data from image", ex)
} finally {
if (stream != null) {
try {
stream.close()
} catch (ignored: IOException) {
}
}
}
return ExifWrapper(null)
}
}