• <Rust><iced><resvg>基于rust使用iced构建GUI实例:使用resvg库实现svg转png


    前言
    本文是使用rust库resvg来将svg图片转为png图片。

    环境配置
    系统:windows
    平台:visual studio code
    语言:rust
    库:resvg

    代码分析

    resvg是一个基于rust的svg渲染库,其官方地址:
    An SVG rendering library

    resvg库的核心是svg的渲染,但本文暂且不关注如何渲染svg,本文关注如何将svg转为png格式,官方有提供演示代码。

    本文参考官方示例,将代码稍作修改,并结合rust的文件库rfd,编写一个简单的程序,可以导入svg图片,然后转为png图片,并保存。

    首先看一下核心的转换代码:
    官方代码:

    fn main() {
        let args: Vec<String> = std::env::args().collect();
        if args.len() != 3 {
            println!("Usage:\n\tminimal  ");
            return;
        }
    
        let tree = {
            let mut opt = usvg::Options::default();
            // Get file's absolute directory.
            opt.resources_dir = std::fs::canonicalize(&args[1])
                .ok()
                .and_then(|p| p.parent().map(|p| p.to_path_buf()));
    
            opt.fontdb_mut().load_system_fonts();
    
            let svg_data = std::fs::read(&args[1]).unwrap();
            usvg::Tree::from_data(&svg_data, &opt).unwrap()
        };
    
        let pixmap_size = tree.size().to_int_size();
        let mut pixmap = tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
        resvg::render(&tree, tiny_skia::Transform::default(), &mut pixmap.as_mut());
        pixmap.save_png(&args[2]).unwrap();
    }
    

    本地使用时,简单封装成一个函数,如下:

    ///
    /// svg转png
    /// 
    pub fn svgtopng(
        svgpath: &str,
        destimgpath: &str,
    )
    {
        let mut opt=resvg::usvg::Options::default();
        opt.resources_dir=std::fs::canonicalize(svgpath)
                                     .ok()
                                     .and_then(|p| p.parent().map(|p| p.to_path_buf()));
        opt.fontdb_mut().load_system_fonts();
    
        let svgdata=std::fs::read(svgpath).unwrap();
        let tree=resvg::usvg::Tree::from_data(&svgdata,&opt).unwrap();
        let pixmap_size = tree.size().to_int_size();
        let mut pixmap = resvg::tiny_skia::Pixmap::new(pixmap_size.width(), pixmap_size.height()).unwrap();
        resvg::render(&tree, resvg::tiny_skia::Transform::default(), &mut pixmap.as_mut());
        pixmap.save_png(destimgpath).unwrap();
    }
    

    转换的程序就好了,然后我们结合rust的GUI库iced来编写一个简单的带UI的转换程序,所以,我们还需要添加iced库,看一下toml文件:

    [package]
    name = "gui-serial"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    iced={version="0.12.1"}
    iced_widget={version="0.12.3",features=[]}
    serialport="4.3.0"
    clap="4.5.7"
    
    image="0.25.1"
    
    resvg={version="0.42.0",features=[]}
    

    关于iced以及rfd库的使用,此处不再赘述,可以参考本人的另外的博文:
    Rust UI开发(三):iced如何打开图片(对话框)并在窗口显示图片?

    我直接把主程序的代码贴在这:

    use std::{io::{self,Write}, process::CommandArgs};
    
    use eximg::codecs::png;
    use imgtoicon::image_to_icon;
    use resvg::usvg::filter::Merge;
    use serialport::{DataBits,StopBits,Parity};
    //use clap::{value_parser, Arg, ArgAction, Command};
    mod ser;
    mod imgtoicon;
    mod resvgpro;
    use iced::{Application, Command, Element, Font, Renderer, Settings, Subscription};
    use iced_widget::{container,button,text,column,row,svg,image};
    
    use rfd::FileDialog;
    
    extern  crate resvg;
    extern crate image as eximg;
    
    #[derive(Debug,Clone)]
    enum Message{
        Cvt,
        Open,
        Save,
    }
    struct Serial{
        portname:String,
        baudrate:u32,
        databits:DataBits,
        stopbits:StopBits,
        parity:Parity,
        timeout:u64,
        openfile:String,
        destfile:String,
    }
    fn main() ->iced::Result {
        
        let myicon=imgtoicon::image_to_icon("..\\gui-serial\\img\\mainicon4.png");
        let myfont="微软雅黑";
        Serial::run(Settings{
            id:Some("mw".to_string()),
            window:iced::window::Settings{
                size:iced::Size{width:800.0,height:600.0},
                min_size:Some(iced::Size { width: 200.0, height: 200.0 }),
                max_size:Some(iced::Size { width: 1000.0, height: 800.0 }),
                position:iced::window::Position::Specific(iced::Point::new(100.0,100.0)),
                icon:Some(myicon),
                level:iced::window::Level::Normal,
                ..Default::default()
            },
            default_font:Font::with_name(myfont),
            ..Default::default()
        })
    
        
    }
    
    impl Application for Serial{
        type Executor = iced::executor::Default;
        type Message = Message;
        type Flags = ();
        type Theme = iced::Theme;
        fn new(flags: Self::Flags) -> (Self, Command<Self::Message>) {
            (Self{
                portname:String::from("COM1"),
                baudrate:9600,
                databits:DataBits::Eight,
                stopbits:StopBits::One,
                parity:Parity::None,
                timeout:1000,
                openfile:String::from(""),
                destfile:String::from(""),
    
            },
            Command::none())
        }
        fn title(&self) -> String {
            String::from("串口调试工具-rs")
        }
        fn update(&mut self, message: Self::Message) -> Command<Self::Message> {
            
            match message{
                Message::Cvt=>{
                    resvgpro::svgtopng(&self.openfile,
                                &self.destfile)
                }
                Message::Open=>{
                if let Some(file)=FileDialog::new()
                                                .set_title("打开文件")
                                                .add_filter("svg", &["svg"])
                                            .pick_file(){
                        self.openfile=file.display().to_string();
                                            }
                else{
                    println!("打开文件失败")
                };
                }
                Message::Save=>{
                    if let Some(file)=FileDialog::new()
                                                .set_title("保存文件")
                                                .add_filter("png", &["png"])
                                                .save_file(){
                                        let filestr=file.display().to_string();
                                        resvgpro::svgtopng(&self.openfile, &filestr);
                                        self.destfile=filestr;
                                                }
                    else{
                        println!("保存文件失败")
                    };
                }
            }
            Command::none()
        }
        fn subscription(&self) -> Subscription<Self::Message> {
            
            Subscription::none()
        }
        fn view(&self) -> Element<'_, Self::Message, Self::Theme, crate::Renderer> {
            
            //let btn1=button("转换").on_press(Message::Cvt);
            let btn2=button("打开").on_press(Message::Open);
            let btn3=button("转换并保存").on_press(Message::Save);
            let svghandle=svg::Handle::from_path(&self.openfile);
            let pnghandle=image::Handle::from_path(&self.destfile);
            let col1=column![
                btn2,
                text(format!("打开文件路径:{}",&self.openfile)).size(15),
                btn3,
                text(format!("保存文件路径:{}",&self.destfile)).size(15),
                //btn1,
                row![
                    svg(svghandle).content_fit(iced::ContentFit::Contain).width(300),
                    image(pnghandle).content_fit(iced::ContentFit::Contain).width(300),
                ].padding(5).spacing(20),
            ].padding(5).spacing(5);
            
            let cont=container(col1).padding(5);
            
            cont.into()
        }
    }
    
    
    

    实例演示:
    在这里插入图片描述

  • 相关阅读:
    GIT命令
    基于 HPSO 与多核 LSSVM 的网络入侵检测
    Qt编写视频监控管理平台(支持海康/大华/宇视/华为/天地伟业/H264/H265等)
    入门指南:网站UI原型设计的简单方法
    什么是mvvm模式,优点是什么
    抖音支付十万级 TPS 流量发券实践
    mp4转gif在线转换,视频转换成gif动图怎么做?
    IT行业哪个方向比较好就业?
    1230: 蜂巢
    Excel找到某个指定值的最大或者最小日期/数值
  • 原文地址:https://blog.csdn.net/normer123456/article/details/139774973