Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for HEIC images in Compose Multiplatform #2318

Closed
antailyaqwer opened this issue Jun 16, 2024 · 1 comment
Closed

Add support for HEIC images in Compose Multiplatform #2318

antailyaqwer opened this issue Jun 16, 2024 · 1 comment
Labels
enhancement New feature or request

Comments

@antailyaqwer
Copy link

Is your feature request related to a problem? Please describe.
In skia (as well as in skiko) HEIC format doesn't supported.
This leads to incorrect flow on modern ios devices for image rendering (for example, custom media picker).

My idea is to add support of it inside a library.

Describe the solution you'd like
I have a simple workaround of this problem: convert heic image to jpeg.
My solution isn't elegant enough as I don't have access to coil internal methods.
I think there is a better way to support it

var builder = ImageRequest.Builder(LocalPlatformContext.current)
    .data(uri) // uri is usually a path. For example, /var/storage/image.HEIC

if (uri.substringAfterLast('.').equals("HEIC", ignoreCase = true)) {
    builder = builder.decoderFactory { result, options, imageLoader ->
        HEICImageDecoderFactory().create(result, options, imageLoader)
    }
}

AsyncImage(
    model = builder.build(),
    contentDescription = null,
)


expect class HEICImageDecoder(
    source: ImageSource,
    options: Options,
) : Decoder

// Decoder logic
class HEICImageDecoderFactory : Decoder.Factory {

    override fun create(
        result: SourceFetchResult,
        options: Options,
        imageLoader: ImageLoader,
    ): Decoder {
        return HEICImageDecoder(result.source, options)
    }
}


// ios implementation of expect class
private const val COMPRESSION = 0.3

actual class HEICImageDecoder actual constructor(
    private val source: ImageSource,
    private val options: Options
) : Decoder {

    @OptIn(ExperimentalCoilApi::class)
    override suspend fun decode(): DecodeResult {
        val originalBytes = source.source().use { it.readByteArray() }
        val jpegBytes = originalBytes.toJpegBytes()
        val image = Image.makeFromEncoded(jpegBytes)

        val isSampled: Boolean
        val bitmap: Bitmap
        try {
            bitmap = Bitmap.makeFromImage(image, options)
            bitmap.setImmutable()
            isSampled = bitmap.width < image.width || bitmap.height < image.height
        } finally {
            image.close()
        }

        return DecodeResult(
            image = bitmap.asCoilImage(),
            isSampled = isSampled,
        )
    }
}

@OptIn(ExperimentalForeignApi::class)
private fun ByteArray.toJpegBytes(): ByteArray = memScoped {
    val image = UIImage(data = this@toJpegBytes.toNSData())

    return UIImageJPEGRepresentation(image, compressionQuality = COMPRESSION)!!.toByteArray()
}
@antailyaqwer antailyaqwer added the enhancement New feature or request label Jun 16, 2024
@colinrtwhite
Copy link
Member

I think this is best implemented as an external library to Coil since it could be implemented with a custom Decoder.Factory and doesn't require access to Coil's internals. HEIC is a complex format and adding support for it on all of Coil's platforms would be a large task.

@colinrtwhite colinrtwhite closed this as not planned Won't fix, can't repro, duplicate, stale Jun 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants