pnte 0.3.3

2D Graphics library for Windows in Rust
Documentation
use wiard::ToLogical;

fn main() -> anyhow::Result<()> {
    pnte::co_initialize(pnte::CoInit::ApartmentThreaded)?;
    let mut event_rx = wiard::EventReceiver::new();
    let window = wiard::Window::builder(&event_rx)
        .title("pnte gallery")
        .build()?;
    let size = window.inner_size().unwrap();
    let dpi = window.dpi().unwrap();
    let mut ctx = pnte::Context::new(pnte::Direct2D::new()?)?;
    ctx.set_scale_factor(dpi as f32 / 96.0);
    let target = ctx.create_render_target(&window, (size.width, size.height))?;
    let image = pnte::Image::from_file(&ctx, "./assets/ferris.png")?;
    let image_size = image.size();
    let image_size = pnte::Size::new(image_size.width / 8.0, image_size.height / 8.0);
    let white = pnte::SolidColorBrush::new(&ctx, (1.0, 1.0, 1.0, 1.0))?;
    let text_format = pnte::TextFormat::new(&ctx)
        .font(pnte::Font::File(
            &std::path::Path::new(
                "./assets/Inconsolata/static/Inconsolata/Inconsolata-Regular.ttf",
            ),
            "Inconsolata",
        ))
        .size(pnte::FontPoint(25.0))
        .build()?;
    let text_layout = pnte::TextLayout::new(&ctx)
        .text("abcdefghijklmnopqrstuvwxyz")
        .format(&text_format)
        .build()?;
    let pt_text = pnte::Point::new(10.0, 530.0);
    let layout_size = text_layout.size();
    let mut hit_test_display: Option<(char, bool)> = None;
    loop {
        let Some((event, _)) = event_rx.recv() else {
            break;
        };
        match event {
            wiard::Event::MouseInput(m) => {
                let left_button = m.button == wiard::MouseButton::Left
                    && m.button_state == wiard::ButtonState::Released;
                if left_button {
                    let dpi = window.dpi().unwrap();
                    let mouse_position = m.mouse_state.position.to_logical(dpi as i32);
                    let mouse_position =
                        pnte::Point::new(mouse_position.x as f32, mouse_position.y as f32);
                    let inside = pt_text.x <= mouse_position.x
                        && pt_text.y <= mouse_position.y
                        && pt_text.x + layout_size.width >= mouse_position.x
                        && pt_text.y + layout_size.height >= mouse_position.y;
                    if inside {
                        let result = text_layout.hit_test((
                            mouse_position.x - pt_text.x,
                            mouse_position.y - pt_text.y,
                        ))?;
                        if result.inside {
                            hit_test_display = Some((result.c, result.trailing_hit));
                        }
                    }
                }
                window.redraw(None);
            }
            wiard::Event::Draw(_) => {
                ctx.draw(&target, |cmd| -> anyhow::Result<()> {
                    cmd.clear((0.0, 0.0, 0.3, 0.0));
                    let pt = pnte::Point::new(10.0, 0.0);
                    cmd.draw_text("image", pt, &white)?;
                    let pt = pnte::Point::new(10.0, 20.0);
                    cmd.draw_image(
                        &image,
                        None,
                        (
                            pt.x + 10.0,
                            pt.y,
                            pt.x + 10.0 + image_size.width,
                            pt.y + 10.0 + image_size.height,
                        ),
                        None,
                        pnte::Interpolation::HighQualityCubic,
                    );

                    let pt = pnte::Point::new(10.0, 20.0 + image_size.height + 10.0);
                    cmd.draw_text("image (opacity = 0.5)", pt, &white)?;
                    let pt = pnte::Point::new(pt.x, pt.y + 20.0);
                    cmd.draw_image(
                        &image,
                        None,
                        (
                            pt.x + 10.0,
                            pt.y,
                            pt.x + 10.0 + image_size.width,
                            pt.y + 10.0 + image_size.height,
                        ),
                        Some(0.5),
                        pnte::Interpolation::HighQualityCubic,
                    );

                    let pt = pnte::Point::new(image_size.width + 100.0, 0.0);
                    cmd.draw_text("stroke line (width = 2.0)", pt, &white)?;
                    cmd.stroke(
                        &pnte::Line::new((pt.x, 30.0), (pt.x + 200.0, 90.0)),
                        &white,
                        2.0,
                        None,
                    );

                    let pt = pnte::Point::new(pt.x, 90.0 + 10.0);
                    let line_style = pnte::StrokeStyle::new(
                        &ctx,
                        &pnte::StrokeStyleProperties {
                            start_cap: pnte::CapStyle::Round,
                            end_cap: pnte::CapStyle::Triangle,
                            line_join: pnte::LineJoin::Miter,
                            dash: Some(pnte::Dash {
                                cap: pnte::CapStyle::Flat,
                                style: pnte::DashStyle::DashDot,
                                offset: 0.0,
                            }),
                        },
                    )?;
                    cmd.draw_text("stroke line (styled)", pt, &white)?;
                    cmd.stroke(
                        &pnte::Line::new((pt.x, pt.y + 30.0), (pt.x + 200.0, pt.y + 90.0)),
                        &white,
                        2.0,
                        Some(&line_style),
                    );
                    let pt = pnte::Point::new(pt.x, pt.y + 90.0 + 10.0);
                    cmd.draw_text("stroke quadratic bezier", pt, &white)?;
                    let pt = pnte::Point::new(pt.x, pt.y + 30.0);
                    let path = pnte::Path::builder(&ctx, pt)?
                        .quadratic_bezier_to(
                            (pt.x + 20.0, pt.y + 90.0),
                            (pt.x + 200.0, pt.y + 90.0),
                        )
                        .build(pnte::PathEnd::Open)?;
                    cmd.stroke(&path, &white, 2.0, None);

                    let pt = pnte::Point::new(pt.x, pt.y + 90.0 + 10.0);
                    cmd.draw_text("stroke cubic bezier", pt, &white).unwrap();
                    let pt = pnte::Point::new(pt.x, pt.y + 30.0);
                    let path = pnte::Path::builder(&ctx, pt)?
                        .cubic_bezier_to(
                            (pt.x + 100.0, pt.y),
                            (pt.x + 100.0, pt.y + 90.0),
                            (pt.x + 200.0, pt.y + 90.0),
                        )
                        .build(pnte::PathEnd::Open)?;
                    cmd.stroke(&path, &white, 2.0, None);

                    let pt_text_caption = pnte::Point::new(pt_text.x, pt_text.y - 30.0);
                    cmd.draw_text("text", pt_text_caption, &white)?;
                    cmd.draw_text(&text_layout, pt_text, &white)?;
                    if let Some((c, trailing_hit)) = hit_test_display.as_ref() {
                        let pt_text = pnte::Point::new(pt_text.x, pt_text.y + 35.0);
                        cmd.draw_text(
                            &format!("{c}, trailing_hit = {trailing_hit}"),
                            pt_text,
                            &white,
                        )?;
                    }
                    let pt_text_caption = pnte::Point::new(pt_text.x, pt_text.y + 35.0 + 30.0);
                    cmd.draw_text("accent", pt_text_caption, &white)?;
                    let pt_text = pnte::Point::new(pt_text_caption.x, pt_text_caption.y + 30.0);
                    cmd.draw_text("é à â ü ç", pt_text, &white)?;

                    let pt = pnte::Point::new(pt.x + 230.0, 0.0);
                    cmd.draw_text("fill rectangle", pt, &white)?;
                    let pt = pnte::Point::new(pt.x, pt.y + 30.0);
                    cmd.fill(&pnte::Rect::from_point_size(pt, (60.0, 60.0)), &white);

                    let pt = pnte::Point::new(pt.x, pt.y + 90.0);
                    cmd.draw_text("fill circle", pt, &white)?;
                    let pt_circle = pnte::Point::new(pt.x + 30.0, pt.y + 30.0 + 30.0);
                    cmd.fill(&pnte::Circle::new(pt_circle, 30.0), &white);

                    let pt = pnte::Point::new(pt.x, pt_circle.y + 60.0);
                    cmd.draw_text("fill ellipse", pt, &white)?;
                    let pt_ellipse = pnte::Point::new(pt.x + 30.0, pt.y + 30.0 + 30.0);
                    cmd.fill(&pnte::Ellipse::new(pt_ellipse, 30.0, 15.0), &white);

                    let pt = pnte::Point::new(pt.x + 150.0, 0.0);
                    let grad = pnte::LinearGradientBrush::new(
                        &ctx,
                        pt,
                        (pt.x + 60.0, pt.y),
                        pnte::GradientMode::Clamp,
                        &[
                            pnte::GradientStop::new(0.0, (1.0, 0.0, 0.0, 1.0)),
                            pnte::GradientStop::new(0.5, (0.0, 1.0, 0.0, 1.0)),
                            pnte::GradientStop::new(1.0, (0.0, 0.0, 1.0, 1.0)),
                        ],
                    )?;
                    cmd.draw_text("line gradient", pt, &white)?;
                    let pt = pnte::Point::new(pt.x, pt.y + 30.0);
                    cmd.fill(&pnte::Rect::from_point_size(pt, (60.0, 60.0)), &grad);

                    let pt = pnte::Point::new(pt.x, pt.y + 60.0 + 30.0);
                    cmd.draw_text("radial gradient", pt, &white)?;
                    let pt = pnte::Point::new(pt.x, pt.y + 30.0);
                    let grad = pnte::RadialGradientBrush::new(
                        &ctx,
                        pnte::Circle::new((pt.x + 30.0, pt.y + 30.0), 30.0).to_ellipse(),
                        (0.0, 0.0),
                        pnte::GradientMode::Clamp,
                        &[
                            pnte::GradientStop::new(0.0, (1.0, 0.0, 0.0, 1.0)),
                            pnte::GradientStop::new(0.5, (0.0, 1.0, 0.0, 1.0)),
                            pnte::GradientStop::new(1.0, (0.0, 0.0, 1.0, 1.0)),
                        ],
                    )?;
                    cmd.fill(&pnte::Rect::from_point_size(pt, (60.0, 60.0)), &grad);
                    Ok(())
                })??;
            }
            _ => {}
        }
    }
    Ok(())
}