.NET生成漂亮桌面背景

前言

一天,我朋友指着某某付费软件对我说,这个东西不错,每天生成一张桌面背景,还能学英语(放置名人名言和翻译)!我说,这东西搞不好我也能做,然后朋友说,“如果你搞出来了,我愿意给你付费$$$$元”,然后就有了今天的故事?。

该桌面背景效果如下:

640?wx_fmt=jpeg

该桌面背景有4个特点:

  1. 背景为一张从 必应下载的壁纸

  2. 英文为随机的名人名言,从 API获取

  3. 注意文件下文有阴影,使用 Direct2D

  4. 英文被翻译成了中文,使用了 AzureCognitiveService

当然还有重要的,需要将这张图片设为桌面背景,这通过 WindowsAPI完成。下面我将对里面的功能点一一讲解。

第一步 下载必应壁纸

bing.com每天提供了一张壁纸,下载 bing壁纸是最简单的方式。根据用户协议,必应每日图片允许(也只允许)用户将其设置为桌面背景,因此可以放心使用。

bing壁纸的 API如下:

https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=5&mkt=zh-cn

使用浏览器访问,格式如下:

640?wx_fmt=png

由图可见, API返回了一个 JSON,里面一个 images的数组,里面元素中的 url属性即是 bing壁纸。可以通过拼接 https://www.bing.com来下载今天的 bing壁纸:

640?wx_fmt=png

因此,本段也分为三小步,用 C#代码可以这样写:

1. 下载 bing.com壁纸查询 API

下载使用 HttpClient,注意 HttpClient在单个应用中应该定义为静态的。代码如下:

var http = new HttpClient();	
string url = @"https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=5&mkt=zh-cn";	
string content = await http.GetStringAsync(url);

注意其中的 &n=5中的 5,指的是最新的 5张照片,如果想获取 10张,可以将 5改成 10

2. 解析返回的壁纸 JSON信息

解析 JSON有很多方式,本文使用传统的 Json.NET/ Newtonsoft.Json来做:

string json = JToken.Parse(content);	
string images = json["images"]	.Select(x => x["url"].ToString())	.Select(x => "https://cn.bing.com" + x);	
string pictureUrl = images.First();

