Code Monkey home page Code Monkey logo

compose-imageloader's Introduction

Hi there 👋

My name is Seiko, an Android software engineer.

  • 🌱 I'm currently learning ComposeUI
  • 📫 How to reach me:
  • ⚡ I'm not a computer major, just like programming.

Anurag's GitHub stats

compose-imageloader's People

Contributors

datl4g avatar joesteven avatar qdsfdhvh avatar renovate[bot] avatar sebastianaigner avatar syer10 avatar tadeaskriz avatar taehoonleee avatar terrakok avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

compose-imageloader's Issues

Lazy Column

Hi
I want use imageloader in Lazy Column
how to use that?

Support Nine-patch images

Hello, one of the needs that may arise when using the image library is to be able to display 9-patch images - which can stretch while maintaining angles. My quick glance at Compose suggests that there is no low-level option to draw Nine-patch, unlike Flutter, where there is a special command.
Screenshot from 2023-10-03 23-24-20

I had such a need and I made a simple draft version of NinePatchPainter for my tasks. Here you can see an example.

I will leave the source code here in case there is a desire to modify it and integrate it into the library.
Maybe it can become a starting point for the implementation of Nine-patch images.

class NinePatchPainter(
    private val image: ImageBitmap,
    private val centerSlice: CenterRect,
    private val scale: Float = 1f,
    private val filterQuality: FilterQuality = FilterQuality.Medium,
) : Painter() {

    private val centerWidth = max(1, centerSlice.width)
    private val centerHeight = max(1, centerSlice.height)
    private val widthLeft = centerSlice.left
    private val widthRight = image.width - centerSlice.right
    private val heightTop = centerSlice.top
    private val heightBottom = image.height - centerSlice.bottom

    // Source Offset
    private val offsetTopLeft = IntOffset.Zero
    private val offsetTop = IntOffset(widthLeft, 0)
    private val offsetTopRight = IntOffset(centerSlice.right, 0)
    private val offsetLeft = IntOffset(0, heightTop)
    private val offsetCenter = IntOffset(centerSlice.left, heightTop)
    private val offsetRight = IntOffset(centerSlice.right, heightTop)
    private val offsetBottomLeft = IntOffset(0, centerSlice.bottom)
    private val offsetBottom = IntOffset(centerSlice.left, centerSlice.bottom)
    private val offsetBottomRight = IntOffset(centerSlice.right, centerSlice.bottom)

    // Source Size
    private val sizeTopLeft = IntSize(widthLeft, heightTop)
    private val sizeTop = IntSize(centerWidth, heightTop)
    private val sizeTopRight = IntSize(widthRight, heightTop)
    private val sizeLeft = IntSize(widthLeft, centerHeight)
    private val sizeCenter = IntSize(centerWidth, centerHeight)
    private val sizeRight = IntSize(widthRight, centerHeight)
    private val sizeBottomLeft = IntSize(widthLeft, heightBottom)
    private val sizeBottom = IntSize(centerWidth, heightBottom)
    private val sizeBottomRight = IntSize(widthRight, heightBottom)

    private val size: IntSize = IntSize(image.width, image.height) // validateSize(IntOffset.Zero, surfaceSize)
    private var alpha: Float = 1.0f
    private var colorFilter: ColorFilter? = null

    override fun DrawScope.onDraw() {
        if (image.width < centerSlice.right || image.height < centerSlice.bottom) return println("incorrect bitmap")
        val drawWidth = [email protected]()
        val drawHeight = [email protected]()
        val factor = min(drawWidth / image.width.toFloat(), drawHeight / image.height.toFloat())
        val drawScale = min(1f, factor * scale)
        val scaleLeft = max(1, (widthLeft * drawScale).toInt())
        val scaleRight = max(1, (widthRight * drawScale).toInt())
        val scaleTop = max(1, (heightTop * drawScale).toInt())
        val scaleBottom = max(1, (heightBottom * drawScale).toInt())
        val scaleWidth = max(1, drawWidth - scaleLeft - scaleRight)
        val scaleHeight = max(1, drawHeight - scaleTop - scaleBottom)

        // Center
        drawImage(
            image, offsetCenter, sizeCenter,
            dstOffset = IntOffset(scaleLeft, scaleTop),
            dstSize = IntSize(scaleWidth, scaleHeight),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // Top
        drawImage(
            image, offsetTop, sizeTop,
            dstOffset = IntOffset(scaleLeft, 0),
            dstSize = IntSize(scaleWidth, scaleTop),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // Left
        drawImage(
            image, offsetLeft, sizeLeft,
            dstOffset = IntOffset(0, scaleTop),
            dstSize = IntSize(scaleLeft, scaleHeight),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // Right
        drawImage(
            image, offsetRight, sizeRight,
            dstOffset = IntOffset(drawWidth - scaleRight, scaleTop),
            dstSize = IntSize(scaleRight, scaleHeight),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // Bottom
        drawImage(
            image, offsetBottom, sizeBottom,
            dstOffset = IntOffset(scaleLeft, drawHeight - scaleBottom),
            dstSize = IntSize(scaleWidth, scaleBottom),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // TopLeft
        drawImage(
            image, offsetTopLeft, sizeTopLeft,
            dstOffset = IntOffset.Zero,
            dstSize = IntSize(scaleLeft, scaleTop),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // TopRight
        drawImage(
            image, offsetTopRight, sizeTopRight,
            dstOffset = IntOffset(drawWidth - scaleRight, 0),
            dstSize = IntSize(scaleRight, scaleTop),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // BottomLeft
        drawImage(
            image, offsetBottomLeft, sizeBottomLeft,
            dstOffset = IntOffset(0, drawHeight - scaleBottom),
            dstSize = IntSize(scaleLeft, scaleBottom),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
        // BottomRight
        drawImage(
            image, offsetBottomRight, sizeBottomRight,
            dstOffset = IntOffset(drawWidth - scaleRight, drawHeight - scaleBottom),
            dstSize = IntSize(scaleRight, scaleBottom),
            alpha = alpha, colorFilter = colorFilter, filterQuality = filterQuality
        )
    }

    override val intrinsicSize: Size get() = size.toSize()

    override fun applyAlpha(alpha: Float): Boolean {
        this.alpha = alpha
        return true
    }

    override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
        this.colorFilter = colorFilter
        return true
    }

    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (other !is NinePatchPainter) return false
        if (image != other.image) return false
        if (centerSlice != other.centerSlice) return false
        if (scale != other.scale) return false
        if (filterQuality != other.filterQuality) return false
        return true
    }

    override fun hashCode(): Int {
        var result = image.hashCode()
        result = 31 * result + centerSlice.hashCode()
        result = 31 * result + size.hashCode()
        result = 31 * result + scale.hashCode()
        result = 31 * result + filterQuality.hashCode()
        return result
    }

    override fun toString(): String {
        return "NinePathPainter(image=$image, centerSlice=$centerSlice, scale=$scale)"
    }

}
@Immutable
class CenterRect(left: Int, top: Int, right: Int, bottom: Int) {
    @Stable
    val left: Int = max(0, left)

    @Stable
    val top: Int = max(0, top)

    @Stable
    val right: Int = max(0, right)

    @Stable
    val bottom: Int = max(0, bottom)

    @Stable
    val width: Int get() = max(1, right - left)

    @Stable
    val height: Int get() = max(1, bottom - top)

    override fun toString(): String {
        return "CenterRect{$left, $top, $right, $bottom}"
    }
}
@Composable
fun rememberNinePatchPainter(
    action: ImageAction,
    center: CenterRect,
    scale: Float = 1f,
    editor: Boolean,
    filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
    placeholderPainter: (@Composable () -> Painter)? = null,
    errorPainter: (@Composable () -> Painter)? = null,
): Painter {
    return when (action) {
        is ImageEvent -> placeholderPainter?.invoke() ?: EmptyPainter
        is ImageResult -> {
            return when (action) {
                is ImageResult.Painter -> remember(action) { action.painter }
                is ImageResult.Bitmap ->
                    if (editor) remember(action, center, scale, filterQuality) {
                        NinePatchPainter(
                            action.bitmap.asImageBitmap(),
                            centerSlice = center,
                            scale = scale,
                            filterQuality = filterQuality
                        )
                    } else remember(action, filterQuality) {
                        NinePatchPainter(
                            action.bitmap.asImageBitmap(),
                            centerSlice = center,
                            scale = scale,
                            filterQuality = filterQuality
                        )
                    }

                is ImageResult.Image -> remember(action) { action.image.toPainter() }
                is ImageResult.Error, is ImageResult.Source -> errorPainter?.invoke() ?: EmptyPainter
            }.also { painter ->
                when (painter) {
                    is AnimationPainter -> LaunchedEffect(painter) {
                        while (painter.isPlay()) {
                            withFrameMillis { frameTimeMillis -> painter.update(frameTimeMillis) }
                        }
                    }
                }
            }
        }
    }
}

Memory cache conflicts when using the maxImageSize option

Hi! I found an issue related to the recently introduced maxImageSize option.

The current default cache key generation logic does not take care about the option, so memory cache maybe returns different size images.

I know we can add own custom Keyer implementation to handle the situation, but I think this one should be handled in the library itself.


Repro:
https://github.com/h6ah4i/ComposeImageLoaderMemCacheConfliction

Expected Actual (the right image is NOT scaled)

Possible disk space leak

After I built in the function to calculate the size of the application's cache, I found the following behavior:
every time I open a screen with multiple images (including GIFs), the application cache size increases by 6MB (images are always the same)

This behavior only happens on a real device with Android 9.0
The files have the format name tmp[NUMBER].tmp
The files are created at /data/user/0/[App]/cache/ and may not have a space limit.

Through searching for tmp I found this line:

The original files are also located along the path /data/user/0/[App]/cache/image_cache

Memory/Disk cache support on all platforms

I am trying to implement this section Memory/Disk cache support on all platforms.

Aside from README file, I can't find usage in the sample code.

Questions:

  1. Which location or file to put the all the actual implementation here?
//actuals on the platforms:
//Android
actual fun ComponentRegistryBuilder.setupDefaultComponents() = this.setupDefaultComponents(App.context)
actual fun getImageCacheDirectoryPath(): Path = App.context.cacheDir.absolutePath.toPath()

//iOS
actual fun ComponentRegistryBuilder.setupDefaultComponents() = this.setupDefaultComponents()
actual fun getImageCacheDirectoryPath(): Path {
    val cacheDir = NSSearchPathForDirectoriesInDomains(
        NSCachesDirectory,
        NSUserDomainMask,
        true
    ).first() as String
    return (cacheDir + "/media").toPath()
}

//Desktop
actual fun ComponentRegistryBuilder.setupDefaultComponents() = this.setupDefaultComponents()
actual fun getImageCacheDirectoryPath(): Path = "media/".toPath()
  1. What is App in App.context. ?

I am very new to Kotlin Multiplatform, so can't get it at first if this is quite obvious to you.

Expected class ImageLoaderBuilder does not have default constructor

For this part of your example usage code

private fun generateImageLoader(): ImageLoader {
    return ImageLoaderBuilder().build()
}

IntelliJ is giving me the error message

Expected class ImageLoaderBuilder does not have default constructor

This must be an IntelliJ issue because the code compiles and runs just well but maybe this error message could be avoided if you would just provide that default constructor in your code.

Otherwise your code seems to work nicely. Great job!

Invalid conversion: The generator cannot convert to match the request, ignoring dimensions

crash in ios.

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Invalid conversion: The generator cannot convert to match the request, ignoring dimensions
        at org.jetbrains.skia.Codec$Companion._validateResult$skiko(Codec.kt:28)
        at org.jetbrains.skia.Codec.readPixels(Codec.kt:191)
        at com.seiko.imageloader.util.GifPainter.onDraw(GifPainter.kt:61)
        at androidx.compose.ui.graphics.painter.Painter.draw-x_KDEd0(Painter.kt:212)
        at com.seiko.imageloader.AsyncImagePainter.onDraw(AsyncImagePainter.kt:113)
        at androidx.compose.ui.graphics.painter.Painter.draw-x_KDEd0(Painter.kt:212)
        at androidx.compose.ui.draw.PainterModifier.draw(PainterModifier.kt:281)
        at androidx.compose.ui.node.DrawEntity.draw(DrawEntity.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:329)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:48)
        at androidx.compose.foundation.NoIndication$NoIndicationInstance.drawIndication(Indication.kt:136)
        at androidx.compose.foundation.IndicationModifier.draw(Indication.kt:183)
        at androidx.compose.ui.node.DrawEntity.draw(DrawEntity.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:329)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:48)
        at androidx.compose.material.ripple.CommonRippleIndicationInstance.drawIndication(CommonRipple.kt:70)
        at androidx.compose.foundation.IndicationModifier.draw(Indication.kt:183)
        at androidx.compose.ui.node.DrawEntity.draw(DrawEntity.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:329)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:48)
        at androidx.compose.foundation.Background.draw(Background.kt:107)
        at androidx.compose.ui.node.DrawEntity.draw(DrawEntity.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:329)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:48)
        at androidx.compose.foundation.Background.draw(Background.kt:107)
        at androidx.compose.ui.node.DrawEntity.draw(DrawEntity.kt:98)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:329)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.ModifiedLayoutNode.performDraw(ModifiedLayoutNode.kt:245)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.access$drawContainedDrawModifiers(LayoutNodeWrapper.kt:64)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:351)
        at androidx.compose.ui.node.LayoutNodeWrapper$invoke$1.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2103)
        at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:128)
        at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui(OwnerSnapshotObserver.kt:120)
        at androidx.compose.ui.node.LayoutNodeWrapper.invoke(LayoutNodeWrapper.kt:350)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.node.LayoutNodeWrapper$invokeOnCanvasInstance$1.invoke(LayoutNodeWrapper.kt:186)
        at androidx.compose.ui.platform.SkiaLayer.performDrawLayer(SkiaLayer.skiko.kt:264)
        at androidx.compose.ui.platform.SkiaLayer.drawLayer(SkiaLayer.skiko.kt:225)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:314)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.node.InnerPlaceable.performDraw(InnerPlaceable.kt:143)
        at androidx.compose.ui.node.LayoutNodeWrapper.drawContainedDrawModifiers(LayoutNodeWrapper.kt:327)
        at androidx.compose.ui.node.LayoutNodeWrapper.draw(LayoutNodeWrapper.kt:319)
        at androidx.compose.ui.node.LayoutNode.draw$ui(LayoutNode.kt:933)
        at androidx.compose.ui.platform.SkiaBasedOwner.draw(SkiaBasedOwner.skiko.kt:352)
        at androidx.compose.ui.ComposeScene.render(ComposeScene.skiko.kt:386)
        at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:297)
        at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:296)
        at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:96)
        at androidx.compose.ui.awt.ComposeLayer.access$catchExceptions(ComposeLayer.desktop.kt:76)
        at androidx.compose.ui.awt.ComposeLayer$1.onRender(ComposeLayer.desktop.kt:296)
        at org.jetbrains.skiko.SkiaLayer.update$skiko(SkiaLayer.awt.kt:507)
        at org.jetbrains.skiko.redrawer.MetalRedrawer.update(MetalRedrawer.kt:66)
        at org.jetbrains.skiko.redrawer.MetalRedrawer.access$update(MetalRedrawer.kt:13)
        at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invokeSuspend(MetalRedrawer.kt:38)
        at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invoke(MetalRedrawer.kt)
        at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invoke(MetalRedrawer.kt)
        at org.jetbrains.skiko.FrameDispatcher$job$1.invokeSuspend(FrameDispatcher.kt:33)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
        at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
        at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
        at java.base/java.security.AccessController.doPrivileged(Unknown Source)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
        at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
        Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@511bac43, SwingDispatcher@1882c825

