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

Color brush - Added configurable gradient repeat

Color brush - Fixed gradient positioning in the clip render mode
Color brush - Added gradient tile modes in clip render mode
Layer properties - Allow disabling support of keyframes via the description decorator
This commit is contained in:
SpoinkyNL 2020-08-03 00:18:12 +02:00
parent aa7c914b92
commit b659be1f48
8 changed files with 86 additions and 19 deletions

View File

@ -1,7 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Windows.Documents;
using Artemis.Core.Annotations; using Artemis.Core.Annotations;
using SkiaSharp; using SkiaSharp;
using Stylet; using Stylet;
@ -30,14 +32,37 @@ namespace Artemis.Core.Models.Profile.Colors
} }
} }
public SKColor[] GetColorsArray() public SKColor[] GetColorsArray(int timesToRepeat = 0)
{ {
if (timesToRepeat == 0)
return Stops.OrderBy(c => c.Position).Select(c => c.Color).ToArray(); return Stops.OrderBy(c => c.Position).Select(c => c.Color).ToArray();
var colors = Stops.OrderBy(c => c.Position).Select(c => c.Color).ToList();
var result = new List<SKColor>();
for (var i = 0; i <= timesToRepeat; i++)
result.AddRange(colors);
return result.ToArray();
} }
public float[] GetPositionsArray() public float[] GetPositionsArray(int timesToRepeat = 0)
{ {
if (timesToRepeat == 0)
return Stops.OrderBy(c => c.Position).Select(c => c.Position).ToArray(); return Stops.OrderBy(c => c.Position).Select(c => c.Position).ToArray();
// Create stops and a list of divided stops
var stops = Stops.OrderBy(c => c.Position).Select(c => c.Position / (timesToRepeat + 1)).ToList();
var result = new List<float>();
// For each repeat cycle, add the base stops to the end result
for (var i = 0; i <= timesToRepeat; i++)
{
var localStops = stops.Select(s => s + result.LastOrDefault()).ToList();
result.AddRange(localStops);
}
return result.ToArray();
} }
public void OnColorValuesUpdated() public void OnColorValuesUpdated()

View File

@ -334,6 +334,7 @@ namespace Artemis.Core.Models.Profile
if (Parent is Folder parentFolder) if (Parent is Folder parentFolder)
targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location; targetLocation = Path.Bounds.Location - parentFolder.Path.Bounds.Location;
canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint); canvas.DrawBitmap(_layerBitmap, targetLocation, layerPaint);
} }

View File

@ -38,5 +38,10 @@ namespace Artemis.Core.Models.Profile.LayerProperties.Attributes
/// Maximum input value, only enforced in the UI /// Maximum input value, only enforced in the UI
/// </summary> /// </summary>
public object MaxInputValue { get; set; } public object MaxInputValue { get; set; }
/// <summary>
/// Whether or not keyframes are supported, true by default and cannot be changed at runtime
/// </summary>
public bool KeyframesSupported { get; set; } = true;
} }
} }

View File

@ -28,9 +28,9 @@ namespace Artemis.Core.Models.Profile.LayerProperties
public LayerPropertyGroup Parent { get; internal set; } public LayerPropertyGroup Parent { get; internal set; }
/// <summary> /// <summary>
/// Gets whether keyframes are supported on this property /// Gets whether keyframes are supported on this type of property
/// </summary> /// </summary>
public bool KeyframesSupported { get; protected set; } = true; public bool KeyframesSupported { get; protected internal set; } = true;
/// <summary> /// <summary>
/// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is /// Gets or sets whether keyframes are enabled on this property, has no effect if <see cref="KeyframesSupported" /> is

View File

@ -156,6 +156,7 @@ namespace Artemis.Core.Models.Profile
instance.ProfileElement = profileElement; instance.ProfileElement = profileElement;
instance.Parent = this; instance.Parent = this;
instance.PropertyDescription = (PropertyDescriptionAttribute) propertyDescription; instance.PropertyDescription = (PropertyDescriptionAttribute) propertyDescription;
instance.KeyframesSupported = instance.PropertyDescription.KeyframesSupported;
InitializeProperty(profileElement, path + propertyInfo.Name, instance); InitializeProperty(profileElement, path + propertyInfo.Name, instance);
propertyInfo.SetValue(this, instance); propertyInfo.SetValue(this, instance);

