diff --git a/src/Artemis.Core/Models/Profile/Layer.cs b/src/Artemis.Core/Models/Profile/Layer.cs index cc8affc16..35ef188ed 100644 --- a/src/Artemis.Core/Models/Profile/Layer.cs +++ b/src/Artemis.Core/Models/Profile/Layer.cs @@ -50,6 +50,7 @@ namespace Artemis.Core.Models.Profile public ReadOnlyCollection LayerElements => _layerElements.AsReadOnly(); public SKRect RenderRectangle { get; set; } + public SKRect AbsoluteRenderRectangle { get; set; } public SKPath RenderPath { get; set; } public override void Update(double deltaTime) @@ -60,17 +61,40 @@ namespace Artemis.Core.Models.Profile public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas) { + if (RenderRectangle == null || AbsoluteRenderRectangle == null || RenderPath == null) + return; + canvas.Save(); - canvas.ClipPath(RenderPath); foreach (var layerElement in LayerElements) layerElement.RenderPreProcess(surface, canvas); - foreach (var layerElement in LayerElements) - layerElement.Render(surface, canvas); + using (var bitmap = new SKBitmap(new SKImageInfo((int) RenderRectangle.Width, (int) RenderRectangle.Height))) + using (var layerCanvas = new SKCanvas(bitmap)) + { + layerCanvas.Clear(); - foreach (var layerElement in LayerElements) - layerElement.RenderPostProcess(surface, canvas); + foreach (var layerElement in LayerElements) + layerElement.Render(surface, layerCanvas); + + var baseShader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat); + foreach (var layerElement in LayerElements) + { + var newBaseShader = layerElement.RenderPostProcess(surface, bitmap, baseShader); + if (newBaseShader == null) + continue; + + // Dispose the old base shader if the layer element provided a new one + if (!ReferenceEquals(baseShader, newBaseShader)) + baseShader.Dispose(); + + baseShader = newBaseShader; + } + + canvas.ClipPath(RenderPath); + canvas.DrawRect(RenderRectangle, new SKPaint {Shader = baseShader, FilterQuality = SKFilterQuality.Low}); + baseShader.Dispose(); + } canvas.Restore(); } @@ -169,21 +193,34 @@ namespace Artemis.Core.Models.Profile // Determine to top-left and bottom-right var minX = Leds.Min(l => l.AbsoluteRenderRectangle.Left); var minY = Leds.Min(l => l.AbsoluteRenderRectangle.Top); - var maxX = Leds.Max(l => l.AbsoluteRenderRectangle.Bottom); - var maxY = Leds.Max(l => l.AbsoluteRenderRectangle.Right); + var maxX = Leds.Max(l => l.AbsoluteRenderRectangle.Right); + var maxY = Leds.Max(l => l.AbsoluteRenderRectangle.Bottom); RenderRectangle = SKRect.Create(minX, minY, maxX - minX, maxY - minY); + AbsoluteRenderRectangle = SKRect.Create(0, 0, maxX - minX, maxY - minY); var path = new SKPath {FillType = SKPathFillType.Winding}; foreach (var artemisLed in Leds) path.AddRect(artemisLed.AbsoluteRenderRectangle); RenderPath = path; + OnRenderPropertiesUpdated(); } public override string ToString() { return $"Layer - {nameof(Name)}: {Name}, {nameof(Order)}: {Order}"; } + + #region Events + + public event EventHandler RenderPropertiesUpdated; + + private void OnRenderPropertiesUpdated() + { + RenderPropertiesUpdated?.Invoke(this, EventArgs.Empty); + } + + #endregion } } \ No newline at end of file diff --git a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs index a529a8b7d..2ec7a5d41 100644 --- a/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs +++ b/src/Artemis.Core/Plugins/LayerElement/LayerElement.cs @@ -27,21 +27,43 @@ namespace Artemis.Core.Plugins.LayerElement /// Called before rendering every frame, write your update logic here /// /// - public abstract void Update(double deltaTime); + public virtual void Update(double deltaTime) + { + } /// - /// Called before rendering, in the order configured on the layer + /// Allows you to perform rendering on the surface before any layer-clipping is applied + /// Called before rendering, in the order configured on the layer /// - public abstract void RenderPreProcess(ArtemisSurface surface, SKCanvas canvas); + /// The surface the layer is rendered on + /// The entire surface canvas + public virtual void RenderPreProcess(ArtemisSurface surface, SKCanvas canvas) + { + } /// - /// Called during rendering, in the order configured on the layer + /// The main method of rendering anything to the layer. The provided is specific to the layer + /// and matches it's width and height. + /// Called during rendering, in the order configured on the layer /// - public abstract void Render(ArtemisSurface surface, SKCanvas canvas); + /// The surface the layer is rendered on + /// The layer canvas + public virtual void Render(ArtemisSurface surface, SKCanvas canvas) + { + } /// - /// Called after rendering, in the order configured on the layer + /// Allows you to modify the used to draw the layer's on the + /// . + /// Called after rendering, in the order configured on the layer. /// - public abstract void RenderPostProcess(ArtemisSurface surface, SKCanvas canvas); + /// The surface the layer is rendered on + /// The bitmap created from the layer canvas + /// The current shader used to draw the bitmap on the surface canvas + /// The resulting shader used to draw the bitmap on the surface canvas + public virtual SKShader RenderPostProcess(ArtemisSurface surface, SKBitmap bitmap, SKShader shader) + { + return shader; + } } } \ No newline at end of file diff --git a/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj b/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj index daf7ce136..7ef80e783 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj +++ b/src/Artemis.Plugins.LayerTypes.Brush/Artemis.Plugins.LayerElements.Brush.csproj @@ -52,6 +52,7 @@ ..\packages\SkiaSharp.1.68.1\lib\net45\SkiaSharp.dll + False ..\packages\Stylet.1.3.0\lib\net45\Stylet.dll diff --git a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs index d0569be56..0ef34561a 100644 --- a/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs +++ b/src/Artemis.Plugins.LayerTypes.Brush/BrushLayerElement.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Models.Profile; +using System.Collections.Generic; +using Artemis.Core.Models.Profile; using Artemis.Core.Models.Surface; using Artemis.Core.Plugins.LayerElement; using SkiaSharp; @@ -7,9 +8,39 @@ namespace Artemis.Plugins.LayerElements.Brush { public class BrushLayerElement : LayerElement { + private SKShader _shader; + private List _testColors; + public BrushLayerElement(Layer layer, BrushLayerElementSettings settings, LayerElementDescriptor descriptor) : base(layer, settings, descriptor) { Settings = settings ?? new BrushLayerElementSettings(); + + _testColors = new List(); + for (var i = 0; i < 9; i++) + { + if (i != 8) + _testColors.Add(SKColor.FromHsv(i * 32, 100, 100)); + else + _testColors.Add(SKColor.FromHsv(0, 100, 100)); + } + + CreateShader(); + Layer.RenderPropertiesUpdated += (sender, args) => CreateShader(); + } + + private void CreateShader() + { + var shader = SKShader.CreateLinearGradient( + new SKPoint(0, 0), + new SKPoint(Layer.AbsoluteRenderRectangle.Width, 0), + _testColors.ToArray(), + null, + SKShaderTileMode.Clamp); + + var oldShader = _shader; + _shader = shader; + + oldShader?.Dispose(); } public new BrushLayerElementSettings Settings { get; } @@ -19,21 +50,12 @@ namespace Artemis.Plugins.LayerElements.Brush return new BrushLayerElementViewModel(this); } - public override void Update(double deltaTime) - { - } - - public override void RenderPreProcess(ArtemisSurface surface, SKCanvas canvas) - { - } - public override void Render(ArtemisSurface surface, SKCanvas canvas) { - canvas.DrawRect(Layer.RenderRectangle, new SKPaint {Color = new SKColor(255, 255, 255, 255)}); - } - - public override void RenderPostProcess(ArtemisSurface surface, SKCanvas canvas) - { + using (var paint = new SKPaint { Shader = _shader, FilterQuality = SKFilterQuality.Low }) + { + canvas.DrawRect(Layer.AbsoluteRenderRectangle, paint); + } } } } \ No newline at end of file diff --git a/src/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj b/src/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj index d7fbc09be..af8d5a9fa 100644 --- a/src/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj +++ b/src/Artemis.Plugins.Modules.General/Artemis.Plugins.Modules.General.csproj @@ -46,6 +46,7 @@ ..\packages\SkiaSharp.1.68.1\lib\net45\SkiaSharp.dll + False ..\packages\Stylet.1.3.0\lib\net45\Stylet.dll diff --git a/src/Artemis.Plugins.Modules.General/GeneralModule.cs b/src/Artemis.Plugins.Modules.General/GeneralModule.cs index 565d7612a..c732dfd12 100644 --- a/src/Artemis.Plugins.Modules.General/GeneralModule.cs +++ b/src/Artemis.Plugins.Modules.General/GeneralModule.cs @@ -59,6 +59,9 @@ namespace Artemis.Plugins.Modules.General public override void Render(double deltaTime, ArtemisSurface surface, SKCanvas canvas) { + base.Render(deltaTime, surface, canvas); + return; + foreach (var device in surface.Devices) { using (var bitmap = new SKBitmap(new SKImageInfo((int) device.RenderRectangle.Width, (int) device.RenderRectangle.Height))) @@ -66,7 +69,7 @@ namespace Artemis.Plugins.Modules.General using (var layerCanvas = new SKCanvas(bitmap)) { layerCanvas.Clear(); - SKShader shader = null; + SKShader shader; if (DeviceShaders.ContainsKey(device)) shader = DeviceShaders[device]; else @@ -80,18 +83,19 @@ namespace Artemis.Plugins.Modules.General DeviceShaders.Add(device, shader); } - layerCanvas.DrawRect(0, 0, device.RenderRectangle.Width, device.RenderRectangle.Height, new SKPaint {Shader = shader}); + using (var paint = new SKPaint {Shader = shader, FilterQuality = SKFilterQuality.Low}) + { + layerCanvas.DrawRect(0, 0, device.RenderRectangle.Width, device.RenderRectangle.Height, paint); + } } - var bitmapShader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat); - - canvas.Save(); - canvas.ClipRect(device.RenderRectangle); - - var scaledRect = SKRect.Create(device.RenderRectangle.Left, device.RenderRectangle.Top, device.RenderRectangle.Width * 2, device.RenderRectangle.Height * 2); - canvas.Translate(device.RenderRectangle.Width / 100 * MovePercentage * -1, 0); - canvas.DrawRect(scaledRect, new SKPaint {Shader = bitmapShader}); - canvas.Restore(); + using (var bitmapShader = SKShader.CreateBitmap(bitmap, SKShaderTileMode.Repeat, SKShaderTileMode.Repeat)) + using (var translated = SKShader.CreateLocalMatrix(bitmapShader, SKMatrix.MakeTranslation(device.RenderRectangle.Width / 100 * MovePercentage * -1, 0))) + using (var translatedPaint = new SKPaint {Shader = translated, FilterQuality = SKFilterQuality.Low}) + { + // Here we can let each module modify the shader as needed + canvas.DrawRect(device.RenderRectangle, translatedPaint); + } } } }