Android Resize Image Save to File Using Fresco (Kotlin)

October 25, 2019

Resize Image to File

fun resizeImageToFile(uri: Uri, size: Int, mimeType: String?, file: File): Boolean{
    val request = ImageRequestBuilder.newBuilderWithSource(uri)
        .setResizeOptions(ResizeOptions(size, size))
        .build()

    val imagePipeline = Fresco.getImagePipeline()
    val dataSource = imagePipeline.fetchDecodedImage(request, null)
    // the ResizeOptions in the imageRequest will be ignored for this fetch
    // val dataSource = imagePipeline.fetchEncodedImage(request, null)

    // val ref = dataSource.result
    val ref = DataSources.waitForFinalResult(dataSource)

    ref?.use { ref ->

        val image = ref.get()
        if (image is CloseableBitmap) {
            val bitmap = image.underlyingBitmap

            if (mimeType == "image/jpeg" || mimeType == null) {
                val outputStream = file.outputStream()
                outputStream.use { outputStream ->
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outputStream)
                    // https://caniuse.com/#feat=webp
                    // bitmap.compress(Bitmap.CompressFormat.WEBP, 85, outputStream)
                    return true
                }
            }
            // TODO - handle other file format
        }
    }

    return false
}

Usage

val filePath = ...
val file = File(filePath)
val outputFile = File(...)

if (file.exists()) {
    val mimeType = MediaUtils.extractMime(filePath)
    resizeImageToFile(Uri.fromFile(file), 1600, mimeType, outputFile)
}

Resize Image to InputStream

fun resizeImageToInputStream(uri: Uri, size: Int, mimeType: String?): InputStream? {
    val request = ImageRequestBuilder.newBuilderWithSource(uri)
        .setResizeOptions(ResizeOptions(size, size))
        .build()

    val imagePipeline = Fresco.getImagePipeline()
    val dataSource = imagePipeline.fetchDecodedImage(request, null)

    val ref = DataSources.waitForFinalResult(dataSource)

    ref?.use { ref ->
        val image = ref.get()
        if (image is CloseableBitmap) {
            val bitmap = image.underlyingBitmap

            if (mimeType == "image/jpeg" || mimeType == null) {
                val outputStream = ByteArrayOutputStream()
                // val inputStream = PipedInputStream()
                // val outputStream = PipedOutputStream(inputStream)
                outputStream.use { outputStream ->
                    bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outputStream)
                    // https://caniuse.com/#feat=webp
                    // bitmap.compress(Bitmap.CompressFormat.WEBP, 85, outputStream)

                    // toByteArray will trigger copy, thus double memory usage
                    // consider this solution: https://github.com/nickrussler/ByteArrayInOutStream
                    return ByteArrayInputStream(outputStream.toByteArray())
                }
            }
            // TODO - handle other file format
        }
    }

    return null
}

Another solution is via Piped Stream to save memory usage.

fun resizeImageToInputStream(uri: Uri, size: Int, mimeType: String?): InputStream? {
    val request = ImageRequestBuilder.newBuilderWithSource(uri)
        .setResizeOptions(ResizeOptions(size, size))
        .build()

    val imagePipeline = Fresco.getImagePipeline()
    val dataSource = imagePipeline.fetchDecodedImage(request, null)

    // val ref = dataSource.result
    val ref = DataSources.waitForFinalResult(dataSource)

    ref?.use { ref ->

        val image = ref.get()
        if (image is CloseableBitmap) {
            val bitmap = image.underlyingBitmap

            if (mimeType == "image/jpeg" || mimeType == null) {
                val inputStream = PipedInputStream()
                val outputStream = PipedOutputStream(inputStream)

                // using coroutines: need to launch this in a thread
                launch(Dispatchers.Default) {
                    outputStream.use { outputStream ->
                        // outputStream will not be written/complete until the inputStream is read
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 85, outputStream)
                    }
                }
                return inputStream
            }
        }
    }

    return null
}

NOTE: Refer Android Convert OutputStream to InputStream.

This work is licensed under a
Creative Commons Attribution-NonCommercial 4.0 International License.