mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Added configurable LED sample size
This commit is contained in:
parent
2f8671045c
commit
b1870e9e64
@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Artemis.Core.Extensions
|
||||
{
|
||||
public static class DoubleExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int RoundToInt(this double number)
|
||||
{
|
||||
return (int) Math.Round(number, MidpointRounding.AwayFromZero);
|
||||
|
||||
@ -118,6 +118,10 @@ namespace Artemis.Core.Models.Profile
|
||||
return;
|
||||
|
||||
canvas.Save();
|
||||
canvas.ClipPath(Path);
|
||||
// Placeholder
|
||||
if (LayerShape?.RenderPath != null)
|
||||
canvas.DrawPath(LayerShape.RenderPath, new SKPaint(){Color = new SKColor(255,0,0)});
|
||||
LayerBrush?.Render(canvas);
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ namespace Artemis.Core.Models.Profile.LayerShapes
|
||||
|
||||
var path = new SKPath();
|
||||
path.AddOval(rect);
|
||||
path.Transform(SKMatrix.MakeTranslation(Layer.Rectangle.Left, Layer.Rectangle.Top));
|
||||
|
||||
RenderPath = path;
|
||||
RenderRectangle = path.GetRect();
|
||||
|
||||
@ -20,6 +20,7 @@ namespace Artemis.Core.Models.Profile.LayerShapes
|
||||
var rect = SKRect.Create(Position.X * width, Position.Y * height, Size.Width * width, Size.Height * height);
|
||||
var path = new SKPath();
|
||||
path.AddRect(rect);
|
||||
path.Transform(SKMatrix.MakeTranslation(Layer.Rectangle.Left, Layer.Rectangle.Top));
|
||||
|
||||
RenderPath = path;
|
||||
RenderRectangle = path.GetRect();
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Artemis.Core.Extensions;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
using RGB.NET.Core;
|
||||
using SkiaSharp;
|
||||
|
||||
@ -9,10 +10,13 @@ namespace Artemis.Core.RGB.NET
|
||||
{
|
||||
public class BitmapBrush : AbstractDecoratable<IBrushDecorator>, IBrush, IDisposable
|
||||
{
|
||||
private readonly PluginSetting<int> _sampleSizeSetting;
|
||||
|
||||
#region Constructors
|
||||
|
||||
public BitmapBrush(Scale scale)
|
||||
public BitmapBrush(Scale scale, PluginSetting<int> sampleSizeSetting)
|
||||
{
|
||||
_sampleSizeSetting = sampleSizeSetting;
|
||||
Scale = scale;
|
||||
}
|
||||
|
||||
@ -61,12 +65,65 @@ namespace Artemis.Core.RGB.NET
|
||||
if (Bitmap == null)
|
||||
CreateBitmap(RenderedRectangle);
|
||||
|
||||
if (_sampleSizeSetting.Value == 1)
|
||||
TakeCenter(renderTargets);
|
||||
else
|
||||
TakeSamples(renderTargets);
|
||||
}
|
||||
|
||||
private void TakeCenter(IEnumerable<BrushRenderTarget> renderTargets)
|
||||
{
|
||||
foreach (var renderTarget in renderTargets)
|
||||
{
|
||||
// TODO: Right now the sample size is 1, make this configurable to something higher and average the samples out
|
||||
var scaledLocation = renderTarget.Point * Scale;
|
||||
if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height)
|
||||
RenderedTargets[renderTarget] = Bitmap.GetPixel(RoundToInt(scaledLocation.X), RoundToInt(scaledLocation.Y)).ToRgbColor();
|
||||
RenderedTargets[renderTarget] = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor();
|
||||
}
|
||||
}
|
||||
|
||||
private void TakeSamples(IEnumerable<BrushRenderTarget> renderTargets)
|
||||
{
|
||||
var sampleSize = _sampleSizeSetting.Value;
|
||||
var sampleDepth = Math.Sqrt(sampleSize).RoundToInt();
|
||||
var pixelSpan = Bitmap.GetPixelSpan();
|
||||
|
||||
foreach (var renderTarget in renderTargets)
|
||||
{
|
||||
// SKRect has all the good stuff we need
|
||||
var rect = SKRect.Create(
|
||||
(float) ((renderTarget.Rectangle.Location.X + 4) * Scale.Horizontal),
|
||||
(float) ((renderTarget.Rectangle.Location.Y + 4) * Scale.Vertical),
|
||||
(float) ((renderTarget.Rectangle.Size.Width - 8) * Scale.Horizontal),
|
||||
(float) ((renderTarget.Rectangle.Size.Height - 8) * Scale.Vertical));
|
||||
|
||||
var verticalSteps = rect.Height / (sampleDepth - 1);
|
||||
var horizontalSteps = rect.Width / (sampleDepth - 1);
|
||||
|
||||
var a = 0;
|
||||
var r = 0;
|
||||
var g = 0;
|
||||
var b = 0;
|
||||
for (var horizontalStep = 0; horizontalStep < sampleDepth; horizontalStep++)
|
||||
{
|
||||
for (var verticalStep = 0; verticalStep < sampleDepth; verticalStep++)
|
||||
{
|
||||
var x = (rect.Left + horizontalSteps * horizontalStep).RoundToInt();
|
||||
var y = (rect.Top + verticalSteps * verticalStep).RoundToInt();
|
||||
if (x < 0 || x > Bitmap.Width || y < 0 || y > Bitmap.Height)
|
||||
continue;
|
||||
|
||||
var (pixelA, pixelR, pixelG, pixelB) = GetRgbColor(pixelSpan, x, y);
|
||||
a += pixelA;
|
||||
r += pixelR;
|
||||
g += pixelG;
|
||||
b += pixelB;
|
||||
|
||||
// Uncomment to view the sample pixels in the debugger, need a checkbox in the actual debugger but this was a quickie
|
||||
// Bitmap.SetPixel(x, y, new SKColor(0, 255, 0));
|
||||
}
|
||||
}
|
||||
|
||||
RenderedTargets[renderTarget] = new Color(a / sampleSize, r / sampleSize, g / sampleSize, b / sampleSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,16 +131,24 @@ namespace Artemis.Core.RGB.NET
|
||||
|
||||
private void CreateBitmap(Rectangle rectangle)
|
||||
{
|
||||
// TODO: Test this max size, it applied to System.Drawing.Bitmap but SKBitmap might scale better or worse
|
||||
var width = Math.Min((rectangle.Location.X + rectangle.Size.Width) * Scale.Horizontal, 4096);
|
||||
var height = Math.Min((rectangle.Location.Y + rectangle.Size.Height) * Scale.Vertical, 4096);
|
||||
Bitmap = new SKBitmap(new SKImageInfo(width.RoundToInt(), height.RoundToInt()));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int RoundToInt(double number)
|
||||
private Tuple<int, int, int, int> GetRgbColor(in ReadOnlySpan<byte> pixelSpan, float x, float y)
|
||||
{
|
||||
return (int) Math.Round(number, MidpointRounding.AwayFromZero);
|
||||
var index = ((int) y * Bitmap.Width + (int) x) * 4;
|
||||
if (index + 3 > pixelSpan.Length)
|
||||
return new Tuple<int, int, int, int>(0, 0, 0, 0);
|
||||
|
||||
var b = pixelSpan[index];
|
||||
var g = pixelSpan[index + 1];
|
||||
var r = pixelSpan[index + 2];
|
||||
var a = pixelSpan[index + 3];
|
||||
|
||||
return new Tuple<int, int, int, int>(a, r, g, b);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@ -308,7 +308,7 @@ namespace Artemis.Core.Services
|
||||
|
||||
public Plugin GetDevicePlugin(IRGBDevice rgbDevice)
|
||||
{
|
||||
return GetPluginsOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices.Contains(rgbDevice));
|
||||
return GetPluginsOfType<DeviceProvider>().First(d => d.RgbDeviceProvider.Devices != null && d.RgbDeviceProvider.Devices.Contains(rgbDevice));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@ -1,12 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Core.Events;
|
||||
using Artemis.Core.Plugins.Models;
|
||||
using Artemis.Core.RGB.NET;
|
||||
using Artemis.Core.Services.Interfaces;
|
||||
using RGB.NET.Brushes;
|
||||
using RGB.NET.Brushes.Gradients;
|
||||
using RGB.NET.Core;
|
||||
using RGB.NET.Groups;
|
||||
using Serilog;
|
||||
@ -21,6 +18,7 @@ namespace Artemis.Core.Services
|
||||
private readonly List<IRGBDevice> _loadedDevices;
|
||||
private readonly ILogger _logger;
|
||||
private readonly PluginSetting<double> _renderScaleSetting;
|
||||
private readonly PluginSetting<int> _sampleSizeSetting;
|
||||
private readonly PluginSetting<int> _targetFrameRateSetting;
|
||||
private readonly TimerUpdateTrigger _updateTrigger;
|
||||
private ListLedGroup _surfaceLedGroup;
|
||||
@ -30,6 +28,7 @@ namespace Artemis.Core.Services
|
||||
_logger = logger;
|
||||
_renderScaleSetting = settingsService.GetSetting("Core.RenderScale", 1.0);
|
||||
_targetFrameRateSetting = settingsService.GetSetting("Core.TargetFrameRate", 25);
|
||||
_sampleSizeSetting = settingsService.GetSetting("Core.SampleSize", 1);
|
||||
|
||||
Surface = RGBSurface.Instance;
|
||||
|
||||
@ -105,7 +104,7 @@ namespace Artemis.Core.Services
|
||||
if (_surfaceLedGroup == null)
|
||||
{
|
||||
// Apply the application wide brush and decorator
|
||||
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value));
|
||||
BitmapBrush = new BitmapBrush(new Scale(_renderScaleSetting.Value), _sampleSizeSetting);
|
||||
_surfaceLedGroup = new ListLedGroup(Surface.Leds) {Brush = BitmapBrush};
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,8 +80,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
// Remove the tool from the canvas
|
||||
if (_activeToolViewModel != null)
|
||||
{
|
||||
CanvasViewModels.Remove(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
lock (CanvasViewModels)
|
||||
{
|
||||
CanvasViewModels.Remove(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the new tool
|
||||
@ -89,8 +92,11 @@ namespace Artemis.UI.Screens.Module.ProfileEditor.Visualization
|
||||
// Add the new tool to the canvas
|
||||
if (_activeToolViewModel != null)
|
||||
{
|
||||
CanvasViewModels.Add(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
lock (CanvasViewModels)
|
||||
{
|
||||
CanvasViewModels.Add(_activeToolViewModel);
|
||||
NotifyOfPropertyChange(() => CanvasViewModels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,6 +130,28 @@
|
||||
<ComboBox Width="80" SelectedItem="{Binding SelectedTargetFrameRate}" ItemsSource="{Binding TargetFrameRates}" DisplayMemberPath="Item1"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<Separator Style="{StaticResource MaterialDesignSeparator}" Margin="-15 5" />
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0">
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}">LED sample size</TextBlock>
|
||||
<TextBlock Style="{StaticResource MaterialDesignBody1TextBlock}" Foreground="{DynamicResource MaterialDesignNavigationItemSubheader}" TextWrapping="Wrap">
|
||||
Sets the amount of samples that is taken to determine each LEDs color.
|
||||
Best you leave it on 1 but included for completeness, a higher value increases CPU-usage quite rapidly.
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="0" Grid.Column="1" VerticalAlignment="Center">
|
||||
<ComboBox Width="80" SelectedItem="{Binding SampleSize}" ItemsSource="{Binding SampleSizes}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</materialDesign:Card>
|
||||
</StackPanel>
|
||||
|
||||
@ -45,8 +45,13 @@ namespace Artemis.UI.Screens.Settings
|
||||
TargetFrameRates = new List<Tuple<string, int>>();
|
||||
for (var i = 10; i <= 30; i += 5)
|
||||
TargetFrameRates.Add(new Tuple<string, int>(i + " FPS", i));
|
||||
|
||||
// Anything else is kinda broken right now
|
||||
SampleSizes = new List<int> {1, 9};
|
||||
}
|
||||
|
||||
public List<int> SampleSizes { get; set; }
|
||||
|
||||
public BindableCollection<DeviceSettingsViewModel> DeviceSettingsViewModels { get; set; }
|
||||
|
||||
public List<Tuple<string, double>> RenderScales { get; set; }
|
||||
@ -85,6 +90,16 @@ namespace Artemis.UI.Screens.Settings
|
||||
}
|
||||
}
|
||||
|
||||
public int SampleSize
|
||||
{
|
||||
get => _settingsService.GetSetting("Core.SampleSize", 1).Value;
|
||||
set
|
||||
{
|
||||
_settingsService.GetSetting("Core.SampleSize", 1).Value = value;
|
||||
_settingsService.GetSetting("Core.SampleSize", 1).Save();
|
||||
}
|
||||
}
|
||||
|
||||
public string Title => "Settings";
|
||||
|
||||
protected override void OnActivate()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user