Skip to content

Commit afa8f6a

Browse files
committed
v7.0 information
1 parent 90a4440 commit afa8f6a

3 files changed

Lines changed: 329 additions & 4 deletions

File tree

MigrateTo7.0.md

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
# Migrating to StreamDeck-Tools v7.0
2+
3+
This guide helps you upgrade your Stream Deck plugin from StreamDeck-Tools v6.x to v7.0.
4+
5+
> **AI-assisted migration:** This document is designed to work well with AI coding assistants (Cursor, Copilot, etc.). Point your AI at this file and ask it to migrate your plugin -- the tables and recipes below give it everything it needs.
6+
7+
## What's New in v7.0
8+
9+
- **Cross-platform support** -- plugins can now run on both Windows and macOS
10+
- **SkiaSharp graphics** -- new cross-platform drawing APIs alongside the existing System.Drawing ones
11+
- **.NET 10 support** -- library targets `netstandard2.0`, `net48`, `net8.0`, and `net10.0`
12+
- **Self-contained deployment** -- ship your plugin with the runtime included, no user install required
13+
14+
## Do I Need to Change My Code?
15+
16+
Maybe not! Here's a quick check:
17+
18+
```
19+
Does your plugin use System.Drawing directly in its own code?
20+
21+
├── NO → Just upgrade the NuGet package. Build. Done.
22+
│ You'll see [Obsolete] warnings -- these are informational.
23+
│ Everything still works on Windows.
24+
25+
└── YES → Does it use GraphicsPath, LockBits, RotateTransform, or SetPixel?
26+
27+
├── NO → Straightforward rename using the tables below.
28+
29+
└── YES → Rewrite those sections using SkiaSharp directly.
30+
```
31+
32+
Most plugins (roughly half) fall into the "just upgrade" category.
33+
34+
## Step 1: Upgrade the NuGet Package
35+
36+
```
37+
Update-Package StreamDeck-Tools
38+
```
39+
40+
Or set the version manually in your `.csproj`:
41+
42+
```xml
43+
<PackageReference Include="StreamDeck-Tools" Version="7.0.0-beta.2" />
44+
```
45+
46+
Build your project. Everything should compile. You'll see `[Obsolete]` warnings on System.Drawing methods -- these point you to the SkiaSharp replacements.
47+
48+
## Step 2: Add SkiaSharp Usings
49+
50+
In files where you do image rendering, add:
51+
52+
```csharp
53+
using SkiaSharp;
54+
```
55+
56+
The `BarRaider.SdTools` namespace already contains `SkiaTools`, `SkiaGraphicsTools`, and `SkiaExtensionMethods`.
57+
58+
## Step 3: Replace System.Drawing Calls
59+
60+
### Class Mapping
61+
62+
| Legacy Class | Replacement |
63+
|---|---|
64+
| `Tools` (image/font methods) | `SkiaTools` |
65+
| `GraphicsTools` | `SkiaGraphicsTools` |
66+
| `ExtensionMethods` (on `Image`/`Graphics`) | `SkiaExtensionMethods` (on `SKBitmap`/`SKCanvas`) |
67+
68+
### Type Mapping
69+
70+
| System.Drawing | SkiaSharp | Notes |
71+
|---|---|---|
72+
| `Image` / `Bitmap` | `SKBitmap` | Disposable |
73+
| `Graphics` | `SKCanvas` | Created from `SKBitmap` |
74+
| `Color` | `SKColor` | Struct, no disposal |
75+
| `Font` | `SKFont` | Disposable |
76+
| `FontFamily` | `SKTypeface` | `SKTypeface.FromFamilyName(...)` |
77+
| `FontStyle` | `SKFontStyle` | `SKFontStyle.Bold`, `.Normal`, etc. |
78+
| `SolidBrush` | `SKPaint` | `Style = SKPaintStyle.Fill` |
79+
| `Pen` | `SKPaint` | `Style = SKPaintStyle.Stroke` |
80+
| `PointF` | `SKPoint` | |
81+
| `Rectangle` / `RectangleF` | `SKRect` / `SKRectI` | |
82+
| `ColorTranslator.FromHtml(...)` | `SkiaTools.ColorFromHex(...)` | |
83+
| `Image.FromFile(...)` | `SkiaTools.LoadImage(path)` | |
84+
85+
### Common Patterns
86+
87+
| Before (System.Drawing) | After (SkiaSharp) |
88+
|---|---|
89+
| `new SolidBrush(color)` | `new SKPaint { Color = skColor, Style = SKPaintStyle.Fill }` |
90+
| `ColorTranslator.FromHtml("#FF0000")` | `SkiaTools.ColorFromHex("#FF0000")` |
91+
| `new Font("Arial", 12)` | `SkiaTools.CreateFont("Arial", 12)` |
92+
| `Image.FromFile("icon.png")` | `SkiaTools.LoadImage("icon.png")` |
93+
| `graphics.DrawString(text, font, brush, point)` | `canvas.DrawText(text, x, y, font, paint)` |
94+
| `graphics.FillRectangle(brush, rect)` | `canvas.DrawRect(rect, paint)` |
95+
96+
### Method Migration
97+
98+
| Legacy | Replacement |
99+
|---|---|
100+
| `Tools.GenerateKeyImage(type, out Graphics)` | `SkiaTools.GenerateKeyImage(type, out SKCanvas)` |
101+
| `Tools.GenerateGenericKeyImage(out Graphics)` | `SkiaTools.GenerateGenericKeyImage(out SKCanvas)` |
102+
| `Tools.ImageToBase64(Image, bool)` | `SkiaTools.ImageToBase64(SKBitmap, bool)` |
103+
| `Tools.Base64StringToImage(string)``Image` | `SkiaTools.Base64StringToImage(string)``SKBitmap` |
104+
| `Tools.FileToBase64(string, bool)` | `SkiaTools.FileToBase64(string, bool)` |
105+
| `Tools.LoadImage(string/Stream)``Image` | `SkiaTools.LoadImage(string/Stream)``SKBitmap` |
106+
| `Tools.CreateFont(...)``Font` | `SkiaTools.CreateFont(name, size, SKFontStyle?)``SKFont` |
107+
| `GraphicsTools.ResizeImage(Image, w, h)` | `SkiaGraphicsTools.ResizeImage(SKBitmap, w, h)` |
108+
| `GraphicsTools.CreateOpacityImage(Image, f)` | `SkiaGraphicsTools.CreateOpacityImage(SKBitmap, f)` |
109+
| `Connection.SetImageAsync(Image)` | `Connection.SetImageAsync(SKBitmap)` |
110+
| `graphics.AddTextPath(tp, h, w, text)` | `canvas.AddTextPath(tp, h, w, text)` |
111+
| `graphics.GetTextCenter(text, w, font)` | `canvas.GetTextCenter(text, w, skFont)` |
112+
| `text.SplitToFitKey(tp, ...)` | `text.SplitToFitKey(tp, skFont, ...)` |
113+
114+
> **Coordinate difference:** `Graphics.DrawString` positions text from the top-left corner. `SKCanvas.DrawText` uses the text **baseline**. You may need to adjust Y coordinates when migrating (add the font ascent to the Y value).
115+
116+
> **SKFont from TitleParameters:** Some methods now require an `SKFont`. Create one from `TitleParameters` like this: `new SKFont(titleParameters.TitleTypeface, (float)titleParameters.FontSizeInPixelsScaledToDefaultImage)`. Remember to dispose it.
117+
118+
## Code Recipes
119+
120+
### Key Image Rendering
121+
122+
**Before:**
123+
```csharp
124+
using (Image image = Tools.GenerateGenericKeyImage(out Graphics graphics))
125+
{
126+
graphics.FillRectangle(new SolidBrush(Color.White), 0, 0, image.Width, image.Height);
127+
graphics.DrawString("Hello", new Font("Arial", 20), new SolidBrush(Color.Black), new PointF(10, 50));
128+
graphics.Dispose();
129+
await Connection.SetImageAsync(image);
130+
}
131+
```
132+
133+
**After:**
134+
```csharp
135+
using (SKBitmap image = SkiaTools.GenerateGenericKeyImage(out SKCanvas canvas))
136+
{
137+
canvas.Clear(SKColors.White);
138+
using (var font = SkiaTools.CreateFont("Arial", 20))
139+
using (var paint = new SKPaint { Color = SKColors.Black, IsAntialias = true })
140+
{
141+
canvas.DrawText("Hello", 10, 50, font, paint);
142+
}
143+
canvas.Dispose();
144+
await Connection.SetImageAsync(image);
145+
}
146+
```
147+
148+
### Title Text with TitleParameters
149+
150+
**Before:**
151+
```csharp
152+
TitleParameters tp = new TitleParameters(new FontFamily("Arial"), FontStyle.Bold, 20, Color.White, true, TitleVerticalAlignment.Middle);
153+
using (Image image = Tools.GenerateGenericKeyImage(out Graphics graphics))
154+
{
155+
graphics.AddTextPath(tp, image.Height, image.Width, "My Title");
156+
graphics.Dispose();
157+
await Connection.SetImageAsync(image);
158+
}
159+
```
160+
161+
**After:**
162+
```csharp
163+
TitleParameters tp = new TitleParameters(new FontFamily("Arial"), FontStyle.Bold, 20, Color.White, true, TitleVerticalAlignment.Middle);
164+
using (SKBitmap image = SkiaTools.GenerateGenericKeyImage(out SKCanvas canvas))
165+
{
166+
canvas.AddTextPath(tp, image.Height, image.Width, "My Title");
167+
canvas.Dispose();
168+
await Connection.SetImageAsync(image);
169+
}
170+
```
171+
172+
### Color Parsing
173+
174+
**Before:** `Color c = ColorTranslator.FromHtml("#FF0000");`
175+
176+
**After:** `SKColor c = SkiaTools.ColorFromHex("#FF0000");`
177+
178+
### Image Loading
179+
180+
**Before:**
181+
```csharp
182+
Image img = Tools.LoadImage("icon.png");
183+
string base64 = Tools.ImageToBase64(img, true);
184+
await Connection.SetImageAsync(base64);
185+
```
186+
187+
**After:**
188+
```csharp
189+
using (SKBitmap img = SkiaTools.LoadImage("icon.png"))
190+
{
191+
string base64 = SkiaTools.ImageToBase64(img, true);
192+
await Connection.SetImageAsync(base64);
193+
}
194+
```
195+
196+
### Font Creation
197+
198+
**Before:** `Font font = new Font("Arial", 14, FontStyle.Bold, GraphicsUnit.Pixel);`
199+
200+
**After:**
201+
```csharp
202+
using (SKFont font = SkiaTools.CreateFont("Arial", 14, SKFontStyle.Bold))
203+
{
204+
// use font for drawing
205+
}
206+
```
207+
208+
## Going Cross-Platform (Optional)
209+
210+
If you want your plugin to run on both Windows and macOS:
211+
212+
### 1. Target .NET 10 with Self-Contained Deployment
213+
214+
```xml
215+
<PropertyGroup>
216+
<TargetFramework>net10.0</TargetFramework>
217+
<SelfContained>true</SelfContained>
218+
<PublishTrimmed>true</PublishTrimmed>
219+
<TrimMode>partial</TrimMode>
220+
<RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
221+
</PropertyGroup>
222+
```
223+
224+
`osx-x64` is recommended for macOS -- it runs natively on Intel and via Rosetta 2 on Apple Silicon, covering all Mac users with a single binary.
225+
226+
### 2. Update manifest.json
227+
228+
```json
229+
{
230+
"CodePath": "com.your.plugin",
231+
"CodePathWin": "win-x64/com.your.plugin.exe",
232+
"CodePathMac": "osx-x64/com.your.plugin",
233+
"OS": [
234+
{ "Platform": "windows", "MinimumVersion": "10" },
235+
{ "Platform": "mac", "MinimumVersion": "13" }
236+
]
237+
}
238+
```
239+
240+
### 3. Publish for Both Platforms
241+
242+
```powershell
243+
dotnet publish -c Release -r win-x64
244+
dotnet publish -c Release -r osx-x64
245+
```
246+
247+
Assemble both outputs into your `.sdPlugin` folder with `win-x64/` and `osx-x64/` subdirectories, alongside shared assets (manifest, images, Property Inspector).
248+
249+
### Known Gotcha: System.Management and Trimming
250+
251+
`System.Management` (WMI) does not work in trimmed .NET 10 builds due to COM interop incompatibility. If your plugin or any dependency uses WMI, replace those calls with alternatives (e.g., Windows Registry reads). This only affects trimmed self-contained builds.
252+
253+
## Deprecation Timeline
254+
255+
| Release | What Happens |
256+
|---|---|
257+
| **v7.0** (current) | System.Drawing APIs marked `[Obsolete]` with guidance. Everything still works on Windows. SkiaSharp APIs available in parallel. |
258+
| **v7.x** | Stability period. No removals. |
259+
| **v8.0** (future) | System.Drawing APIs evaluated for removal based on adoption. |
260+
261+
## Compatibility Notes
262+
263+
- System.Drawing APIs continue to work on **Windows** across all TFMs.
264+
- For **macOS/Linux**, you must use the SkiaSharp APIs. System.Drawing throws `PlatformNotSupportedException` on non-Windows.
265+
- `TitleParameters` is unchanged. The new SkiaSharp properties (`TitleSKColor`, `TitleTypeface`) are additive.
266+
- SkiaSharp 3.x is MIT-licensed. No licensing impact.
267+
268+
## Complete Example: Cross-Platform Plugin Action
269+
270+
```csharp
271+
using BarRaider.SdTools;
272+
using BarRaider.SdTools.Wrappers;
273+
using SkiaSharp;
274+
using System.Threading.Tasks;
275+
276+
[PluginActionId("com.example.myplugin")]
277+
public class MyAction : KeypadBase
278+
{
279+
public MyAction(ISDConnection connection, InitialPayload payload) : base(connection, payload) { }
280+
281+
public async override void KeyPressed(KeyPayload payload)
282+
{
283+
using (SKBitmap image = SkiaTools.GenerateGenericKeyImage(out SKCanvas canvas))
284+
{
285+
canvas.Clear(SKColors.DarkBlue);
286+
287+
using (var font = SkiaTools.CreateFont("Arial", 18, SKFontStyle.Bold))
288+
using (var paint = new SKPaint { Color = SKColors.White, IsAntialias = true })
289+
{
290+
float x = canvas.GetTextCenter("Pressed!", image.Width, font);
291+
canvas.DrawText("Pressed!", x, image.Height / 2f, font, paint);
292+
}
293+
294+
canvas.Dispose();
295+
await Connection.SetImageAsync(image);
296+
}
297+
}
298+
299+
public async override void KeyReleased(KeyPayload payload)
300+
{
301+
await Connection.SetDefaultImageAsync();
302+
}
303+
304+
public override void OnTick() { }
305+
public override void Dispose() { }
306+
public override void ReceivedSettings(ReceivedSettingsPayload payload) { }
307+
public override void ReceivedGlobalSettings(ReceivedGlobalSettingsPayload payload) { }
308+
}
309+
```

