原文 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了。

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

感觉一切都顺畅多了~

UWP应用载入SVG图片的兼容性方案的更多相关文章

  1. Adobe Edge Animate –解决图形边缘精确检测问题-通过jquery加载svg图片

    Adobe Edge Animate –解决图形边缘精确检测问题-通过jquery加载svg图片 版权声明: 本文版权属于 北京联友天下科技发展有限公司. 转载的时候请注明版权和原文地址. 在edge ...

  2. Svg图片在asp网站上的使用

    最近需要做一个动态的根据后台的返回数据而动态显示的导航图,然后我就采用了jquery+ajax+SVG矢量图来实现这个功能. 首先,客户给了个ai的矢量图,我对这一块不懂就找以前同事帮我转成了svg图 ...

  3. C#技术分享【PDF转换成图片——13种方案】(2013-07-25重新整理)

    原文:C#技术分享[PDF转换成图片--13种方案](2013-07-25重新整理) 重要说明:本博已迁移到 石佳劼的博客,有疑问请到 文章新地址 留言!!! 写在最前面:为了节约大家时间,撸主把最常 ...

  4. 安卓CTS官方文档之兼容性方案概览

    兼容性方案概览 安卓的兼容性方案让安卓手机生产商能够很容易就开发中可兼容的安卓设备(天地会珠海分舵注:可兼容什么呢?就是可以兼容标准google提供的安卓系统可以支持的功能,以防手机生产商把开源的安卓 ...

  5. Ubuntu 下将 svg 图片转换为其他格式 (如 png)

    参考 How to Convert SVG Files to other Image Formats on Ubuntu 12.04/11.10 Ubuntu 下将 svg 图片转换为其他格式 (如 ...

  6. SVG图片如何调整大小和颜色

    设计妹子给了SVG图片,在开发的时候尺寸不对,颜色也要修改,应当如何解决? 1.修改大小:在<svg> 标签中修改width.height 属性(默认单位是px)2.修改颜色:在<p ...

  7. SVG图片背景透明

    今天在调整网页的时候,将logo以原有直接贴代码形式,改为加载文件. 其实真正的目的是做SEO.上次SEO交流后得出 结论:核心在于内容的本身的优化.信噪比很重要.也就是有效信息需要占文章的主要内容, ...

  8. Unity3d载入外部图片文件

    unity里的图片在生成时会压缩成资源文件,有时客户想自己放一些图片用unity显示,就必须载入外部图片. 大体思路:用Application.streamingAssetsPath或Applicat ...

  9. 关于SVG图片不显示

    SVG图片在本地调试时.可以正常显示.可是上传到server或者虚拟主机以后不显示. 这个问题该怎么解决呢? 两种解决的方法: 第一种:在server上 IIS 或者其它Webserver上 加入 S ...

随机推荐

  1. JBoss AS 7之初步了解(The Return Of The King)

    watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvam9obl9mX2xhdQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...

  2. html5和html的区别是什么(精问)

    html5和html的区别是什么(精问) 一.总结 一句话总结:html5:简洁(文档生命,链接引入) 语义化(语义化标签)  API(canvas,地理位置等)  一些标签(input新类型) 二. ...

  3. 轻松学习JavaScript十八:DOM编程学习之DOM简单介绍

    一DOM概述 DOM(文档对象模型)是HTML和XML的应用程序接口(API).DOM将把整个页面规划成由节点层级构成的文档. DOM描绘了一个层次化的节点树,执行开发者加入,移除和改动页面的某一部分 ...

  4. freemarker自己定义标签(一)

    freemarker自己定义标签 1.自己定义标签说明 宏变量存储模板片段能够被用作自己定义指令macro 2.演示样例说明 <html> <head> <meta ht ...

  5. 汉高澳大利亚matrix矩阵计算器

    我在梦中的超级计算机超级计算机锯,使用大量阵列的cpu记忆,完成并行计算.一个手机制造商由于使用普通机械提供的服务,往往造成停机.是铁道部列车网络售票的事实. 无法使用云服务.上万台计算机并行处理,因 ...

  6. 【30.00%】【vijos 1909】寻找道路

    描述 在有向图 G 中,每条边的长度均为 1,现给定起点和终点,请你在图中找一条从起点到 终点的路径,该路径满足以下条件: 路径上的所有点的出边所指向的点都直接或间接与终点连通. 在满足条件 1 的情 ...

  7. MFC下WM_NOTIFY消息处理流程

    参考文章:MFC的消息反射机制 在前一篇文章:MFC消息处理流程概述中描述了MFC消息处理的大体流程.由CWnd::OnWndMsg函数可知,当消息为WM_NOTIFY消息时,调用的是virtual ...

  8. 【Codeforces Round #438 C】 Qualification Rounds

    [链接]h在这里写链接 [题意] 给你n个问题,每个人都知道一些问题. 然后让你选择一些问题,使得每个人知道的问题的数量,不超过这些问题的数量的一半. [题解] 想法题. 只要有两个问题. 这两个问题 ...

  9. Delphi程序员如何找到高薪的工作?(赚不到钱,原因只有一个,就是他们没有被公司录取。Delphi必须要独自进行深入研究,才能精通,同时也不能自由性太强)

    转帖自:http://www.tommstudio.com/ViewNews.aspx?ID=187http://hi.baidu.com/rarnu/blog/ 本文翻译自<美国优秀经理观念大 ...

  10. Struts2——(4)OGNL与struts标签

    一.OGNL Object Graphic Navigation Language  对象图导航语言 依赖于 ognl.jar包 OGNL不是Struts框架独有的,它是和框架独立的一种技术. 例如: ...