Broken cache with buildFeatures.compose

Image cache is not working when buildFeatures.compose is set to true. When Image is recomposed, library try to download image from url again and not hit the cache. When buildFeatures.compose is turned off, the cache is working.

diskCacheConfig {
    directory(context.applicationContext.cacheDir.toOkioPath())
    maxSizeBytes(512L * 1024 * 1024) // 512MB
}

Versions:
Kotlin = 1.8.10
Gradle = 8.0.2
AGP = 7.4.2
Compose = 1.3.1 (multiplatform)
ImageLoader = 1.3.1 (for 1.4.0 the same result)

Support for WASM

I tried using this with compose 1.4.0-dev-wasm06 and it conflicts with that version, so I cannot use it

Improve support for locally provided image data

Thank you for producing and sharing this library; the power of the Compose/Multiplatform ecosystem is beginning to show!

While the cross-platform ability is great; I noticed you have focused API's on loading resources asynchronously.
My use case is for showing SVG (and potentially other format) files from local bytes which are much faster to load and so the overhead of managing a progress 'spinner' etc. is undesirable.

If you are planning any major new features for this library; please consider building API's intended for local resources: that take byte data for images directly, and minimise overhead to display.

Small size of svg images

After update to 1.5.1 (from 1.3.1), there is broken size of some downloaded SVG images. I would like the image to fill the entire image view. Here's en example of two similar SVG images and comparison with Coil-Compose:
imageloader
As you can see, one of the images has broken size and setting contentScale or other imageLoader options has no effect to it.
Here's my code:

    Image(
        painter = rememberAsyncImagePainter(
            url = url,
            contentScale = ContentScale.FillWidth,
        ),
        contentDescription = null,
        modifier = Modifier
            .size(50.dp)
            .background(color = Color.Red),
        contentScale = ContentScale.FillWidth,
    )

