StreamDeckSharp/OpenMacroBoard.SDK/ButtonPressEffectAdapter.cs
2026-08-03 22:34:16 +02:00

119 lines
4.2 KiB
C#

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
namespace OpenMacroBoard.SDK {
/// <summary>
/// Macro board adapter that implements a software button press effect.
/// </summary>
/// <remarks>
/// <para>This vaguely mimics the perspective of a real button being pushed
/// and provides better feedback to the user that a button push was registered.</para>
/// </remarks>
public class ButtonPressEffectAdapter : MacroBoardAdapter {
private readonly Dictionary<int, KeyBitmap> mostRecentKeyBitmaps = new();
private readonly Dictionary<int, bool> keyPressedState = new();
/// <summary>
/// Initializes a new instance of the <see cref="ButtonPressEffectAdapter"/> class.
/// </summary>
/// <param name="macroBoard">The <see cref="IMacroBoard"/> this effect should be applied to.</param>
public ButtonPressEffectAdapter(IMacroBoard macroBoard)
: this(macroBoard, null) {
}
/// <summary>
/// Initializes a new instance of the <see cref="ButtonPressEffectAdapter"/> class.
/// </summary>
/// <param name="macroBoard">The board that is wrapped with the button press effect.</param>
/// <param name="config">The configuration that should be used. If null the default configuration will be used.</param>
public ButtonPressEffectAdapter(IMacroBoard macroBoard, ButtonPressEffectConfig config) : base(macroBoard) {
this.ButtonPressed += this.ButtonPressEffectAdapter_ButtonPressed;
this.ButtonReleased += this.ButtonPressEffectAdapter_ButtonReleased;
//KeyStateChanged += SoftwareButtonFeature_KeyStateChanged;
Config = config ?? new ButtonPressEffectConfig();
}
private void ButtonPressEffectAdapter_ButtonReleased(Object sender, KeyPressEvent e) {
this.keyPressedState[e.Key] = false;
this.UpdateKeyBitmap(e.Key);
}
private void ButtonPressEffectAdapter_ButtonPressed(Object sender, KeyPressEvent e) {
this.keyPressedState[e.Key] = true;
this.UpdateKeyBitmap(e.Key);
}
/// <summary>
/// The configuration that controls the behavior of the button press effect feature.
/// </summary>
public ButtonPressEffectConfig Config {
get;
}
/// <inheritdoc/>
public override void SetButtonImage(int keyId, KeyBitmap bitmapData) {
mostRecentKeyBitmaps[keyId] = bitmapData;
UpdateKeyBitmap(keyId);
}
private void UpdateKeyBitmap(int keyId) {
var bitmap = GetBitmapForKey(keyId);
if(bitmap != null) {
base.SetButtonImage(keyId, bitmap);
}
}
private bool IsKeyPressed(int keyId) {
if(keyPressedState.TryGetValue(keyId, out var keyPressed)) {
return keyPressed;
}
return false;
}
private KeyBitmap GetBitmapForKey(int keyId) {
if(!mostRecentKeyBitmaps.TryGetValue(keyId, out var bitmap)) {
return null;
}
return IsKeyPressed(keyId) ? ResizeBitmap(bitmap) : bitmap;
}
private KeyBitmap ResizeBitmap(KeyBitmap keyBitmap) {
var bitmapDataAccess = (IKeyBitmapDataAccess)keyBitmap;
if(bitmapDataAccess.IsEmpty) {
return KeyBitmap.Black;
}
var targetWidth = (int)Math.Round(Config.Scale * keyBitmap.Width);
var targetHeight = (int)Math.Round(Config.Scale * keyBitmap.Height);
var smallerImage = bitmapDataAccess.ToImage();
smallerImage.Mutate(x => x.Resize(targetWidth, targetHeight));
var offsetLeft = (int)Math.Round((keyBitmap.Width - targetWidth) * Config.OriginX);
var offsetTop = (int)Math.Round((keyBitmap.Height - targetHeight) * Config.OriginY);
var color = Color.FromPixel(new Rgb24(
Config.BackgroundColor.R,
Config.BackgroundColor.G,
Config.BackgroundColor.B)
);
var newImage = new Image<Bgr24>(keyBitmap.Width, keyBitmap.Height);
newImage.Mutate(x => {
x.BackgroundColor(color);
x.DrawImage(smallerImage, new Point(offsetLeft, offsetTop), 1);
});
return KeyBitmap.Create.FromImageSharpImage(newImage);
}
}
}