mirror of
https://github.com/kwsch/PKHeX
synced 2024-11-11 15:07:11 +00:00
Make BitmapAnimator usage single-object
Don't unlock/lock the GlowBase bitmap repeatedly; store the bytes separately for frame generating. Fixes any memory access exceptions (unlock->unlock due to program lag). Do more preprocessing of egg glow sprites for cleaner appearance (now glow inside sprite) extract glow method for easier reuse
This commit is contained in:
parent
77bd91397a
commit
2e88da9d3c
4 changed files with 83 additions and 52 deletions
|
@ -8,16 +8,16 @@ namespace PKHeX.WinForms.Controls
|
|||
{
|
||||
public sealed class BitmapAnimator : Timer
|
||||
{
|
||||
public BitmapAnimator(Bitmap baseImage, Bitmap extraLayer = null)
|
||||
public BitmapAnimator(Image extraLayer = null)
|
||||
{
|
||||
GlowBase = baseImage;
|
||||
ExtraLayer = extraLayer;
|
||||
Elapsed += TimerElapsed;
|
||||
}
|
||||
|
||||
private Bitmap GlowBase;
|
||||
private Bitmap ExtraLayer;
|
||||
private Bitmap[] GlowCache;
|
||||
private Image GlowBase;
|
||||
private byte[] GlowData;
|
||||
private readonly Image ExtraLayer;
|
||||
private Image[] GlowCache;
|
||||
public Image OriginalBackground;
|
||||
|
||||
private PictureBox pb;
|
||||
|
@ -27,29 +27,29 @@ namespace PKHeX.WinForms.Controls
|
|||
public int GlowFps { get; set; } = 60;
|
||||
public Color GlowToColor { get; set; } = Color.LightSkyBlue;
|
||||
public Color GlowFromColor { get; set; } = Color.White;
|
||||
private readonly object Lock = new object();
|
||||
|
||||
public new void Start() => throw new ArgumentException();
|
||||
|
||||
public new void Stop()
|
||||
{
|
||||
lock (Lock)
|
||||
StopTimer();
|
||||
}
|
||||
|
||||
private void StopTimer()
|
||||
{
|
||||
if (pb == null)
|
||||
return;
|
||||
Enabled = false;
|
||||
|
||||
// reset logic
|
||||
GlowCounter = 0;
|
||||
pb.BackgroundImage = OriginalBackground;
|
||||
GlowBase = ExtraLayer = null;
|
||||
OriginalBackground = null;
|
||||
for (int i = 0; i < GlowCache.Length; i++)
|
||||
GlowCache[i] = null;
|
||||
}
|
||||
|
||||
public void Start(PictureBox pbox, Image original)
|
||||
public void Start(PictureBox pbox, Image baseImage, byte[] glowData, Image original)
|
||||
{
|
||||
GlowBase = baseImage;
|
||||
GlowData = glowData;
|
||||
pb = pbox;
|
||||
OriginalBackground = original;
|
||||
GlowCache = new Bitmap[GlowFps];
|
||||
GlowCache = new Image[GlowFps];
|
||||
GlowInterval = 1000 / GlowFps;
|
||||
Interval = GlowInterval;
|
||||
Enabled = true;
|
||||
|
@ -57,25 +57,14 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
private void TimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
|
||||
{
|
||||
lock (Lock)
|
||||
{
|
||||
if (!Enabled)
|
||||
return; // timer canceled, was waiting to proceed
|
||||
GlowCounter = (GlowCounter + 1) % (GlowInterval * 2); // loop backwards
|
||||
int frameIndex = GlowCounter >= GlowInterval ? (GlowInterval * 2) - GlowCounter : GlowCounter;
|
||||
try
|
||||
{
|
||||
var frame = GetFrame(frameIndex);
|
||||
pb.BackgroundImage = ImageUtil.LayerImage(OriginalBackground, frame, 0, 0, 1);
|
||||
}
|
||||
catch
|
||||
{
|
||||
StopTimer();
|
||||
}
|
||||
}
|
||||
if (!Enabled)
|
||||
return; // timer canceled, was waiting to proceed
|
||||
GlowCounter = (GlowCounter + 1) % (GlowInterval * 2); // loop backwards
|
||||
int frameIndex = GlowCounter >= GlowInterval ? (GlowInterval * 2) - GlowCounter : GlowCounter;
|
||||
pb.BackgroundImage = GetFrame(frameIndex);
|
||||
}
|
||||
|
||||
private Bitmap GetFrame(int frameIndex)
|
||||
private Image GetFrame(int frameIndex)
|
||||
{
|
||||
var frame = GlowCache[frameIndex];
|
||||
if (frame != null)
|
||||
|
@ -83,9 +72,13 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
var elapsedFraction = (double)frameIndex / GlowInterval;
|
||||
var frameColor = GetFrameColor(elapsedFraction);
|
||||
frame = ImageUtil.ChangeAllColorTo(GlowBase, frameColor);
|
||||
var frameData = (byte[])GlowData.Clone();
|
||||
ImageUtil.ChangeAllColorTo(frameData, frameColor);
|
||||
|
||||
frame = ImageUtil.GetBitmap(frameData, GlowBase.Width, GlowBase.Height);
|
||||
if (ExtraLayer != null)
|
||||
frame = ImageUtil.LayerImage(frame, ExtraLayer, 0, 0, 1);
|
||||
frame = ImageUtil.LayerImage(OriginalBackground, frame, 0, 0, 1);
|
||||
return GlowCache[frameIndex] = frame;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace PKHeX.WinForms.Controls
|
|||
public bool GlowHover { get; set; } = true;
|
||||
public Color GlowInitial { get; set; } = Color.White;
|
||||
public Color GlowFinal { get; set; } = Color.LightSkyBlue;
|
||||
public BitmapAnimator HoverWorker;
|
||||
public readonly BitmapAnimator HoverWorker;
|
||||
|
||||
private SaveFile SAV => SE.SAV;
|
||||
public SlotChangeInfo DragInfo;
|
||||
|
@ -41,6 +41,7 @@ namespace PKHeX.WinForms.Controls
|
|||
|
||||
public SlotChangeManager(SAVEditor se)
|
||||
{
|
||||
HoverWorker = new BitmapAnimator(Resources.slotHover);
|
||||
SE = se;
|
||||
Reset();
|
||||
}
|
||||
|
@ -63,15 +64,6 @@ namespace PKHeX.WinForms.Controls
|
|||
BeginHoverSlot(pb);
|
||||
}
|
||||
|
||||
private Bitmap GetGlowSprite(PKM pk)
|
||||
{
|
||||
var baseSprite = SpriteUtil.GetSprite(pk.Species, pk.AltForm, pk.Gender, 0, pk.IsEgg, false, pk.Format);
|
||||
|
||||
var pixels = ImageUtil.GetPixelData((Bitmap)baseSprite);
|
||||
ImageUtil.GlowEdges(pixels, new[] {GlowInitial.B, GlowInitial.G, GlowInitial.R}, baseSprite.Width);
|
||||
return ImageUtil.GetBitmap(pixels, baseSprite.Width, baseSprite.Height);
|
||||
}
|
||||
|
||||
private void BeginHoverSlot(PictureBox pb)
|
||||
{
|
||||
var view = WinFormsUtil.FindFirstControlOfType<ISlotViewer<PictureBox>>(pb);
|
||||
|
@ -84,12 +76,14 @@ namespace PKHeX.WinForms.Controls
|
|||
Bitmap hover;
|
||||
if (GlowHover)
|
||||
{
|
||||
HoverWorker?.Stop();
|
||||
HoverWorker.Stop();
|
||||
|
||||
var GlowBase = GetGlowSprite(pk);
|
||||
var bgr = new[] { GlowInitial.B, GlowInitial.G, GlowInitial.R };
|
||||
SpriteUtil.GetSpriteGlow(pk, bgr, out var glowdata, out var GlowBase);
|
||||
hover = ImageUtil.LayerImage(GlowBase, Resources.slotHover, 0, 0);
|
||||
HoverWorker = new BitmapAnimator(GlowBase, Resources.slotHover) { GlowFromColor = GlowInitial, GlowToColor = GlowFinal };
|
||||
HoverWorker.Start(pb, OriginalBackground);
|
||||
HoverWorker.GlowToColor = GlowFinal;
|
||||
HoverWorker.GlowFromColor = GlowInitial;
|
||||
HoverWorker.Start(pb, GlowBase, glowdata, OriginalBackground);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -108,8 +102,7 @@ namespace PKHeX.WinForms.Controls
|
|||
{
|
||||
if (HoveredSlot != null)
|
||||
{
|
||||
HoverWorker?.Stop();
|
||||
HoverWorker = null;
|
||||
HoverWorker.Stop();
|
||||
HoveredSlot.BackgroundImage = OriginalBackground;
|
||||
HoveredSlot = null;
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace PKHeX.WinForms
|
|||
GetBitmapData(bmp, out BitmapData bmpData, out IntPtr ptr, out byte[] data);
|
||||
|
||||
Marshal.Copy(ptr, data, 0, data.Length);
|
||||
SetAllColorTo(data, c);
|
||||
ChangeAllColorTo(data, c);
|
||||
Marshal.Copy(data, 0, ptr, data.Length);
|
||||
bmp.UnlockBits(bmpData);
|
||||
|
||||
|
@ -106,13 +106,33 @@ namespace PKHeX.WinForms
|
|||
return argbData;
|
||||
}
|
||||
|
||||
public static void SetAllUsedPixelsOpaque(byte[] data)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i += 4)
|
||||
if (data[i + 3] != 0)
|
||||
data[i + 3] = 0xFF;
|
||||
}
|
||||
|
||||
public static void RemovePixels(byte[] pixels, byte[] original)
|
||||
{
|
||||
for (int i = 0; i < original.Length; i += 4)
|
||||
{
|
||||
if (original[i + 3] == 0)
|
||||
continue;
|
||||
pixels[i + 0] = 0;
|
||||
pixels[i + 1] = 0;
|
||||
pixels[i + 2] = 0;
|
||||
pixels[i + 3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetAllTransparencyTo(byte[] data, double trans)
|
||||
{
|
||||
for (int i = 0; i < data.Length; i += 4)
|
||||
data[i + 3] = (byte)(data[i + 3] * trans);
|
||||
}
|
||||
|
||||
private static void SetAllColorTo(byte[] data, Color c)
|
||||
public static void ChangeAllColorTo(byte[] data, Color c)
|
||||
{
|
||||
byte R = c.R;
|
||||
byte G = c.G;
|
||||
|
|
|
@ -107,6 +107,31 @@ namespace PKHeX.WinForms
|
|||
return sprite;
|
||||
}
|
||||
|
||||
public static void GetSpriteGlow(PKM pk, byte[] bgr, out byte[] pixels, out Image baseSprite, bool forceHollow = false)
|
||||
{
|
||||
bool egg = pk.IsEgg;
|
||||
baseSprite = GetSprite(pk.Species, pk.AltForm, pk.Gender, 0, egg, false, pk.Format);
|
||||
GetSpriteGlow(baseSprite, bgr, out pixels, forceHollow || egg);
|
||||
}
|
||||
|
||||
public static void GetSpriteGlow(Image baseSprite, byte[] bgr, out byte[] pixels, bool forceHollow = false)
|
||||
{
|
||||
pixels = ImageUtil.GetPixelData((Bitmap) baseSprite);
|
||||
if (!forceHollow)
|
||||
{
|
||||
ImageUtil.GlowEdges(pixels, bgr, baseSprite.Width);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the image has any transparency, any derived background will bleed into it.
|
||||
// Need to undo any transparency values if any present.
|
||||
// Remove opaque pixels from original image, leaving only the glow effect pixels.
|
||||
var original = (byte[]) pixels.Clone();
|
||||
ImageUtil.SetAllUsedPixelsOpaque(pixels);
|
||||
ImageUtil.GlowEdges(pixels, bgr, baseSprite.Width);
|
||||
ImageUtil.RemovePixels(pixels, original);
|
||||
}
|
||||
|
||||
// Extension Methods
|
||||
public static Image WallpaperImage(this SaveFile SAV, int box) => GetWallpaper(SAV, box);
|
||||
public static Image Sprite(this MysteryGift gift) => GetSprite(gift);
|
||||
|
|
Loading…
Reference in a new issue