I have attached a demo project - ImageDemo.zip

Versions:
ImageLoader - 1.5.1
Kotlin - 1.8.20
Compose - 1.4.0
Gradle - 8.0.2

Bad svg image quality

Hello. I have a problem with your library.
This is result when i'm loading picture via your library:
Снимок экрана 2023-04-14 в 01 10 17
This is the picture:
Снимок экрана 2023-04-14 в 01 11 50
Actual fun:
@Composable actual fun rememberAsyncImagePainter(fileResource: FileResource): AsyncImagePainter { return rememberAsyncImagePainter(fileResource.rawResId) }
Expect fun:
@Composable expect fun rememberAsyncImagePainter(fileResource: FileResource): AsyncImagePainter
Note: files storage is provided by Moko-Resources
Libraries versions:
imageloader: 1.2.10 (upgrading to 1.3.1 did no impact)
compose: 1.3.0-rc05
moko-resources: 0.20.1

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Repository problems

These problems occurred while renovating this repository. View logs.

  • WARN: Package lookup failures

Warning

Renovate failed to look up the following dependencies: Could not determine new digest for update (github-tags package dawidd6/action-download-artifact).

Files affected: .github/workflows/CompareScreenshotComment.yml


Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Ignored or Blocked

These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

Detected dependencies

github-actions
.github/workflows/Check.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • actions/upload-artifact v4
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
.github/workflows/CompareScreenshot.yml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • actions/setup-java v3.13.0@0ab4596768b603586c0de567f2430c30f5b0d2b0
  • gradle/gradle-build-action v3.4.2@66535aaf56f831b35e3a8481c9c99b665b84dd45
  • dawidd6/action-download-artifact v6
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
.github/workflows/CompareScreenshotComment.yml
  • dawidd6/action-download-artifact v2.28.1@f29d1b6a8930683e80acedfbe6baa2930cd646b4
  • actions/checkout v4
  • dawidd6/action-download-artifact v2.28.1@f29d1b6a8930683e80acedfbe6baa2930cd646b4
  • peter-evans/find-comment v3
  • peter-evans/create-or-update-comment v4
