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

Consider using tiny-skia #357

Closed
RazrFalcon opened this issue Sep 30, 2020 · 32 comments
Closed

Consider using tiny-skia #357

RazrFalcon opened this issue Sep 30, 2020 · 32 comments
Milestone

Comments

@RazrFalcon
Copy link

RazrFalcon commented Sep 30, 2020

Since you're already using raqote, I though you might be interest in a faster, more actively developed alternative.

tiny-skia is a direct raqote rival. It's basically a Skia CPU subset ported to pure Rust.
And it's usually 2-6x faster than raqote, while still 2-3 times slower than Skia (it's mainly SIMD issues). In many cases it's even faster than cairo.

Is still in development (90% is done) and I plan to use it in resvg very soon. And I'm interested in any kind of feedback.

@FloVanGH
Copy link
Contributor

FloVanGH commented Oct 1, 2020

hi, thank you nice work 🙂. Looks promising I will try to create a tiny-skia render backend.

@arctic-alpaca
Copy link
Contributor

tiny-skia is looking great. Would this mean no text on Canvas widgets? As far as I can tell, raqote does support (some) text while tiny-skia doesn't/won't for quite some time support any.

@FloVanGH
Copy link
Contributor

FloVanGH commented Oct 1, 2020

@arctic-alpaca with the text drawing should be not problem, we uses for raqote also a custom implementation based on rusttype.

@RazrFalcon
Copy link
Author

@arctic-alpaca raqote has a very rudimentary text support which relies on system libraries. I plan to use a pure Rust implementation eventually.

@arctic-alpaca
Copy link
Contributor

Thanks for the explanations!
@FloVanGH Out of interest and to understand OrbTk better, what needs to be done to implement a new drawing backend? Is it enough to implement RenderContext2D and modify the renderer/src/lib.rs file?

@FloVanGH
Copy link
Contributor

FloVanGH commented Oct 1, 2020

@arctic-alpaca you are welcome. Yes that is the main effort of implement a new render backend. What you have to do then is to connect you RenderContext2D with a window. That is done inside of the shell crate.

But with alpha-5 I want to decouple that parts more from the other crates. My goal is that you can inject render context and events from outside. This will make it possible to use OrbTk also I use of a game engine.

@kivimango
Copy link
Contributor

maybe it is worth to creating a RenderBackend trait.

@FloVanGH
Copy link
Contributor

FloVanGH commented Oct 1, 2020

@kivimango yes that's the plan 🙂. And I want to separate each render and window backend in a own crate. This will reduce a lot of cfg code.

@FloVanGH FloVanGH added this to the 0.3.1-alpha5 milestone Oct 23, 2020
@FloVanGH
Copy link
Contributor

With the upcoming milestone alpha5 I want to do same further performance improvements. And I will also start an experimental tiny-skia render backend.

@RazrFalcon
Copy link
Author

RazrFalcon commented Oct 23, 2020

Glad to here. After 3 weeks, I'm still at 90%. This is just how software development works. =)

The library become 20-50% faster and there are only two things left: stroke dashing (easy) and clipping (hard). Once again, I hope to finish in two weeks. But we'll see.

From the performance prospective, tiny-skia is 2-3x faster than cairo in most cases, but there are still weird cases when cairo is faster. And compared to raqote, we are 4-7x faster, depending on a task. Also, unlike raqote, tiny-skia supports bicubic bitmaps transformation and hairline stroking (stroke-width < 1px).

@FloVanGH
Copy link
Contributor

Glad to here. After 3 weeks, I'm still at 90%. This is just how software development works. =)

That's true ;-)

The library become 20-50% faster and there are only two things left: stroke dashing (easy) and clipping (hard). Once again, I hope to finish in two weeks. But we'll see.

That's great I think clippling is the last part we currently need from tiny skia.

@sandmor
Copy link
Contributor

sandmor commented Oct 29, 2020

Here is an implementation of the tiny-skia backend, currently is not complete but is understandable for the majority of the widgets, missing functionalities needed to finish it:

  • Clipping support although I think the rectangular one would work for the UI for the Canvas widget is expected that the clip function clips the current path.
  • Support for draw arcs, with beziers you only can approximate them but never match it!.

Also, I have a problem with the gradients, for some reason they don't work, and LinearGradient::new returns me None

@RazrFalcon
Copy link
Author

Yes, clipping with path will be supported in the release.

Support for draw arcs, with beziers you only can approximate them but never match it!.

Skia converts Arcs to conic curves and then into quads. So it still not as perfect, I guess. tiny-skia skips arcs completely for now.

LinearGradient::new returns me None

Can you provide the input values you are using?

@sandmor
Copy link
Contributor

sandmor commented Oct 30, 2020

Can you provide the input values you are using?

I think is better with the context, I checked the input in which it fails is not empty and its two points are separated, also it fails in points_to_unit_ts but I do not understand that function!

Yes, clipping with path will be supported in the release.

I saw the roadmap thank you!

tiny-skia skips arcs completely for now

Well, then I will implement them with beziers, thanks
Tell me if I'm wrong: for a gradient, the coordinates to pass are the ones of the two ends, right?

@RazrFalcon
Copy link
Author

Yes, I saw this code. I'm asking about the actual values/number, so I can try to reproduce it myself.

points_to_unit_ts

Looks like a malformed transform for me.

@sandmor
Copy link
Contributor

sandmor commented Oct 30, 2020

Sorry, here is the input values for one of the gradients of the calculator example:

[crates/render/src/tiny-skia/mod.rs:89] tstart = Point {
    x: 132.0,
    y: 332.0,
}
[crates/render/src/tiny-skia/mod.rs:89] tend = Point {
    x: 132.0,
    y: 284.0,
}
[crates/render/src/tiny-skia/mod.rs:89] &g_stops = [
    GradientStop {
        position: 0,
        color: Color {
            r: 0.47843137,
            g: 0.56078434,
            b: 0.6392157,
            a: 1,
        },
    },
    GradientStop {
        position: 1,
        color: Color {
            r: 0.39215687,
            g: 0.48235294,
            b: 0.5686275,
            a: 1,
        },
    },
]
[crates/render/src/tiny-skia/mod.rs:89] spread = Pad

Looks like a malformed transform for me.

I'm using Transform::identity() directly in the argument

@RazrFalcon
Copy link
Author

Thanks. I was able to reproduce it. Will see what's wrong.

PS: this is because start_x and end_x are the same. Not sure why this is a problem.

@RazrFalcon
Copy link
Author

It wasn't a trivial fix, but it's fixed now.

@sandmor
Copy link
Contributor

sandmor commented Oct 30, 2020

Thank you very much, your crate is pretty fast

@RazrFalcon
Copy link
Author

Does it visually noticeable compared to raqote?

@sandmor
Copy link
Contributor

sandmor commented Oct 30, 2020

Very, I don't know if I'm oversensitive but for me is a great speed up. You can check yourself.
Also, can you give a manner to modifying the alpha channel of a Color after its creation, please? I need to apply for the global alpha.

@RazrFalcon
Copy link
Author

Nice to hear.

I will add set_* methods to Color.

@RazrFalcon
Copy link
Author

@sandmor I've added set_* methods as well as Color::apply_opacity, which should be handy.

@sandmor
Copy link
Contributor

sandmor commented Oct 30, 2020

Thanks, but you don't have to run so much 😄, this of anyway will not get merged until at least rectangular clipping gets supported.

@RazrFalcon
Copy link
Author

@sandmor I've just added clipping.

@sandmor
Copy link
Contributor

sandmor commented Nov 7, 2020

@RazrFalcon Thank you, currently I'm working in the arcs, draw the extremes is a bit complex but with the help of Stack Overflow I expect to soon have the function ended then I will look into the path and if there is another bugs on my implementation, if not I will do a pull request, Thanks!

@sandmor
Copy link
Contributor

sandmor commented Nov 8, 2020

@RazrFalcon is set_clip_path stackable? so, I can cut a region, build another path and stack it over, so the two ones clip the following operations? that is not require for the UI but so is as RenderContext2d would work... well, that is a corner case(I think) so don't worry very much about.
Also, can you setup a boundings check when a path is traced? I hit various "unreachable" errors in my experiments when I attempt to draw off-boundings, that is very annoying when you just want to see why your operation is doing that, also I think that can be trigger by OrbTk itself in some cases attempting to draw the UI, so can you fix that, please?
EDIT: OrbTk uses save and restore methods to deal with multiple paths if you are interested although I will do a PR of anyway at least as an experimental backend

@RazrFalcon
Copy link
Author

RazrFalcon commented Nov 8, 2020

@sandmor I can port Skia's implementation of arc if you like. It's very robust.

No, tiny-skia is intentionally doesn't have "layers". Maybe it would change someday, but for now is just a single layer.

I hit various "unreachable" errors in my experiments when I attempt to draw off-boundings

This must not happen. Could you provide a minimal example?

OrbTk uses save and restore methods to deal with multiple paths

What paths? Clip paths?

@sandmor
Copy link
Contributor

sandmor commented Nov 8, 2020

This must not happen. Could you provide a minimal example?
Is weird I can not reproduce it out of my code and the thing I was used to test was one of my recently started projects that I modify to use as canvas to draw arcs, I'd prefer not show it although I trigger one the errors here, apparently you have a bunch of unreachable functions that are called.
No, tiny-skia is intentionally doesn't have "layers". Maybe it would change someday, but for now is just a single layer.
I don't need layers and OrbTk don't use it neither, but works as next:

  • You have a not clipped canvas
  • You build a path
  • You save the current canvas state
  • You use the path to clip the desire area
  • You draw some stuff
  • You build another path
  • You use the new path to clip the desire area, this results in a clipped area that is the overlaps of the already apply paths
  • Then you can call restore and restore the clipped area to the last save state.
    For example, I apply a square mask using clipping, then I clip another time but with a circular mask, I expect the next result if I paint the whole area:
    canvas
    Like you see, I don't need multiple layers, only the capability of save the clipping mask when RenderContext2D.save is called and the capability of overlap the new clipping path(or clipping mask) with the previous one. I can use set_clip_mask or something then to restore the saved clipping mask.

I can port Skia's implementation of arc if you like

Don't worry I already implemented it, although I'd appreciate if you can view some obvious optimization on my code.... I think it have too many conditions I tried to decrease them but I don't have many experience with this.

@RazrFalcon
Copy link
Author

RazrFalcon commented Nov 8, 2020

Yes, tiny-skia doesn't support save/restore either. At this point I guess it would be easier to re-implement in on the orbtk side. So you will need to store the last/current transform and clip mask. And I can make the clip mask public.
I'm trying too keep tiny-skia as simple as possible.

This is the one I'm using in resvg. It works fine.
Skia uses a different algorithm.
And don't worry about performance. Compared to actual rendering it's nothing.

I trigger one the errors here

It was already fixed. At least on my tests.

@sandmor
Copy link
Contributor

sandmor commented Nov 9, 2020

it would be easier to re-implement in on the orbtk side

Yeah, good idea, but then I need some tools: The capability of get and set the clippping mask, and the capability to overlap a path area over a clipping mask or if you can maintain simple the things at cost of some performance then only the power of generate a clipping mask from a path and then I manually merge the result with the current clipping mask

This is the one I'm using in resvg. It works fine.

So... I can not see how it draws an arc, only calculates some parameters, I can do that with sin/cos, the problem is calculate the bezier control points needed to trace the arc contour but don't worry you have reason, the performance is negligible in this case I only like to make the things as efficiently as possible... even if somethings sacrifice some code prettiness

@sandmor sandmor mentioned this issue Dec 3, 2020
5 tasks
@rzerres
Copy link
Contributor

rzerres commented May 15, 2022

Merged in.

@rzerres rzerres closed this as completed May 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants