如何使用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
相关产品推荐
相关产品推荐