.github/workflows/DocsDeploy.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • gradle/gradle-build-action v3
  • actions/setup-node v4
  • peaceiris/actions-gh-pages v4
.github/workflows/DocsTest.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • gradle/gradle-build-action v3
  • actions/setup-node v4
.github/workflows/Publish.yml
  • actions/checkout v4
  • gradle/wrapper-validation-action v3
  • actions/setup-java v3
  • gradle/gradle-build-action v3
  • actions/checkout v4
  • softprops/action-gh-release v2
.github/workflows/StoreScreenshot.yml
  • actions/checkout v4.1.7@692973e3d937129bcbf40652eb9f2f61becf3332
  • actions/setup-java v3.13.0@0ab4596768b603586c0de567f2430c30f5b0d2b0
  • gradle/gradle-build-action v3.4.2@66535aaf56f831b35e3a8481c9c99b665b84dd45
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
gradle
gradle.properties
settings.gradle.kts
  • com.gradle.enterprise 3.17.5
build.gradle.kts
app/android/build.gradle.kts
app/android/benchmark/build.gradle.kts
app/common/gradle.properties
app/common/build.gradle.kts
app/desktop/build.gradle.kts
app/intellij-plugin/build.gradle.kts
  • org.jetbrains.intellij 1.17.3
app/macos/build.gradle.kts
app/wasmJs/build.gradle.kts
app/web/build.gradle.kts
build-logic/gradle.properties
build-logic/settings.gradle.kts
build-logic/convention/build.gradle.kts
extension/compose-resources/gradle.properties
extension/compose-resources/build.gradle.kts
extension/imageio/gradle.properties
extension/imageio/build.gradle.kts
  • com.twelvemonkeys.imageio:imageio-batik 3.11.0
  • org.apache.xmlgraphics:batik-transcoder 1.17
extension/ktor-network/gradle.properties
extension/ktor-network/build.gradle.kts
extension/moko-resources/gradle.properties
extension/moko-resources/build.gradle.kts
  • dev.icerock.moko:resources 0.24.1
extension/nine-patch/gradle.properties
extension/nine-patch/build.gradle.kts
extension/svg/gradle.properties
extension/svg/build.gradle.kts
gradle/libs.versions.toml
  • com.android.tools.build:gradle 8.5.0
  • org.jetbrains.kotlin:kotlin-gradle-plugin 2.0.0
  • org.jetbrains.compose:compose-gradle-plugin 1.6.11
  • org.jetbrains.kotlin:compose-compiler-gradle-plugin 2.0.0
  • org.jetbrains.kotlinx:kotlinx-coroutines-core 1.8.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-test 1.8.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-android 1.8.1
  • org.jetbrains.kotlinx:kotlinx-coroutines-swing 1.8.1
  • org.jetbrains.kotlinx:kotlinx-serialization-json 1.7.1
  • androidx.core:core-ktx 1.13.1
  • androidx.collection:collection 1.4.0
  • androidx.appcompat:appcompat-resources 1.7.0
  • androidx.exifinterface:exifinterface 1.3.7
  • androidx.activity:activity-compose 1.9.0
  • androidx.lifecycle:lifecycle-runtime-ktx 2.8.2
  • junit:junit 4.13.2
  • androidx.test.ext:junit 1.1.5
  • androidx.test.espresso:espresso-core 3.5.1
  • androidx.test.uiautomator:uiautomator 2.3.0
  • androidx.benchmark:benchmark-macro-junit4 1.2.4
  • androidx.profileinstaller:profileinstaller 1.3.1
  • org.robolectric:robolectric 4.12.2
  • io.github.takahirom.roborazzi:roborazzi 1.20.0
  • io.github.takahirom.roborazzi:roborazzi-junit-rule 1.20.0
  • io.github.takahirom.roborazzi:roborazzi-compose 1.20.0
  • io.github.takahirom.roborazzi:roborazzi-compose-desktop 1.20.0
  • com.squareup.okio:okio 3.9.0
  • com.squareup.okio:okio-fakefilesystem 3.9.0
  • io.ktor:ktor-serialization-kotlinx-json 2.3.11
  • io.ktor:ktor-client-core 2.3.11
  • io.ktor:ktor-client-logging 2.3.11
  • io.ktor:ktor-client-content-negotiation 2.3.11
  • io.ktor:ktor-client-okhttp 2.3.11
  • io.ktor:ktor-client-darwin 2.3.11
  • io.ktor:ktor-client-cio 2.3.11
  • io.ktor:ktor-client-js-js 2.3.11
  • io.ktor:ktor-client-js-wasm-js 2.3.11
  • com.eygraber:uri-kmp 0.0.18
  • co.touchlab:kermit 2.0.4
  • com.caverock:androidsvg-aar 1.4
  • com.darkrockstudios:mpfilepicker 3.1.0
  • com.android.application 8.5.0
  • com.android.library 8.5.0
  • com.android.test 8.5.0
  • org.jetbrains.kotlin.jvm 2.0.0
  • org.jetbrains.kotlin.android 2.0.0
  • org.jetbrains.kotlin.multiplatform 2.0.0
  • org.jetbrains.kotlin.plugin.serialization 2.0.0
  • org.jetbrains.kotlin.plugin.compose 2.0.0
  • org.jetbrains.compose 1.6.11
  • com.diffplug.spotless 6.25.0
  • com.vanniktech.maven.publish 0.29.0
  • org.jetbrains.dokka 1.9.20
  • androidx.baselineprofile 1.2.4
  • io.github.takahirom.roborazzi 1.20.0
  • dev.drewhamilton.poko 0.16.0
image-loader/gradle.properties
image-loader/build.gradle.kts
image-loader-singleton/gradle.properties
image-loader-singleton/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.8
npm
docs/package.json
  • @docusaurus/core 2.4.3
  • @docusaurus/preset-classic 2.4.3
  • @mdx-js/react ^1.6.22
  • clsx ^1.2.1
  • prism-react-renderer ^1.3.5
  • react ^18.0.0
  • react-dom ^18.0.0
  • @docusaurus/module-type-aliases 2.4.3
  • node >=16.14

  • Check this box to trigger a request for Renovate to run again on this repository

NPE in Android SVG decoder

This code in your Android SVG decoder

    if (svg.documentSVGVersion.startsWith("2")) {
        throw RuntimeException("Un support SVG version '2.0'")
    }

