Этап 10
This commit is contained in:
@@ -5,6 +5,7 @@ using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Threading;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Minint.Core.Models;
|
||||
@@ -341,6 +342,7 @@ public partial class EditorViewModel : ViewModelBase
|
||||
|
||||
public void OnToolDown(int px, int py)
|
||||
{
|
||||
if (IsPlaying) return;
|
||||
if (Container is null || ActiveDocument is null || ActiveLayer is null)
|
||||
return;
|
||||
|
||||
@@ -374,6 +376,7 @@ public partial class EditorViewModel : ViewModelBase
|
||||
|
||||
public void OnToolDrag(int px, int py)
|
||||
{
|
||||
if (IsPlaying) return;
|
||||
if (ActiveTool is ToolType.Fill or ToolType.Select) return;
|
||||
OnToolDown(px, py);
|
||||
}
|
||||
@@ -408,6 +411,7 @@ public partial class EditorViewModel : ViewModelBase
|
||||
/// <summary>Called by PixelCanvas when selection drag starts.</summary>
|
||||
public void BeginSelection(int px, int py)
|
||||
{
|
||||
if (IsPlaying) return;
|
||||
SelectionRect = (px, py, 0, 0);
|
||||
}
|
||||
|
||||
@@ -490,13 +494,14 @@ public partial class EditorViewModel : ViewModelBase
|
||||
|
||||
public void MovePaste(int px, int py)
|
||||
{
|
||||
if (!IsPasting) return;
|
||||
if (IsPlaying || !IsPasting) return;
|
||||
PastePosition = (px, py);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public void CommitPaste()
|
||||
{
|
||||
if (IsPlaying) return;
|
||||
if (!IsPasting || Clipboard is null || ActiveDocument is null || ActiveLayer is null || Container is null)
|
||||
return;
|
||||
|
||||
@@ -603,6 +608,125 @@ public partial class EditorViewModel : ViewModelBase
|
||||
|
||||
#endregion
|
||||
|
||||
#region Animation playback
|
||||
|
||||
private DispatcherTimer? _animationTimer;
|
||||
private int _animationFrameIndex;
|
||||
|
||||
[ObservableProperty]
|
||||
[NotifyPropertyChangedFor(nameof(IsNotPlaying))]
|
||||
private bool _isPlaying;
|
||||
|
||||
public bool IsNotPlaying => !IsPlaying;
|
||||
|
||||
[RelayCommand]
|
||||
private void PlayAnimation()
|
||||
{
|
||||
if (Container is null || Container.Documents.Count < 2) return;
|
||||
if (IsPlaying) return;
|
||||
|
||||
IsPlaying = true;
|
||||
_animationFrameIndex = ActiveDocument is not null
|
||||
? Container.Documents.IndexOf(ActiveDocument)
|
||||
: 0;
|
||||
if (_animationFrameIndex < 0) _animationFrameIndex = 0;
|
||||
|
||||
AdvanceAnimationFrame();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void StopAnimation()
|
||||
{
|
||||
_animationTimer?.Stop();
|
||||
_animationTimer = null;
|
||||
IsPlaying = false;
|
||||
}
|
||||
|
||||
private void AdvanceAnimationFrame()
|
||||
{
|
||||
if (Container is null || !IsPlaying)
|
||||
{
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
var docs = Container.Documents;
|
||||
if (docs.Count == 0)
|
||||
{
|
||||
StopAnimation();
|
||||
return;
|
||||
}
|
||||
|
||||
_animationFrameIndex %= docs.Count;
|
||||
var doc = docs[_animationFrameIndex];
|
||||
|
||||
_suppressDocumentSync = true;
|
||||
ActiveDocument = doc;
|
||||
_suppressDocumentSync = false;
|
||||
RefreshCanvasFor(doc);
|
||||
|
||||
uint delay = doc.FrameDelayMs;
|
||||
if (delay < 10) delay = 10;
|
||||
|
||||
_animationTimer?.Stop();
|
||||
_animationTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(delay) };
|
||||
_animationTimer.Tick += (_, _) =>
|
||||
{
|
||||
_animationTimer?.Stop();
|
||||
_animationFrameIndex++;
|
||||
AdvanceAnimationFrame();
|
||||
};
|
||||
_animationTimer.Start();
|
||||
}
|
||||
|
||||
private void RefreshCanvasFor(MinintDocument doc)
|
||||
{
|
||||
if (Container is null)
|
||||
{
|
||||
CanvasBitmap = null;
|
||||
return;
|
||||
}
|
||||
|
||||
int w = Container.Width;
|
||||
int h = Container.Height;
|
||||
uint[] argb = _compositor.Composite(doc, w, h);
|
||||
|
||||
var bmp = new WriteableBitmap(
|
||||
new PixelSize(w, h),
|
||||
new Vector(96, 96),
|
||||
Avalonia.Platform.PixelFormat.Bgra8888);
|
||||
|
||||
using (var fb = bmp.Lock())
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
var dst = new Span<uint>((void*)fb.Address, w * h);
|
||||
for (int i = 0; i < argb.Length; i++)
|
||||
{
|
||||
uint px = argb[i];
|
||||
byte a2 = (byte)(px >> 24);
|
||||
byte r2 = (byte)((px >> 16) & 0xFF);
|
||||
byte g2 = (byte)((px >> 8) & 0xFF);
|
||||
byte b2 = (byte)(px & 0xFF);
|
||||
|
||||
if (a2 == 255) { dst[i] = px; }
|
||||
else if (a2 == 0) { dst[i] = 0; }
|
||||
else
|
||||
{
|
||||
r2 = (byte)(r2 * a2 / 255);
|
||||
g2 = (byte)(g2 * a2 / 255);
|
||||
b2 = (byte)(b2 * a2 / 255);
|
||||
dst[i] = (uint)(b2 | (g2 << 8) | (r2 << 16) | (a2 << 24));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CanvasBitmap = bmp;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public ICompositor Compositor => _compositor;
|
||||
public IPaletteService PaletteService => _paletteService;
|
||||
public IDrawingService DrawingService => _drawingService;
|
||||
|
||||
Reference in New Issue
Block a user