NUGET.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
**Author's website and contact information:** [https://barraider.com](https://barraider.com)
88
** Samples of plugins using this framework: [Samples][1]
99

10-
### Version 3.2 is out!
11-
- Created new `ISDConnection` interface which is now implemented by SDConnection and used by PluginAction.
12-
- GlobalSettingsManager now has a short delay before calling GetGlobalSettings(), to reduce spamming the Stream Deck SDK.
13-
- Updated dependencies to latest version
10+
### Version 7.0 (Beta)
11+
- **Cross-platform support** -- plugins can now run on Windows and macOS
12+
- **SkiaSharp graphics** -- new cross-platform drawing APIs (`SkiaTools`, `SkiaGraphicsTools`, `SkiaExtensionMethods`)
13+
- **.NET 10 support** -- targets `netstandard2.0`, `net48`, `net8.0`, `net10.0`
14+
- All System.Drawing APIs marked `[Obsolete]` with pointers to SkiaSharp replacements (still works on Windows)
15+
16+
**Upgrading from v6.x?** See the [Migration Guide](https://github.com/BarRaider/streamdeck-tools/blob/master/MigrateTo7.0.md) for step-by-step instructions and code recipes.
1417

1518
## Features
1619
- Sample plugin now included in this project on Github

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66

77
**Author's website and contact information:** [https://barraider.com](https://barraider.com)
88

9+
# Version 7.0 now in Beta!
10+
- **Cross-platform support** -- plugins can now run on Windows and macOS
11+
- **SkiaSharp graphics** -- new `SkiaTools`, `SkiaGraphicsTools`, and `SkiaExtensionMethods` classes provide cross-platform drawing APIs
12+
- **New `SetImageAsync` overloads** -- accept `SKBitmap` or `byte[]` (PNG) in addition to base64 strings
13+
- **SkiaSharp properties on `TitleParameters`** -- `TitleSKColor`, `TitleTypeface`, `FontStyleToSKFontStyle()`
14+
- **.NET 10 target** added (`netstandard2.0`, `net48`, `net8.0`, `net10.0`)
15+
- All System.Drawing APIs marked `[Obsolete]` with pointers to SkiaSharp replacements (still fully functional on Windows)
16+
17+
**Migration guide:** See [MigrateTo7.0.md](MigrateTo7.0.md) for step-by-step upgrade instructions, type mapping tables, and code recipes. This file is also designed to work with AI coding assistants (Cursor, Copilot, etc.) -- point your AI at it and ask it to migrate your plugin.
18+
919
# Stream Deck+ Support
1020
Instead of `PluginBase`, Derive from either `KeypadBase` (if you don't support dials), `EncoderBase` (for only dials), `KeyAndEncoderBase` (for both keys and dials)
1121

@@ -33,6 +43,9 @@ Introducing our new [wiki](https://github.com/BarRaider/streamdeck-tools/wiki) p
3343

3444
# Change Log
3545

46+
### Version 7.0 (Beta)
47+
- See [Version 7.0 now in Beta!](#version-70-now-in-beta) above
48+
3649
### Version 6.4
3750
- Support for Stream Deck Plus XL, Galleon 100 SD
3851

0 commit comments

Comments
 (0)