results in an NPE for many of my icons because documentSVGVersion returns null. After replacing this with

    if (svg.documentSVGVersion != null && svg.documentSVGVersion.startsWith("2")) {
        throw RuntimeException("Un support SVG version '2.0'")
    }

everything was good again.

js image loading not working in 1.2.8

hey, thanks for the work on this.

we're trying to adopt this library for js, but version 1.2.8 doesn't seem to be working for us (nothing loads). our code is fairly straightforward:

onWasmReady {
    Window("") {
        CompositionLocalProvider(
            LocalImageLoader provides ImageLoader {
                logger = DebugLogger(LogPriority.VERBOSE)
                components {
                    setupDefaultComponents(imageScope)
                }
                interceptor {
                    memoryCacheConfig {
                        maxSizePercent(0.25)
                    }
                }
            },
        ) {
            Image(
                modifier = Modifier.fillMaxSize(),
                painter = rememberAsyncImagePainter("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png"),
                contentDescription = "",
                contentScale = ContentScale.Crop,
            )
        }
    }
}

this roughly matches the image loader configuration in the js sample here. i've also tried with just ImageLoader { } and also without providing our own. but unfortunately, all approaches have resulted in nothing loading.

here are the logs we're seeing:

DEBUG MappedInterceptor : [image data] https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png
[message] map -> https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png
build.umd.js:3103 WARNING AsyncImagePainter : [image data] https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png
[message] load image error

the last version I tried was 1.2.5 and that worked without a problem, but we're looking to update to jb-compose:1.3.0.

any thoughts?

ImageLoader httpClient

Hi

val painter = rememberImagePainter(
                    url = "https://example.com/image",
                    imageLoader = generateImageLoader()
                )
                Image(
                    modifier = Modifier.size(70.dp, 70.dp).clip(CircleShape),
                    painter = painter,
                    contentDescription = null
                )
@Composable
fun generateImageLoader(
    tokenManager: TokenManager = koinInject()
): ImageLoader {
    return ImageLoader {
        components {
            setupDefaultComponents(
                httpClient = {
                    HttpClient{
                        defaultRequest {
                            bearerAuth(tokenManager.getAuthorization)
                        }
                    }
                }
            )
        }
    }
}

Hi
when i set image loader for rememberImagePainter, i got the following error:

io.ktor.client.HttpClient -- REQUEST https://example.com/image failed with exception: java.util.concurrent.CancellationException: The coroutine scope left the composition

how can i fix that?

Disk Cache not being used

I have a ImageLoader with DiskCache and MemoryCache configured but it seems to only be using the MemoryCache. My images use a custom class that I have Mappers and Keyers setup for but it doesnt seem to allow them to be inserted into the DiskCache even though they are loading the images. This happens on both Desktop and Android.

I have tried with multiple configurations using the builder and even disabling the memory cache, which does not seem to help. It doesnt seem like its hitting the DiskCacheInterceptor at all and Napier says nothing about errors.

In general I cannot figure out whats wrong.

What is the replacement for Painter.requestState and ImageRequestState?

The new version have removed ImageRequestState.

So how do we now conditionally render loading, error and success states.

Earlier you could do this.

when (painter.requestState) {
            is ImageRequestState.Loading -> {}
            is ImageRequestState.Failure -> {}
            is ImageRequestState.Success -> {}
}

I suppose this partially fixes the problem, but

rememberImageActionPainter(action, placeholderPainter, errorPainter)

But I need to run code when the Image is successfully loaded. How do you do it in these newer versions?

Image loading causing crash on desktop

Hey! thanks for making this library.

I am using version 1.2.9. It's working on ios and android and not on desktops only.

library version: 1.2.9
kotlin version = 1.8.0
compose version = "1.3.0"

Stack trace:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing(MainDispatchers.kt:118)
	at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.isDispatchNeeded(MainDispatchers.kt:96)
	at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:319)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
	at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25)
	at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110)
	at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch(Builders.common.kt:56)
	at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
	at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default(Builders.common.kt:47)
	at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
	at kotlinx.coroutines.flow.FlowKt__CollectKt.launchIn(Collect.kt:49)
	at kotlinx.coroutines.flow.FlowKt.launchIn(Unknown Source)
	at com.seiko.imageloader.AsyncImagePainter.onRemembered(AsyncImagePainter.kt:140)
	at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:1091)
	at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:818)
	at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:839)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:585)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:503)
	at androidx.compose.runtime.BroadcastFrameClock$FrameAwaiter.resume(BroadcastFrameClock.kt:42)
	at androidx.compose.runtime.BroadcastFrameClock.sendFrame(BroadcastFrameClock.kt:71)
	at androidx.compose.ui.ComposeScene.render(ComposeScene.skiko.kt:411)
	at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:326)
	at androidx.compose.ui.awt.ComposeLayer$1$onRender$1.invoke(ComposeLayer.desktop.kt:325)
	at androidx.compose.ui.awt.ComposeLayer.catchExceptions(ComposeLayer.desktop.kt:109)
	at androidx.compose.ui.awt.ComposeLayer.access$catchExceptions(ComposeLayer.desktop.kt:87)
	at androidx.compose.ui.awt.ComposeLayer$1.onRender(ComposeLayer.desktop.kt:325)
	at org.jetbrains.skiko.SkiaLayer.update$skiko(SkiaLayer.awt.kt:525)
	at org.jetbrains.skiko.redrawer.AWTRedrawer.update(AWTRedrawer.kt:54)
	at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invokeSuspend(MetalRedrawer.kt:50)
	at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invoke(MetalRedrawer.kt)
	at org.jetbrains.skiko.redrawer.MetalRedrawer$frameDispatcher$1.invoke(MetalRedrawer.kt)
	at org.jetbrains.skiko.FrameDispatcher$job$1.invokeSuspend(FrameDispatcher.kt:33)
	at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
	at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
	at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:313)
	at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
	at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
	at java.base/java.security.AccessController.doPrivileged(Native Method)
	at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
	at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelled}@6a8d944, Dispatchers.Main[missing, cause=java.lang.NoClassDefFoundError: android/os/Looper]]
	Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [androidx.compose.ui.awt.ComposeLayer$coroutineExceptionHandler$1@bb7cb9, androidx.compose.runtime.BroadcastFrameClock@2b0a0a66, StandaloneCoroutine{Cancelling}@2f02b1ff, FlushCoroutineDispatcher@5296454b]
