From 6eaae47140cb5266559b65e33fc9ac0475472969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Gerber?= Date: Fri, 17 Sep 2021 16:45:47 +0200 Subject: [PATCH 1/4] Comply with EXIF settings when editing a picture --- .../photoEdit/FilterListFragment.kt | 18 +++++++++++------- .../photoEdit/PhotoEditActivity.kt | 15 ++++++++++++++- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt index fd449696..39540f7a 100644 --- a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt +++ b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt @@ -1,6 +1,8 @@ package org.pixeldroid.app.postCreation.photoEdit import android.graphics.Bitmap +import android.graphics.ImageDecoder +import android.os.Build import android.os.Bundle import android.provider.MediaStore import android.util.TypedValue @@ -54,17 +56,19 @@ class FilterListFragment : Fragment() { private fun displayImage(bitmap: Bitmap?) { val r = Runnable { val tbImage: Bitmap = (if (bitmap == null) { - // TODO: Shouldn't use deprecated API on newer versions of Android, - // but the proper way to do it seems to crash for OpenGL reasons - //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - // ImageDecoder.decodeBitmap( - // ImageDecoder.createSource(requireActivity().contentResolver, PhotoEditActivity.imageUri!!)) - //} else { + // TODO: Check that there is no crash for OpenGL reasons on newer versions of Android + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // Honor EXIF orientation if API >= 28 + ImageDecoder.decodeBitmap(ImageDecoder + .createSource(requireActivity().contentResolver, PhotoEditActivity.imageUri!!)) + .copy(Bitmap.Config.ARGB_8888,true) + } else { + // Ignore EXIF orientation otherwise MediaStore.Images.Media.getBitmap( requireActivity().contentResolver, PhotoEditActivity.imageUri ) - //} + } } else { Bitmap.createScaledBitmap(bitmap, 100, 100, false) }) diff --git a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt index c1abd83a..470041fc 100644 --- a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt @@ -5,9 +5,12 @@ import android.app.AlertDialog import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.graphics.ImageDecoder import android.graphics.Point import android.graphics.drawable.BitmapDrawable import android.net.Uri +import android.os.Build import android.os.Bundle import android.provider.MediaStore import android.view.Menu @@ -116,7 +119,17 @@ class PhotoEditActivity : BaseActivity() { private fun loadImage() { - originalImage = MediaStore.Images.Media.getBitmap(contentResolver, imageUri) + // TODO: Check that there is no crash for OpenGL reasons on newer versions of Android + originalImage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // Honor EXIF orientation if API >= 28 + ImageDecoder + .decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri!!)) + .copy(BITMAP_CONFIG,true) + } else { + // Ignore EXIF orientation otherwise + MediaStore.Images.Media.getBitmap(contentResolver, imageUri) + } + compressedImage = resizeImage(originalImage!!) compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true) filteredImage = compressedImage!!.copy(BITMAP_CONFIG, true) From ad0729686b215f978d080a894f7c9672dbb11f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Gerber?= Date: Fri, 17 Sep 2021 16:47:54 +0200 Subject: [PATCH 2/4] Remove useless import --- .../pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt index 470041fc..1bbbe420 100644 --- a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt @@ -5,7 +5,6 @@ import android.app.AlertDialog import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.graphics.ImageDecoder import android.graphics.Point import android.graphics.drawable.BitmapDrawable From 73fc84ad7595ae9f0fc307235bef0ed450e1cee9 Mon Sep 17 00:00:00 2001 From: Matthieu <24-artectrex@users.noreply.shinice.net> Date: Sat, 18 Sep 2021 13:11:10 +0200 Subject: [PATCH 3/4] Refactor bitmap and make less copies --- .../photoEdit/FilterListFragment.kt | 15 ++------------ .../photoEdit/PhotoEditActivity.kt | 16 ++------------- .../java/org/pixeldroid/app/utils/Utils.kt | 20 +++++++++++++++---- 3 files changed, 20 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt index 39540f7a..1d02bbc7 100644 --- a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt +++ b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/FilterListFragment.kt @@ -18,6 +18,7 @@ import com.zomato.photofilters.FilterPack import com.zomato.photofilters.imageprocessors.Filter import com.zomato.photofilters.utils.ThumbnailItem import com.zomato.photofilters.utils.ThumbnailsManager +import org.pixeldroid.app.utils.bitmapFromUri class FilterListFragment : Fragment() { @@ -56,19 +57,7 @@ class FilterListFragment : Fragment() { private fun displayImage(bitmap: Bitmap?) { val r = Runnable { val tbImage: Bitmap = (if (bitmap == null) { - // TODO: Check that there is no crash for OpenGL reasons on newer versions of Android - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - // Honor EXIF orientation if API >= 28 - ImageDecoder.decodeBitmap(ImageDecoder - .createSource(requireActivity().contentResolver, PhotoEditActivity.imageUri!!)) - .copy(Bitmap.Config.ARGB_8888,true) - } else { - // Ignore EXIF orientation otherwise - MediaStore.Images.Media.getBitmap( - requireActivity().contentResolver, - PhotoEditActivity.imageUri - ) - } + bitmapFromUri(requireActivity().contentResolver, PhotoEditActivity.imageUri) } else { Bitmap.createScaledBitmap(bitmap, 100, 100, false) }) diff --git a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt index 1bbbe420..ca0847db 100644 --- a/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt +++ b/app/src/main/java/org/pixeldroid/app/postCreation/photoEdit/PhotoEditActivity.kt @@ -5,13 +5,10 @@ import android.app.AlertDialog import android.content.Intent import android.content.pm.PackageManager import android.graphics.Bitmap -import android.graphics.ImageDecoder import android.graphics.Point import android.graphics.drawable.BitmapDrawable import android.net.Uri -import android.os.Build import android.os.Bundle -import android.provider.MediaStore import android.view.Menu import android.view.MenuItem import android.view.View.GONE @@ -30,6 +27,7 @@ import com.zomato.photofilters.imageprocessors.Filter import com.zomato.photofilters.imageprocessors.subfilters.BrightnessSubFilter import com.zomato.photofilters.imageprocessors.subfilters.ContrastSubFilter import com.zomato.photofilters.imageprocessors.subfilters.SaturationSubfilter +import org.pixeldroid.app.utils.bitmapFromUri import java.io.File import java.io.IOException import java.io.OutputStream @@ -116,18 +114,8 @@ class PhotoEditActivity : BaseActivity() { binding.tabs.setupWithViewPager(binding.viewPager) } - private fun loadImage() { - // TODO: Check that there is no crash for OpenGL reasons on newer versions of Android - originalImage = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - // Honor EXIF orientation if API >= 28 - ImageDecoder - .decodeBitmap(ImageDecoder.createSource(contentResolver, imageUri!!)) - .copy(BITMAP_CONFIG,true) - } else { - // Ignore EXIF orientation otherwise - MediaStore.Images.Media.getBitmap(contentResolver, imageUri) - } + originalImage = bitmapFromUri(contentResolver, imageUri) compressedImage = resizeImage(originalImage!!) compressedOriginalImage = compressedImage!!.copy(BITMAP_CONFIG, true) diff --git a/app/src/main/java/org/pixeldroid/app/utils/Utils.kt b/app/src/main/java/org/pixeldroid/app/utils/Utils.kt index 8769881b..0cf84d4f 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/Utils.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/Utils.kt @@ -1,13 +1,13 @@ package org.pixeldroid.app.utils -import android.content.ActivityNotFoundException -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences +import android.content.* import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.ImageDecoder import android.net.ConnectivityManager import android.net.Uri import android.os.Build +import android.provider.MediaStore import android.util.DisplayMetrics import android.view.WindowManager import androidx.appcompat.app.AppCompatDelegate @@ -63,6 +63,18 @@ fun normalizeDomain(domain: String): String { .trim(Char::isWhitespace) } +fun bitmapFromUri(contentResolver: ContentResolver, uri: Uri?): Bitmap = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + ImageDecoder + .decodeBitmap( + ImageDecoder.createSource(contentResolver, uri!!) + ) + { decoder, _, _ -> decoder.isMutableRequired = true } + } else { + //FIXME EXIF orientation is ignored by getBitmap, respect it manually? + MediaStore.Images.Media.getBitmap(contentResolver, uri) + } + fun BaseActivity.openUrl(url: String): Boolean{ val intent = CustomTabsIntent.Builder().build() From d64728d8edbdb72dfacbc26605eea9235c779312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Gerber?= Date: Thu, 23 Sep 2021 22:35:20 +0200 Subject: [PATCH 4/4] Consider EXIF orientation manually for APIs < 28 Co-authored-by: Matthieu <24-artectrex@users.noreply.shinice.net> --- .../java/org/pixeldroid/app/utils/Utils.kt | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/pixeldroid/app/utils/Utils.kt b/app/src/main/java/org/pixeldroid/app/utils/Utils.kt index 0cf84d4f..95720644 100644 --- a/app/src/main/java/org/pixeldroid/app/utils/Utils.kt +++ b/app/src/main/java/org/pixeldroid/app/utils/Utils.kt @@ -4,6 +4,7 @@ import android.content.* import android.content.res.Resources import android.graphics.Bitmap import android.graphics.ImageDecoder +import android.graphics.Matrix import android.net.ConnectivityManager import android.net.Uri import android.os.Build @@ -12,13 +13,14 @@ import android.util.DisplayMetrics import android.view.WindowManager import androidx.appcompat.app.AppCompatDelegate import androidx.browser.customtabs.CustomTabsIntent +import androidx.exifinterface.media.ExifInterface import androidx.fragment.app.Fragment import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import org.pixeldroid.app.R import okhttp3.HttpUrl +import org.pixeldroid.app.R import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty @@ -71,10 +73,39 @@ fun bitmapFromUri(contentResolver: ContentResolver, uri: Uri?): Bitmap = ) { decoder, _, _ -> decoder.isMutableRequired = true } } else { - //FIXME EXIF orientation is ignored by getBitmap, respect it manually? - MediaStore.Images.Media.getBitmap(contentResolver, uri) + val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri) + modifyOrientation(bitmap!!, contentResolver, uri!!) } +fun modifyOrientation( + bitmap: Bitmap, + contentResolver: ContentResolver, + uri: Uri +): Bitmap { + val inputStream = contentResolver.openInputStream(uri)!! + val ei = ExifInterface(inputStream) + return when (ei.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)) { + ExifInterface.ORIENTATION_ROTATE_90 -> bitmap.rotate(90f) + ExifInterface.ORIENTATION_ROTATE_180 -> bitmap.rotate(180f) + ExifInterface.ORIENTATION_ROTATE_270 -> bitmap.rotate(270f) + ExifInterface.ORIENTATION_FLIP_HORIZONTAL -> bitmap.flip(horizontal = true, vertical = false) + ExifInterface.ORIENTATION_FLIP_VERTICAL -> bitmap.flip(horizontal = false, vertical = true) + else -> bitmap + } +} + +fun Bitmap.rotate(degrees: Float): Bitmap { + val matrix = Matrix() + matrix.postRotate(degrees) + return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) +} + +fun Bitmap.flip(horizontal: Boolean, vertical: Boolean): Bitmap { + val matrix = Matrix() + matrix.preScale(if (horizontal) -1f else 1f, if (vertical) -1f else 1f) + return Bitmap.createBitmap(this, 0, 0, width, height, matrix, true) +} + fun BaseActivity.openUrl(url: String): Boolean{ val intent = CustomTabsIntent.Builder().build()