View File

@ -27,9 +27,10 @@ namespace Artemis.Core.Plugins.LayerBrush.Abstract
internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint) internal override void InternalRender(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
{ {
// This lil' snippet renders per LED, it's neater but doesn't support translations // We don't want translations on this canvas because that'll displace the LEDs, translations are applied to the points of each LED instead
Layer.ExcludeCanvasFromTranslation(canvas, true); Layer.ExcludeCanvasFromTranslation(canvas, true);
// Apply a translated version of the shape as the clipping mask
var shapePath = new SKPath(Layer.LayerShape.Path); var shapePath = new SKPath(Layer.LayerShape.Path);
Layer.IncludePathInTranslation(shapePath, true); Layer.IncludePathInTranslation(shapePath, true);
canvas.ClipPath(shapePath); canvas.ClipPath(shapePath);

View File

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using Artemis.Core.Models.Profile;
using Artemis.Core.Plugins.LayerBrush.Abstract; using Artemis.Core.Plugins.LayerBrush.Abstract;
using SkiaSharp; using SkiaSharp;
@ -17,6 +18,8 @@ namespace Artemis.Plugins.LayerBrushes.Color
Layer.RenderPropertiesUpdated += HandleShaderChange; Layer.RenderPropertiesUpdated += HandleShaderChange;
Properties.GradientType.BaseValueChanged += HandleShaderChange; Properties.GradientType.BaseValueChanged += HandleShaderChange;
Properties.Color.BaseValueChanged += HandleShaderChange; Properties.Color.BaseValueChanged += HandleShaderChange;
Properties.GradientTileMode.BaseValueChanged += HandleShaderChange;
Properties.GradientRepeat.BaseValueChanged += HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged; Properties.Gradient.BaseValue.PropertyChanged += BaseValueOnPropertyChanged;
} }
@ -25,6 +28,8 @@ namespace Artemis.Plugins.LayerBrushes.Color
Layer.RenderPropertiesUpdated -= HandleShaderChange; Layer.RenderPropertiesUpdated -= HandleShaderChange;
Properties.GradientType.BaseValueChanged -= HandleShaderChange; Properties.GradientType.BaseValueChanged -= HandleShaderChange;
Properties.Color.BaseValueChanged -= HandleShaderChange; Properties.Color.BaseValueChanged -= HandleShaderChange;
Properties.GradientTileMode.BaseValueChanged -= HandleShaderChange;
Properties.GradientRepeat.BaseValueChanged -= HandleShaderChange;
Properties.Gradient.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged; Properties.Gradient.BaseValue.PropertyChanged -= BaseValueOnPropertyChanged;
_paint?.Dispose(); _paint?.Dispose();
@ -45,12 +50,24 @@ namespace Artemis.Plugins.LayerBrushes.Color
} }
public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint) public override void Render(SKCanvas canvas, SKImageInfo canvasInfo, SKPath path, SKPaint paint)
{
if (Layer.General.FillType.CurrentValue == LayerFillType.Clip)
{
var layerBounds = new SKRect(0, 0, Layer.Bounds.Width, Layer.Bounds.Height);
if (layerBounds != _shaderBounds)
{
_shaderBounds = layerBounds;
CreateShader();
}
}
else
{ {
if (path.Bounds != _shaderBounds) if (path.Bounds != _shaderBounds)
{ {
_shaderBounds = path.Bounds; _shaderBounds = path.Bounds;
CreateShader(); CreateShader();
} }
}
paint.Shader = _shader; paint.Shader = _shader;
canvas.DrawPath(path, paint); canvas.DrawPath(path, paint);
@ -69,26 +86,27 @@ namespace Artemis.Plugins.LayerBrushes.Color
private void CreateShader() private void CreateShader()
{ {
var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY); var center = new SKPoint(_shaderBounds.MidX, _shaderBounds.MidY);
var repeat = Properties.GradientRepeat.CurrentValue;
var shader = Properties.GradientType.CurrentValue switch var shader = Properties.GradientType.CurrentValue switch
{ {
GradientType.Solid => SKShader.CreateColor(_color), GradientType.Solid => SKShader.CreateColor(_color),
GradientType.LinearGradient => SKShader.CreateLinearGradient( GradientType.LinearGradient => SKShader.CreateLinearGradient(
new SKPoint(_shaderBounds.Left, _shaderBounds.Top), new SKPoint(_shaderBounds.Left, _shaderBounds.Top),
new SKPoint(_shaderBounds.Right, _shaderBounds.Top), new SKPoint(_shaderBounds.Right, _shaderBounds.Top),
Properties.Gradient.BaseValue.GetColorsArray(), Properties.Gradient.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
SKShaderTileMode.Clamp), Properties.GradientTileMode.CurrentValue),
GradientType.RadialGradient => SKShader.CreateRadialGradient( GradientType.RadialGradient => SKShader.CreateRadialGradient(
center, center,
Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f, Math.Max(_shaderBounds.Width, _shaderBounds.Height) / 2f,
Properties.Gradient.BaseValue.GetColorsArray(), Properties.Gradient.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
SKShaderTileMode.Clamp), Properties.GradientTileMode.CurrentValue),
GradientType.SweepGradient => SKShader.CreateSweepGradient( GradientType.SweepGradient => SKShader.CreateSweepGradient(
center, center,
Properties.Gradient.BaseValue.GetColorsArray(), Properties.Gradient.BaseValue.GetColorsArray(repeat),
Properties.Gradient.BaseValue.GetPositionsArray(), Properties.Gradient.BaseValue.GetPositionsArray(repeat),
SKShaderTileMode.Clamp, Properties.GradientTileMode.CurrentValue,
0, 0,
360), 360),
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()

View File

@ -12,22 +12,32 @@ namespace Artemis.Plugins.LayerBrushes.Color
[PropertyDescription(Description = "The type of color brush to draw")] [PropertyDescription(Description = "The type of color brush to draw")]
public EnumLayerProperty<GradientType> GradientType { get; set; } public EnumLayerProperty<GradientType> GradientType { get; set; }
[PropertyDescription(Description = "How to handle the layer having to stretch beyond it's regular size")]
public EnumLayerProperty<SKShaderTileMode> GradientTileMode { get; set; }
[PropertyDescription(Description = "The color of the brush")] [PropertyDescription(Description = "The color of the brush")]
public SKColorLayerProperty Color { get; set; } public SKColorLayerProperty Color { get; set; }
[PropertyDescription(Description = "The gradient of the brush")] [PropertyDescription(Description = "The gradient of the brush")]
public ColorGradientLayerProperty Gradient { get; set; } public ColorGradientLayerProperty Gradient { get; set; }
[PropertyDescription(KeyframesSupported = false, Description = "How many times to repeat the colors in the selected gradient", MinInputValue = 0, MaxInputValue = 10)]
public IntLayerProperty GradientRepeat { get; set; }
protected override void PopulateDefaults() protected override void PopulateDefaults()
{ {
GradientType.DefaultValue = LayerBrushes.Color.GradientType.Solid; GradientType.DefaultValue = LayerBrushes.Color.GradientType.Solid;
Color.DefaultValue = new SKColor(255, 0, 0); Color.DefaultValue = new SKColor(255, 0, 0);
Gradient.DefaultValue = ColorGradient.GetUnicornBarf(); Gradient.DefaultValue = ColorGradient.GetUnicornBarf();
GradientRepeat.DefaultValue = 0;
} }
protected override void OnPropertiesInitialized() protected override void OnPropertiesInitialized()
{ {
GradientType.BaseValueChanged += (sender, args) => UpdateVisibility(); GradientType.BaseValueChanged += (sender, args) => UpdateVisibility();
if (ProfileElement is Layer layer)
layer.General.FillType.BaseValueChanged += (sender, args) => UpdateVisibility();
UpdateVisibility(); UpdateVisibility();
} }
@ -35,6 +45,12 @@ namespace Artemis.Plugins.LayerBrushes.Color
{ {
Color.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.Solid; Color.IsHidden = GradientType.BaseValue != LayerBrushes.Color.GradientType.Solid;
Gradient.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid; Gradient.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid;
GradientRepeat.IsHidden = GradientType.BaseValue == LayerBrushes.Color.GradientType.Solid;
if (ProfileElement is Layer layer)
GradientTileMode.IsHidden = layer.General.FillType.CurrentValue != LayerFillType.Clip;
else
GradientTileMode.IsHidden = true;
} }
} }