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

Merge pull request #280 from DarthAffe/development

Angular Brush
This commit is contained in:
Robert Beekman 2017-01-14 18:08:02 +01:00 committed by GitHub
commit 80cd620247
8 changed files with 522 additions and 17 deletions

View File

@ -56,6 +56,7 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
@ -65,6 +66,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>EAC088BE27A2DE790AE6F37A020409F4A1B5EC0E</ManifestCertificateThumbprint>
@ -491,6 +493,13 @@
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\DX9ScreenCapture.cs" />
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\IScreenCapture.cs" />
<Compile Include="Profiles\Layers\Types\AmbientLight\ScreenCapturing\ScreenCaptureManager.cs" />
<Compile Include="Profiles\Layers\Types\AngularBrush\AngularBrushPropertiesModel.cs" />
<Compile Include="Profiles\Layers\Types\AngularBrush\AngularBrushPropertiesView.xaml.cs">
<DependentUpon>AngularBrushPropertiesView.xaml</DependentUpon>
</Compile>
<Compile Include="Profiles\Layers\Types\AngularBrush\AngularBrushPropertiesViewModel.cs" />
<Compile Include="Profiles\Layers\Types\AngularBrush\AngularBrushType.cs" />
<Compile Include="Profiles\Layers\Types\AngularBrush\Drawing\GradientDrawer.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\AudioCapture.cs" />
<Compile Include="Profiles\Layers\Types\Audio\AudioCapturing\AudioCaptureManager.cs" />
<Compile Include="Events\AudioDeviceChangedEventArgs.cs" />
@ -862,6 +871,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Profiles\Layers\Types\AngularBrush\AngularBrushPropertiesView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Profiles\Layers\Types\Audio\AudioPropertiesView.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

View File

