1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-13 05:48:35 +00:00

Merge pull request #517 from Artemis-RGB/feature/color-calibration

Added Per device color calibration
This commit is contained in:
Robert Beekman 2020-12-12 21:11:41 +01:00 committed by GitHub
commit 36058efc4d
8 changed files with 343 additions and 46 deletions

View File

@ -27,6 +27,9 @@ namespace Artemis.Core
Rotation = 0; Rotation = 0;
Scale = 1; Scale = 1;
ZIndex = 1; ZIndex = 1;
RedScale = 1;
GreenScale = 1;
BlueScale = 1;
deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath); deviceProvider.DeviceLayoutPaths.TryGetValue(rgbDevice, out string? layoutPath);
LayoutPath = layoutPath; LayoutPath = layoutPath;
@ -171,6 +174,45 @@ namespace Artemis.Core
} }
} }
/// <summary>
/// Gets or sets the scale of the red color component used for calibration
/// </summary>
public double RedScale
{
get => DeviceEntity.RedScale;
set
{
DeviceEntity.RedScale = value;
OnPropertyChanged(nameof(RedScale));
}
}
/// <summary>
/// Gets or sets the scale of the green color component used for calibration
/// </summary>
public double GreenScale
{
get => DeviceEntity.GreenScale;
set
{
DeviceEntity.GreenScale = value;
OnPropertyChanged(nameof(GreenScale));
}
}
/// <summary>
/// Gets or sets the scale of the blue color component used for calibration
/// </summary>
public double BlueScale
{
get => DeviceEntity.BlueScale;
set
{
DeviceEntity.BlueScale = value;
OnPropertyChanged(nameof(BlueScale));
}
}
/// <summary> /// <summary>
/// Gets the path to where the layout of the device was (attempted to be) loaded from /// Gets the path to where the layout of the device was (attempted to be) loaded from
/// </summary> /// </summary>

View File

@ -57,5 +57,14 @@ namespace Artemis.Core
{ {
return RgbLed.ToString(); return RgbLed.ToString();
} }
/// <summary>
/// Gets the color of this led, reverting the correction done to the parent device
/// </summary>
/// <returns></returns>
public Color GetOriginalColor()
{
return RgbLed.Color.DivideRGB(Device.RedScale, Device.GreenScale, Device.BlueScale);
}
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using RGB.NET.Core; using RGB.NET.Core;
using SkiaSharp; using SkiaSharp;
@ -101,7 +101,13 @@ namespace Artemis.Core
{ {
Point scaledLocation = renderTarget.Point * Scale; Point scaledLocation = renderTarget.Point * Scale;
if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height) if (scaledLocation.X < Bitmap.Width && scaledLocation.Y < Bitmap.Height)
RenderedTargets[renderTarget] = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor(); {
var pixel = Bitmap.GetPixel(scaledLocation.X.RoundToInt(), scaledLocation.Y.RoundToInt()).ToRgbColor();
var artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
if (artemisDevice is not null)
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
RenderedTargets[renderTarget] = pixel;
}
} }
} }
@ -148,8 +154,13 @@ namespace Artemis.Core
// Bitmap.SetPixel(x, y, new SKColor(0, 255, 0)); // Bitmap.SetPixel(x, y, new SKColor(0, 255, 0));
} }
} }
var pixel = new Color(a / sampleSize, r / sampleSize, g / sampleSize, b / sampleSize);
RenderedTargets[renderTarget] = new Color(a / sampleSize, r / sampleSize, g / sampleSize, b / sampleSize); var artemisDevice = Surface?.GetArtemisLed(renderTarget.Led)?.Device;
if (artemisDevice is not null)
pixel = pixel.MultiplyRGB(artemisDevice.RedScale, artemisDevice.GreenScale, artemisDevice.BlueScale);
RenderedTargets[renderTarget] = pixel;
} }
} }

View File

@ -15,6 +15,9 @@ namespace Artemis.Storage.Entities.Surface
public double Rotation { get; set; } public double Rotation { get; set; }
public double Scale { get; set; } public double Scale { get; set; }
public int ZIndex { get; set; } public int ZIndex { get; set; }
public double RedScale { get; set; }
public double GreenScale { get; set; }
public double BlueScale { get; set; }
public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; } public List<DeviceInputIdentifierEntity> InputIdentifiers { get; set; }
} }

