slint
slint copied to clipboard
Path's that have a stroke suddenly get a margin
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;}
}
}
}

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?
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.
I guess this might be a bug in femto-vg. Here is how it render for me: (left = GL backend, right = Qt backend)
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.
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;
}
}
}
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.