Caused by: java.lang.NoClassDefFoundError: android/os/Looper
	at kotlinx.coroutines.android.AndroidDispatcherFactory.createDispatcher(HandlerDispatcher.kt:55)
	at kotlinx.coroutines.internal.MainDispatchersKt.tryCreateDispatcher(MainDispatchers.kt:57)
	at kotlinx.coroutines.internal.MainDispatcherLoader.loadMainDispatcher(MainDispatchers.kt:38)
	at kotlinx.coroutines.internal.MainDispatcherLoader.<clinit>(MainDispatchers.kt:22)
	at kotlinx.coroutines.Dispatchers.getMain(Dispatchers.kt:57)
	at com.seiko.imageloader.ImageLoaderConfigBuilder.<init>(ImageLoaderConfig.kt:20)
	at com.seiko.imageloader.ImageLoaderConfigKt.ImageLoaderConfig(ImageLoaderConfig.kt:47)
	at com.seiko.imageloader.ImageLoaderKt.ImageLoader(ImageLoader.kt:22)
	at com.seiko.imageloader.ImageLoaderKt.ImageLoader$default(ImageLoader.kt:17)
	at com.seiko.imageloader.LocalImageLoader_desktopKt$createImageLoaderProvidableCompositionLocal$1.invoke(LocalImageLoader.desktop.kt:25)
	at com.seiko.imageloader.LocalImageLoader_desktopKt$createImageLoaderProvidableCompositionLocal$1.invoke(LocalImageLoader.desktop.kt:24)
	at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
	at androidx.compose.runtime.LazyValueHolder.getCurrent(ValueHolders.kt:29)
	at androidx.compose.runtime.LazyValueHolder.getValue(ValueHolders.kt:31)
	at androidx.compose.runtime.ComposerImpl.resolveCompositionLocal(Composer.kt:1981)
	at androidx.compose.runtime.ComposerImpl.consume(Composer.kt:1949)
	at com.seiko.imageloader.AsyncImagePainterKt.rememberAsyncImagePainter-DJqXB-Q(AsyncImagePainter.kt:225)
	at com.kashif.common.presentation.components.AsyncImageKt.AsyncImage(AsyncImage.kt:16)
	at com.kashif.common.presentation.AppKt$MovieCard$2.invoke(App.kt:152)
	at com.kashif.common.presentation.AppKt$MovieCard$2.invoke(App.kt:149)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.material.SurfaceKt$Surface$1.invoke(Surface.kt:134)
	at androidx.compose.material.SurfaceKt$Surface$1.invoke(Surface.kt:117)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.material.SurfaceKt.Surface-F-jzlyU(Surface.kt:114)
	at androidx.compose.material.CardKt.Card-F-jzlyU(Card.kt:68)
	at com.kashif.common.presentation.AppKt.MovieCard(App.kt:145)
	at com.kashif.common.presentation.AppKt$MoviesScreen$1$1$invoke$$inlined$items$default$4.invoke(LazyDsl.kt:424)
	at com.kashif.common.presentation.AppKt$MoviesScreen$1$1$invoke$$inlined$items$default$4.invoke(LazyDsl.kt:145)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:135)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.foundation.lazy.LazyListItemProviderImpl$1.invoke(LazyListItemProvider.kt:80)
	at androidx.compose.foundation.lazy.LazyListItemProviderImpl$1.invoke(LazyListItemProvider.kt:79)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:135)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.foundation.lazy.layout.DefaultLazyLayoutItemsProvider.Item(LazyLayoutItemProvider.kt:118)
	at androidx.compose.foundation.lazy.LazyListItemProviderImpl.Item(LazyListItemProvider.kt)
	at androidx.compose.foundation.lazy.layout.DefaultDelegatingLazyLayoutItemProvider.Item(LazyLayoutItemProvider.kt:197)
	at androidx.compose.foundation.lazy.LazyListItemProviderKt$rememberLazyListItemProvider$1$1.Item(LazyListItemProvider.kt)
	at androidx.compose.foundation.lazy.layout.LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$1.invoke(LazyLayoutItemContentFactory.kt:99)
	at androidx.compose.foundation.lazy.layout.LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1$1.invoke(LazyLayoutItemContentFactory.kt:98)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
	at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:84)
	at androidx.compose.foundation.lazy.layout.LazySaveableStateHolder.SaveableStateProvider(LazySaveableStateHolder.kt:84)
	at androidx.compose.foundation.lazy.layout.LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1.invoke(LazyLayoutItemContentFactory.kt:98)
	at androidx.compose.foundation.lazy.layout.LazyLayoutItemContentFactory$CachedItemContent$createContentLambda$1.invoke(LazyLayoutItemContentFactory.kt:90)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:107)
	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34)
	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:145)
	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2375)
	at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:2643)
	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3260)
	at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:3238)
	at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341)
	at androidx.compose.runtime.SnapshotStateKt.observeDerivedStateRecalculations(Unknown Source)
	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3238)
	at androidx.compose.runtime.ComposerImpl.recompose$runtime(Composer.kt:3203)
	at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:771)
	at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1031)
	at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:125)
	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$2.invoke(Recomposer.kt:534)
	... 30 more
Caused by: java.lang.ClassNotFoundException: android.os.Looper
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 96 more

This is the stack trace i am getting as a result of the crash

Why use Batik based SVG renderer on desktop

For the desktop version you are using the Batik based twelvemonkeys SVG renderer. Why do you do that? All desktop versions are also Skia versions and thus have the Skia SVG built in. It would save people from a lot of dependency downloads if you would drop that. The Skia SVG renderer works nicely on desktop so I don't see any reason to use the Batik one. It would make the final distribution bundles smaller.

Min SDK Version changed

Hello, I've noticed that the minimum SDK version has changed in the latest release, but there was no mention of this in the release notes. Is this change intentional?

Affected Version: 1.6.5
Expected Behavior: Min SDK Version should be consistent with previous versions or mentioned in release notes if changed.
Actual Behavior: Min SDK Version changed without any notice.
Thank you for your time and effort on this project.

1.6.4...1.6.5

1.7.0 version doesn't load image on android

                   Image(
                        modifier = Modifier
                            .fillMaxWidth()
                            .height(200.dp),
                        painter = rememberImagePainter(product.image),
                        contentDescription = null
                    )

If I change the version to 1.2.8. & 1.6.0 & 1.6.8

Then the image loads successfully on android

Support tvos targets

Supporting tvos should be a very simple add-on. Supporting tvos is the same as supporting ios.

All you would need to do is add the following kotlin multiplatform build targets:

tvosArm64()
tvosX64()
tvosSimulatorArm64()

Changelog

Please provide a changelog, or some other means, so we know what version of Compose each version matches with and which version of ktor it uses as well.

How to build demo projet

Hi,
I clone the project and try to build it, but the gradle sync failed.
The error :

'pod install' command failed with code 1.
Full command: pod install
Error message:
Analyzing dependencies
[!] CocoaPods could not find compatible versions for pod "combine":
  In Podfile:
    combine (from `../ios-combine`)
Specs satisfying the `combine (from `../ios-combine`)` dependency were found, but they required a higher minimum deployment target.
        Please, check that podfile contains following lines in header:
        source 'https://cdn.cocoapods.org'

         Please, check that each target depended on combine contains following dependencies:

Cache blocks re-loading of images after failed attempts

If the image loader cannot successfully download an image on the first try, then, when caching is enabled, it does not make any attempt to download the image again at a later time. When the disk cache is enabled it does not even retry this when the application is terminated and started again. It seems that even in case of a download error something is stored in the cache which then blocks any further attempts. I tested this by removing my images from my server, then start the application, then copy the images back to the server while the application is still running. Switching screens or restarting the application does not force a re-load of the images. This all works when no caching is configured but this is no option for me.

I have configured caching like this:

    fun generateImageLoader(): ImageLoader {
        return ImageLoaderBuilder().apply {
            diskCache {
                DiskCacheBuilder()
                    .directory("... path to some cache directory ...".toPath())
                    .maxSizeBytes(1024 * 1024 * 50) // 50 MB
                    .build()
            }
            memoryCache {
                MemoryCacheBuilder()
                    .build()
            }
        }.build()
    }

Support for bitmap or drawable in Android target

Would it be possible to provide a Bitmap or Drawable in addition to Painter when the image is successfully downloaded in Android target similar to Coil-Compose?
Coil:

DisposableEffect(url) {
    val request = ImageRequest.Builder(context)
        .data(data = url)
        .target { drawable ->
            val bitmap = drawable.toBitmapOrNull() ?: return@target
            // TODO use bitmap
        }
        .build()

    val disposable = ImageLoader.Builder(context)
        .components { add(SvgDecoder.Factory()) }
        .build()
        .enqueue(request)

    onDispose {
        disposable.dispose()
    }
}

Crash on iOS application

Hello,
Thanks for the SDK. I'm using this beautiful imageLoader in my KMM android-ios project. The iOS application crashes when fast scrolling, but this situation does not occur when 'rememberCoroutineScope()' is passed to imageLoader for iOS consumer app.

The error report when I debug it with Xcode ,

