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

On the Web platform, VectorPainter cannot get the correct size and draw content when running inside other Painters #4993

Closed
panpf opened this issue Jun 18, 2024 · 1 comment
Assignees
Labels
bug Something isn't working rendering Low level rendering web

Comments

@panpf
Copy link

panpf commented Jun 18, 2024

Describe the bug

On the Web platform, VectorPainter cannot obtain the accurate size and draw content when running inside other Painters.

Affected platforms

  • Web (K/Wasm) - Canvas based API
  • Web (K/JS) - Canvas based API

Versions

  • Compose Multiplatform 1.6.11
  • Kotlin 2.0.0
  • MacBook Pro 2021 14 Inch; macOS 14.2.1
  • Chrome 126.0.6478.62; arm64
  • JDK 17
  • Android Simulator API 24; arm64

To Reproduce

ic_image_outline.xml from Android Vector Asset

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:tint="#000000"
    android:viewportWidth="24"
    android:viewportHeight="24">
    <path
        android:fillColor="#000000"
        android:pathData="M19,5v14L5,19L5,5h14m0,-2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM14.14,11.86l-3,3.87L9,13.14 6,17h12l-3.86,-5.14z" />
</vector>

The sample code is as follows:

  • Code Block 1: Demonstrates the use of Image to display VectorPainter, and its effect is normal
  • Code Block 2: Demonstrates calling Painter's draw method in drawWithContent to draw VectorPainter, and its effect is also normal.
  • Code Block 3: Demonstrates the use of custom IconPainter including VectorPainter. At this time, it is impossible to obtain the exact size of VectorPainter and draw content.
@Composable
@Preview
fun App() {
    MaterialTheme {
        Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
            // Code Block 1
            val iconImagePainter = painterResource(Res.drawable.ic_image_outline)
            Image(
                painter = iconImagePainter,
                contentDescription = null,
                modifier = Modifier.size(100.dp).background(Color.Cyan)
            )

            // Code Block 2
            val iconImagePainter2 = painterResource(Res.drawable.ic_image_outline)
            Box(modifier = Modifier.size(100.dp)
                .background(Color.Magenta)
                .drawWithContent {
                    with(iconImagePainter2) {
                        draw(Size(24f, 24f))
                    }
                }
            )

            // Code Block 3
            val iconImagePainter3 = painterResource(Res.drawable.ic_image_outline)
            val iconPainter = remember {
                IconPainter(icon = iconImagePainter3, background = Color.Green)
            }
            Image(
                painter = iconPainter,
                contentDescription = null,
                modifier = Modifier.size(100.dp)
            )

            val iconImagePainter4 = painterResource(Res.drawable.ic_image_outline)
            println("App. painterSize: ${iconImagePainter4.intrinsicSize}")
            Text("painterSize: ${iconImagePainter4.intrinsicSize}")
        }
    }
}

@Stable
open class IconPainter(
    val icon: Painter,
    val background: Color? = null,
    val iconSize: Size? = null,
    val iconTint: Color? = null,
) : Painter() {

    private var alpha: Float = 1.0f
    private var colorFilter: ColorFilter? = null

    override val intrinsicSize: Size = Size.Unspecified

    override fun DrawScope.onDraw() {
        val icon = icon
        val background = background

        if (background != null) {
            drawRect(
                color = background,
                topLeft = Offset.Zero,
                size = this@onDraw.size,
                alpha = alpha,
                colorFilter = colorFilter
            )
        }

        val realIconSize = iconSize ?: icon.intrinsicSize
        val translateLeft = (size.width - realIconSize.width) / 2
        val translateTop = (size.height - realIconSize.height) / 2
        translate(left = translateLeft, top = translateTop) {
            with(icon) {
                val filter = iconTint?.let { ColorFilter.tint(it) }
                draw(size = realIconSize, colorFilter = filter)
            }
        }
        println(
            "IconPainter.onDraw. " +
                    "size=$size, " +
                    "icon.intrinsicSize=${icon.intrinsicSize}, " +
                    "iconSize=$iconSize, " +
                    "realIconSize=$realIconSize, " +
                    "translate=${translateLeft}x${translateTop}"
        )
    }

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

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

The screenshot of the log of running the effect on the web platform is as follows (the green color block cannot display the icon correctly):
Snipaste_2024-06-18_15-53-12
Snipaste_2024-06-18_15-43-21

The running effect on desktop platform and Android is as follows (the green color block can display the icon correctly):
desktop:
Snipaste_2024-06-18_15-53-47
Snipaste_2024-06-18_15-45-08

android:
Snipaste_2024-06-18_15-54-29
Snipaste_2024-06-18_15-55-43

The strange thing is that when creating IconPainter in Code Block 3 without using remember, the icon can be displayed correctly, as follows:

// Code Block 3
val iconImagePainter3 = painterResource(Res.drawable.ic_image_outline)
val iconPainter = IconPainter(icon = iconImagePainter3, background = Color.Green)
Image(
    painter = iconPainter,
    contentDescription = null,
    modifier = Modifier.size(100.dp)
)

At this time, the running effect and log screenshots on the web platform are as follows:
Snipaste_2024-06-18_16-30-03
Snipaste_2024-06-18_16-29-52

Example Project

KMPProject.zip

@panpf panpf added bug Something isn't working submitted labels Jun 18, 2024
@MatkovIvan MatkovIvan added rendering Low level rendering web and removed submitted labels Jun 18, 2024
@okushnikov
Copy link
Collaborator

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working rendering Low level rendering web
Projects
None yet
Development

No branches or pull requests

4 participants