slint icon indicating copy to clipboard operation
slint copied to clipboard

Path's that have a stroke suddenly get a margin

Open soundprojects opened this issue 2 years ago • 3 comments

Hi

I am trying to draw Path lines next to each other using a HorizontalLayout. All is well until I decide to have a stroke around my lines. When applying the code below in the editor:

export Demo := Window {
    width:  300px;
    height: 300px;
    HorizontalLayout{
    width:80px;
    height:40px;

	    Path{
	        fill:#000;
	        stroke-width:2px;
	        stroke:#000;
	        MoveTo{x:0;y: 0;}
	        LineTo{x:40;y: 0;}
        }
        Path{
	        fill:#000;
	        stroke-width:2px;
	        stroke:#000;
	        MoveTo{x:0;y: 0;}
	        LineTo{x:40;y: 0;}
        }
    }    
}
Schermafbeelding 2022-02-23 om 19 00 04

Is this a simple consequence of the margin pushing a margin around the path and therefore makes it impossible to align these two Path's?

soundprojects avatar Feb 23 '22 18:02 soundprojects

I think that is only a consequence of the use of a path stroke - as you suspect. If you change the stroke width to zero the paths align. The stroke cap should be "butt" style and be square, but perhaps there's a rounding error somewhere.

This could either be a bug in the underlying femtovg library or perhaps an artefact of that cap choice. We might need to add a stroke-linecap property to make this configurable, if using an explicitly square cap fixes it.

If you strictly need horizontal lines, then an alternative would perhaps be to use Rectangle elements.

tronical avatar Feb 25 '22 19:02 tronical

I guess this might be a bug in femto-vg. Here is how it render for me: (left = GL backend, right = Qt backend)

Screenshot_20220301_100100

ogoffart avatar Mar 01 '22 09:03 ogoffart

Yes, I agree.

We have other similar issues with the path rendering also in the iot dashboard with femtovg. I've been contemplating trying to replace the custom stroker and tesselator with Lyon's one.

tronical avatar Mar 01 '22 09:03 tronical

Here is a similar issue that is reproduced with both qt and winit-femtovg, it might be a different cause though.

Is looks like the stroke causes the document to grow out of the viewport a bit and that the layout is made using the full bounds instead of using the specified viewport size, so the SVG document is scaled and I can't use my UI's coordinates anymore even if I don't care about clipping.

Setting viewbox-width and height like this feels like a hack to start with though. It would be nice if there was just a boolean property that force the Path to use the same coordinate system instead of its own, and potentially overriding the mechanism at fault here. Somewhat related to the discussion in #1742.

export component Test inherits Rectangle {
    background: lightgreen;
    Rectangle {
        background: red;
        width: 50%;
        height: 5%;

        Path {
            viewbox-width: self.width / 1px;
            viewbox-height: self.height / 1px;

            MoveTo { x: 0; y: 0; }
            LineTo { x: parent.viewbox-width; y: 0; }
            LineTo { x: parent.viewbox-width; y: parent.viewbox-height; }
            LineTo { x: 0; y: parent.viewbox-height; }
            Close {}

            stroke: blue;
            stroke-width: 1px;
            fill: yellow;
        }
    }
}

SlintPad

image

jturcotte avatar Nov 17 '23 21:11 jturcotte

After gutting out the skia renderer, I figured out that I need this for it to be pixel-perfect. The main issue is that I misunderstood LineTo to mean LineLength or LineUntil, while what it does is move the cursor with the stroke width and caps affecting pixels around that cursor. So with a Path width of 100px and a stroke-width of 1px, I have to draw a line between x=0 and x=99 to have a drawn line of 100px long.

For a stroke-width of 2px, I should draw from x=1 to x=98 to account for the stroke caps. But even if caps were configurable, the same issue applies for a line drawn along the clip line instead of against it, I have to move the cursor inside to make sure that the stroke width fits inside the viewbox the same way as Rectangle draws.

The other thing that affects my issue, but not the original reported example, is that I Slint adjusts the viewbox size to exclude the stroke width but I think it would be clearer if it was included. I made a pull request for this but feel free to correct my assumptions if they are wrong.

So with the change above and this corrected example I get the expected result:

export component Test inherits Rectangle {
    background: lightgreen;
    Rectangle {
        background: red;
        width: 50%;
        height: 5%;

        Path {
            viewbox-width: self.width / 1px;
            viewbox-height: self.height / 1px;

            MoveTo { x: 0; y: 0; }
            LineTo { x: parent.viewbox-width - 1; y: 0; }
            LineTo { x: parent.viewbox-width - 1; y: parent.viewbox-height - 1; }
            LineTo { x: 0; y: parent.viewbox-height - 1; }
            Close {}

            stroke: blue;
            stroke-width: 1px;
            fill: yellow;
        }
    }
}

Edit: Corrected this comment about non-integer coordinates being needed, as Slint already offset each path element by stroke_width / 2. Fixing the two issues mentioned now seems like a better explanation.

jturcotte avatar Nov 18 '23 00:11 jturcotte