|
|
|
|
@ -1,19 +1,10 @@ |
|
|
|
|
package net.pokeranalytics.android.util |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import android.content.Context |
|
|
|
|
import android.content.Intent |
|
|
|
|
import android.graphics.* |
|
|
|
|
import android.graphics.Paint.FILTER_BITMAP_FLAG |
|
|
|
|
import android.media.ExifInterface |
|
|
|
|
import android.net.Uri |
|
|
|
|
import android.os.Environment |
|
|
|
|
import androidx.core.content.ContextCompat |
|
|
|
|
import kotlinx.coroutines.Dispatchers |
|
|
|
|
import kotlinx.coroutines.GlobalScope |
|
|
|
|
import kotlinx.coroutines.launch |
|
|
|
|
import net.pokeranalytics.android.R |
|
|
|
|
import timber.log.Timber |
|
|
|
|
import java.io.File |
|
|
|
|
import java.io.FileOutputStream |
|
|
|
|
import java.io.IOException |
|
|
|
|
@ -23,78 +14,7 @@ import java.util.* |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
object ImageUtils { |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Rotate a bitmap if it's necessary (depending of the EXIF data) |
|
|
|
|
* Some devices don't rotate the picture but instead add the orientation |
|
|
|
|
* value in the EXIF data. |
|
|
|
|
* That's why we need sometimes to rotate by ourselves the bitmap |
|
|
|
|
* |
|
|
|
|
* @param src The file to check (for getting the Exif data) |
|
|
|
|
* @param bitmap The bitmap to modify (if necessary) |
|
|
|
|
* @return The bitmap in the correct orientation |
|
|
|
|
*/ |
|
|
|
|
fun rotateBitmap(src: String, bitmap: Bitmap, updateFile: Boolean): Bitmap { |
|
|
|
|
try { |
|
|
|
|
val orientation = getExifOrientation(src) |
|
|
|
|
|
|
|
|
|
if (orientation == ExifInterface.ORIENTATION_NORMAL) { |
|
|
|
|
return bitmap |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
val matrix = Matrix() |
|
|
|
|
when (orientation) { |
|
|
|
|
ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> matrix.setScale(-1f, 1f) |
|
|
|
|
ExifInterface.ORIENTATION_ROTATE_180 -> matrix.setRotate(180f) |
|
|
|
|
ExifInterface.ORIENTATION_FLIP_VERTICAL -> { |
|
|
|
|
matrix.setRotate(180f) |
|
|
|
|
matrix.postScale(-1f, 1f) |
|
|
|
|
} |
|
|
|
|
ExifInterface.ORIENTATION_TRANSPOSE -> { |
|
|
|
|
matrix.setRotate(90f) |
|
|
|
|
matrix.postScale(-1f, 1f) |
|
|
|
|
} |
|
|
|
|
ExifInterface.ORIENTATION_ROTATE_90 -> matrix.setRotate(90f) |
|
|
|
|
ExifInterface.ORIENTATION_TRANSVERSE -> { |
|
|
|
|
matrix.setRotate(-90f) |
|
|
|
|
matrix.postScale(-1f, 1f) |
|
|
|
|
} |
|
|
|
|
ExifInterface.ORIENTATION_ROTATE_270 -> matrix.setRotate(-90f) |
|
|
|
|
else -> return bitmap |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
val oriented = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) |
|
|
|
|
bitmap.recycle() |
|
|
|
|
if (updateFile) { |
|
|
|
|
updateFile(src, oriented) |
|
|
|
|
} |
|
|
|
|
return oriented |
|
|
|
|
} catch (e: OutOfMemoryError) { |
|
|
|
|
e.printStackTrace() |
|
|
|
|
return bitmap |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} catch (e: IOException) { |
|
|
|
|
e.printStackTrace() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return bitmap |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Get the Exif orientation value |
|
|
|
|
* |
|
|
|
|
* @param filePath The path of the file |
|
|
|
|
* @return the orientation value |
|
|
|
|
* @throws IOException |
|
|
|
|
*/ |
|
|
|
|
@Throws(IOException::class) |
|
|
|
|
private fun getExifOrientation(filePath: String): Int { |
|
|
|
|
val exifInterface = ExifInterface(filePath) |
|
|
|
|
return exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Save a bitmap into a file (& apply 90% compression) |
|
|
|
|
* |
|
|
|
|
@ -117,46 +37,7 @@ object ImageUtils { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Resize a file with the given maximum width or height (and keep the ratio!) |
|
|
|
|
* @param filePath String: File path |
|
|
|
|
* @param bitmap Bitmap: Image |
|
|
|
|
* @param maxWidth int: Max width |
|
|
|
|
* @param maxHeight int: Max height |
|
|
|
|
*/ |
|
|
|
|
fun resizeFile(filePath: String, bitmap: Bitmap, maxWidth: Int, maxHeight: Int) { |
|
|
|
|
var bm = bitmap |
|
|
|
|
|
|
|
|
|
val options = BitmapFactory.Options() |
|
|
|
|
options.inJustDecodeBounds = true |
|
|
|
|
BitmapFactory.decodeFile(filePath, options) |
|
|
|
|
val imageWidth = options.outWidth |
|
|
|
|
val imageHeight = options.outHeight |
|
|
|
|
|
|
|
|
|
var newWidth: Int |
|
|
|
|
var newHeight: Int |
|
|
|
|
|
|
|
|
|
if (imageWidth > imageHeight) { |
|
|
|
|
newWidth = maxWidth |
|
|
|
|
newHeight = imageHeight * maxWidth / imageWidth |
|
|
|
|
if (newHeight > maxHeight) { |
|
|
|
|
newHeight = maxHeight |
|
|
|
|
newWidth = imageWidth * maxHeight / imageHeight |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
newHeight = maxHeight |
|
|
|
|
newWidth = imageWidth * maxHeight / imageHeight |
|
|
|
|
if (newWidth > maxWidth) { |
|
|
|
|
newWidth = maxWidth |
|
|
|
|
newHeight = imageHeight * maxWidth / imageWidth |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bm = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true) |
|
|
|
|
updateFile(filePath, bm) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Create a unique temp image file name |
|
|
|
|
* |
|
|
|
|
@ -266,37 +147,4 @@ object ImageUtils { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Save the bitmap in a file |
|
|
|
|
*/ |
|
|
|
|
fun saveBitmapInFile(context: Context, bitmap: Bitmap, filename: String, action: (filePath: String) -> Unit) { |
|
|
|
|
|
|
|
|
|
GlobalScope.launch { |
|
|
|
|
|
|
|
|
|
val outputFile = File(context.filesDir, filename) |
|
|
|
|
|
|
|
|
|
var out: FileOutputStream? = null |
|
|
|
|
try { |
|
|
|
|
out = FileOutputStream(outputFile.absolutePath) |
|
|
|
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out) // bmp is your Bitmap instance |
|
|
|
|
} catch (e: Exception) { |
|
|
|
|
e.printStackTrace() |
|
|
|
|
} finally { |
|
|
|
|
try { |
|
|
|
|
if (out != null) { |
|
|
|
|
out.close() |
|
|
|
|
} |
|
|
|
|
} catch (e: IOException) { |
|
|
|
|
e.printStackTrace() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
GlobalScope.launch(Dispatchers.Main) { |
|
|
|
|
Timber.d("Save file here: ${outputFile.absolutePath}") |
|
|
|
|
action(outputFile.absolutePath) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |