Files
SourMesen 40335ce735 UI: Update to Avalonia 12, .NET 10 and replace ReactiveUI with MVVM community toolkit (#142)
Also fixes DX11.0 support (for Win7 PCs that aren't fully updated), which stopped working after the recent variable refresh rate changes
2026-07-03 16:04:42 +09:00

217 lines
8.1 KiB
C#

using Avalonia;
using Avalonia.Controls;
using Avalonia.Platform;
using Mesen.Interop;
using Mesen.Utilities;
using Mesen.ViewModels;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
namespace Mesen.Debugger.ViewModels
{
public partial class SpritePreviewModel : ViewModelBase
{
[ObservableProperty] public partial int SpriteIndex { get; set; }
[ObservableProperty] public partial int X { get; set; }
[ObservableProperty] public partial int Y { get; set; }
[ObservableProperty] public partial int RawX { get; set; }
[ObservableProperty] public partial int RawY { get; set; }
[ObservableProperty] public partial int PreviewX { get; set; }
[ObservableProperty] public partial int PreviewY { get; set; }
[ObservableProperty] public partial int Width { get; set; }
[ObservableProperty] public partial int Height { get; set; }
[ObservableProperty] public partial int TileIndex { get; set; }
[ObservableProperty] public partial int TileAddress { get; set; }
[ObservableProperty] public partial DebugSpritePriority Priority { get; set; }
[ObservableProperty] public partial DebugSpriteMode Mode { get; set; }
[ObservableProperty] public partial int Bpp { get; set; }
[ObservableProperty] public partial TileFormat Format { get; set; }
[ObservableProperty] public partial int Palette { get; set; }
[ObservableProperty] public partial int PaletteAddress { get; set; }
[ObservableProperty] public partial SpriteVisibility Visibility { get; set; }
[ObservableProperty] public partial string? Flags { get; set; }
[ObservableProperty] public partial NullableBoolean HorizontalMirror { get; set; }
[ObservableProperty] public partial NullableBoolean VerticalMirror { get; set; }
[ObservableProperty] public partial NullableBoolean MosaicEnabled { get; set; }
[ObservableProperty] public partial NullableBoolean TransformEnabled { get; set; }
[ObservableProperty] public partial NullableBoolean DoubleSize { get; set; }
[ObservableProperty] public partial sbyte TransformParamIndex { get; set; }
[ObservableProperty] public partial bool UseExtendedVram { get; set; }
[ObservableProperty] public partial NullableBoolean UseSecondTable { get; set; }
public UInt32 TileCount { get; set; }
public UInt32 WrapWidth { get; set; }
public UInt32 WrapHeight { get; set; }
public UInt32[] TileAddresses { get; set; } = Array.Empty<UInt32>();
private UInt32[] _rawPreview = new UInt32[128 * 128];
[ObservableProperty] public partial DynamicBitmap? SpritePreview { get; set; }
[ObservableProperty] public partial double SpritePreviewZoom { get; set; }
[ObservableProperty] public partial bool FadePreview { get; set; }
public int RealWidth => Width / (DoubleSize == NullableBoolean.True ? 2 : 1);
public int RealHeight => Height / (DoubleSize == NullableBoolean.True ? 2 : 1);
public unsafe void Init(ref DebugSpriteInfo sprite, UInt32[] spritePreviews, DebugSpritePreviewInfo previewInfo)
{
SpriteIndex = sprite.SpriteIndex;
X = sprite.X;
Y = sprite.Y;
RawX = sprite.RawX;
RawY = sprite.RawY;
PreviewX = sprite.X + previewInfo.CoordOffsetX;
PreviewY = sprite.Y + previewInfo.CoordOffsetY;
Width = sprite.Width;
Height = sprite.Height;
TileIndex = sprite.TileIndex;
Priority = sprite.Priority;
Mode = sprite.Mode;
Bpp = sprite.Bpp;
Format = sprite.Format;
Palette = sprite.Palette;
TileAddress = sprite.TileAddress;
PaletteAddress = sprite.PaletteAddress;
UseSecondTable = sprite.UseSecondTable;
UseExtendedVram = sprite.UseExtendedVram;
WrapWidth = previewInfo.WrapRightToLeft ? previewInfo.Width : 0;
WrapHeight = previewInfo.WrapBottomToTop ? previewInfo.Height : 0;
TileCount = sprite.TileCount;
fixed(UInt32* p = sprite.TileAddresses) {
if(TileAddresses == null || TileAddresses.Length != TileCount) {
TileAddresses = new UInt32[TileCount];
}
for(int i = 0; i < sprite.TileCount; i++) {
TileAddresses[i] = p[i];
}
}
fixed(UInt32* p = spritePreviews) {
bool needUpdate = false;
UInt32* spritePreview = p + (sprite.SpriteIndex * 128 * 128);
if(SpritePreview == null || SpritePreview.PixelSize.Width != sprite.Width || SpritePreview.PixelSize.Height != sprite.Height) {
SpritePreview = new DynamicBitmap(new PixelSize(Width, Height), new Vector(96, 96), PixelFormat.Bgra8888, AlphaFormat.Premul);
needUpdate = true;
}
int len = Width * Height;
for(int i = 0; i < len; i++) {
if(_rawPreview[i] != spritePreview[i]) {
needUpdate = true;
}
_rawPreview[i] = spritePreview[i];
}
if(needUpdate) {
int spriteSize = len * sizeof(UInt32);
using(var bitmapLock = SpritePreview.Lock()) {
Buffer.MemoryCopy(spritePreview, (void*)bitmapLock.FrameBuffer.Address, spriteSize, spriteSize);
}
}
}
if(Width == 0 && Height == 0) {
//Can happen when reloading rom while the UI is updating the sprite list (sprite list will be empty data - all 0s)
SpritePreviewZoom = 1.0;
} else {
SpritePreviewZoom = 32.0 / Math.Max(Width, Height);
}
Visibility = sprite.Visibility;
FadePreview = sprite.Visibility != SpriteVisibility.Visible;
HorizontalMirror = sprite.HorizontalMirror;
VerticalMirror = sprite.VerticalMirror;
MosaicEnabled = sprite.MosaicEnabled;
TransformEnabled = sprite.TransformEnabled;
DoubleSize = sprite.DoubleSize;
TransformParamIndex = sprite.TransformParamIndex;
string flags = sprite.HorizontalMirror == NullableBoolean.True ? "H" : "";
flags += sprite.VerticalMirror == NullableBoolean.True ? "V" : "";
flags += sprite.Visibility == SpriteVisibility.Disabled ? "D" : "";
flags += sprite.TransformEnabled == NullableBoolean.True ? "T" : "";
flags += sprite.Mode == DebugSpriteMode.Blending ? "B" : "";
flags += sprite.Mode == DebugSpriteMode.Window ? "W" : "";
flags += sprite.MosaicEnabled == NullableBoolean.True ? "M" : "";
flags += sprite.UseSecondTable == NullableBoolean.True ? "N" : "";
Flags = flags;
}
public ValueTuple<Rect, Rect, Rect, Rect> GetPreviewRect()
{
Rect mainRect = new Rect(PreviewX, PreviewY, Width, Height);
Rect wrapTopRect = default;
Rect wrapLeftRect = default;
Rect wrapBothRect = default;
bool wrapVertical = WrapHeight > 0 && PreviewY + Height > WrapHeight;
bool wrapHorizontal = WrapWidth > 0 && PreviewX + Width > WrapWidth;
if(wrapVertical) {
//Generate a 2nd rect for the portion of the sprite that wraps
//around to the top of the screen when it goes past the screen's
//bottom, when the WrapBottomToTop flag is set (used by SNES/SMS/GBA)
wrapTopRect = new Rect(PreviewX, PreviewY - WrapHeight, Width, Height);
}
if(wrapHorizontal) {
//Generate a 2nd rect for the portion of the sprite that wraps
//around to the left of the screen when it goes past the screen's
//right side, when the WrapRightToLeft flag is set (used by GBA)
wrapLeftRect = new Rect(PreviewX - WrapWidth, PreviewY, Width, Height);
}
if(wrapVertical && wrapHorizontal) {
wrapBothRect = new Rect(PreviewX - WrapWidth, PreviewY - WrapHeight, Width, Height);
}
return (mainRect, wrapTopRect, wrapLeftRect, wrapBothRect);
}
public SpritePreviewModel Clone()
{
SpritePreviewModel copy = new SpritePreviewModel();
CopyTo(copy);
return copy;
}
public void CopyTo(SpritePreviewModel dst)
{
dst.SpriteIndex = SpriteIndex;
dst.X = X;
dst.Y = Y;
dst.RawX = RawX;
dst.RawY = RawY;
dst.PreviewX = PreviewX;
dst.PreviewY = PreviewY;
dst.Width = Width;
dst.Height = Height;
dst.TileIndex = TileIndex;
dst.TileAddress = TileAddress;
dst.Priority = Priority;
dst.Mode = Mode;
dst.Bpp = Bpp;
dst.Format = Format;
dst.Palette = Palette;
dst.PaletteAddress = PaletteAddress;
dst.Visibility = Visibility;
dst.Flags = Flags;
dst.HorizontalMirror = HorizontalMirror;
dst.VerticalMirror = VerticalMirror;
dst.FadePreview = FadePreview;
dst.MosaicEnabled = MosaicEnabled;
dst.TransformEnabled = TransformEnabled;
dst.DoubleSize = DoubleSize;
dst.TransformParamIndex = TransformParamIndex;
dst.UseExtendedVram = UseExtendedVram;
dst.UseSecondTable = UseSecondTable;
}
}
}