@ -3,13 +3,10 @@ using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Windows.Media;
using Artemis.DeviceProviders;
using Artemis.ViewModels;
using Ninject.Extensions.Logging;
using Color = System.Drawing.Color;
using Timer = System.Timers.Timer;
namespace Artemis.Managers
{
@ -21,7 +18,8 @@ namespace Artemis.Managers
private readonly DebugViewModel _debugViewModel;
private readonly DeviceManager _deviceManager;
private readonly ILogger _logger;
private readonly Timer _loopTimer;
//private readonly Timer _loopTimer;
private readonly Task _loopTask;
private readonly ModuleManager _moduleManager;
public LoopManager(ILogger logger, ModuleManager moduleManager, DeviceManager deviceManager,
@ -33,10 +31,10 @@ namespace Artemis.Managers
_debugViewModel = debugViewModel;
// Setup timers
_loopTimer = new Timer(40);
_loopTimer.Elapsed += LoopTimerOnElapsed;
_loopTimer.Start();
//_loopTimer = new Timer(40);
//_loopTimer.Elapsed += LoopTimerOnElapsed;
//_loopTimer.Start();
_loopTask = Task.Factory.StartNew(ProcessLoop);
_logger.Info("Intialized LoopManager");
}
@ -49,22 +47,45 @@ namespace Artemis.Managers
public void Dispose()
{
_loopTimer.Stop();
_loopTimer.Dispose();
_loopTask.Dispose();
//_loopTimer.Stop();
//_loopTimer.Dispose();
}
private void LoopTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
private void ProcessLoop()
{
try
//TODO DarthAffe 14.01.2017: A stop-condition and a real cleanup instead of just aborting might be better
while (true)
{
Render();
}
catch (Exception e)
{
_logger.Warn(e, "Exception in render loop");
try
{
long preUpdateTicks = DateTime.Now.Ticks;
Render();
int sleep = (int)(40f - ((DateTime.Now.Ticks - preUpdateTicks) / 10000f));
if (sleep > 0)
Thread.Sleep(sleep);
}
catch (Exception e)
{
_logger.Warn(e, "Exception in render loop");
}
}
}
//private void LoopTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
//{
// try
// {
// Render();
// }
// catch (Exception e)
// {
// _logger.Warn(e, "Exception in render loop");
// }
//}
public Task StartAsync()
{
return Task.Run(() => Start());

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Windows.Media;
using Artemis.Profiles.Layers.Models;
namespace Artemis.Profiles.Layers.Types.AngularBrush
{
public class AngularBrushPropertiesModel : LayerPropertiesModel
{
#region Properties & Fields
public IList<Tuple<double, Color>> GradientStops { get; set; }
#endregion
#region Constructors
public AngularBrushPropertiesModel(LayerPropertiesModel properties = null)
: base(properties)
{ }
#endregion
#region Methods
#endregion
}
}

View File

@ -0,0 +1,80 @@
<UserControl x:Class="Artemis.Profiles.Layers.Types.AngularBrush.AngularBrushPropertiesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:ncore="http://schemas.ncore.com/wpf/xaml/colorbox"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<!-- Colors -->
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<!-- Animation -->
<TextBlock Grid.Row="0" Grid.Column="0" Margin="10" FontSize="13.333" Text="Animation:"
VerticalAlignment="Center"
Height="18" />
<ComboBox Grid.Row="0" Grid.Column="1" Margin="10,10,10,0" x:Name="LayerAnimations" VerticalAlignment="Top"
Height="22">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name, Mode=OneWay}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Animation Speed -->
<TextBlock Grid.Row="0" Grid.Column="2" Margin="10" FontSize="13.333" Text="Animation speed:"
VerticalAlignment="Center" Height="18" />
<Slider x:Name="RotationSpeed" Grid.Row="0" Grid.Column="3" VerticalAlignment="Center"
TickPlacement="None" TickFrequency="0.05"
Value="{Binding Path=LayerModel.Properties.AnimationSpeed, Mode=TwoWay}" Minimum="0.05" Maximum="3"
SmallChange="0" IsSnapToTickEnabled="True" Margin="10,12,10,2" Height="24" />
<!-- ClippingType -->
<TextBlock Grid.Row="1" Grid.Column="0" Margin="10, 13, 10, 10" FontSize="13.333" Text="Clipping type:"
VerticalAlignment="Center" Height="23" />
<controls:ToggleSwitch IsChecked="{Binding Path=LayerModel.Properties.Contain, Mode=TwoWay}"
Grid.Row="1"
Grid.Column="1" OnLabel="Contain" OffLabel="Cut-off" Margin="10,1,5,1"
VerticalAlignment="Center"
Height="36" />
<!-- Colors -->
<StackPanel Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Orientation="Horizontal" x:Name="ShowBrush">
<TextBlock Margin="10,13,10,0" FontSize="13.333" Text="Color(s):"
VerticalAlignment="Top" Height="18" Width="130" />
<Border Margin="10" BorderBrush="{StaticResource ControlBorderBrush}"
BorderThickness="1" SnapsToDevicePixels="True" ToolTip="Click to edit">
<ncore:ColorBox Brush="{Binding Brush, Mode=TwoWay}" ShowNone="False" Height="24" Width="134"
VerticalAlignment="Top" ShowLinear="True" ShowRadial="False" ShowSolid="False" />
</Border>
</StackPanel>
<!-- Dynamic -->
<Label Grid.Row="3" Grid.Column="0" FontSize="20" HorizontalAlignment="Left"
Content="Dynamic" Width="97" VerticalAlignment="Bottom" />
<!-- Dynamic property views -->
<ContentControl Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="4" x:Name="HeightProperties" />
<ContentControl Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="4" x:Name="WidthProperties" />
<ContentControl Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="4" x:Name="OpacityProperties" />
<ContentControl Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="4" x:Name="LayerTweenViewModel" />
</Grid>
</UserControl>

View File

@ -0,0 +1,12 @@
using System.Windows.Controls;
namespace Artemis.Profiles.Layers.Types.AngularBrush
{
public partial class AngularBrushPropertiesView : UserControl
{
public AngularBrushPropertiesView()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Linq;
using System.Windows.Media;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Utilities;
using Artemis.ViewModels;
using Artemis.ViewModels.Profiles;
using Caliburn.Micro;
namespace Artemis.Profiles.Layers.Types.AngularBrush
{
public class AngularBrushPropertiesViewModel : LayerPropertiesViewModel
{
#region Properties & Fields
private ILayerAnimation _selectedLayerAnimation;
public BindableCollection<GeneralHelpers.PropertyCollection> DataModelProps { get; set; }
public BindableCollection<ILayerAnimation> LayerAnimations { get; set; }
public LayerDynamicPropertiesViewModel HeightProperties { get; set; }
public LayerDynamicPropertiesViewModel WidthProperties { get; set; }
public LayerDynamicPropertiesViewModel OpacityProperties { get; set; }
public LayerTweenViewModel LayerTweenViewModel { get; set; }
public ILayerAnimation SelectedLayerAnimation
{
get { return _selectedLayerAnimation; }
set
{
if (Equals(value, _selectedLayerAnimation)) return;
_selectedLayerAnimation = value;
NotifyOfPropertyChange(() => SelectedLayerAnimation);
}
}
#endregion
#region Constructors
public AngularBrushPropertiesViewModel(LayerEditorViewModel editorVm)
: base(editorVm)
{
LayerAnimations = new BindableCollection<ILayerAnimation>(editorVm.LayerAnimations);
HeightProperties = new LayerDynamicPropertiesViewModel("Height", editorVm);
WidthProperties = new LayerDynamicPropertiesViewModel("Width", editorVm);
OpacityProperties = new LayerDynamicPropertiesViewModel("Opacity", editorVm);
LayerTweenViewModel = new LayerTweenViewModel(editorVm);
SelectedLayerAnimation =
LayerAnimations.FirstOrDefault(l => l.Name == editorVm.ProposedLayer.LayerAnimation?.Name) ??
LayerAnimations.First(l => l.Name == "None");
}
#endregion
#region Methods
public override void ApplyProperties()
{
HeightProperties.Apply(LayerModel);
WidthProperties.Apply(LayerModel);
OpacityProperties.Apply(LayerModel);
LayerModel.Properties.Brush = Brush;
LayerModel.LayerAnimation = SelectedLayerAnimation;
}
#endregion
}
}

View File

@ -0,0 +1,130 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using Artemis.Modules.Abstract;
using Artemis.Profiles.Layers.Abstract;
using Artemis.Profiles.Layers.Animations;
using Artemis.Profiles.Layers.Interfaces;
using Artemis.Profiles.Layers.Models;
using Artemis.Profiles.Layers.Types.AngularBrush.Drawing;
using Artemis.ViewModels;
namespace Artemis.Profiles.Layers.Types.AngularBrush
{
public class AngularBrushType : ILayerType
{
#region Properties & Fields
private GradientDrawer _gradientDrawer;
public string Name => "Angular Brush";
public bool ShowInEdtor => true;
public DrawType DrawType => DrawType.Keyboard;
#endregion
public AngularBrushType()
{
_gradientDrawer = new GradientDrawer();
}
#region Methods
public ImageSource DrawThumbnail(LayerModel layer)
{
//TODO DarthAffe 14.01.2017: This could be replaced with the real brush but it complaints about the thread too
Rect thumbnailRect = new Rect(0, 0, 18, 18);
DrawingVisual visual = new DrawingVisual();
using (DrawingContext c = visual.RenderOpen())
if (layer.Properties.Brush != null)
c.DrawRectangle(layer.Properties.Brush,
new Pen(new SolidColorBrush(Colors.White), 1),
thumbnailRect);
DrawingImage image = new DrawingImage(visual.Drawing);
return image;
}
public void Draw(LayerModel layerModel, DrawingContext c)
{
AngularBrushPropertiesModel properties = layerModel.Properties as AngularBrushPropertiesModel;
if (properties == null) return;
Brush origBrush = layerModel.Brush;
//TODO DarthAffe 14.01.2017: Check if an update is needed
_gradientDrawer.GradientStops = GetGradientStops(layerModel.Brush).Select(x => new Tuple<double, Color>(x.Offset, x.Color)).ToList();
_gradientDrawer.Update();
layerModel.Brush = _gradientDrawer.Brush.Clone();
// If an animation is present, let it handle the drawing
if (layerModel.LayerAnimation != null && !(layerModel.LayerAnimation is NoneAnimation))
{
layerModel.LayerAnimation.Draw(layerModel, c);
return;
}
// Otherwise draw the rectangle with its layer.AppliedProperties dimensions and brush
Rect rect = layerModel.Properties.Contain
? layerModel.LayerRect()
: new Rect(layerModel.Properties.X * 4, layerModel.Properties.Y * 4,
layerModel.Properties.Width * 4, layerModel.Properties.Height * 4);
Rect clip = layerModel.LayerRect();
// Can't meddle with the original brush because it's frozen.
Brush brush = layerModel.Brush.Clone();
brush.Opacity = layerModel.Opacity;
c.PushClip(new RectangleGeometry(clip));
c.DrawRectangle(brush, null, rect);
c.Pop();
layerModel.Brush = origBrush;
}
public void Update(LayerModel layerModel, ModuleDataModel dataModel, bool isPreview = false)
{
layerModel.ApplyProperties(true);
if (isPreview || dataModel == null)
return;
// If not previewing, apply dynamic properties according to datamodel
foreach (DynamicPropertiesModel dynamicProperty in layerModel.Properties.DynamicProperties)
dynamicProperty.ApplyProperty(dataModel, layerModel);
}
public void SetupProperties(LayerModel layerModel)
{
if (layerModel.Properties is AngularBrushPropertiesModel)
return;
layerModel.Properties = new AngularBrushPropertiesModel(layerModel.Properties);
}
public LayerPropertiesViewModel SetupViewModel(LayerEditorViewModel layerEditorViewModel, LayerPropertiesViewModel layerPropertiesViewModel)
{
return (layerPropertiesViewModel as AngularBrushPropertiesViewModel) ?? new AngularBrushPropertiesViewModel(layerEditorViewModel);
}
private GradientStopCollection GetGradientStops(Brush brush)
{
LinearGradientBrush linearBrush = brush as LinearGradientBrush;
if (linearBrush != null)
return linearBrush.GradientStops;
RadialGradientBrush radialBrush = brush as RadialGradientBrush;
if (radialBrush != null)
return radialBrush.GradientStops;
SolidColorBrush solidBrush = brush as SolidColorBrush;
if (solidBrush != null)
return new GradientStopCollection(new[] { new GradientStop(solidBrush.Color, 0), new GradientStop(solidBrush.Color, 1) });
return null;
}
#endregion
}
}

View File

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Artemis.Profiles.Layers.Types.AngularBrush.Drawing
{
public class GradientDrawer
{
#region Constants
private static readonly double ORIGIN = Math.Atan2(-1, 0);
#endregion
#region Properties & Fields
private WriteableBitmap _bitmap;
private IList<Tuple<double, Color>> _gradientStops;
public IList<Tuple<double, Color>> GradientStops
{
set { _gradientStops = FixGradientStops(value); }
}
public Brush Brush { get; private set; }
#endregion
#region Methods
public void Update()
{
if (_bitmap == null)
CreateBrush();
unsafe
{
_bitmap.Lock();
byte* buffer = (byte*)_bitmap.BackBuffer.ToPointer();
int width = _bitmap.PixelWidth;
double widthHalf = width / 2.0;
int height = _bitmap.PixelHeight;
double heightHalf = height / 2.0;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
int offset = (((y * width) + x) * 4);
double gradientOffset = CalculateGradientOffset(x, y, widthHalf, heightHalf);
GetColor(_gradientStops, gradientOffset,
ref buffer[offset + 3], ref buffer[offset + 2],
ref buffer[offset + 1], ref buffer[offset]);
}
_bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height));
_bitmap.Unlock();
}
}
private void CreateBrush()
{
_bitmap = new WriteableBitmap(100, 100, 96, 96, PixelFormats.Bgra32, null);
Brush = new ImageBrush(_bitmap) { Stretch = Stretch.UniformToFill };
}
private double CalculateGradientOffset(double x, double y, double centerX, double centerY)
{
double angle = Math.Atan2(y - centerY, x - centerX) - ORIGIN;
if (angle < 0) angle += Math.PI * 2;
return angle / (Math.PI * 2);
}
private static void GetColor(IList<Tuple<double, Color>> gradientStops, double offset, ref byte colA, ref byte colR, ref byte colG, ref byte colB)
{
if (gradientStops.Count == 0)
{
colA = 0;
colR = 0;
colG = 0;
colB = 0;
return;
}
if (gradientStops.Count == 1)
{
Color color = gradientStops.First().Item2;
colA = color.A;
colR = color.R;
colG = color.G;
colB = color.B;
return;
}
Tuple<double, Color> beforeStop = null;
double afterOffset = -1;
Color afterColor = default(Color);
for (int i = 0; i < gradientStops.Count; i++)
{
Tuple<double, Color> gradientStop = gradientStops[i];
double o = gradientStop.Item1;
if (o <= offset)
beforeStop = gradientStop;
if (o >= offset)
{
afterOffset = gradientStop.Item1;
afterColor = gradientStop.Item2;
break;
}
}
double beforeOffset = beforeStop.Item1;
Color beforeColor = beforeStop.Item2;
double blendFactor = 0f;
if (beforeOffset != afterOffset)
blendFactor = ((offset - beforeOffset) / (afterOffset - beforeOffset));
colA = (byte)((afterColor.A - beforeColor.A) * blendFactor + beforeColor.A);
colR = (byte)((afterColor.R - beforeColor.R) * blendFactor + beforeColor.R);
colG = (byte)((afterColor.G - beforeColor.G) * blendFactor + beforeColor.G);
colB = (byte)((afterColor.B - beforeColor.B) * blendFactor + beforeColor.B);
}
private IList<Tuple<double, Color>> FixGradientStops(IList<Tuple<double, Color>> gradientStops)
{
if (gradientStops == null) return new List<Tuple<double, Color>>();
List<Tuple<double, Color>> stops = gradientStops.OrderBy(x => x.Item1).ToList();
Tuple<double, Color> firstStop = stops.First();
if (firstStop.Item1 > 0)
stops.Insert(0, new Tuple<double, Color>(0, firstStop.Item2));
Tuple<double, Color> lastStop = stops.Last();
if (lastStop.Item1 < 1)
stops.Add(new Tuple<double, Color>(1, lastStop.Item2));
return stops;
}
#endregion
}
}