Skip to content

Commit 21b0f6b

Browse files
committed
Try replacing WPF with SkiaSharp
1 parent 39c72a2 commit 21b0f6b

File tree

8 files changed

+1180
-13
lines changed

8 files changed

+1180
-13
lines changed

src/COM/DesktopWallpaper.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ public struct COLORREF
1616
public byte B;
1717
}
1818

19+
[StructLayout(LayoutKind.Sequential)]
20+
public struct RECT
21+
{
22+
public int Left;
23+
public int Top;
24+
public int Right;
25+
public int Bottom;
26+
}
27+
1928
public enum DesktopSlideshowOptions
2029
{
2130
DSO_SHUFFLEIMAGES = 0x01,
@@ -59,7 +68,7 @@ public interface IDesktopWallpaper
5968

6069
uint GetMonitorDevicePathCount();
6170

62-
System.Windows.Rect GetMonitorRECT([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
71+
RECT GetMonitorRECT([MarshalAs(UnmanagedType.LPWStr)] string monitorID);
6372

6473
void SetBackgroundColor([MarshalAs(UnmanagedType.U4)] COLORREF color);
6574

src/SkiaSharp/BitmapCache.cs

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
using System;
6+
using System.Collections.Generic;
7+
using System.IO;
8+
using System.Linq;
9+
using System.Reflection;
10+
using System.Windows.Forms;
11+
using SkiaSharp;
12+
13+
namespace WinDynamicDesktop.SkiaSharp
14+
{
15+
sealed class BitmapCache
16+
{
17+
readonly int maxWidth;
18+
readonly int maxHeight;
19+
readonly object cacheLock = new object();
20+
readonly Dictionary<Uri, SKBitmap> images = new Dictionary<Uri, SKBitmap>();
21+
22+
public SKBitmap this[Uri uri]
23+
{
24+
get
25+
{
26+
lock (cacheLock)
27+
{
28+
if (images.ContainsKey(uri))
29+
{
30+
return images[uri];
31+
}
32+
else
33+
{
34+
var img = CreateImage(uri);
35+
if (img != null)
36+
{
37+
images.Add(uri, img);
38+
}
39+
return img;
40+
}
41+
}
42+
}
43+
}
44+
45+
public void Clear()
46+
{
47+
lock (cacheLock)
48+
{
49+
foreach (var bitmap in images.Values)
50+
{
51+
bitmap?.Dispose();
52+
}
53+
images.Clear();
54+
}
55+
GC.Collect();
56+
}
57+
58+
public BitmapCache(bool limitDecodeSize = true)
59+
{
60+
if (limitDecodeSize)
61+
{
62+
int maxArea = 0;
63+
foreach (Screen screen in Screen.AllScreens)
64+
{
65+
int area = screen.Bounds.Width * screen.Bounds.Height;
66+
if (area > maxArea)
67+
{
68+
maxArea = area;
69+
maxWidth = screen.Bounds.Width;
70+
maxHeight = screen.Bounds.Height;
71+
}
72+
}
73+
}
74+
else
75+
{
76+
maxWidth = int.MaxValue;
77+
maxHeight = int.MaxValue;
78+
}
79+
}
80+
81+
private SKBitmap CreateImage(Uri uri)
82+
{
83+
try
84+
{
85+
Stream stream = null;
86+
87+
if (uri.IsAbsoluteUri && uri.Scheme == "file")
88+
{
89+
string path = uri.LocalPath;
90+
if (File.Exists(path))
91+
{
92+
stream = File.OpenRead(path);
93+
}
94+
}
95+
else if (!uri.IsAbsoluteUri)
96+
{
97+
// Embedded resource
98+
stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(uri.OriginalString);
99+
}
100+
101+
if (stream == null)
102+
{
103+
return null;
104+
}
105+
106+
using (stream)
107+
{
108+
using (var codec = SKCodec.Create(stream))
109+
{
110+
if (codec == null)
111+
{
112+
return null;
113+
}
114+
115+
var info = codec.Info;
116+
117+
// Calculate scaled dimensions
118+
int targetWidth = info.Width;
119+
int targetHeight = info.Height;
120+
121+
if (info.Width > maxWidth || info.Height > maxHeight)
122+
{
123+
float scale = Math.Min((float)maxWidth / info.Width, (float)maxHeight / info.Height);
124+
targetWidth = (int)(info.Width * scale);
125+
targetHeight = (int)(info.Height * scale);
126+
}
127+
128+
var bitmap = new SKBitmap(targetWidth, targetHeight, info.ColorType, info.AlphaType);
129+
130+
if (targetWidth == info.Width && targetHeight == info.Height)
131+
{
132+
// No scaling needed
133+
codec.GetPixels(bitmap.Info, bitmap.GetPixels());
134+
}
135+
else
136+
{
137+
// Decode at full size then scale down with high quality
138+
using (var fullBitmap = new SKBitmap(info))
139+
{
140+
codec.GetPixels(fullBitmap.Info, fullBitmap.GetPixels());
141+
fullBitmap.ScalePixels(bitmap, SKFilterQuality.High);
142+
}
143+
}
144+
145+
return bitmap;
146+
}
147+
}
148+
}
149+
catch
150+
{
151+
return null;
152+
}
153+
}
154+
}
155+
}

src/SkiaSharp/RelayCommand.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// This Source Code Form is subject to the terms of the Mozilla Public
2+
// License, v. 2.0. If a copy of the MPL was not distributed with this
3+
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
5+
using System;
6+
using System.ComponentModel;
7+
using System.Runtime.CompilerServices;
8+
9+
namespace WinDynamicDesktop.SkiaSharp
10+
{
11+
public class RelayCommand : ICommand
12+
{
13+
private readonly Action execute;
14+
private readonly Func<bool> canExecute;
15+
16+
public event EventHandler CanExecuteChanged;
17+
18+
public RelayCommand(Action execute, Func<bool> canExecute = null)
19+
{
20+
this.execute = execute;
21+
this.canExecute = canExecute;
22+
}
23+
24+
public bool CanExecute(object parameter)
25+
{
26+
return canExecute?.Invoke() ?? true;
27+
}
28+
29+
public void Execute(object parameter)
30+
{
31+
execute?.Invoke();
32+
}
33+
34+
public void RaiseCanExecuteChanged()
35+
{
36+
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
37+
}
38+
}
39+
40+
public interface ICommand
41+
{
42+
event EventHandler CanExecuteChanged;
43+
bool CanExecute(object parameter);
44+
void Execute(object parameter);
45+
}
46+
}

0 commit comments

Comments
 (0)