Workshop - Layout submission WIP

This commit is contained in:
Robert committed 2023-10-21 20:19:15 +02:00
1 parent 190d797f1a
commit c77d51fb58
9 files changed
+364 -58

No files matched your search

@@ -1,12 +1,11 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Shared.Events;
using Artemis.UI.Shared.Extensions;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
@@ -36,7 +35,7 @@ public class DeviceVisualizer : Control
private ArtemisDevice? _oldDevice;
private bool _loading;
private Color[] _previousState = Array.Empty<Color>();
/// <inheritdoc />
public DeviceVisualizer()
{
@@ -69,11 +68,7 @@ public class DeviceVisualizer : Control
// Render device and LED images
if (_deviceImage != null)
drawingContext.DrawImage(
_deviceImage,
new Rect(_deviceImage.Size),
new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height)
);
drawingContext.DrawImage(_deviceImage, new Rect(_deviceImage.Size), new Rect(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height));
if (!ShowColors)
return;
@@ -163,7 +158,7 @@ public class DeviceVisualizer : Control
{
if (Device == null || float.IsNaN(Device.RgbDevice.ActualSize.Width) || float.IsNaN(Device.RgbDevice.ActualSize.Height))
return new Rect();
Rect deviceRect = new(0, 0, Device.RgbDevice.ActualSize.Width, Device.RgbDevice.ActualSize.Height);
Geometry geometry = new RectangleGeometry(deviceRect);
geometry.Transform = new RotateTransform(Device.Rotation);
@@ -316,34 +311,15 @@ public class DeviceVisualizer : Control
private RenderTargetBitmap? GetDeviceImage(ArtemisDevice device)
{
string? path = device.Layout?.Image?.LocalPath;
if (path == null)
ArtemisLayout? layout = device.Layout;
if (layout == null)
return null;
if (BitmapCache.TryGetValue(path, out RenderTargetBitmap? existingBitmap))
if (BitmapCache.TryGetValue(layout.FilePath, out RenderTargetBitmap? existingBitmap))
return existingBitmap;
if (!File.Exists(path))
{
BitmapCache[path] = null;
return null;
}
// Create a bitmap that'll be used to render the device and LED images just once
// Render 4 times the actual size of the device to make sure things look sharp when zoomed in
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) device.RgbDevice.ActualSize.Width * 2, (int) device.RgbDevice.ActualSize.Height * 2));
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
using Bitmap bitmap = new(path);
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
lock (_deviceVisualizerLeds)
{
foreach (DeviceVisualizerLed deviceVisualizerLed in _deviceVisualizerLeds)
deviceVisualizerLed.DrawBitmap(context, 2 * device.Scale);
}
// BitmapCache[path] = renderTargetBitmap;
RenderTargetBitmap renderTargetBitmap = layout.RenderLayout(false);
BitmapCache[layout.FilePath] = renderTargetBitmap;
return renderTargetBitmap;
}
@@ -1,9 +1,7 @@
using System;
using System.IO;
using Artemis.Core;
using Avalonia;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using RGB.NET.Core;
using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
@@ -30,27 +28,7 @@ internal class DeviceVisualizerLed
public ArtemisLed Led { get; }
public Geometry? DisplayGeometry { get; private set; }
public void DrawBitmap(DrawingContext drawingContext, double scale)
{
if (Led.Layout?.Image == null || !File.Exists(Led.Layout.Image.LocalPath))
return;
try
{
using Bitmap bitmap = new(Led.Layout.Image.LocalPath);
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(new PixelSize((Led.RgbLed.Size.Width * scale).RoundToInt(), (Led.RgbLed.Size.Height * scale).RoundToInt()));
drawingContext.DrawImage(
scaledBitmap,
new Rect(Led.RgbLed.Location.X * scale, Led.RgbLed.Location.Y * scale, scaledBitmap.Size.Width, scaledBitmap.Size.Height)
);
}
catch
{
// ignored
}
}
public void RenderGeometry(DrawingContext drawingContext)
{
if (DisplayGeometry == null)
@@ -0,0 +1,139 @@
using System;
using System.IO;
using Artemis.Core;
using Avalonia;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using RGB.NET.Core;
using Color = Avalonia.Media.Color;
using SolidColorBrush = Avalonia.Media.SolidColorBrush;
namespace Artemis.UI.Shared.Extensions;
/// <summary>
/// Provides extension methods for the <see cref="ArtemisLayout" /> type.
/// </summary>
public static class ArtemisLayoutExtensions
{
/// <summary>
/// Renders the layout to a bitmap.
/// </summary>
/// <param name="layout">The layout to render</param>
/// <returns>The resulting bitmap.</returns>
public static RenderTargetBitmap RenderLayout(this ArtemisLayout layout, bool previewLeds)
{
string? path = layout.Image?.LocalPath;
// Create a bitmap that'll be used to render the device and LED images just once
// Render 4 times the actual size of the device to make sure things look sharp when zoomed in
RenderTargetBitmap renderTargetBitmap = new(new PixelSize((int) layout.RgbLayout.Width * 2, (int) layout.RgbLayout.Height * 2));
using DrawingContext context = renderTargetBitmap.CreateDrawingContext();
// Draw device background
if (path != null && File.Exists(path))
{
using Bitmap bitmap = new(path);
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(renderTargetBitmap.PixelSize);
context.DrawImage(scaledBitmap, new Rect(scaledBitmap.Size));
}
// Draw LED images
foreach (ArtemisLedLayout led in layout.Leds)
{
string? ledPath = led.Image?.LocalPath;
if (ledPath == null || !File.Exists(ledPath))
continue;
using Bitmap bitmap = new(ledPath);
using Bitmap scaledBitmap = bitmap.CreateScaledBitmap(new PixelSize((led.RgbLayout.Width * 2).RoundToInt(), (led.RgbLayout.Height * 2).RoundToInt()));
context.DrawImage(scaledBitmap, new Rect(led.RgbLayout.X * 2, led.RgbLayout.Y * 2, scaledBitmap.Size.Width, scaledBitmap.Size.Height));
}
if (!previewLeds)
return renderTargetBitmap;
// Draw LED geometry using a rainbow gradient
ColorGradient colors = ColorGradient.GetUnicornBarf();
colors.ToggleSeamless();
context.PushTransform(Matrix.CreateScale(2, 2));
foreach (ArtemisLedLayout led in layout.Leds)
{
Geometry? geometry = CreateLedGeometry(led);
if (geometry == null)
continue;
Color color = colors.GetColor((led.RgbLayout.X + led.RgbLayout.Width / 2) / layout.RgbLayout.Width).ToColor();
SolidColorBrush fillBrush = new() {Color = color, Opacity = 0.4};
SolidColorBrush penBrush = new() {Color = color};
Pen pen = new(penBrush) {LineJoin = PenLineJoin.Round};
context.DrawGeometry(fillBrush, pen, geometry);
}
return renderTargetBitmap;
}
private static Geometry? CreateLedGeometry(ArtemisLedLayout led)
{
// The minimum required size for geometry to be created
if (led.RgbLayout.Width < 2 || led.RgbLayout.Height < 2)
return null;
switch (led.RgbLayout.Shape)
{
case Shape.Custom:
if (led.DeviceLayout.RgbLayout.Type is RGBDeviceType.Keyboard or RGBDeviceType.Keypad)
return CreateCustomGeometry(led, 2.0);
return CreateCustomGeometry(led, 1.0);
case Shape.Rectangle:
if (led.DeviceLayout.RgbLayout.Type is RGBDeviceType.Keyboard or RGBDeviceType.Keypad)
return CreateKeyCapGeometry(led);
return CreateRectangleGeometry(led);
case Shape.Circle:
return CreateCircleGeometry(led);
default:
throw new ArgumentOutOfRangeException();
}
}
private static RectangleGeometry CreateRectangleGeometry(ArtemisLedLayout led)
{
return new RectangleGeometry(new Rect(led.RgbLayout.X + 0.5, led.RgbLayout.Y + 0.5, led.RgbLayout.Width - 1, led.RgbLayout.Height - 1));
}
private static EllipseGeometry CreateCircleGeometry(ArtemisLedLayout led)
{
return new EllipseGeometry(new Rect(led.RgbLayout.X + 0.5, led.RgbLayout.Y + 0.5, led.RgbLayout.Width - 1, led.RgbLayout.Height - 1));
}
private static RectangleGeometry CreateKeyCapGeometry(ArtemisLedLayout led)
{
return new RectangleGeometry(new Rect(led.RgbLayout.X + 1, led.RgbLayout.Y + 1, led.RgbLayout.Width - 2, led.RgbLayout.Height - 2));
}
private static Geometry? CreateCustomGeometry(ArtemisLedLayout led, double deflateAmount)
{
try
{
if (led.RgbLayout.ShapeData == null)
return null;
double width = led.RgbLayout.Width - deflateAmount;
double height = led.RgbLayout.Height - deflateAmount;
Geometry geometry = Geometry.Parse(led.RgbLayout.ShapeData);
geometry.Transform = new TransformGroup
{
Children = new Transforms
{
new ScaleTransform(width, height),
new TranslateTransform(led.RgbLayout.X + deflateAmount / 2, led.RgbLayout.Y + deflateAmount / 2)
}
};
return geometry;
}
catch (Exception)
{
return CreateRectangleGeometry(led);
}
}
}