How to load and draw PNG file on widget canvas (via DrawCtx) using the Druid crate?

935 Views Asked by At

I'm making a simple checkers like game in druid. At this point I want to replace rectangles I used for prototyping with some pixel sprites. My initial plan was to try and load them in the root function I pass to

WindowDesc::new(root)

using

include_bytes!

, and the below function:


fn split(rows: usize, columns: usize, data: &[u8]) -> Vec<ImageBuf> {

    let image: ImageBuf = ImageBuf::from_data(data).unwrap();
    let mut ret: Vec<ImageBuf> = Vec::new();
    let Size {width, height} = image.size();
    let (row_h, column_w) = (height as usize / rows, width as usize / columns);

    for idx in 0..(columns * rows) {

        let x = (idx % columns) as u32;
        let y = (idx / columns) as u32;

        let mut frag = SubImage::new(&image,
                                 x * column_w as u32,
                                 y * row_h as u32,
                                 (x+1) * column_w as u32,
                                 (y+1) * row_h as u32);

        // I'm not sure how to convert SubImage into ImageBuf here...


    }

    ret
}

However I've encountered few problems. First being that I didn't find any way to get ImageBuf from SubImage. Second being that PaintCtx::draw_image() takes in a reference to Bitmap struct which seems to be a part of d2d sub-crate within piet - and I assume is used by the graphical backend and is not intended to be used by the user.

What is an idiomatic and conventional way to load and display image in druid at some x and y in widget canvas (without requirement to keep it in filesystem as dependency)?

1

There are 1 best solutions below

0
On BEST ANSWER

I would advise you to use the image crate, to load the image (you did not specify the format, so I used a RGBA png with 4 bytes per pixel in my example).

  1. Get the pixels from the image as a Vec<u8>
  2. Draw those pixels using ctx.make_image and ctx.draw_image

Here is a full example based on the custom_widget example:

#![windows_subsystem = "windows"]

use druid::piet::{ImageFormat, InterpolationMode};
use druid::widget::prelude::*;
use druid::{AppLauncher, Color, LocalizedString, Rect, WindowDesc};

struct CustomWidget;

impl Widget<String> for CustomWidget {
    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut String, _env: &Env) {}

    fn lifecycle(
        &mut self,
        _ctx: &mut LifeCycleCtx,
        _event: &LifeCycle,
        _data: &String,
        _env: &Env,
    ) {
    }

    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &String, _data: &String, _env: &Env) {}

    fn layout(
        &mut self,
        _layout_ctx: &mut LayoutCtx,
        bc: &BoxConstraints,
        _data: &String,
        _env: &Env,
    ) -> Size {
        if bc.is_width_bounded() | bc.is_height_bounded() {
            let size = Size::new(100.0, 100.0);
            bc.constrain(size)
        } else {
            bc.max()
        }
    }

    fn paint(&mut self, ctx: &mut PaintCtx, _data: &String, _env: &Env) {
        let size = ctx.size();
        let rect = size.to_rect();
        ctx.fill(rect, &Color::WHITE);

        let image_data = get_image();
        let image = ctx
            .make_image(256, 256, &image_data, ImageFormat::RgbaSeparate)
            .unwrap();
        // The image is automatically scaled to fit the rect you pass to draw_image
        ctx.draw_image(&image, Rect::new(20.0, 20.0, 200.0, 200.0), InterpolationMode::Bilinear);
    }
}

pub fn main() {
    let window = WindowDesc::new(|| CustomWidget {}).title(LocalizedString::new("Fancy Colors"));
    AppLauncher::with_window(window)
        .use_simple_logger()
        .launch("Druid + Piet".to_string())
        .expect("launch failed");
}

fn get_image() -> Vec<u8> {
    let bytes = include_bytes!("my_image.png");
    let img = image::load_from_memory_with_format(bytes, image::ImageFormat::Png).unwrap();
    img.into_bytes()
}

You will need these dependencies:

[dependencies]
druid = "0.7.0"
image = "0.23.14"

and a PNG picture using RGBA8 format named my_image.png in the same directory as your main.rs.