UWP应用载入SVG图片的兼容性方案

简介: 原文 UWP应用载入SVG图片的兼容性方案 新版本《纸书科学计算器》的更新点之一,就是优化了表达式的显示方式。在旧版本中,表达式里的符号是用png图片显示的,当用户放大看的时候会发现一些锯齿,非常影响使用体验。

原文 UWP应用载入SVG图片的兼容性方案

新版本《纸书科学计算器》的更新点之一,就是优化了表达式的显示方式。在旧版本中,表达式里的符号是用png图片显示的,当用户放大看的时候会发现一些锯齿,非常影响使用体验。图片的等比缩放也会导致符号的粗细不一,比较明显的如下:

(矩阵的硕大括号非常违和,细看还会有些锯齿)

 

在开发新版本时,为了使表达式的显示更精细,我打算用svg图片来代替之前的png图片,这种基于xml的矢量图形格式既小巧又能有效避免锯齿。虽然我也考虑过xaml里的path路径,但是因为时间原因,我并不想大幅改动之前的代码。如果uwp的Image控件能像HTML5的img标签一样支持svg图片那就太好了。那么Image控件到底滋不滋瓷svg呢?

 

答案很尴尬:首先你问我滋不滋瓷,我肯定是滋瓷的。但是只有在Windows 10 Creators Update (10.0.15063.0)之后才能直接支持。15063可是今年上半年才出的更新,毫无疑问还有大量用户停留在14393甚至更低的版本。这里不得不吐槽一下巨硬,svg的支持居然做得这么晚,一向思维领先的uwp这次真的落后了很多。难道是为了推荐开发者一律用path路径吗?

 

由于要兼容14393及以下的版本,在这些版本的系统上只能使用第三方库。经过搜索了解到,有个开源的矢量图加载库Mntone.SvgForXaml可以显示svg图片。经过实际测试,发现Mntone.SvgForXaml内部是parse了svg文件的xml再转换成Bitmap绘制在Image控件上实现的。虽然确实可以支持svg,但是显示效果不佳:由于图片经过了栅格化,用viewbox缩放后还是会有锯齿。虽感无奈,但为了保证对低版本系统的支持也只能这样了。

 

最后决定,14393及以下的版本的系统上使用Mntone.SvgForXaml,在15063以上的系统还是直接采用Image控件载入svg,因为这个无论怎么缩放都是无锯齿的。

 

 

一、Mntone.SvgForXaml的使用及坑

 

关于Mntone.SvgForXaml的使用,网上已经有了很多帖子,比如UWP项目中加载svg矢量图 - 菜鸟之路 - CSDN博客,基本的使用方法简要介绍如下(大部分转自该文章):

 

1.用NuGet包管理器在项目里添加Mntone.SvgForXaml,会自动添加依赖包Win2D.uwp;

 

2.在xaml文件中添加命名空间:

xmlns:svg="using:Mntone.SvgForXaml.UI.Xaml"

 

3.在xaml文件中声明该控件:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">  
    <svg:SvgImage x:Name="SvgControl"/> </Grid> 

 

4.用C#代码载入图片(也可以在xaml中将SvgImage控件的Source属性用Bind绑定赋值):

