Rust图形渲染库iced_tiny_skia的使用,轻量级2D图形绘制与跨平台UI集成方案
Rust图形渲染库iced_tiny_skia的使用,轻量级2D图形绘制与跨平台UI集成方案
安装
在项目目录中运行以下Cargo命令:
cargo add iced_tiny_skia
或者在Cargo.toml中添加以下行:
iced_tiny_skia = "0.13.0"
基本使用示例
以下是一个完整的iced_tiny_skia使用示例,展示了如何创建一个简单的2D图形绘制应用:
use iced::{
widget::{canvas, container},
Application, Color, Command, Element, Length, Settings, Theme,
};
use iced_tiny_skia as tiny_skia;
struct Example {
cache: canvas::Cache,
}
#[derive(Debug, Clone)]
enum Message {}
impl Application for Example {
type Executor = iced::executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
(
Example {
cache: canvas::Cache::new(),
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("iced_tiny_skia示例")
}
fn update(&mut self, _message: Message) -> Command<Message> {
Command::none()
}
fn view(&self) -> Element<Message> {
let canvas = canvas(self as &Self)
.width(Length::Fill)
.height(Length::Fill);
container(canvas)
.width(Length::Fill)
.height(Length::Fill)
.padding(20)
.into()
}
}
impl canvas::Program<Message> for Example {
type State = ();
fn draw(
&self,
_state: &Self::State,
renderer: &iced::Renderer,
theme: &Theme,
bounds: iced::Rectangle,
_cursor: canvas::Cursor,
) -> Vec<canvas::Geometry> {
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
// 使用Tiny Skia绘制2D图形
let mut pixmap = tiny_skia::Pixmap::new(
frame.width() as u32,
frame.height() as u32,
)
.unwrap();
// 创建画笔
let mut paint = tiny_skia::Paint::default();
paint.set_color(tiny_skia::Color::from_rgba8(255, 0, 0, 255));
paint.anti_alias = true;
// 绘制一个圆
let circle = tiny_skia::PathBuilder::from_circle(
frame.width() / 2.0,
frame.height() / 2.0,
100.0,
)
.unwrap();
pixmap.fill_path(
&circle,
&paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
// 将Tiny Skia的Pixmap转换为iced的图像
frame.draw_image(canvas::Image::new(
iced::advanced::graphics::image::Handle::from_pixels(
frame.width(),
frame.height(),
pixmap.data().to_vec(),
),
));
});
vec![geometry]
}
}
fn main() -> iced::Result {
Example::run(Settings {
window: iced::window::Settings {
size: (800, 600),
..Default::default()
},
..Default::default()
})
}
功能说明
- 轻量级2D渲染:iced_tiny_skia基于Tiny Skia库,提供高效的2D图形渲染能力
- 跨平台UI集成:可与iced UI框架无缝集成,创建跨平台的图形界面应用
- 简单易用:提供简洁的API用于绘制基本图形、路径和图像
完整示例代码
以下是一个更完整的示例,展示了如何使用iced_tiny_skia绘制多种图形:
use iced::{
widget::{canvas, container, text},
Application, Color, Command, Element, Length, Settings, Theme,
};
use iced_tiny_skia as tiny_skia;
struct DrawingApp {
cache: canvas::Cache,
}
#[derive(Debug, Clone)]
enum Message {
Redraw,
}
impl Application for DrawingApp {
type Executor = iced::executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
(
DrawingApp {
cache: canvas::Cache::new(),
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("高级iced_tiny_skia示例")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::Redraw => {
self.cache.clear();
Command::none()
}
}
}
fn view(&self) -> Element<Message> {
let content = container(
iced::widget::column![
text("点击画布重新绘制图形").size(20),
canvas(self as &Self)
.width(Length::Fill)
.height(Length::Fill)
]
.spacing(10),
)
.width(Length::Fill)
.height(Length::Fill)
.padding(20)
.center_x()
.center_y();
content.into()
}
}
impl canvas::Program<Message> for DrawingApp {
type State = ();
fn draw(
&self,
_state: &Self::State,
renderer: &iced::Renderer,
_theme: &Theme,
bounds: iced::Rectangle,
_cursor: canvas::Cursor,
) -> Vec<canvas::Geometry> {
let geometry = self.cache.draw(renderer, bounds.size(), |frame| {
// 创建Tiny Skia画布
let mut pixmap = tiny_skia::Pixmap::new(
frame.width() as u32,
frame.height() as u32,
)
.unwrap();
// 绘制渐变背景
let gradient = tiny_skia::LinearGradient::new(
tiny_skia::Point::from_xy(0.0, 0.0),
tiny_skia::Point::from_xy(frame.width(), frame.height()),
vec![
tiny_skia::GradientStop::new(0.0, tiny_skia::Color::from_rgba8(100, 150, 255, 255)),
tiny_skia::GradientStop::new(1.0, tiny_skia::Color::from_rgba8(200, 100, 255, 255)),
],
tiny_skia::SpreadMode::Pad,
tiny_skia::Transform::identity(),
)
.unwrap();
let mut paint = tiny_skia::Paint::default();
paint.shader = gradient;
pixmap.fill_rect(
tiny_skia::Rect::from_xywh(
0.0,
0.0,
frame.width(),
frame.height(),
)
.unwrap(),
&paint,
tiny_skia::Transform::identity(),
None,
);
// 绘制红色圆形
let mut circle_paint = tiny_skia::Paint::default();
circle_paint.set_color(tiny_skia::Color::from_rgba8(255, 0, 0, 200));
circle_paint.anti_alias = true;
let circle = tiny_skia::PathBuilder::from_circle(
frame.width() * 0.25,
frame.height() / 2.0,
80.0,
)
.unwrap();
pixmap.fill_path(
&circle,
&circle_paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
// 绘制蓝色矩形
let mut rect_paint = tiny_skia::Paint::default();
rect_paint.set_color(tiny_skia::Color::from_rgba8(0, 0, 255, 180));
let rect = tiny_skia::Rect::from_xywh(
frame.width() * 0.5 - 100.0,
frame.height() / 2.0 - 60.0,
200.0,
120.0,
)
.unwrap();
pixmap.fill_rect(
&rect,
&rect_paint,
tiny_skia::Transform::identity(),
None,
);
// 绘制绿色三角形
let mut triangle_paint = tiny_skia::Paint::default();
triangle_paint.set_color(tiny_skia::Color::from_rgba8(0, 255, 0, 220));
let mut pb = tiny_skia::PathBuilder::new();
pb.move_to(frame.width() * 0.75, frame.height() / 2.0 - 80.0);
pb.line_to(frame.width() * 0.75 + 80.0, frame.height() / 2.0 + 80.0);
pb.line_to(frame.width() * 0.75 - 80.0, frame.height() / 2.0 + 80.0);
pb.close();
let triangle = pb.finish().unwrap();
pixmap.fill_path(
&triangle,
&triangle_paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
// 转换为iced图像
frame.draw_image(canvas::Image::new(
iced::advanced::graphics::image::Handle::from_pixels(
frame.width(),
frame.height(),
pixmap.data().to_vec(),
),
));
});
vec![geometry]
}
}
fn main() -> iced::Result {
DrawingApp::run(Settings {
window: iced::window::Settings {
size: (1000, 700),
..Default::default()
},
..Default::default()
})
}
功能说明
- 轻量级2D渲染:iced_tiny_skia基于Tiny Skia库,提供高效的2D图形渲染能力
- 跨平台UI集成:可与iced UI框架无缝集成,创建跨平台的图形界面应用
- 简单易用:提供简洁的API用于绘制基本图形、路径和图像
1 回复
Rust图形渲染库iced_tiny_skia的使用:轻量级2D图形绘制与跨平台UI集成方案
iced_tiny_skia
是一个结合了iced
UI框架和tiny-skia
2D图形库的Rust解决方案,提供了轻量级的2D图形绘制能力,并能无缝集成到跨平台UI应用中。
核心特性
- 轻量级:基于高效的tiny-skia图形库
- 跨平台:支持Windows、macOS和Linux
- 与iced深度集成:可作为iced的图形绘制后端
- 高性能:利用硬件加速的2D渲染
- 简单API:提供直观的绘图接口
基本使用方法
添加依赖
首先在Cargo.toml
中添加依赖:
[dependencies]
iced = "0.10"
iced_tiny_skia = "0.4"
基本绘图示例
use iced::{
widget::canvas::{self, Canvas, Frame, Path},
Application, Color, Command, Element, Length, Settings, Theme,
};
use iced_tiny_skia::tiny_skia;
pub fn main() -> iced::Result {
MyApp::run(Settings::default())
}
struct MyApp;
impl Application for MyApp {
type Message = ();
type Theme = Theme;
type Executor = iced::executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
(MyApp, Command::none())
}
fn title(&self) -> String {
String::from("Iced + tiny-skia Demo")
}
fn update(&mut self, _message: Self::Message) -> Command<Self::Message> {
Command::none()
}
fn view(&self) -> Element<Self::Message> {
Canvas::new(Drawing::new())
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
struct Drawing;
impl Drawing {
fn new() -> Self {
Drawing
}
}
impl canvas::Program<()> for Drawing {
type State = ();
fn draw(&self, _state: &Self::State, bounds: iced::Rectangle,极客时间 _cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
let mut frame = Frame::new(bounds.size());
// 使用tiny-skia绘制一个简单的图形
if let Some(pixmap) = frame.as_skia() {
let mut paint = tiny_skia::Paint::default();
paint.set_color_rgba8(50, 127, 150, 200);
paint.anti_alias = true;
let rect = tiny_skia::Rect::from_xywh(
100.0, 100.0,
bounds.width - 200.0,
bounds.height - 200.0
).unwrap();
let mut pb = tiny_skia::PathBuilder::new();
pb.push_rect(rect);
let path = pb.finish().unwrap();
pixmap.fill_path(
&path,
&paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
}
vec![frame.into_geometry()]
}
}
高级功能
绘制复杂路径
// 在draw方法中添加:
let mut pb = tiny_skia::PathBuilder::new();
pb.move_to(200.0, 200.极客时间0);
pb.line_to(400.0, 200.0);
pb.quad_to(500.0, 300.0, 400.0, 400.0);
pb.line_to(200.0, 400.0);
pb.close();
let path = pb.finish().unwrap();
paint.set_color_rgba8(200, 100, 50, 220);
pixmap.fill_path(
&path,
&paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
使用渐变填充
let gradient = tiny_skia::LinearGradient::new(
tiny_skia::Point::from_xy(100.0, 100.0),
tiny_skia::Point::from_xy(300.0, 300.0),
vec![
tiny_skia::GradientStop::new(0.0, tiny_skia::Color::from_rgba8(255, 0, 0, 255)),
tiny_skia::GradientStop::new(0.5, tiny_skia::Color::from_rgba8(0, 255, 0, 255)),
tiny_skia::GradientStop::new(1.0, tiny_skia::Color::from_rgba8(0, 0, 255, 255)),
],
tiny_skia::SpreadMode::Pad,
tiny_skia::Transform::identity(),
).unwrap();
paint.shader = gradient;
pixmap.fill_path(
&path,
&paint,
tiny_skia::FillRule::Winding,
tiny_skia::Transform::identity(),
None,
);
与iced UI组件集成
iced_tiny_skia
可以与常规iced UI组件无缝集成:
fn view(&self) -> Element<Self::Message> {
let column = iced::widget::column![
iced::widget::text("这是一个iced UI与tiny-skia集成的示例")
.size(24),
iced::widget::button("点击我"),
Canvas::new(Drawing::new())
.width(Length::Fill)
.height(Length::Fill)
];
column.into()
}
性能优化技巧
- 重用Paint对象:避免在每帧创建新的Paint对象
- 预计算路径:对于静态图形,提前计算Path对象
- 限制重绘区域:使用
iced::widget::canvas::Cache
缓存静态图形 - 简化复杂路径:减少路径中的点数量
跨平台注意事项
- 在Linux上可能需要安装额外的字体库
- Windows平台默认支持良好
- macOS可能需要处理Retina显示的高DPI缩放
iced_tiny_skia
为Rust开发者提供了一个轻量级但功能强大的2D图形解决方案,特别适合需要在跨平台UI应用中集成自定义绘图功能的场景。
完整示例代码
下面是一个结合了基本绘图、复杂路径和渐变填充的完整示例:
use iced::{
widget::canvas::{self, Canvas, Frame},
Application, Command, Element, Length, Settings, Theme,
};
use iced_tiny_skia::tiny_skia;
pub fn main() -> iced::Result {
GraphicDemo::run(Settings::default())
}
struct GraphicDemo;
impl Application for GraphicDemo {
type Message = ();
type Theme = Theme;
type Executor = iced::executor::Default;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Self::Message>) {
(GraphicDemo, Command::none())
}
fn title(&self) -> String {
String::from("Iced tiny-skia 综合演示")
}
fn update(&mut self, _message: Self::Message) -> Command<Self::Message> {
Command::none()
}
fn view(&self) -> Element<Self::Message> {
Canvas::new(Graphic::new())
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
struct Graphic;
impl Graphic {
fn new() -> Self {
Graphic
}
}
impl canvas::Program<()> for Graphic {
type State = ();
fn draw(&self, _state: &Self::State, bounds: iced::Rectangle, _cursor: canvas::Cursor) -> Vec<canvas::Geometry> {
let mut frame = Frame::new(bounds.size());
if let Some(pixmap) = frame.as_skia() {
// 绘制一个带渐变填充的矩形背景
let bg_rect = tiny_skia::Rect::from_xywh(0.0, 0.0, bounds.width, bounds.height).unwrap();
let mut bg_paint = tiny_skia::Paint::default();
let bg_gradient = tiny_skia::LinearGradient::new(
tiny_skia::Point::from_xy(0.0, 0.0),
tiny_skia::Point::from_xy(0.0, bounds.height),
vec![
tiny_skia::GradientStop::new(0.0, tiny_skia::Color::from_rgba8(30, 30, 40, 255)),
tiny_skia::GradientStop::new(1.0, tiny_skia::Color::from_rgba8(10, 10, 20, 255)),
],
tiny_skia::SpreadMode::Pad,
tiny_skia::Transform::identity(),
).unwrap();
bg_paint.shader = bg_gradient;
let mut pb = tiny_skia::PathBuilder::new();
pb.push_rect(bg_rect);
pixmap.fill_path(&pb.finish().unwrap(), &bg_paint, tiny_skia::FillRule::Winding, tiny_skia::Transform::identity(), None);
// 绘制一个复杂路径(五角星)
let mut star_pb = tiny_skia::PathBuilder::new();
let center_x = bounds.width / 2.0;
let center_y = bounds.height / 2.0;
let radius = bounds.width.min(bounds.height) * 0.4;
for i in 0..5 {
let angle = std::f32::consts::PI * 2.0 * i as f32 / 5.0 - std::f32::consts::PI / 2.0;
let x = center_x + angle.cos() * radius;
let y = center_y + angle.sin() * radius;
if i == 0 {
star_pb.move_to(x, y);
} else {
star_pb.line_to(x, y);
}
let inner_angle = angle + std::f32::consts::PI / 5.0;
let inner_x = center_x + inner_angle.cos() * radius * 0.5;
let inner_y = center_y + inner_angle.sin() * radius * 0.5;
star_pb.line_to(inner_x, inner_y);
}
star_pb.close();
let star_path = star_pb.finish().unwrap();
// 为五角星设置渐变填充
let mut star_paint = tiny_skia::Paint::default();
star_paint.anti_alias = true;
let star_gradient = tiny_skia::RadialGradient::new(
tiny_skia::Point::from_xy(center_x, center_y),
radius,
vec![
tiny_skia::GradientStop::new(0.0, tiny_skia::Color::from_rgba8(255, 215, 0, 255)),
tiny_skia::GradientStop::new(0.7, tiny_skia::Color::from_rgba8(255, 165, 0, 255)),
tiny_skia::GradientStop::new(1.0, tiny_skia::Color::from_rgba8(255, 69, 0, 200)),
],
tiny_skia::SpreadMode::Pad,
tiny_skia::Transform::identity(),
).unwrap();
star_paint.shader = star_gradient;
pixmap.fill_path(&star_path, &star_paint, tiny_skia::FillRule::Winding, tiny_skia::Transform::identity(), None);
// 添加描边效果
let mut stroke_paint = tiny_skia::Paint::default();
stroke_paint.set_color_rgba8(255, 255, 255, 180);
stroke_paint.anti_alias = true;
stroke_paint.stroke = true;
stroke_paint.stroke_width = 2.0;
pixmap.stroke_path(&star_path, &stroke_paint, &tiny_skia::Stroke::default(), tiny_skia::Transform::identity(), None);
}
vec![frame.into_geometry()]
}
}
这个完整示例展示了:
- 创建渐变背景
- 绘制复杂形状(五角星)
- 使用径向渐变填充
- 添加描边效果
- 抗锯齿处理
运行后会显示一个五角星形状,带有金色到橙色的径向渐变填充和白色描边,背景是深色渐变。