diff --git a/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs b/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs
index e80dfcec7..de02522f9 100755
--- a/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs
+++ b/Project-Aurora/Project-Aurora/EffectsEngine/EffectLayer.cs
@@ -475,6 +475,87 @@ public EffectLayer Set(KeySequence sequence, Color color)
return this;
}
+ ///
+ /// Allows drawing some arbitrary content to the sequence bounds, including translation, scaling and rotation.
+ /// Usage:
+ /// someEffectLayer.DrawTransformed(Properties.Sequence,
+ /// m => {
+ /// // We are prepending the transformations since we want the mirroring to happen BEFORE the rotation and scaling happens.
+ /// m.Translate(100, 0, MatrixOrder.Prepend); // These two are backwards because we are Prepending (so this is prepended first)
+ /// m.Scale(-1, 1, MatrixOrder.Prepend); // Then this is prepended before the tranlate.
+ /// },
+ /// gfx => {
+ /// gfx.FillRectangle(Brushes.Red, 0, 0, 30, 100);
+ /// gfx.FillRectangle(Brushes.Blue, 70, 0, 30, 100);
+ /// },
+ /// new RectangleF(0, 0, 100, 100);
+ /// This code will draw an X-mirrored image of a red stipe and a blue stripe (with a transparent gap in between) to the target keysequence area.
+ ///
+ /// The target sequence whose bounds will be used as the target location on the drawing canvas.
+ /// An action that further configures the transformation matrix before render is called.
+ /// An action that receives a transformed graphics context and can render whatever it needs to.
+ /// The source region of the rendered content. This is used when calculating the transformation matrix, so that this
+ /// rectangle in the render context is transformed to the keysequence bounds in the layer's context. Note that no clipping is performed.
+ public EffectLayer DrawTransformed(KeySequence sequence, Action configureMatrix, Action render, RectangleF sourceRegion) {
+ // The matrix represents the transformation that will be applied to the rendered content
+ var matrix = new Matrix();
+
+ // The bounds represent the target position of the render part
+ // Note that we round the X and Y off to properly imitate the above `Set(KeySequence, Color)` method. Unsure exactly why this is done, but it _is_ done to replicate behaviour properly.
+ // Also unsure why the X and Y are rounded using math.Round but Width and Height are just truncated using an int cast??
+ var boundsRaw = sequence.GetAffectedRegion();
+ var bounds = new RectangleF((int)Math.Round(boundsRaw.X), (int)Math.Round(boundsRaw.Y), (int)boundsRaw.Width, (int)boundsRaw.Height);
+
+ using (var gfx = Graphics.FromImage(colormap)) {
+
+ // First, calculate the scaling required to transform the sourceRect's size into the bounds' size
+ float sx = bounds.Width / sourceRegion.Width, sy = bounds.Height / sourceRegion.Height;
+
+ // Perform this scale first
+ // Note: that if the scale is zero, when setting the graphics transform to the matrix, it throws an error, so we must have NON-ZERO values
+ // Note 2: Also tried using float.Epsilon but this also caused the exception, so a somewhat small number will have to suffice. Not noticed any visual issues with 0.001f.
+ matrix.Scale(sx == 0 ? .001f : sx, sy == 0 ? .001f : sy, MatrixOrder.Append);
+
+ // Second, for freeform objects, apply the rotation. This needs to be done AFTER the scaling, else the scaling is applied to the rotated object, which skews it
+ // We rotate around the central point of the source region, but we need to take the scaling of the dimensions into account
+ if (sequence.type == KeySequenceType.FreeForm)
+ matrix.RotateAt(sequence.freeform.Angle, new PointF((sourceRegion.Left + (sourceRegion.Width / 2f)) * sx, (sourceRegion.Top + (sourceRegion.Height / 2f)) * sy), MatrixOrder.Append);
+
+ // Third, we can translate the matrix from the source to the target location.
+ matrix.Translate(bounds.X - sourceRegion.Left, bounds.Y - sourceRegion.Top, MatrixOrder.Append);
+
+ // Finally, call the custom matrix configure action
+ configureMatrix(matrix);
+
+ // Apply the matrix transform to the graphics context and then render
+ gfx.Transform = matrix;
+ render(gfx);
+ }
+
+ return this;
+ }
+
+ ///
+ /// Allows drawing some arbitrary content to the sequence bounds, including translation, scaling and rotation.
+ /// See for usage.
+ ///
+ /// The target sequence whose bounds will be used as the target location on the drawing canvas.
+ /// An action that receives a transformed graphics context and can render whatever it needs to.
+ /// The source region of the rendered content. This is used when calculating the transformation matrix, so that this
+ /// rectangle in the render context is transformed to the keysequence bounds in the layer's context. Note that no clipping is performed.
+ public EffectLayer DrawTransformed(KeySequence sequence, Action render, RectangleF sourceRegion)
+ => DrawTransformed(sequence, _ => { }, render, sourceRegion);
+
+ ///
+ /// Allows drawing some arbitrary content to the sequence bounds, including translation, scaling and rotation.
+ /// Uses the full canvas size as the source region.
+ /// See for usage.
+ ///
+ /// The target sequence whose bounds will be used as the target location on the drawing canvas.
+ /// An action that receives a transformed graphics context and can render whatever it needs to.
+ public EffectLayer DrawTransformed(KeySequence sequence, Action render) =>
+ DrawTransformed(sequence, render, new RectangleF(0, 0, Effects.canvas_width, Effects.canvas_height));
+
///
/// Sets one DeviceKeys key with a specific color on the bitmap
///
diff --git a/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs b/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs
index 8b20ba459..41d7bf2b3 100644
--- a/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs
+++ b/Project-Aurora/Project-Aurora/Settings/Layers/EqualizerLayerHandler.cs
@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
+using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@@ -152,6 +153,11 @@ public class EqualizerLayerHandler : LayerHandler {
+ // Here we draw the equalizer relative to our source rectangle and the DrawTransformed method handles sizing and positioning it correctly for us
- using (Graphics g = equalizer_layer.GetGraphics())
- {
- g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
+ // Draw a rectangle background over the entire source rect if bg is enabled
+ if (BgEnabled)
+ g.FillRectangle(new SolidBrush(Properties.DimColor), sourceRect);
- int wave_step_amount = _local_fft.Length / (int)rect.Width;
+ g.CompositingMode = CompositingMode.SourceCopy;
+
+ int wave_step_amount = _local_fft.Length / (int)sourceRect.Width;
- switch (Properties.EQType)
- {
+ switch (Properties.EQType) {
case EqualizerType.Waveform:
- for (int x = 0; x < (int)rect.Width; x++)
- {
+ var halfHeight = sourceRect.Height / 2f;
+ for (int x = 0; x < (int)sourceRect.Width; x++) {
float fft_val = _local_fft.Length > x * wave_step_amount ? _local_fft[x * wave_step_amount].X : 0.0f;
-
- Brush brush = GetBrush(fft_val, x, rect.Width);
-
- g.DrawLine(new Pen(brush), x + rect.X, (rect.Height / 2) + rect.Y, x + rect.X, (rect.Height / 2) + rect.Y - Math.Max(Math.Min(fft_val / scaled_max_amplitude * 500.0f, rect.Height / 2), -rect.Height / 2));
+ Brush brush = GetBrush(fft_val, x, sourceRect.Width);
+ var yOff = -Math.Max(Math.Min(fft_val / scaled_max_amplitude * 500.0f, halfHeight), -halfHeight);
+ g.DrawLine(new Pen(brush), x, halfHeight, x, halfHeight + yOff);
}
break;
+
case EqualizerType.Waveform_Bottom:
- for (int x = 0; x < (int)rect.Width; x++)
- {
+ for (int x = 0; x < (int)sourceRect.Width; x++) {
float fft_val = _local_fft.Length > x * wave_step_amount ? _local_fft[x * wave_step_amount].X : 0.0f;
-
- Brush brush = GetBrush(fft_val, x, rect.Width);
-
- g.DrawLine(new Pen(brush), x + rect.X, rect.Height + rect.Y, x + rect.X, rect.Height + rect.Y - Math.Min(Math.Abs(fft_val / scaled_max_amplitude) * 1000.0f, rect.Height));
+ Brush brush = GetBrush(fft_val, x, sourceRect.Width);
+ g.DrawLine(new Pen(brush), x, sourceRect.Height, x, sourceRect.Height - Math.Min(Math.Abs(fft_val / scaled_max_amplitude) * 1000.0f, sourceRect.Height));
}
break;
- case EqualizerType.PowerBars:
+ case EqualizerType.PowerBars:
//Perform FFT again to get frequencies
FastFourierTransform.FFT(false, (int)Math.Log(fftLength, 2.0), _local_fft);
while (flux_array.Count < freqs.Length)
- {
flux_array.Add(0.0f);
- }
int startF = 0;
int endF = 0;
@@ -340,7 +336,7 @@ public override EffectLayer Render(IGameState gamestate)
//System.Diagnostics.Debug.WriteLine($"flux max: {flux_array.Max()}");
- float bar_width = rect.Width / (float)(freqs.Length - 1);
+ float bar_width = sourceRect.Width / (float)(freqs.Length - 1);
for (int f_x = 0; f_x < freq_results.Length - 1; f_x++)
{
@@ -351,9 +347,9 @@ public override EffectLayer Render(IGameState gamestate)
if (previous_freq_results[f_x] - fft_val > 0.10)
fft_val = previous_freq_results[f_x] - 0.15f;
- float x = (f_x * bar_width) + rect.X;
- float y = rect.Height + rect.Y;
- float height = fft_val * rect.Height;
+ float x = f_x * bar_width;
+ float y = sourceRect.Height;
+ float height = fft_val * sourceRect.Height;
previous_freq_results[f_x] = fft_val;
@@ -361,10 +357,11 @@ public override EffectLayer Render(IGameState gamestate)
g.FillRectangle(brush, x, y - height, bar_width, height);
}
-
break;
}
- }
+
+ }, sourceRect);
+
var hander = NewLayerRender;
if (hander != null)
@@ -420,7 +417,6 @@ private int freqToBin(float freq)
private Brush GetBrush(float value, float position, float max_position)
{
- var rect = Properties.Sequence.GetAffectedRegion();
if (Properties.ViewType == EqualizerPresentationType.AlternatingColor)
{
if (value >= 0)
@@ -432,9 +428,10 @@ private Brush GetBrush(float value, float position, float max_position)
return new SolidBrush(Properties.Gradient.GetColorSpectrum().GetColorAt(position, max_position));
else if (Properties.ViewType == EqualizerPresentationType.GradientHorizontal)
{
- EffectBrush e_brush = new EffectBrush(Properties.Gradient.GetColorSpectrum());
- e_brush.start = new PointF(rect.X, 0);
- e_brush.end = new PointF(rect.Width + rect.X, 0);
+ EffectBrush e_brush = new EffectBrush(Properties.Gradient.GetColorSpectrum()) {
+ start = PointF.Empty,
+ end = new PointF(sourceRect.Width, 0)
+ };
return e_brush.GetDrawingBrush();
}
@@ -442,9 +439,10 @@ private Brush GetBrush(float value, float position, float max_position)
return new SolidBrush(Properties.Gradient.GetColorSpectrum().GetColorAt(Utils.Time.GetMilliSeconds(), 1000));
else if (Properties.ViewType == EqualizerPresentationType.GradientVertical)
{
- EffectBrush e_brush = new EffectBrush(Properties.Gradient.GetColorSpectrum());
- e_brush.start = new PointF(0, rect.Height + rect.Y);
- e_brush.end = new PointF(0, rect.Y);
+ EffectBrush e_brush = new EffectBrush(Properties.Gradient.GetColorSpectrum()) {
+ start = new PointF(0, sourceRect.Height),
+ end = PointF.Empty
+ };
return e_brush.GetDrawingBrush();
}