View File

@ -0,0 +1,27 @@
using Artemis.Storage.Migrations.Interfaces;
using LiteDB;
namespace Artemis.Storage.Migrations
{
public class M9DeviceCalibration : IStorageMigration
{
public int UserVersion => 9;
/// <inheritdoc />
public void Apply(LiteRepository repository)
{
ILiteCollection<BsonDocument> collection = repository.Database.GetCollection("SurfaceEntity");
foreach (BsonDocument bsonDocument in collection.FindAll())
{
foreach (BsonValue bsonDevice in bsonDocument["DeviceEntities"].AsArray)
{
bsonDevice["RedScale"] = 1d;
bsonDevice["GreenScale"] = 1d;
bsonDevice["BlueScale"] = 1d;
}
collection.Update(bsonDocument);
}
}
}
}

View File

@ -38,9 +38,10 @@ namespace Artemis.UI.Shared
if (DisplayGeometry == null) if (DisplayGeometry == null)
return; return;
byte r = Led.RgbLed.Color.GetR(); RGB.NET.Core.Color originalColor = Led.GetOriginalColor();
byte g = Led.RgbLed.Color.GetG(); byte r = originalColor.GetR();
byte b = Led.RgbLed.Color.GetB(); byte g = originalColor.GetG();
byte b = originalColor.GetB();
drawingContext.DrawRectangle(isDimmed drawingContext.DrawRectangle(isDimmed
? new SolidColorBrush(Color.FromArgb(100, r, g, b)) ? new SolidColorBrush(Color.FromArgb(100, r, g, b))

View File

@ -1,40 +1,172 @@
<UserControl x:Class="Artemis.UI.Screens.SurfaceEditor.Dialogs.SurfaceDeviceConfigView" <UserControl x:Class="Artemis.UI.Screens.SurfaceEditor.Dialogs.SurfaceDeviceConfigView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:s="https://github.com/canton7/Stylet" xmlns:s="https://github.com/canton7/Stylet"
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:surfaceEditor="clr-namespace:Artemis.UI.Screens.SurfaceEditor.Dialogs" xmlns:surfaceEditor="clr-namespace:Artemis.UI.Screens.SurfaceEditor.Dialogs"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="351.305" d:DesignWidth="262.163"
d:DataContext="{d:DesignInstance {x:Type surfaceEditor:SurfaceDeviceConfigViewModel}}"> d:DataContext="{d:DesignInstance {x:Type surfaceEditor:SurfaceDeviceConfigViewModel}}">
<StackPanel Margin="16"> <UserControl.Resources>
<TextBlock Text="{Binding Title}" Style="{StaticResource MaterialDesignHeadline6TextBlock}" /> <shared:SKColorToColorConverter x:Key="SKColorToColorConverter" />
<TextBlock Text="Note: These are not being validated yet" Style="{StaticResource MaterialDesignSubtitle1TextBlock}" /> </UserControl.Resources>
<StackPanel Margin="16" Width="400">
<!-- Title -->
<TextBlock Text="{Binding Device.RgbDevice.DeviceInfo.DeviceName}" Style="{StaticResource MaterialDesignHeadline6TextBlock}" />
<TextBox materialDesign:HintAssist.Hint="X-coordinate" <!-- Body -->
materialDesign:TextFieldAssist.SuffixText="mm" <Grid Margin="0 25 0 0">
Text="{Binding X, UpdateSourceTrigger=PropertyChanged}" <Grid.ColumnDefinitions>
Style="{StaticResource MaterialDesignFloatingHintTextBox}" <ColumnDefinition Width="2*" />
Margin="0 10" /> <ColumnDefinition Width="40" />
<TextBox materialDesign:HintAssist.Hint="Y-coordinate" <ColumnDefinition Width="3*" />
materialDesign:TextFieldAssist.SuffixText="mm" </Grid.ColumnDefinitions>
Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Scale"
materialDesign:TextFieldAssist.SuffixText="times"
Text="{Binding Scale, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<TextBox materialDesign:HintAssist.Hint="Rotation"
materialDesign:TextFieldAssist.SuffixText="deg"
Text="{Binding Rotation, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 10" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right"> <!-- Left side -->
<Button Style="{StaticResource MaterialDesignFlatButton}" IsCancel="True" Margin="0 8 8 0" <StackPanel Grid.Column="0" Orientation="Vertical">
<TextBlock Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Properties
</TextBlock>
<TextBox materialDesign:HintAssist.Hint="X-coordinate"
materialDesign:TextFieldAssist.SuffixText="mm"
Text="{Binding X, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Y-coordinate"
materialDesign:TextFieldAssist.SuffixText="mm"
Text="{Binding Y, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Scale"
materialDesign:TextFieldAssist.SuffixText="times"
Text="{Binding Scale, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5" />
<TextBox materialDesign:HintAssist.Hint="Rotation"
materialDesign:TextFieldAssist.SuffixText="deg"
Text="{Binding Rotation, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
Margin="0 5 0 12" />
</StackPanel>
<!-- Center divider -->
<Rectangle Grid.Column="1" VerticalAlignment="Stretch" Fill="{StaticResource MaterialDesignTextBoxBorder}" Width="1" Margin="0 0 0 5" />
<!-- Right side -->
<Grid Grid.Column="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.ColumnSpan="3" Style="{StaticResource MaterialDesignSubtitle1TextBlock}">
Color calibration
</TextBlock>
<TextBlock Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Style="{StaticResource MaterialDesignCaptionTextBlock}"
Foreground="{DynamicResource MaterialDesignBodyLight}"
TextWrapping="Wrap"
TextAlignment="Justify">
Use the sliders below to adjust the colors of your device so that it matches your other devices.
</TextBlock>
<Label Grid.Row="2"
Grid.Column="0"
Content="R"
VerticalAlignment="Center" />
<Slider Grid.Row="2"
Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding RedScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10 0"
VerticalAlignment="Center" />
<TextBox Grid.Row="2"
Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding RedScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
<Label Grid.Row="3"
Grid.Column="0"
Content="G"
VerticalAlignment="Center" />
<Slider Grid.Row="3"
Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding GreenScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10 0"
VerticalAlignment="Center" />
<TextBox Grid.Row="3"
Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding GreenScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
<Label Grid.Row="4"
Grid.Column="0"
Content="B"
VerticalAlignment="Center" />
<Slider Grid.Row="4"
Grid.Column="1"
Minimum="0"
Maximum="200"
ValueChanged="{s:Action ApplyScaling}"
Value="{Binding BlueScale, UpdateSourceTrigger=PropertyChanged}"
Margin="10 0"
Ticks="100"
VerticalAlignment="Center" />
<TextBox Grid.Row="4"
Grid.Column="2"
VerticalAlignment="Center"
Text="{Binding BlueScale, StringFormat={}{0:0.0}, UpdateSourceTrigger=PropertyChanged}"
materialDesign:TextFieldAssist.SuffixText="%"
Width="50" />
<Grid Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="3">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox Grid.Column="0"
IsChecked="{Binding DisplayOnDevices}"
Content="Show preview"
VerticalAlignment="Center" />
<shared:ColorPicker Grid.Column="1"
Margin="0,0,5,0"
HorizontalAlignment="Right"
Color="{Binding CurrentColor, Converter={StaticResource SKColorToColorConverter}}"
VerticalAlignment="Center"/>
</Grid>
</Grid>
</Grid>
<!-- Buttons -->
<StackPanel Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Style="{StaticResource MaterialDesignFlatButton}"
IsCancel="True"
Margin="0 8 8 0"
Command="{s:Action Cancel}"> Command="{s:Action Cancel}">
<Button.CommandParameter> <Button.CommandParameter>
<system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib"> <system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib">
@ -43,7 +175,9 @@
</Button.CommandParameter> </Button.CommandParameter>
CANCEL CANCEL
</Button> </Button>
<Button Style="{StaticResource MaterialDesignFlatButton}" IsDefault="True" Margin="0 8 8 0" <Button Style="{StaticResource MaterialDesignFlatButton}"
IsDefault="True"
Margin="0 8 8 0"
Command="{s:Action Accept}"> Command="{s:Action Accept}">
<Button.CommandParameter> <Button.CommandParameter>
<system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib"> <system:Boolean xmlns:system="clr-namespace:System;assembly=mscorlib">

View File

@ -2,6 +2,7 @@
using Artemis.Core; using Artemis.Core;
using Artemis.Core.Services; using Artemis.Core.Services;
using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services;
using SkiaSharp;
using Stylet; using Stylet;
namespace Artemis.UI.Screens.SurfaceEditor.Dialogs namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
@ -9,34 +10,54 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
public class SurfaceDeviceConfigViewModel : DialogViewModelBase public class SurfaceDeviceConfigViewModel : DialogViewModelBase
{ {
private readonly ICoreService _coreService; private readonly ICoreService _coreService;
private readonly double _initialRedScale;
private readonly double _initialGreenScale;
private readonly double _initialBlueScale;
private int _rotation; private int _rotation;
private double _scale; private double _scale;
private string _title;
private int _x; private int _x;
private int _y; private int _y;
private double _redScale;
private double _greenScale;
private double _blueScale;
private SKColor _currentColor;
private bool _displayOnDevices;
public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator<SurfaceDeviceConfigViewModel> validator) : base(validator) public SurfaceDeviceConfigViewModel(ArtemisDevice device, ICoreService coreService, IModelValidator<SurfaceDeviceConfigViewModel> validator) : base(validator)
{ {
_coreService = coreService; _coreService = coreService;
Device = device; Device = device;
Title = $"{Device.RgbDevice.DeviceInfo.DeviceName} - Properties";
X = (int) Device.X; X = (int)Device.X;
Y = (int) Device.Y; Y = (int)Device.Y;
Scale = Device.Scale; Scale = Device.Scale;
Rotation = (int) Device.Rotation; Rotation = (int)Device.Rotation;
RedScale = Device.RedScale * 100d;
GreenScale = Device.GreenScale * 100d;
BlueScale = Device.BlueScale * 100d;
//we need to store the initial values to be able to restore them when the user clicks "Cancel"
_initialRedScale = Device.RedScale;
_initialGreenScale = Device.GreenScale;
_initialBlueScale = Device.BlueScale;
CurrentColor = SKColors.White;
_coreService.FrameRendering += OnFrameRendering;
}
private void OnFrameRendering(object sender, FrameRenderingEventArgs e)
{
if (!_displayOnDevices)
return;
using SKPaint overlayPaint = new SKPaint
{
Color = CurrentColor
};
e.Canvas.DrawRect(0, 0, e.Canvas.LocalClipBounds.Width, e.Canvas.LocalClipBounds.Height, overlayPaint);
} }
public ArtemisDevice Device { get; } public ArtemisDevice Device { get; }
public string Title
{
get => _title;
set => SetAndNotify(ref _title, value);
}
public int X public int X
{ {
get => _x; get => _x;
@ -61,6 +82,36 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
set => SetAndNotify(ref _rotation, value); set => SetAndNotify(ref _rotation, value);
} }
public double RedScale
{
get => _redScale;
set => SetAndNotify(ref _redScale, value);
}
public double GreenScale
{
get => _greenScale;
set => SetAndNotify(ref _greenScale, value);
}
public double BlueScale
{
get => _blueScale;
set => SetAndNotify(ref _blueScale, value);
}
public SKColor CurrentColor
{
get => _currentColor;
set => SetAndNotify(ref _currentColor, value);
}
public bool DisplayOnDevices
{
get => _displayOnDevices;
set => SetAndNotify(ref _displayOnDevices, value);
}
public async Task Accept() public async Task Accept()
{ {
await ValidateAsync(); await ValidateAsync();
@ -74,10 +125,29 @@ namespace Artemis.UI.Screens.SurfaceEditor.Dialogs
Device.Y = Y; Device.Y = Y;
Device.Scale = Scale; Device.Scale = Scale;
Device.Rotation = Rotation; Device.Rotation = Rotation;
Device.RedScale = RedScale / 100d;
Device.GreenScale = GreenScale / 100d;
Device.BlueScale = BlueScale / 100d;
_coreService.ModuleRenderingDisabled = false; _coreService.ModuleRenderingDisabled = false;
_coreService.FrameRendering -= OnFrameRendering;
Session.Close(true); Session.Close(true);
} }
public void ApplyScaling()
{
Device.RedScale = RedScale / 100d;
Device.GreenScale = GreenScale / 100d;
Device.BlueScale = BlueScale / 100d;
}
public override void Cancel()
{
Device.RedScale = _initialRedScale;
Device.GreenScale = _initialGreenScale;
Device.BlueScale = _initialBlueScale;
base.Cancel();
}
} }
} }