You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Druid crate在控件画布上通过DrawCtx加载并绘制PNG文件

Druid 内置图片资源加载与精灵图切分显示的常规实现

核心问题解决方案

1. SubImage 转 ImageBuf 的实现

druid 本身重新导出了image crate 的能力,不需要依赖SubImage做转换,直接从原图的像素缓冲区截取对应区域的像素即可构造ImageBuf,补全后的切分函数如下:

use druid::image::{GenericImageView};
use druid::{ImageBuf, Size};

fn split(rows: usize, columns: usize, data: &[u8]) -> Vec<ImageBuf> {
    let image = ImageBuf::from_data(data).unwrap();
    let mut ret = Vec::with_capacity(rows * columns);
    let Size { width, height } = image.size();
    let (row_h, column_w) = (height as usize / rows, width as usize / columns);

    // 获取底层可操作的像素图像实例
    let raw_image = image.as_raw_image();

    for idx in 0..(columns * rows) {
        let x = (idx % columns) * column_w;
        let y = (idx / columns) * row_h;
        // 直接截取指定区域的子图像
        let sub_img = raw_image.view(x as u32, y as u32, column_w as u32, row_h as u32);
        // 转换为druid可用的ImageBuf
        let sub_buf = ImageBuf::from_dynamic_image(sub_img.to_image());
        ret.push(sub_buf);
    }
    ret
}

2. 控件绘制图片的标准实现

你不需要接触底层piet的d2d Bitmap 结构,druid 提供了面向普通用户的封装接口:

  • 切分好的ImageBuf可以直接调用to_image(ctx.render_ctx)方法转换为可绘制的Image实例
  • 调用PaintCtx::draw_image即可指定坐标绘制,第一次调用时会自动上传到GPU缓存,后续调用直接复用缓存,性能无损耗

完整的控件绘制示例如下:

use druid::{widget::prelude::*, RenderContext, Point, ImageBuf, Size, Env};

// 存储精灵图资源的游戏控件示例
struct GameBoard {
    // 提前切分好的精灵图集合,存在控件状态里,不要在绘制逻辑中重复切分
    sprites: Vec<ImageBuf>,
}

impl Widget<()> for GameBoard {
    fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut (), _env: &Env) {}
    fn lifecycle(&mut self, _ctx: &mut LifeCycleCtx, _event: &LifeCycle, _data: &(), _env: &Env) {}
    fn update(&mut self, _ctx: &mut UpdateCtx, _old_data: &(), _data: &(), _env: &Env) {}
    fn layout(&mut self, _ctx: &mut LayoutCtx, bc: &BoxConstraints, _data: &(), _env: &Env) -> Size {
        bc.max()
    }
    fn paint(&mut self, ctx: &mut PaintCtx, _data: &(), _env: &Env) {
        // 示例:把序号为0的精灵绘制在坐标(100,100)的位置
        let target_sprite = &self.sprites[0];
        let render_image = target_sprite.to_image(ctx.render_ctx);
        // 构造绘制区域,按照精灵原始尺寸绘制
        let draw_area = Point::new(100.0, 100.0).to_rect(target_sprite.size());
        // 像素风格精灵选择NearestNeighbor插值模式避免缩放模糊
        ctx.draw_image(&render_image, draw_area, druid::piet::InterpolationMode::NearestNeighbor);
    }
}

完整流程说明

  • 资源加载阶段直接用include_bytes!把精灵图编译进二进制文件,完全不需要依赖外部文件系统
  • 在应用初始化阶段提前调用切分函数把精灵图拆分为多个ImageBuf,存储到全局状态或者控件状态中
  • 绘制阶段直接从状态中取对应的精灵ImageBuf转换绘制即可

内容的提问来源于stack exchange,提问作者Nikolai Savulkin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 09:15:04