Fix: WebP images can't be sent as media. (#1501)
* Fix: WebP images can't be sent as media. * Place the `BitmapFactory.Options` mode change and comment where it belongs.
This commit is contained in:
parent
6f73a28699
commit
ffef9d936a
4 changed files with 27 additions and 20 deletions
1
changelog.d/1483.bugfix
Normal file
1
changelog.d/1483.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
|||
WebP images can't be sent as media.
|
||||
|
|
@ -31,6 +31,7 @@ object MimeTypes {
|
|||
const val BadJpg = "image/jpg"
|
||||
const val Jpeg = "image/jpeg"
|
||||
const val Gif = "image/gif"
|
||||
const val WebP = "image/webp"
|
||||
|
||||
const val Videos = "video/*"
|
||||
const val Mp4 = "video/mp4"
|
||||
|
|
|
|||
|
|
@ -66,6 +66,8 @@ class AndroidMediaPreProcessor @Inject constructor(
|
|||
* values may surpass this limit. (i.e.: an image of `480x3000px` would have `inSampleSize=1` and be sent as is).
|
||||
*/
|
||||
private const val IMAGE_SCALE_REF_SIZE = 640
|
||||
|
||||
private val notCompressibleImageTypes = listOf(MimeTypes.Gif, MimeTypes.WebP)
|
||||
}
|
||||
|
||||
private val contentResolver = context.contentResolver
|
||||
|
|
@ -78,7 +80,10 @@ class AndroidMediaPreProcessor @Inject constructor(
|
|||
): Result<MediaUploadInfo> = withContext(coroutineDispatchers.computation) {
|
||||
runCatching {
|
||||
val result = when {
|
||||
mimeType.isMimeTypeImage() -> processImage(uri, mimeType, compressIfPossible && mimeType != MimeTypes.Gif)
|
||||
mimeType.isMimeTypeImage() -> {
|
||||
val shouldBeCompressed = compressIfPossible && mimeType !in notCompressibleImageTypes
|
||||
processImage(uri, mimeType, shouldBeCompressed)
|
||||
}
|
||||
mimeType.isMimeTypeVideo() -> processVideo(uri, mimeType, compressIfPossible)
|
||||
mimeType.isMimeTypeAudio() -> processAudio(uri, mimeType)
|
||||
else -> processFile(uri, mimeType)
|
||||
|
|
@ -125,13 +130,11 @@ class AndroidMediaPreProcessor @Inject constructor(
|
|||
exifInterface?.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED)
|
||||
} ?: ExifInterface.ORIENTATION_UNDEFINED
|
||||
|
||||
val compressionResult = contentResolver.openInputStream(uri).use { input ->
|
||||
imageCompressor.compressToTmpFile(
|
||||
inputStream = requireNotNull(input),
|
||||
resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE),
|
||||
orientation = orientation,
|
||||
).getOrThrow()
|
||||
}
|
||||
val compressionResult = imageCompressor.compressToTmpFile(
|
||||
inputStreamProvider = { contentResolver.openInputStream(uri)!! },
|
||||
resizeMode = ResizeMode.Approximate(IMAGE_SCALE_REF_SIZE, IMAGE_SCALE_REF_SIZE),
|
||||
orientation = orientation,
|
||||
).getOrThrow()
|
||||
val thumbnailResult: ThumbnailResult = thumbnailFactory.createImageThumbnail(compressionResult.file)
|
||||
val imageInfo = compressionResult.toImageInfo(
|
||||
mimeType = mimeType,
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import io.element.android.libraries.androidutils.file.createTmpFile
|
|||
import io.element.android.libraries.di.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
|
|
@ -42,14 +41,14 @@ class ImageCompressor @Inject constructor(
|
|||
* @return a [Result] containing the resulting [ImageCompressionResult] with the temporary [File] and some metadata.
|
||||
*/
|
||||
suspend fun compressToTmpFile(
|
||||
inputStream: InputStream,
|
||||
inputStreamProvider: () -> InputStream,
|
||||
resizeMode: ResizeMode,
|
||||
format: Bitmap.CompressFormat = Bitmap.CompressFormat.JPEG,
|
||||
orientation: Int = ExifInterface.ORIENTATION_UNDEFINED,
|
||||
desiredQuality: Int = 80,
|
||||
): Result<ImageCompressionResult> = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val compressedBitmap = compressToBitmap(inputStream, resizeMode, orientation).getOrThrow()
|
||||
val compressedBitmap = compressToBitmap(inputStreamProvider, resizeMode, orientation).getOrThrow()
|
||||
// Encode bitmap to the destination temporary file
|
||||
val tmpFile = context.createTmpFile(extension = "jpeg")
|
||||
tmpFile.outputStream().use {
|
||||
|
|
@ -65,17 +64,24 @@ class ImageCompressor @Inject constructor(
|
|||
}
|
||||
|
||||
/**
|
||||
* Decodes the [inputStream] into a [Bitmap] and applies the needed transformations (rotation, scale) based on [resizeMode] and [orientation].
|
||||
* Decodes the inputStream from [inputStreamProvider] into a [Bitmap] and applies the needed transformations (rotation, scale)
|
||||
* based on [resizeMode] and [orientation].
|
||||
* @return a [Result] containing the resulting [Bitmap].
|
||||
*/
|
||||
fun compressToBitmap(
|
||||
inputStream: InputStream,
|
||||
inputStreamProvider: () -> InputStream,
|
||||
resizeMode: ResizeMode,
|
||||
orientation: Int,
|
||||
): Result<Bitmap> = runCatching {
|
||||
BufferedInputStream(inputStream).use { input ->
|
||||
val options = BitmapFactory.Options()
|
||||
val options = BitmapFactory.Options()
|
||||
// Decode bounds
|
||||
inputStreamProvider().use { input ->
|
||||
calculateDecodingScale(input, resizeMode, options)
|
||||
}
|
||||
// Decode the actual bitmap
|
||||
inputStreamProvider().use { input ->
|
||||
// Now read the actual image and rotate it to match its metadata
|
||||
options.inJustDecodeBounds = false
|
||||
val decodedBitmap = BitmapFactory.decodeStream(input, null, options)
|
||||
?: error("Decoding Bitmap from InputStream failed")
|
||||
val rotatedBitmap = decodedBitmap.rotateToMetadataOrientation(orientation)
|
||||
|
|
@ -88,7 +94,7 @@ class ImageCompressor @Inject constructor(
|
|||
}
|
||||
|
||||
private fun calculateDecodingScale(
|
||||
inputStream: BufferedInputStream,
|
||||
inputStream: InputStream,
|
||||
resizeMode: ResizeMode,
|
||||
options: BitmapFactory.Options
|
||||
) {
|
||||
|
|
@ -98,14 +104,10 @@ class ImageCompressor @Inject constructor(
|
|||
is ResizeMode.None -> return
|
||||
}
|
||||
// Read bounds only
|
||||
inputStream.mark(inputStream.available())
|
||||
options.inJustDecodeBounds = true
|
||||
BitmapFactory.decodeStream(inputStream, null, options)
|
||||
// Set sample size based on the outWidth and outHeight
|
||||
options.inSampleSize = options.calculateInSampleSize(width, height)
|
||||
// Now read the actual image and rotate it to match its metadata
|
||||
inputStream.reset()
|
||||
options.inJustDecodeBounds = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue