How to draw on winit window by 2D graphics library?

Hi, this is the first topic I write here, and also I am a beginner in programming with rust. my question is:

How to draw on a winit window with 2D graphics library like skia-rust , tiny skia, cairo-rs or raqote?

I need a simple example in which a simple shape is drawn by one of the graphics libraries in the winit window without adding any other dependencies in order for the example to be easy to understand

winit knows nothing about drawing or bitmaps and skia knows about window handles. Something (another dependency) needs to bridge the two, and that bridge has to handle major platform differences.

I have no problem with other dependencies as long as they are necessary to bridge the two libraries

So far the ones I've seen either provide their own gui or provide a full 3D API. It's possible to use either type to just blit an image onscreen and build your own gui on top.

OTOH, I just found this: Softbuffer — Rust GUI library // Lib.rs

2 Likes

What I'm trying to do is write a gui, for that I need to draw the gui components like a button using a 2D graphics library, then handle the events as mouse events using winit. One of the most important features that I am trying to achieve is the ability for the component in the GUI to have more than one shape like a button, for example (rectangular, rounded corners...etc), color options (solid, linear gradient, circular gradient...etc) and anti-aliasing, so I mentioned these libraries . I don't mind other libraries as long as they can achieve the same purpose

This uses winit, softbuffer, and tiny-skia:

use softbuffer::{Context, Surface};
use std::num::NonZeroU32;
use tiny_skia::{Color, FillRule, Paint, PathBuilder, Pixmap, Stroke, Transform};
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;

fn main() {
    let event_loop = EventLoop::new();
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let context = unsafe { Context::new(&window) }.unwrap();
    let mut surface = unsafe { Surface::new(&context, &window) }.unwrap();

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;

        match event {
            Event::RedrawRequested(window_id) if window_id == window.id() => {
                let (width, height) = {
                    let size = window.inner_size();
                    (size.width, size.height)
                };
                surface
                    .resize(
                        NonZeroU32::new(width).unwrap(),
                        NonZeroU32::new(height).unwrap(),
                    )
                    .unwrap();

                let mut pixmap = Pixmap::new(width, height).unwrap();
                pixmap.fill(Color::WHITE);
                let path = PathBuilder::from_circle(
                    (width / 2) as f32,
                    (height / 2) as f32,
                    (width.min(height) / 2) as f32,
                )
                .unwrap();
                let mut paint = Paint::default();
                paint.set_color_rgba8(0, 128, 128, 255);
                pixmap.fill_path(
                    &path,
                    &paint,
                    FillRule::EvenOdd,
                    Transform::identity(),
                    None,
                );
                paint.set_color_rgba8(255, 0, 0, 255);
                let mut stroke = Stroke::default();
                stroke.width = 10.0;
                pixmap.stroke_path(&path, &paint, &stroke, Transform::identity(), None);

                let mut buffer = surface.buffer_mut().unwrap();
                for index in 0..(width * height) as usize {
                    buffer[index] = pixmap.data()[index * 4 + 2] as u32
                        | (pixmap.data()[index * 4 + 1] as u32) << 8
                        | (pixmap.data()[index * 4] as u32) << 16;
                }

                buffer.present().unwrap();
            }
            Event::WindowEvent {
                event: WindowEvent::CloseRequested,
                window_id,
            } if window_id == window.id() => {
                *control_flow = ControlFlow::Exit;
            }
            _ => {}
        }
    });
}
3 Likes

Thanks a lot, that looks great, I'll try it later.
It seems to me that I can do the same thing with other 2D graphics libraries. Thank you again

Of course, it would be great to see other examples and good sources related to the topic from different points of view and opinions

You apparently can pass a raw window handle to safe-skia from winit, but for the life of me I can't find the API. It should be through an API in gpu::surfaces, though.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.