.NET生成漂亮桌面背景
.NET生成漂亮桌面背景
一天,我朋友指着某某付費軟件對我說,這個東西不錯,天天生成一張桌面背景,還能學英語(放置名人名言和翻譯)!我說,這東西搞很差我也能作,而後朋友說,「若是你搞出來了,我願意給你付費$$$$元」,而後就有了今天的故事😎。github
該桌面背景效果以下:
json
該桌面背景有4個特色:api
- 背景爲一張從
必應
下載的壁紙 - 英文爲隨機的名人名言,從
API
獲取 - 注意文件下文有陰影,使用
Direct2D
- 英文被翻譯成了中文,使用了
Azure Cognitive Service
固然還有重要的,須要將這張圖片設爲桌面背景,這經過Windows API
完成。下面我將對裏面的功能點一一講解。數組
第一步 下載必應壁紙
bing.com
天天提供了一張壁紙,下載bing
壁紙是最簡單的方式。根據用戶協議,必應每日圖片容許(也只容許)用戶將其設置爲桌面背景,所以能夠放心使用。瀏覽器
bing
壁紙的API
以下:安全
https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=5&mkt=zh-cn
ruby
使用瀏覽器訪問,格式以下:
微信
由圖可見,API
返回了一個JSON
,裏面一個images
的數組,裏面元素中的url
屬性便是bing
壁紙。能夠經過拼接https://www.bing.com
來下載今天的bing
壁紙:
所以,本段也分爲三小步,用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." } }
能夠看到做者和文本,可使用author
和body
兩個字段來表示。
這部分使用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"]; }
如代碼所示,我將body
和author
兩個字段拼接成了一個字符串,能夠直接使用,像這樣:
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(); } } }
要看的話,要點以下:
- 圖片大小是由主顯示器分辨率決定的,可使用
Screen.PrimaryScreen.Bounds.Width
/Screen.PrimaryScreen.Bounds.Height
獲取; - 必定要注意不一樣電腦的
DPI
設置,這樣能夠保證高DPI
和低DPI
的顯示器都能有完美的效果,這部分是使用d2d.DesktopDpi.Width
獲取的,請注意裏面的使用方式(最重要的是用不一樣客戶的電腦親自運行看看); - 字體大小是根據圖片的高度決定的,如代碼所示,字體大小爲
size.Height / 27
; - 雖然代碼先後順序是先畫文字、再畫陰影,但實際生成代碼部分,是先畫陰影、再畫文字,這樣確保文字在陰影之上;
- 可使用
textLayout.Metrics
獲取生成文字的寬度和高度,這樣能夠確保文件顯示在中心位置; - 注意下文中
dc.UnitMode = UnitMode.Pixels
,這是確保DPI
顯示正常。說來複雜,長話短說就是這其實很合理,前面設置了DPI
,該DPI
不只影響文字,也會影響圖片,但實際上圖片不該該被DPI
影響。
之後有機會我會多聊聊Direct2D
,這簡直是一個寶庫。
第四步 將文字翻譯成中文
翻譯服務API
提供商就更多了,選擇不少。我用的是Azure Cognitive Service
,它也是免費的(也有付費版本)。建立這個服務後,它會提供兩個單獨的key
,使用這個key
便可調用翻譯服務了:
不像是阿里雲、AWS那種,
Azure
會爲不一樣的服務提供不一樣的Access Key
,這樣作可能更容易控制信息安全一些。
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; }
其實它的功能很是強大,甚至還能多國語言同步翻譯等等。
最後一步 設置桌面背景
這一步調用Windows API
,直接使用「祖傳代碼」便可:
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);
注意:因爲第三步中確保了分辨率同樣,所以也不用關心第二個參數。
總結
最後看一下執行效果:
然而最後,我那個朋友說,你這東西要是支持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
喜歡的朋友請關注個人微信公衆號:【DotNet騷操做】