注意第二行代码,其实可以直接获取所有的`bing

3. 下载完成的壁纸图片

这一步也通过 HttpClient完成:

var fileName = Path.GetTempFileName();	
File.WriteAllBytes(fileName, await http.GetByteArrayAsync(url));	
return fileName;

然后下载的图片就保存在 fileName这个变量所表达的路径中了。

注意:不一定非要下载到文件中,下载到内存中亦可,但下文中的代码需要少许调整,这里就不深入了。

第二步 获取名人名言

我在网上找了一下,有不少网站都提供了英语名人名言服务,其中还不乏免费服务。本文使用的是 favqs.com提供的 API(随便找的),该 API每次调用都会返回不同的“名人名言”,我试了一下,可堪一用,免费 API调用地址如下:

https://favqs.com/api/qotd

返回的 json格式如下:

{	"qotd_date": "2019-09-30T00:00:00.000+00:00",	"quote": {	"id": 61060,	"dialogue": false,	"private": false,	"tags": [	"work"	],	"url": "https://favqs.com/quotes/voltaire/61060-let-us-work-w-",	"favorites_count": 0,	"upvotes_count": 1,	"downvotes_count": 0,	"author": "Voltaire",	"author_permalink": "voltaire",	"body": "Let us work without theorizing, tis the only way to make life endurable."	}	
}

可以看到作者和文本,可以使用 authorbody两个字段来表示。

这部分使用 C#代码下载和解析过程如下:

async Task<string> GetQuote()	
{	var url = @"https://favqs.com/api/qotd";	var content = await http.GetStringAsync(url);	var json = JToken.Parse(content);	return json["quote"]["body"] + "\r\n\t\t\t\t——" + json["quote"]["author"];	
}

如代码所示,我将 bodyauthor两个字段拼接成了一个字符串,可以直接使用,像这样:

Let us work without theorizing, tis the only way to make life endurable.	——Voltaire

第三步 生成图片(加阴影)

这步使用 Direct2D,比较复杂,要注意的点很多,各位可以选择跳过这一步(直接拿代码?),或者稍微看看。

string GenerateWallpaper(string pictureFileName, string english, string chinese)	
{	var wic = new WIC.ImagingFactory2();	var d2d = new D2D.Factory();	float dpi = d2d.DesktopDpi.Width;	Size2 size = new Size2(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);	WIC.FormatConverter image = CreateWicImage(wic, pictureFileName);	using (var wicBitmap = new WIC.Bitmap(wic, size.Width, size.Height, WIC.PixelFormat.Format32bppPBGRA, WIC.BitmapCreateCacheOption.CacheOnDemand))	using (var target = new D2D.WicRenderTarget(d2d, wicBitmap, new D2D.RenderTargetProperties()))	using (var dc = target.QueryInterface<D2D.DeviceContext>())	using (var bmpPicture = D2D.Bitmap.FromWicBitmap(target, image))	using (var dwriteFactory = new SharpDX.DirectWrite.Factory())	using (var brush = new SolidColorBrush(target, SharpDX.Color.LightGoldenrodYellow))	using (var bmpLayer = new D2D.Bitmap1(dc, target.PixelSize,	new D2D.BitmapProperties1(new D2D.PixelFormat(SharpDX.DXGI.Format.B8G8R8A8_UNorm, D2D.AlphaMode.Premultiplied), dpi, dpi, D2D.BitmapOptions.Target)))	{	var oldTarget = dc.Target;	dc.Target = bmpLayer;	target.BeginDraw();	{	var textFormat = new DWrite.TextFormat(dwriteFactory, "Tahoma", size.Height / 27);	// draw English	{	var textLayout = new DWrite.TextLayout(dwriteFactory, english, textFormat, target.Size.Width * 0.75f, float.MaxValue);	var center = new Vector2((target.Size.Width - textLayout.Metrics.Width) / 2, (target.Size.Height - textLayout.Metrics.Height) / 2);	target.DrawTextLayout(new Vector2(center.X, center.Y), textLayout, brush);	}	{	// draw Chinese	var textLayout = new DWrite.TextLayout(dwriteFactory, chinese, textFormat, target.Size.Width * 0.75f, float.MaxValue);	var center = new Vector2((target.Size.Width - textLayout.Metrics.Width) / 2, target.Size.Height - textLayout.Metrics.Height - size.Height / 18);	target.DrawTextLayout(new Vector2(center.X, center.Y), textLayout, brush);	}	}	target.EndDraw();	// shadow	var shadow = new D2D.Effects.Shadow(dc);	shadow.SetInput(0, bmpLayer, new RawBool(false));	dc.Target = oldTarget;	target.BeginDraw();	{	target.DrawBitmap(bmpPicture, new RectangleF(0, 0, target.Size.Width, target.Size.Height), 1.0f, BitmapInterpolationMode.Linear);	dc.DrawImage(shadow, new Vector2(size.Height / 150.0f, size.Height / 150.0f));	dc.UnitMode = UnitMode.Pixels;	target.DrawBitmap(bmpLayer, 1.0f, BitmapInterpolationMode.Linear);	}	target.EndDraw();	string wallpaperFileName = Path.GetTempPath() + "wallpaper.png";	using (var wallpaperStream = File.OpenWrite(wallpaperFileName))	{	SaveD2DBitmap(wic, wicBitmap, wallpaperStream);	wallpaperStream.Close();	return wallpaperFileName;	}	}	
}	
WIC.FormatConverter CreateWicImage(WIC.ImagingFactory wicFactory, string filename)	
{	using (var decoder = new WIC.JpegBitmapDecoder(wicFactory))	using (var decodeStream = new WIC.WICStream(wicFactory, filename, NativeFileAccess.Read))	{	decoder.Initialize(decodeStream, WIC.DecodeOptions.CacheOnLoad);	using (var decodeFrame = decoder.GetFrame(0))	{	var converter = new WIC.FormatConverter(wicFactory);	converter.Initialize(decodeFrame, WIC.PixelFormat.Format32bppPBGRA);	return converter;	}	}	
}	
void SaveD2DBitmap(WIC.ImagingFactory wicFactory, WIC.Bitmap wicBitmap, Stream outputStream)	
{	using (var encoder = new WIC.BitmapEncoder(wicFactory, WIC.ContainerFormatGuids.Png))	{	encoder.Initialize(outputStream);	using (var frame = new WIC.BitmapFrameEncode(encoder))	{	frame.Initialize();	frame.SetSize(wicBitmap.Size.Width, wicBitmap.Size.Height);	var pixelFormat = wicBitmap.PixelFormat;	frame.SetPixelFormat(ref pixelFormat);	frame.WriteSource(wicBitmap);	frame.Commit();	encoder.Commit();	}	}	
}

要看的话,要点如下:

  1. 图片大小是由主显示器分辨率决定的,可以使用 Screen.PrimaryScreen.Bounds.WidthScreen.PrimaryScreen.Bounds.Height获取;

  2. 一定要注意不同电脑的 DPI设置,这样可以保证高 DPI和低 DPI的显示器都能有完美的效果,这部分是使用 d2d.DesktopDpi.Width获取的,请注意里面的使用方式(最重要的是用不同客户的电脑亲自运行看看);

  3. 字体大小是根据图片的高度决定的,如代码所示,字体大小为 size.Height/27

  4. 虽然代码前后顺序是先画文字、再画阴影,但实际生成代码部分,是先画阴影、再画文字,这样确保文字在阴影之上;

  5. 可以使用 textLayout.Metrics获取生成文字的宽度和高度,这样可以确保文件显示在中心位置;

  6. 注意下文中 dc.UnitMode=UnitMode.Pixels,这是确保 DPI显示正常。说来复杂,长话短说就是这其实很合理,前面设置了 DPI,该 DPI不仅影响文字,也会影响图片,但实际上图片不应该被 DPI影响。

以后有机会我会多聊聊 Direct2D,这简直是一个宝库。

第四步 将文字翻译成中文

翻译服务 API提供商就更多了,选择很多。我用的是 AzureCognitiveService,它也是免费的(也有付费版本)。创建这个服务后,它会提供两个单独的 key,使用这个 key即可调用翻译服务了:

640?wx_fmt=png

不像是阿里云、AWS那种, Azure会为不同的服务提供不同的 AccessKey,这样做可能更容易控制信息安全一些。

Azure提供了 SDK,因此调用起来非常简单:

async Task<string> EnglishToChinese(string english)	
{	var client = new TranslateClient(new CognitiveServicesConfig { SubscriptionKey = Util.GetPassword("Translate_Free") });	var response = await client.TranslateAsync(new RequestContent { Text = english }, new RequestParameter { To = new string[] { "zh-Hans" } });	return response[0].Translations[0].Text;	
}

其实它的功能非常强大,甚至还能多国语言同步翻译等等。

最后一步 设置桌面背景

这一步调用 WindowsAPI,直接使用“祖传代码”即可:

public sealed class Wallpaper	
{	const int SPI_SETDESKWALLPAPER = 20;	const int SPIF_UPDATEINIFILE = 0x01;	const int SPIF_SENDWININICHANGE = 0x02;	[DllImport("user32.dll", CharSet = CharSet.Auto)]	static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);	public enum Style : int	{	Tiled,	Centered,	Stretched	}	public static void Set(string pictureFileName, Style style)	{	RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Desktop", true);	if (style == Style.Stretched)	{	key.SetValue(@"WallpaperStyle", 2.ToString());	key.SetValue(@"TileWallpaper", 0.ToString());	}	if (style == Style.Centered)	{	key.SetValue(@"WallpaperStyle", 1.ToString());	key.SetValue(@"TileWallpaper", 0.ToString());	}	if (style == Style.Tiled)	{	key.SetValue(@"WallpaperStyle", 1.ToString());	key.SetValue(@"TileWallpaper", 1.ToString());	}	SystemParametersInfo(SPI_SETDESKWALLPAPER,	0,	pictureFileName,	SPIF_UPDATEINIFILE | SPIF_SENDWININICHANGE);	}	
}

使用时,直接这样调用:

Wallpaper.Set(wallpaperFileName, Wallpaper.Style.Centered);

注意:由于第三步中确保了分辨率一样,因此也不用关心第二个参数。

总结

最后看一下执行效果:

640?wx_fmt=jpeg

然而最后,我那个朋友说,你这东西要是支持 Linux就好咯。我不用 Linux,所以我也不打算支持(我看他其实也根本不用 Linux),因此最后说好的 $$$$我一分钱也没拿到?。

所以这部分代码我托盘而出,向各位免费相送,分为带翻译版本和不带翻译版本,不带翻译版本可直接使用,带翻译版本需要注册一个免费 Azure帐号(其实也能运行,只是翻译中文会显示翻译错误)。愿博君一笑:

无翻译:https://github.com/sdcb/blog-data/blob/master/2019/20190930-wallpaper-by-dotnet/PictureAndQuote-Wallpaper.linq	
带翻译:https://github.com/sdcb/blog-data/blob/master/2019/20190930-wallpaper-by-dotnet/PictureAndQuote-Wallpaper-T.linq

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/313568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mysql 执行流程

mysql执行一个查询的过程&#xff0c;到底做了些什么&#xff1a; 客户端发送一条查询给服务器&#xff1b;服务器先检查查询缓存&#xff0c;如果命中了缓存&#xff0c;则立刻返回存储在缓存中的结果。否则进入下一阶段。服务器段进行SQL解析、预处理&#xff0c;在优化器生成…

Autofac的AOP面向切面编程研究

我的理解是 把系统性的编程工作封装起来 》我给这个取个名字叫 “Aspect”&#xff0c;然后通过AOP技术把它切进我们的业务逻辑代码 》 “业务“这样的好处&#xff1a;“Aspect” 和 “业务” 相互独立&#xff0c;既可以让“业务” 用到了 “Aspect” 又让2者互相独立不耦合&…

计算机网络原理梳理丨清晰认识 TCP/IP 协议,图解秒懂!

作者&#xff1a;MobMsg&#xff0c;资深全端工程师一枚&#xff0c;架构师社区合伙人&#xff01;TCP/IP 协议族Internet 的核心协议就是 TCP/IP&#xff0c;广泛应用于局域网和广域网&#xff0c;目前已有20年发展史&#xff0c;是现用国际通行标准。TCP/IP 是个协议族&#…

MySQL 覆盖索引、最左前缀原则、索引下推

1、覆盖索引 1.1 概念 索引是高效找到行的一个方法&#xff0c;当能通过检索索引就可以读取想要的数据&#xff0c;那就不需要再到数据表中读取行了。如果一个索引包含了&#xff08;或覆盖了&#xff09;满足查询语句中字段与条件的数据就叫做覆盖索引。 1.2 判断标准 使用…

Entity Framework Core生成的存储过程在MySQL中需要进行处理及PMC中的常用命令

在使用Entity Framework Core生成MySQL数据库脚本&#xff0c;对于生成的存储过程&#xff0c;在执行的过程中出现错误&#xff0c;需要在存储过程前面添加delimiter //附&#xff1a;可以使用Visual Studio中的程序包管理器控制台执行Entity Framework Core中的迁移命令。PMC …

Exceptionless 5.0.0本地Docker快速部署介绍

在之前我有专门写两篇文章介绍过Exceptionless这款开源日志项目的使用和部署&#xff0c;但是当时是基于4.1.0版本&#xff08;2017年的release&#xff09;&#xff0c;时隔两年多Exceptionless也推出了5.0.0版本。&#xff08;1&#xff09;&#xff08;2&#xff09;01—关于…

数据库事务及隔离级别

一、事务的基本要素&#xff08;ACID&#xff09; 1、原子性&#xff08;Atomicity&#xff09;&#xff1a;事务开始后所有操作&#xff0c;要么全部做完&#xff0c;要么全部不做&#xff0c;不可能停滞在中间环节。事务执行过程中出错&#xff0c;会回滚到事务开始前的状态…

定了!10 月 8 日!Jupyter Notebook 原生支持将正式来到 VS Code!

北京时间 2019 年 9 月 21 日&#xff0c;在 PyCon China 2019 大会上&#xff0c;前不久&#xff0c;我们已经可以尽管如此&#xff0c;还是有许多童鞋来询问这个功能何时能正式发布。现在&#xff0c;我们可以在 VS Code Python 插件的 Release Plan 看到正式的发布时间已经确…

图解MySQL 内连接、左连接、右连接

一、准备工作 用两个表&#xff08;a_table、b_table&#xff09;&#xff0c;关联字段a_table.a_id和b_table.b_id来演示一下MySQL的内连接、外连接&#xff08; 左(外)连接、右(外)连接、全(外)连接&#xff09;。 MySQL版本&#xff1a;Server version: 5.6.31 MySQL Comm…

微软想将新版Edge浏览器引入Linux

继推出 WSL2、将 exFAT 技术添加至 Linux 内核&#xff0c;和宣布第一届微软 Linux 大会后&#xff0c;微软再次瞄准了 Linux。这次 Linux 用户将很可能迎来新版 Edge 浏览器。微软网络技术程序经理 Sean Larson 在 Twitter 上发布了一条消息&#xff0c;表示 Edge 开发团队正在…

一套代码同时支持.NET Framework和.NET Core

在.NET Core的迁移过程中&#xff0c;我们将原有的.NET Framework代码迁移到.NET Core。如果线上只有一个小型的应用还好&#xff0c;迁移升级完成后&#xff0c;只需要维护.NET Core这个版本的代码。但是&#xff0c;如果是一个大型分布式应用&#xff0c;几百台Server&#x…

从壹开始 [Admin] 之五 ║ 实现『按钮』级别权限配置

正文一、前情回顾哈喽大家好&#xff0c;在这个欢庆的日子里&#xff0c;老张祝大家工作都能蒸蒸日上&#xff01;今天正好也是社团成立的第一天&#xff0c;我也是希望今天能是个纪念日&#xff0c;沾沾这个大喜庆&#xff01;放假这两天&#xff0c;倒是学到了很多东西&#…

MySQL——binlog,redo log

一、什么是binlog、redo log binlog属于逻辑日志&#xff0c;是逻辑操作。innodb redo属于物理日志&#xff0c;是物理变更。逻辑日志有个缺点是难以并行&#xff0c;而物理日志可以比较好的并行操作。 binlog是MySQL Server层记录的日志&#xff0c; redo log是InnoDB存储引…

“自启动”树莓派上的 .NET Core 3.0 环境

点击上方蓝字关注“汪宇杰博客”导语昨天发了一篇《自动配置环境变量Rapbian 系统启动时会去执行 .profile 文件里的命令。因此我们只需要把配置环境变量的命令加入 .profile 文件即可。在 Linux 中&#xff0c;.profile 扩展用于终端程序中的文件。Linux 和 Mac OS X 终端程序…

Nginx 配置内网访问树莓派4 ASP.NET Core 3.0 网站

喜迎国庆点击上方蓝字关注“汪宇杰博客”导语前几天发了两篇《能跑就行&#xff1a;Kestrel Hosting如果你的要求只是临时内网访问&#xff0c;可以只用 Kestrel 来承载 Web 服务器&#xff0c;只需要给 dotnet 命令一个 --urls 参数即可设置允许访问的主机名和端口号。我不希望…

常见的NoSQL数据库

NoSQL数据库发展迅猛&#xff0c;据说现在已经有上百种NoSQL数据库了&#xff0c;下面来了解下常见的一些NoSQL数据库 先来看张表&#xff0c;了解下典型的NoSQL数据库的分类 NoSQL一般特征&#xff1a; 临时性键值存储一般作为关系型数据库的缓存来使用由于存在数据丢失的可…

2019年9月中国编程语言排行榜,C#排第三

前两天&#xff0c;在 GitHub 上看到一份报告&#xff0c;上面统计了全国程编程语言排行榜 9 月份的情况&#xff0c;下面跟大家分享一下&#xff0c;这份报告里面都说了什么。https://github.com/juwikuang/china_job_survey/blob/master/notebook/201909/programming_languag…

从0开始编写dapper核心功能、压榨性能、自己动手丰衣足食

我偶然听说sqlsugar的性能比dapper强。对此我表示怀疑&#xff08;由于我一直使用的dapper存在偏见吧&#xff09;&#xff0c;于是自己测试了sqlsugar、freesql、dapper发现他们的给我的结果是sqlsugar>dapper>freesql&#xff08;这里并不是黑那个orm,毕竟不同orm功能不…

免费下载 80多种的微软推出入门级 .NET视频

.NET Core 3.0视频系列中宣布了80多个新的免费视频&#xff0c;这些视频同时放在Microsoft的Channel 9 和youtube上面。 在线观看由于跨洋网络效果不太好&#xff0c;下载到机器上慢慢看是最好的了。 所以Scott Hanselman 特别制作了一个页面 http://dot.net/videos。我把这个页…

数据库连接池原理及常用连接池介绍

一、背景介绍 1.1 什么是连接池 数据库连接池负责分配、管理和释放数据库连接&#xff0c;它允许应用程序重复使用一个现有的数据库连接&#xff0c;而不是再重新建立一个。 1.2 为什么要使用连接池 数据库连接是一种关键的有限的昂贵的资源&#xff0c;这一点在多用户的网…