public MainPage()
{
    this.InitializeComponent(); this.Loaded += MainPage_Loaded; } private async void MainPage_Loaded(object sender, RoutedEventArgs e) { var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/magnifier28.svg")); await this.SvgControl.LoadFileAsync(file); } 

 

5.由于矢量图都是加载到内存中,所以使用完后最好卸载一下:

protected override void OnNavigatingFrom(NavigatingCancelEventArgs e) { this.SvgControl.SafeUnload(); } 

 

但是,在实际使用中,我发现这个库是有坑的。SvgImage控件如果是用C#动态添加到界面(在《纸书》里完全就是这样的),在声明控件对象后不管是用LoadFileAsync方法,还是用它给的LoadSvg方法,还是为Source属性赋值,在添加到界面后图像都会消失不见。在Mntone.SvgForXaml的文档中貌似也没有什么注明。摸索了半天,发现必须需要在控件的Loaded事件里载入图片才行……晕。具体方式将在下文补上。

 

 

二、直接支持svg的Image控件

 

15063以上的系统可以在xaml中直接把svg图片当成普通图片为Image的Source属性赋值。如:

<Image Source="example.svg" />

 

在C#代码中,可以用新的对象SvgImageSource(ImageSource的子类)为Image的Source属性赋值。如:

image1.Source = new SvgImageSource(new Uri($"ms-appx:///{filePath}")); 

 

可见,SvgImageSource类的存在与否可以作为当前系统是否直接支持svg的标志。所以靠ApiInformation类就可以轻易判断了:

if(ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.Imaging.SvgImageSource")) { image1.Source = new SvgImageSource(new Uri($"ms-appx:///{filePath}")); } 

 

虽然uwp对svg支持很晚,但还是非常excited!

 

 

三、两种方案的结合

 

在《纸书》中,我写了一个静态类SvgManager来全局掌控svg资源。由于《纸书》里用到的svg资源较少(运算符和函数不算多)但是会频繁的载入,因此我在SvgManager里把所有svg资源都提到内存里来,以减少硬盘读取次数,加快表达式的显示速度。

 

大体上SvgManager类是这样的:

/// <summary>
/// SVG资源管理类
/// </summary>
public static class SvgManager { /// <summary> /// 15063以上系统的svg资源 /// </summary> private static Dictionary<string, SvgImageSource> svgSources; /// <summary> /// 14393以下系统的svg资源 /// </summary> private static Dictionary<string, SvgDocument> svgDocuments; /// <summary> /// 是否可以直接使用Image控件载入svg /// </summary> public static readonly bool CanDisplaySvgDirectly; //其他成员声明省略 ... static SvgManager() { CanDisplaySvgDirectly = ApiInformation.IsTypePresent("Windows.UI.Xaml.Media.Imaging.SvgImageSource"); //读取svg文件清单作为key if (CanDisplaySvgDirectly) { svgSources = new Dictionary<string, SvgImageSource>(); foreach (var n in svgFileNames) svgSources.Add(n, null); } else { svgDocuments = new Dictionary<string, SvgDocument>(); foreach (var n in svgFileNames) svgDocuments.Add(n, null); } //一次性载入资源 loadResourcesAsync(); } /// <summary> /// 载入SVG文档 /// </summary> private static async void loadResourcesAsync() { if (CanDisplaySvgDirectly) { foreach (var key in svgFileNames) if (svgSources[key] == null) svgSources[key] = getSource(key); } else { foreach (var key in svgFileNames) if (svgDocuments[key] == null) svgDocuments[key] = await getDocumentAsync(key); } } private static SvgImageSource getSource(string fileName) { return new SvgImageSource(new Uri($"ms-appx:///Svg/{fileName}")); } private static async Task<SvgDocument> getDocumentAsync(string fileName) { var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri($"ms-appx:///Svg/{fileName}")); var stream = await file.OpenStreamForReadAsync(); var bytes = new byte[stream.Length]; await stream.ReadAsync(bytes, 0, bytes.Length); //Mntone.SvgForXaml库支持的parse操作 return SvgDocument.Parse(bytes); } /// <summary> /// 获取Source对象 /// </summary> /// <param name="fileName"></param> /// <returns></returns> public static SvgImageSource GetSource(string fileName) { if (svgSources.ContainsKey(fileName)) { if (svgSources[fileName] == null) svgSources[fileName] = getSource(fileName); return svgSources[fileName]; } else throw new Exception(); } /// <summary> /// 在SvgImage控件内显示SVG /// </summary> /// <param name="si"></param> public static async void LoadSvg(SvgImage si) { //初始化SvgImage时在Tag属性里赋上了svg文件名 var filename = si.Tag.ToString(); if (svgDocuments.ContainsKey(filename)) { if (svgDocuments[filename] == null) svgDocuments[filename] = await getDocumentAsync(filename); si.LoadSvg(svgDocuments[filename]); } else throw new Exception(); } } 

 

之后,在创建svg图片控件的时候,先判断一下SvgManager.CanDisplaySvgDirectly的值,如果为true则创建Image控件,再用SvgManager.GetSource(filename)方法为Image的Source属性赋值;如果为false,则创建第三方SvgImage控件,然后把svg文件名赋在Tag属性里,再添加Loaded事件的处理程序:

svgControl.Loaded += (sender, e) => SvgManager.LoadSvg(sender as SvgImage); 

 

这样,在各大版本的Win10下都能愉快显示svg了。

 

最终,新版本《纸书》的表达式显示也得到了优化:

 

感觉一切都顺畅多了~

 

目录
相关文章
|
7月前
|
存储 Android开发 iOS开发
iOS不支持HEIC格式的图片显示和标签函数显示问题及解决方案
iOS不支持HEIC格式的图片显示和标签函数显示问题及解决方案
214 0
|
API 图形学
[√]unity渲染一个文本的细节
[√]unity渲染一个文本的细节
94 0
|
人工智能 前端开发
前端封装库/工具库的字体/图标之Iconfont
在前端开发中,图标是不可或缺的一部分。传统的图片素材虽然能够满足需求,但使用方式较为繁琐,还会增大项目的体积和加载时间。随着前端技术的发展,字体/图标库逐渐成为了替代传统图片素材的新选择。其中一个非常流行的字体/图标库就是 Iconfont。
350 1
|
JSON 前端开发 JavaScript
怎么用Unity打包个WEBGL程序这么麻烦,又得改样式,又得改网页——教你使用WEBGL模板,提高效率
我们在开发WEBGL项目的使用,遇到一个问题,导出的WEBGL界面很简陋,不是很美观。 所以就需要自己去修改js文件,或者CSS文件,以及更换图片等操作 但是如果这些工作是一次的话就好说,但是程序开发总是要修改很多次,每次都更改这些东西,就会显得很繁琐,那么有没有设置一次模板,每次生成的时候都按照这个模板生成呢。 Unity3D已经为我们思考到了这一点,提供了一个叫做自定义Templates模板的功能,会为我们在每次生成的时候设置好模板。 下面就来看一下WEBGL模板是怎么使用的吧。
|
XML 小程序 前端开发
小程序的开发之使用SVG
昨天突然提出要在小程序中使用SVG,因为我们的小程序项目是有主题色的。不同的主题色时有些图片一直是固定的,显的有些格格不入,所以打算使用SVG来实现根据主题色的颜色进行变化。
1951 0
小程序的开发之使用SVG
|
数据安全/隐私保护 iOS开发 开发者
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(二)
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建
334 0
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(二)
|
数据安全/隐私保护 iOS开发 开发者
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(一)
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建
472 0
iOS开发CoreGraphics核心图形框架之九——PDF文件的渲染与创建(一)
|
iOS开发 开发者
iOS文本布局探讨之二——关于TextKit框架中的字体描述
iOS文本布局探讨之二——关于TextKit框架中的字体描述
240 0
iOS文本布局探讨之二——关于TextKit框架中的字体描述
|
缓存 iOS开发
扩展于RCLabel的支持异步加载网络图片的富文本引擎的设计
扩展于RCLabel的支持异步加载网络图片的富文本引擎的设计
173 0
|
C# UED
WPF中加载高分辨率图片性能优化
原文:WPF中加载高分辨率图片性能优化 在最近的项目中,遇到一个关于WPF中同时加载多张图片时,内存占用非常高的问题。 问题背景: 在一个ListView中同时加载多张图片,注意:我们需要加载的图片分辨率非常高。
1540 0