2018-07-23 03:04:28 +00:00
|
|
|
using System;
|
2024-02-03 20:11:17 +00:00
|
|
|
using System.Buffers;
|
2018-07-23 03:04:28 +00:00
|
|
|
using System.Drawing;
|
|
|
|
using System.Timers;
|
|
|
|
using System.Windows.Forms;
|
2019-09-29 16:47:06 +00:00
|
|
|
using PKHeX.Drawing;
|
2018-07-23 03:04:28 +00:00
|
|
|
using Timer = System.Timers.Timer;
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
namespace PKHeX.WinForms.Controls;
|
|
|
|
|
2023-01-22 04:02:33 +00:00
|
|
|
public sealed class BitmapAnimator : IDisposable
|
2018-07-23 03:04:28 +00:00
|
|
|
{
|
2023-01-22 04:02:33 +00:00
|
|
|
public BitmapAnimator() => Timer.Elapsed += TimerElapsed;
|
|
|
|
|
|
|
|
private readonly Timer Timer = new();
|
2018-07-24 01:31:01 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private int imgWidth;
|
|
|
|
private int imgHeight;
|
|
|
|
private byte[]? GlowData;
|
|
|
|
private Image? ExtraLayer;
|
|
|
|
private Image?[]? GlowCache;
|
2023-01-22 04:02:33 +00:00
|
|
|
private Image? OriginalBackground;
|
2022-06-18 18:04:24 +00:00
|
|
|
private readonly object Lock = new();
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private PictureBox? pb;
|
|
|
|
private int GlowInterval;
|
|
|
|
private int GlowCounter;
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
public int GlowFps { get; set; } = 60;
|
|
|
|
public Color GlowToColor { get; set; } = Color.LightSkyBlue;
|
|
|
|
public Color GlowFromColor { get; set; } = Color.White;
|
2023-01-22 04:02:33 +00:00
|
|
|
public bool Enabled { get => Timer.Enabled; set => Timer.Enabled = value; }
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2023-01-22 04:02:33 +00:00
|
|
|
public void Stop()
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
|
|
|
if (pb == null || !Enabled)
|
|
|
|
return;
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
lock (Lock)
|
2018-07-23 03:04:28 +00:00
|
|
|
{
|
2018-08-28 23:40:27 +00:00
|
|
|
Enabled = false;
|
2022-06-18 18:04:24 +00:00
|
|
|
pb.BackgroundImage = OriginalBackground;
|
2018-07-23 03:04:28 +00:00
|
|
|
}
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
// reset logic
|
2023-02-23 09:07:46 +00:00
|
|
|
ArgumentNullException.ThrowIfNull(GlowCache);
|
2022-06-18 18:04:24 +00:00
|
|
|
GlowCounter = 0;
|
|
|
|
for (int i = 0; i < GlowCache.Length; i++)
|
|
|
|
GlowCache[i] = null;
|
|
|
|
}
|
|
|
|
|
2023-01-22 04:02:33 +00:00
|
|
|
public void Start(PictureBox pbox, Image baseImage, byte[] glowData, Image? original, Image extra)
|
2022-06-18 18:04:24 +00:00
|
|
|
{
|
|
|
|
Enabled = false;
|
|
|
|
imgWidth = baseImage.Width;
|
|
|
|
imgHeight = baseImage.Height;
|
|
|
|
GlowData = glowData;
|
|
|
|
GlowCounter = 0;
|
|
|
|
GlowCache = new Image[GlowFps];
|
|
|
|
GlowInterval = 1000 / GlowFps;
|
2023-01-22 04:02:33 +00:00
|
|
|
Timer.Interval = GlowInterval;
|
2022-06-18 18:04:24 +00:00
|
|
|
lock (Lock)
|
2018-07-23 03:04:28 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
pb = pbox;
|
|
|
|
ExtraLayer = extra;
|
|
|
|
OriginalBackground = original;
|
2018-07-23 03:04:28 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
Enabled = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void TimerElapsed(object? sender, ElapsedEventArgs? elapsedEventArgs)
|
|
|
|
{
|
|
|
|
if (!Enabled)
|
|
|
|
return; // timer canceled, was waiting to proceed
|
|
|
|
GlowCounter = (GlowCounter + 1) % (GlowInterval * 2); // loop backwards
|
|
|
|
int frameIndex = GlowCounter >= GlowInterval ? (GlowInterval * 2) - GlowCounter : GlowCounter;
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
lock (Lock)
|
2018-07-23 03:04:28 +00:00
|
|
|
{
|
2022-06-18 18:04:24 +00:00
|
|
|
if (!Enabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pb == null)
|
|
|
|
return;
|
|
|
|
try { pb.BackgroundImage = GetFrame(frameIndex); } // drawing GDI can be silly sometimes #2072
|
|
|
|
catch (AccessViolationException ex) { System.Diagnostics.Debug.WriteLine(ex.Message); }
|
2018-07-23 03:04:28 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
}
|
2018-07-23 03:04:28 +00:00
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
private Image GetFrame(int frameIndex)
|
|
|
|
{
|
|
|
|
var cache = GlowCache;
|
2023-02-23 09:07:46 +00:00
|
|
|
ArgumentNullException.ThrowIfNull(cache);
|
2022-06-18 18:04:24 +00:00
|
|
|
var frame = cache[frameIndex];
|
|
|
|
if (frame != null)
|
|
|
|
return frame;
|
|
|
|
|
|
|
|
var elapsedFraction = (double)frameIndex / GlowInterval;
|
|
|
|
var frameColor = GetFrameColor(elapsedFraction);
|
|
|
|
|
2023-02-23 09:07:46 +00:00
|
|
|
ArgumentNullException.ThrowIfNull(GlowData);
|
2022-06-18 18:04:24 +00:00
|
|
|
|
2024-02-03 20:11:17 +00:00
|
|
|
var frameData = ArrayPool<byte>.Shared.Rent(GlowData.Length);
|
|
|
|
var frameSpan = frameData.AsSpan(0, GlowData.Length);
|
|
|
|
GlowData.AsSpan().CopyTo(frameSpan);
|
|
|
|
ImageUtil.ChangeAllColorTo(frameSpan, frameColor);
|
|
|
|
frame = ImageUtil.GetBitmap(frameData, imgWidth, imgHeight, GlowData.Length);
|
|
|
|
frameSpan.Clear();
|
|
|
|
ArrayPool<byte>.Shared.Return(frameData);
|
|
|
|
|
2022-06-18 18:04:24 +00:00
|
|
|
if (ExtraLayer != null)
|
|
|
|
frame = ImageUtil.LayerImage(frame, ExtraLayer, 0, 0);
|
|
|
|
if (OriginalBackground != null)
|
|
|
|
frame = ImageUtil.LayerImage(OriginalBackground, frame, 0, 0);
|
|
|
|
return cache[frameIndex] = frame;
|
2018-07-23 03:04:28 +00:00
|
|
|
}
|
2022-06-18 18:04:24 +00:00
|
|
|
|
|
|
|
private Color GetFrameColor(double elapsedFraction) => ColorUtil.Blend(GlowToColor, GlowFromColor, elapsedFraction);
|
2023-01-22 04:02:33 +00:00
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
GlowCache = null;
|
|
|
|
Timer.Enabled = false;
|
|
|
|
Timer.Elapsed -= TimerElapsed;
|
|
|
|
Timer.Dispose();
|
|
|
|
}
|
2021-12-10 08:15:04 +00:00
|
|
|
}
|