MidJourney Images Compose Multiplatform`:
    0x1027e1378 <+92>:  bl     0x10312e9cc               ; EnterFrame
    0x1027e137c <+96>:  adrp   x8, 6570
    0x1027e1380 <+100>: add    x8, x8, #0xcdc            ; kotlin::mm::internal::gSuspensionRequested
    0x1027e1384 <+104>: ldarb  w8, [x8]
    0x1027e1388 <+108>: tbz    w8, #0x0, 0x1027e1398     ; <+124> at MetalRedrawer.ios.kt
    0x1027e138c <+112>: b      0x1027e1390               ; <+116> at MetalRedrawer.ios.kt:158:5
    0x1027e1390 <+116>: bl     0x103112df4               ; kotlin::mm::SuspendIfRequestedSlowPath()
->  0x1027e1394 <+120>: b      0x1027e1398               ; <+124> at MetalRedrawer.ios.kt
    0x1027e1398 <+124>: b      0x1027e139c               ; <+128> at MetalRedrawer.ios.kt
    0x1027e139c <+128>: ldr    x1, [sp, #0x10]
    0x1027e13a0 <+132>: ldur   x0, [x29, #-0x8]
    0x1027e13a4 <+136>: bl     0x1023affa0               ; interpretObjCPointer
    0x1027e13a8 <+140>: str    x0, [sp]
    0x1027e13ac <+144>: b      0x1027e13b0               ; <+148> at MetalRedrawer.ios.kt
    0x1027e13b0 <+148>: ldr    x0, [sp]
    0x1027e13b4 <+152>: bl     0x1027e11d0               ; kfun:org.jetbrains.skiko.redrawer.FrameTickListener.onDisplayLinkTick#internal at MetalRedrawer.ios.kt
    0x1027e13b8 <+156>: b      0x1027e13bc               ; <+160> at MetalRedrawer.ios.kt:160:6
    0x1027e13bc <+160>: b      0x1027e13c0               ; <+164> at MetalRedrawer.ios.kt
  0x1027e13c0 <+164>: ldr    x0, [sp, #0x8]
    0x1027e13c4 <+168>: mov    w1, #0x0
    0x1027e13c8 <+172>: mov    w2, #0x4
    0x1027e13cc <+176>: bl     0x10312ea18               ; LeaveFrame

Where could the error be originating from?
Any recommendations? Thanks 🙏

Web app console error: Its dependency '@js-joda/core' was not found

Hi after adding compose-imageloader dependency in my compose multiplatform project, i m getting following error in web app console and app is not loading anything:

caught Error: Error loading module 'compose-instagram-clone-multiplatform'. Its dependency '@js-joda/core' was not found. Please, check whether '@js-joda/core' is loaded prior to 'compose-instagram-clone-multiplatform'.

where compose-instagram-clone-multiplatform is my project name just for clarifications.

If i remove this dependency , it works fine. any idea about this issue?

Error: compiled by a pre-release version of Kotlin

Class 'com.seiko.imageloader.ImageLoader' is compiled by a pre-release version of Kotlin and cannot be loaded by this version of the compiler

I'm using:
Kotlin 1.7.0
Compose JB 1.2.0-alpha01-dev753

Inability to use library due to lacking documentation

The documentation doesn't show how to set an image. I've tried ChatGTP solutions and other solutions like below (which has worked finally) but it does work when given a URL to the image, only works when the below type of data is given.Hence it would be useful if doc showed how to create an Image using this library.

 CompositionLocalProvider{
        val url = "data:image/jpeg;base64,/9j/4"
            val painter = rememberAsyncImagePainter(url)
            Image(painter, null)
        }

Improve Documentation

Thank you for maintaining this library! We desperately need something like this in the community.

The documentation is a little lacking, primarily on compatibility with what versions of Kotlin and Compose. I'm about to try out your library and I'm not confident whether it will work with my version of Kotlin / Compose.

There are also other things that seem to be lacking in the documentation around the configurability of the memory and disk caches.

no image in Paparazzi snapshot

I'm trying to write a Paparazzi ui test, but no image is displayed :

my test :

class TempTest {
    private fun generateImageLoader() = ImageLoader {
        interceptor {
            useDefaultInterceptors = false
            addInterceptor { ImageResult.OfPainter(ColorPainter(Color.Green)) }
        }
    }

    @get:Rule
    val paparazzi = Paparazzi(
        deviceConfig = DeviceConfig.PIXEL_6,
        theme = "android:Theme.Material.NoActionBar",
    )

    @Test
    fun launchComposable() {
        paparazzi.snapshot {
            CompositionLocalProvider(LocalImageLoader provides remember { generateImageLoader() }) {
                AutoSizeImage("", null, modifier = Modifier.size(200.dp))
            }
        }
    }
}

PS : I managed to write this kind of test with Coil

Fails to load images on old Android Versions when using ImageConfig.HARDWARE

I was using the ImageConfig.HARDWARE configuation for thumbnails since it seemed to help perfomance, but I got this exception when I was testing on a older emulator(API 24).

I did some research and it seems like Bitmap.Config.HARDWARE wasn't added until API 26. I changed what I use for ImageConfig to a variable that changes based on the API level, but I think this should be handled at the library level or at least give a warning.

2022-11-08 00:12:12.982 11097-11097/ca.gosyer.jui.android.debug W/ImageLoaderImageKt: Caused by: java.lang.NoSuchFieldError: No field HARDWARE of type Landroid/graphics/Bitmap$Config; in class Landroid/graphics/Bitmap$Config; or its superclasses (declaration of 'android.graphics.Bitmap$Config' appears in /system/framework/framework.jar)
        at com.seiko.imageloader.util.BitmapKt.toBitmapConfig(Bitmap.kt:19)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder.configureConfig(BitmapFactoryDecoder.kt:86)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder.decode(BitmapFactoryDecoder.kt:59)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder.access$decode(BitmapFactoryDecoder.kt:26)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder$decode$2$1.invoke(BitmapFactoryDecoder.kt:34)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder$decode$2$1.invoke(BitmapFactoryDecoder.kt:34)
        at kotlinx.coroutines.InterruptibleKt.runInterruptibleInExpectedContext(Interruptible.kt:51)
        at kotlinx.coroutines.InterruptibleKt.access$runInterruptibleInExpectedContext(Interruptible.kt:1)
        at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invokeSuspend(Interruptible.kt:43)
        at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Interruptible.kt)
        at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Interruptible.kt)
        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
        at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:161)
        at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
        at kotlinx.coroutines.InterruptibleKt.runInterruptible(Interruptible.kt:42)
        at kotlinx.coroutines.InterruptibleKt.runInterruptible$default(Interruptible.kt:39)
        at com.seiko.imageloader.component.decoder.BitmapFactoryDecoder.decode(BitmapFactoryDecoder.kt:34)
        at com.seiko.imageloader.intercept.DecodeInterceptor.decode(DecodeInterceptor.kt:45)
        at com.seiko.imageloader.intercept.DecodeInterceptor.intercept(DecodeInterceptor.kt:19)
        at com.seiko.imageloader.intercept.RealInterceptorChain.proceed(RealInterceptorChain.kt:37)
        at com.seiko.imageloader.intercept.MemoryCacheInterceptor.intercept(MemoryCacheInterceptor.kt:31)
        at com.seiko.imageloader.intercept.RealInterceptorChain.proceed(RealInterceptorChain.kt:37)
        at com.seiko.imageloader.intercept.MappedInterceptor.intercept(MappedInterceptor.kt:12)
        at com.seiko.imageloader.intercept.RealInterceptorChain.proceed(RealInterceptorChain.kt:37)
        at com.seiko.imageloader.RealImageLoader.executeMain(ImageLoader.kt:64)
        at com.seiko.imageloader.RealImageLoader.access$executeMain(ImageLoader.kt:29)
        at com.seiko.imageloader.RealImageLoader$execute$2.invokeSuspend(ImageLoader.kt:50)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)

rememberImageAction does not change image by new url

version 1.6.2
kotlin 1.8.2
platform ios
compose 1.4.3

i have a code

@Composabe
fun myAsyncImage(url: String) {
        val imageAction by rememberImageAction(url)
        val painter = rememberImageActionPainter(imageAction)
}

and then i changed a url happend nothing.

workaround

@Composable
private fun fixedRememberImageAction(
    url: String,
    imageLoader: ImageLoader = LocalImageLoader.current,
): State<ImageAction> {
    val request = >>>>remember(url)<<<< { ImageRequest(url) }
    return rememberImageAction(request, imageLoader)
}

Please, can you fix the rememberImageAction?

加载图片报错

version: api("io.github.qdsfdhvh:image-loader:1.2.10")
kotlin version : 1.8.0
Compose : 1.3.0

image

Ktor 请求日志:

log----start-------
I/System.out: REQUEST: https://www.wanandroid.com/blogimgs/42da12d8-de56-4439-b40c-eab66c227a4b.png
I/System.out: METHOD: HttpMethod(value=GET)
I/System.out: COMMON HEADERS
I/System.out: -> Accept: application/json
I/System.out: -> Accept-Charset: UTF-8
I/System.out: CONTENT HEADERS
I/System.out: -> Content-Length: 0
I/System.out: RESPONSE: 200 OK
I/System.out: METHOD: HttpMethod(value=GET)
I/System.out: FROM: https://www.wanandroid.com/blogimgs/42da12d8-de56-4439-b40c-eab66c227a4b.png
I/System.out: COMMON HEADERS
I/System.out: -> accept-ranges: bytes
I/System.out: -> cache-control: private
I/System.out: -> content-length: 80542
I/System.out: -> content-type: image/png
I/System.out: -> date: Sun, 26 Mar 2023 06:41:36 GMT
I/System.out: -> etag: W/"80542-1647681580000"
I/System.out: -> expires: Thu, 01 Jan 1970 08:00:00 CST
I/System.out: -> last-modified: Sat, 19 Mar 2022 09:19:40 GMT
I/System.out: -> server: Apache-Coyote/1.1
I/System.out: status:Failure(error=java.lang.RuntimeException: Unable to create a decoder that supports: Source(request=com.seiko.imageloader.model.ImageRequest@b719312, source=buffer(source(io.ktor.utils.io.jvm.javaio.InputAdapter@c174ae3)), dataSource=Engine, extra={KEY_MIME_TYPE=image/png}))

log-----end-------

Failure(error=java.lang.RuntimeException: Unable to create a decoder that supports: Source(request=com.seiko.imageloader.model.ImageRequest@b719312, source=buffer(source(io.ktor.utils.io.jvm.javaio.InputAdapter@c174ae3)), dataSource=Engine, extra={KEY_MIME_TYPE=image/png}))

我把painter 的请求状态 打印出来了,打印报错 就是上面提示的错误。
要解决这个错误 需要从哪里入手啊,麻烦大佬给提供个思路

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.