JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
16.19k stars 1.18k forks source link

[Desktop, Android] Artifacts when drawing on Canvas with fractional value coordinates #3068

Closed EzzPress closed 1 month ago

EzzPress commented 1 year ago

Bug Absence of smoothing algorithm for subpixel artifacts when drawing on Canvas with fractional value coordinates. Canvas draws subpixel artifact between drawRect with fractional value coordinates. For example, rectangle (0, 0, 10.5, 10.5) and rectangle (10.5, 0, 10.5, 10.5) have subpixel artifact between them. Android has the same behavior.

image image

With rounded coordinates all works fine: rectangle (0, 0, 10, 10) and rectangle (0, 10, 10, 10)

Affected platforms

Versions

To Reproduce

Canvas(modifier = Modifier.fillMaxSize().background(Color.White)) {
    val chartWidth = size.width
    val chartHeight = size.height
    var x = 0f
//    val rectSize: Float = 10f // works fine
    val rectSize: Float = 10.5f // Show glitches between rectangles
    while (x < chartWidth) {
        var y = 0f
        while (y < chartHeight) {
            drawRect(Color.Black, Offset(x, y), Size(rectSize, rectSize))
            y += rectSize
        }
        x += rectSize
    }
}

Expected behavior Solid color of canvas filled by rectangles

dima-avdeev-jb commented 1 year ago

Can you please provide a code snippet or reproducible sample on GitHub?

EzzPress commented 1 year ago

https://github.com/EzzPress/heatmapcompose here it is

dima-avdeev-jb commented 1 year ago

Made workaround here: https://github.com/EzzPress/heatmapcompose/pull/1

dima-avdeev-jb commented 1 year ago

Android has the same behaviour. I think it is not a bug.

Minimal reproducer:

Canvas(modifier = Modifier.fillMaxSize().background(Color.White)) {
        val chartWidth = size.width
        val chartHeight = size.height
        var x = 0f
//        val rectSize: Float = 50f // works fine
        val rectSize: Float = 50.5f // Show grid
        while (x < chartWidth) {
            var y = 0f
            while (y < chartHeight) {
                drawRect(Color.Black, Offset(x, y), Size(rectSize, rectSize))
                y += rectSize
            }
            x += rectSize
        }
    }
EzzPress commented 1 year ago

It's a bug because these lines are artifacts that not belong to image, but canvas size. There is something missing among the canvas rendering

dima-avdeev-jb commented 1 year ago

Can you apply this workaround? https://github.com/EzzPress/heatmapcompose/pull/1

dima-avdeev-jb commented 1 year ago

Ok, sorry. I will reopen this issue. But I will change the description.

dima-avdeev-jb commented 1 year ago

@EzzPress Can you please check, that changed description is correct?

EzzPress commented 1 year ago

No, this workaround doesn't solve the problem, because it doesn't solve the float mapping, obviously the skia somewhere inside makes the same operations of rounding.

image

To get a quality image we need some how to transform initial m x n matrix to visual matrix k x l with data reducing. In javafx in the jfreechart lib, it plots well, but the time performance is very low, and all the dots are present and could generate onActions, that also ruins performance.

dima-avdeev-jb commented 1 year ago

Can you please check that the changed description of this issue is correct?

EzzPress commented 1 year ago

Can you please check that the changed description of this issue is correct?

I'd say - Absence of smoothing algorithm for subpixel artifacts when drawing on Canvas with fractional value coordinates

okushnikov commented 3 months ago

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