mirror of
https://github.com/SourMesen/Mesen2.git
synced 2025-04-02 10:21:44 -04:00
Debugger: Tile editor - Added shift-click to select color (+ hold shift for tooltip)
This commit is contained in:
parent
771ebff733
commit
2a5c8cbf4f
10 changed files with 171 additions and 50 deletions
|
@ -179,7 +179,19 @@ void PpuTools::RemoveViewer(uint32_t viewerId)
|
|||
_updateTimings.erase(viewerId);
|
||||
}
|
||||
|
||||
int32_t PpuTools::GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y)
|
||||
{
|
||||
int32_t color = 0;
|
||||
GetSetTilePixel(tileAddress, format, x, y, color, true);
|
||||
return color;
|
||||
}
|
||||
|
||||
void PpuTools::SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color)
|
||||
{
|
||||
GetSetTilePixel(tileAddress, format, x, y, color, false);
|
||||
}
|
||||
|
||||
void PpuTools::GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t& color, bool forGet)
|
||||
{
|
||||
ConsoleMemoryInfo memInfo = _emu->GetMemory(tileAddress.Type);
|
||||
if(!memInfo.Memory || memInfo.Size == 0) {
|
||||
|
@ -205,48 +217,55 @@ void PpuTools::SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t
|
|||
|
||||
uint8_t shift = (7 - x);
|
||||
|
||||
auto setBit = [&](uint32_t addr, uint8_t bitNumber, uint8_t bitValue) {
|
||||
ram[addr & ramMask] &= ~(1 << bitNumber);
|
||||
ram[addr & ramMask] |= (bitValue & 0x01) << bitNumber;
|
||||
auto setBit = [&](uint32_t addr, uint8_t pixelNumber, uint8_t bitNumber) {
|
||||
if(forGet) {
|
||||
uint8_t bitValue = ((ram[addr & ramMask] >> pixelNumber) & 0x01);
|
||||
color |= bitValue << bitNumber;
|
||||
} else {
|
||||
uint8_t bitValue = (color >> bitNumber) & 0x01;
|
||||
ram[addr & ramMask] &= ~(1 << pixelNumber);
|
||||
ram[addr & ramMask] |= (bitValue & 0x01) << pixelNumber;
|
||||
}
|
||||
};
|
||||
|
||||
switch(format) {
|
||||
case TileFormat::Bpp2:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 1, shift, 1);
|
||||
break;
|
||||
|
||||
case TileFormat::NesBpp2:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 8, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 8, shift, 1);
|
||||
break;
|
||||
|
||||
case TileFormat::Bpp4:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 16, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 17, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 1, shift, 1);
|
||||
setBit(rowStart + 16, shift, 2);
|
||||
setBit(rowStart + 17, shift, 3);
|
||||
break;
|
||||
|
||||
case TileFormat::Bpp8:
|
||||
case TileFormat::DirectColor:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 16, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 17, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart + 32, shift, (color & 0x10) >> 4);
|
||||
setBit(rowStart + 33, shift, (color & 0x20) >> 5);
|
||||
setBit(rowStart + 48, shift, (color & 0x40) >> 6);
|
||||
setBit(rowStart + 49, shift, (color & 0x80) >> 7);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 1, shift, 1);
|
||||
setBit(rowStart + 16, shift, 2);
|
||||
setBit(rowStart + 17, shift, 3);
|
||||
setBit(rowStart + 32, shift, 4);
|
||||
setBit(rowStart + 33, shift, 5);
|
||||
setBit(rowStart + 48, shift, 6);
|
||||
setBit(rowStart + 49, shift, 7);
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7:
|
||||
case TileFormat::Mode7DirectColor:
|
||||
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
||||
break;
|
||||
|
||||
case TileFormat::Mode7ExtBg:
|
||||
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
||||
if(forGet) {
|
||||
color = ram[(rowStart + x * 2 + 1) & ramMask];
|
||||
} else {
|
||||
ram[(rowStart + x * 2 + 1) & ramMask] = color;
|
||||
}
|
||||
break;
|
||||
|
||||
case TileFormat::PceSpriteBpp4:
|
||||
|
@ -261,33 +280,33 @@ void PpuTools::SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t
|
|||
|
||||
switch(format) {
|
||||
case TileFormat::PceSpriteBpp4:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 32, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart + 64, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 96, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 32, shift, 1);
|
||||
setBit(rowStart + 64, shift, 2);
|
||||
setBit(rowStart + 96, shift, 3);
|
||||
break;
|
||||
|
||||
case TileFormat::PceSpriteBpp2Sp01:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 32, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 32, shift, 1);
|
||||
break;
|
||||
|
||||
case TileFormat::PceSpriteBpp2Sp23:
|
||||
setBit(rowStart + 64, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 96, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart + 64, shift, 2);
|
||||
setBit(rowStart + 96, shift, 3);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TileFormat::PceBackgroundBpp2Cg0:
|
||||
setBit(rowStart, shift, color & 0x01);
|
||||
setBit(rowStart + 1, shift, (color & 0x02) >> 1);
|
||||
setBit(rowStart, shift, 0);
|
||||
setBit(rowStart + 1, shift, 1);
|
||||
break;
|
||||
|
||||
case TileFormat::PceBackgroundBpp2Cg1:
|
||||
setBit(rowStart + 16, shift, (color & 0x04) >> 2);
|
||||
setBit(rowStart + 17, shift, (color & 0x08) >> 3);
|
||||
setBit(rowStart + 16, shift, 2);
|
||||
setBit(rowStart + 17, shift, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
@ -195,6 +195,8 @@ protected:
|
|||
|
||||
bool IsTileHidden(MemoryType memType, uint32_t addr, GetTileViewOptions& options);
|
||||
|
||||
void GetSetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t& color, bool forGet);
|
||||
|
||||
uint8_t Rgb555to8Bit(uint8_t color) { return (color << 3) + (color >> 2); }
|
||||
|
||||
uint32_t Rgb555ToArgb(uint16_t rgb555)
|
||||
|
@ -223,6 +225,7 @@ public:
|
|||
virtual void GetSpritePreview(GetSpritePreviewOptions options, BaseState& state, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, uint32_t* outBuffer) = 0;
|
||||
virtual void GetSpriteList(GetSpritePreviewOptions options, BaseState& baseState, uint8_t* vram, uint8_t* oamRam, uint32_t* palette, DebugSpriteInfo outBuffer[]) = 0;
|
||||
|
||||
int32_t GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y);
|
||||
void SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color);
|
||||
virtual void SetPaletteColor(int32_t colorIndex, uint32_t color) = 0;
|
||||
|
||||
|
|
|
@ -165,6 +165,8 @@ extern "C"
|
|||
|
||||
DllExport DebugPaletteInfo __stdcall GetPaletteInfo(CpuType cpuType, GetPaletteInfoOptions options) { return WithTool(DebugPaletteInfo, GetPpuTools(cpuType), GetPaletteInfo(options)); }
|
||||
DllExport void __stdcall SetPaletteColor(CpuType cpuType, int32_t colorIndex, uint32_t color) { WithToolVoid(GetPpuTools(cpuType), SetPaletteColor(colorIndex, color)); }
|
||||
|
||||
DllExport int32_t __stdcall GetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y) { return WithTool(int32_t, GetPpuTools(DebugUtilities::ToCpuType(tileAddress.Type)), GetTilePixel(tileAddress, format, x, y)); }
|
||||
DllExport void __stdcall SetTilePixel(AddressInfo tileAddress, TileFormat format, int32_t x, int32_t y, int32_t color) { WithToolVoid(GetPpuTools(DebugUtilities::ToCpuType(tileAddress.Type)), SetTilePixel(tileAddress, format, x, y, color)); }
|
||||
|
||||
DllExport void __stdcall SetViewerUpdateTiming(uint32_t viewerId, uint16_t scanline, uint16_t cycle, CpuType cpuType) { WithToolVoid(GetPpuTools(cpuType), SetViewerUpdateTiming(viewerId, scanline, cycle)); }
|
||||
|
|
|
@ -312,7 +312,7 @@ namespace Mesen.Debugger.Controls
|
|||
|
||||
PointerPointProperties props = e.GetCurrentPoint(this).Properties;
|
||||
if(props.IsLeftButtonPressed || props.IsRightButtonPressed) {
|
||||
PositionClickedEventArgs args = new(p.Value, props, PositionClickedEvent);
|
||||
PositionClickedEventArgs args = new(p.Value, props, e, PositionClickedEvent);
|
||||
RaiseEvent(args);
|
||||
|
||||
if(!args.Handled && AllowSelection) {
|
||||
|
@ -335,7 +335,7 @@ namespace Mesen.Debugger.Controls
|
|||
return;
|
||||
}
|
||||
|
||||
PositionClickedEventArgs args = new(p.Value, e.GetCurrentPoint(this).Properties, PositionClickedEvent);
|
||||
PositionClickedEventArgs args = new(p.Value, e.GetCurrentPoint(this).Properties, e, PositionClickedEvent);
|
||||
RaiseEvent(args);
|
||||
|
||||
if(!args.Handled && AllowSelection) {
|
||||
|
@ -515,15 +515,18 @@ namespace Mesen.Debugger.Controls
|
|||
|
||||
public class PositionClickedEventArgs : RoutedEventArgs
|
||||
{
|
||||
public PixelPoint Position;
|
||||
public PointerPointProperties Properties;
|
||||
public PixelPoint Position { get; }
|
||||
public PointerPointProperties Properties { get; }
|
||||
public PointerEventArgs OriginalEvent { get; }
|
||||
|
||||
public PositionClickedEventArgs(PixelPoint position, PointerPointProperties properties, RoutedEvent evt)
|
||||
public PositionClickedEventArgs(PixelPoint position, PointerPointProperties properties, PointerEventArgs originalEvent, RoutedEvent evt)
|
||||
{
|
||||
Position = position;
|
||||
Properties = properties;
|
||||
OriginalEvent = originalEvent;
|
||||
RoutedEvent = evt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class GridRowColumn
|
||||
|
|
|
@ -10,13 +10,18 @@ namespace Mesen.Debugger.Utilities
|
|||
{
|
||||
public class PaletteHelper
|
||||
{
|
||||
public static DynamicTooltip? GetPreviewPanel(UInt32[] rgbPalette, UInt32[] rawPalette, RawPaletteFormat format, int index, DynamicTooltip? tooltipToUpdate)
|
||||
public static DynamicTooltip GetPreviewPanel(UInt32[] rgbPalette, UInt32[] rawPalette, RawPaletteFormat format, int index, DynamicTooltip? tooltipToUpdate, int colorsPerPalette = 0)
|
||||
{
|
||||
TooltipEntries entries = tooltipToUpdate?.Items ?? new();
|
||||
entries.StartUpdate();
|
||||
|
||||
entries.AddEntry("Color", new TooltipColorEntry(rgbPalette[index]));
|
||||
entries.AddEntry("Index", "$" + index.ToString("X2"));
|
||||
if(colorsPerPalette > 0) {
|
||||
entries.AddEntry("Index", "$" + (index % colorsPerPalette).ToString("X2"));
|
||||
} else {
|
||||
entries.AddEntry("Index", "$" + index.ToString("X2"));
|
||||
}
|
||||
|
||||
if(format == RawPaletteFormat.Rgb555) {
|
||||
entries.AddEntry("Value", "$" + rawPalette[index].ToString("X4"));
|
||||
entries.AddEntry("R", "$" + (rawPalette[index] & 0x1F).ToString("X2"));
|
||||
|
|
|
@ -106,16 +106,35 @@ public class TileEditorViewModel : DisposableViewModel
|
|||
DebugShortcutManager.RegisterActions(wnd, ViewMenuActions);
|
||||
}
|
||||
|
||||
public void UpdatePixel(PixelPoint position, bool clearPixel)
|
||||
private record TilePixelPositionInfo(int Column, int Row, int TileX, int TileY);
|
||||
private TilePixelPositionInfo GetPositionInfo(PixelPoint position)
|
||||
{
|
||||
int pixelColor = clearPixel ? 0 : SelectedColor % GetColorsPerPalette(_tileFormat);
|
||||
PixelSize tileSize = _tileFormat.GetTileSize();
|
||||
|
||||
|
||||
int column = position.X / tileSize.Width;
|
||||
int row = position.Y / tileSize.Height;
|
||||
int tileX = position.X % tileSize.Width;
|
||||
int tileY = position.Y % tileSize.Height;
|
||||
DebugApi.SetTilePixel(_tileAddresses[column+row*_columnCount], _tileFormat, tileX, tileY, pixelColor);
|
||||
return new(column, row, tileX, tileY);
|
||||
}
|
||||
|
||||
public int GetColorAtPosition(PixelPoint position)
|
||||
{
|
||||
TilePixelPositionInfo pos = GetPositionInfo(position);
|
||||
int paletteColorIndex = DebugApi.GetTilePixel(_tileAddresses[pos.Column + pos.Row * _columnCount], _tileFormat, pos.TileX, pos.TileY);
|
||||
return SelectedColor - (SelectedColor % GetColorsPerPalette(_tileFormat)) + paletteColorIndex;
|
||||
}
|
||||
|
||||
public void SelectColor(PixelPoint position)
|
||||
{
|
||||
SelectedColor = GetColorAtPosition(position);
|
||||
}
|
||||
|
||||
public void UpdatePixel(PixelPoint position, bool clearPixel)
|
||||
{
|
||||
int pixelColor = clearPixel ? 0 : SelectedColor % GetColorsPerPalette(_tileFormat);
|
||||
TilePixelPositionInfo pos = GetPositionInfo(position);
|
||||
DebugApi.SetTilePixel(_tileAddresses[pos.Column+pos.Row*_columnCount], _tileFormat, pos.TileX, pos.TileY, pixelColor);
|
||||
RefreshViewer();
|
||||
}
|
||||
|
||||
|
@ -151,6 +170,11 @@ public class TileEditorViewModel : DisposableViewModel
|
|||
}));
|
||||
}
|
||||
|
||||
public int GetColorsPerPalette()
|
||||
{
|
||||
return GetColorsPerPalette(_tileFormat);
|
||||
}
|
||||
|
||||
private int GetColorsPerPalette(TileFormat format)
|
||||
{
|
||||
return format.GetBitsPerPixel() switch {
|
||||
|
|
|
@ -53,6 +53,8 @@
|
|||
<TextBlock Text="{l:Translate lblHint1}" />
|
||||
<TextBlock Text="{l:Translate lblHint2}" />
|
||||
<TextBlock Text="{l:Translate lblHint3}" />
|
||||
<TextBlock Text="{l:Translate lblHint4}" />
|
||||
<TextBlock Text="{l:Translate lblHint5}" />
|
||||
</StackPanel>
|
||||
<c:GroupBox Header="{l:Translate lblPreview}">
|
||||
<Border BorderBrush="Gray" Background="{StaticResource ViewerBgBrush}" BorderThickness="1" HorizontalAlignment="Left">
|
||||
|
|
|
@ -13,12 +13,16 @@ using System.Collections.Generic;
|
|||
using Avalonia.Threading;
|
||||
using System.Linq;
|
||||
using Mesen.Config;
|
||||
using Avalonia.Input;
|
||||
|
||||
namespace Mesen.Debugger.Windows
|
||||
{
|
||||
public class TileEditorWindow : Window, INotificationHandler
|
||||
{
|
||||
private TileEditorViewModel _model;
|
||||
private PictureViewer _picViewer;
|
||||
private DynamicTooltip? _tileColorTooltip;
|
||||
private PixelPoint? _lastPosition;
|
||||
|
||||
[Obsolete("For designer only")]
|
||||
public TileEditorWindow() : this(new()) { }
|
||||
|
@ -30,10 +34,12 @@ namespace Mesen.Debugger.Windows
|
|||
this.AttachDevTools();
|
||||
#endif
|
||||
|
||||
PictureViewer picViewer = this.GetControl<ScrollPictureViewer>("picViewer").InnerViewer;
|
||||
picViewer.PositionClicked += PicViewer_PositionClicked;
|
||||
_picViewer = this.GetControl<ScrollPictureViewer>("picViewer").InnerViewer;
|
||||
_picViewer.PositionClicked += PicViewer_PositionClicked;
|
||||
_picViewer.PointerMoved += PicViewer_PointerMoved;
|
||||
_picViewer.PointerExited += PicViewer_PointerExited;
|
||||
_model = model;
|
||||
_model.InitActions(picViewer, this);
|
||||
_model.InitActions(_picViewer, this);
|
||||
DataContext = _model;
|
||||
|
||||
_model.Config.LoadWindowSettings(this);
|
||||
|
@ -47,7 +53,60 @@ namespace Mesen.Debugger.Windows
|
|||
|
||||
private void PicViewer_PositionClicked(object? sender, PositionClickedEventArgs e)
|
||||
{
|
||||
_model.UpdatePixel(e.Position, e.Properties.IsRightButtonPressed);
|
||||
if(e.OriginalEvent.KeyModifiers == KeyModifiers.Shift) {
|
||||
_model.SelectColor(e.Position);
|
||||
} else {
|
||||
_model.UpdatePixel(e.Position, e.Properties.IsRightButtonPressed);
|
||||
}
|
||||
}
|
||||
|
||||
private void PicViewer_PointerMoved(object? sender, PointerEventArgs e)
|
||||
{
|
||||
PixelPoint? p = _picViewer.GetGridPointFromMousePoint(e.GetCurrentPoint(_picViewer).Position);
|
||||
if(_lastPosition != p) {
|
||||
_lastPosition = p;
|
||||
|
||||
if(e.KeyModifiers != KeyModifiers.Shift) {
|
||||
TooltipHelper.HideTooltip(_picViewer);
|
||||
return;
|
||||
}
|
||||
|
||||
if(_lastPosition != null) {
|
||||
ShowColorTooltip();
|
||||
} else {
|
||||
TooltipHelper.HideTooltip(_picViewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowColorTooltip()
|
||||
{
|
||||
if(_lastPosition != null) {
|
||||
int colorIndex = _model.GetColorAtPosition(_lastPosition.Value);
|
||||
_tileColorTooltip = PaletteHelper.GetPreviewPanel(_model.PaletteColors, _model.RawPalette, _model.RawFormat, colorIndex, _tileColorTooltip, _model.GetColorsPerPalette());
|
||||
TooltipHelper.ShowTooltip(_picViewer, _tileColorTooltip, 15);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyDown(e);
|
||||
if(e.Key == Key.LeftShift || e.Key == Key.RightShift) {
|
||||
ShowColorTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnKeyUp(KeyEventArgs e)
|
||||
{
|
||||
base.OnKeyUp(e);
|
||||
if(e.Key == Key.LeftShift || e.Key == Key.RightShift) {
|
||||
TooltipHelper.HideTooltip(_picViewer);
|
||||
}
|
||||
}
|
||||
|
||||
private void PicViewer_PointerExited(object? sender, PointerEventArgs e)
|
||||
{
|
||||
TooltipHelper.HideTooltip(_picViewer);
|
||||
}
|
||||
|
||||
private void InitializeComponent()
|
||||
|
|
|
@ -347,6 +347,8 @@ namespace Mesen.Interop
|
|||
|
||||
[DllImport(DllPath)] public static extern DebugPaletteInfo GetPaletteInfo(CpuType cpuType, GetPaletteInfoOptions options = new());
|
||||
[DllImport(DllPath)] public static extern void SetPaletteColor(CpuType cpuType, int colorIndex, UInt32 color);
|
||||
|
||||
[DllImport(DllPath)] public static extern int GetTilePixel(AddressInfo tileAddress, TileFormat format, int x, int y);
|
||||
[DllImport(DllPath)] public static extern void SetTilePixel(AddressInfo tileAddress, TileFormat format, int x, int y, int color);
|
||||
|
||||
[DllImport(DllPath)] public static extern void SetViewerUpdateTiming(Int32 viewerId, Int32 scanline, Int32 cycle, CpuType cpuType);
|
||||
|
|
|
@ -824,6 +824,8 @@
|
|||
<Control ID="lblHint1">Select color by clicking above.</Control>
|
||||
<Control ID="lblHint2">Left-click: Draw selected color</Control>
|
||||
<Control ID="lblHint3">Right-click: Make pixel transparent</Control>
|
||||
<Control ID="lblHint4">Shift-click: Select color under cursor</Control>
|
||||
<Control ID="lblHint5">Hold shift: Details on pixel under cursor</Control>
|
||||
</Form>
|
||||
|
||||
<Form ID="TileViewerWindow">
|
||||
|
|
Loading…
Add table
Reference in a new issue