Updated graphics renderer to allow for pixel perfect resizing
This commit is contained in:
18
Desktop/Form1.Designer.cs
generated
18
Desktop/Form1.Designer.cs
generated
@@ -28,7 +28,6 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
picScreen = new PictureBox();
|
|
||||||
menuStrip1 = new MenuStrip();
|
menuStrip1 = new MenuStrip();
|
||||||
fileToolStripMenuItem = new ToolStripMenuItem();
|
fileToolStripMenuItem = new ToolStripMenuItem();
|
||||||
openToolStripMenuItem = new ToolStripMenuItem();
|
openToolStripMenuItem = new ToolStripMenuItem();
|
||||||
@@ -44,20 +43,9 @@
|
|||||||
resetToolStripMenuItem1 = new ToolStripMenuItem();
|
resetToolStripMenuItem1 = new ToolStripMenuItem();
|
||||||
optionsToolStripMenuItem = new ToolStripMenuItem();
|
optionsToolStripMenuItem = new ToolStripMenuItem();
|
||||||
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
|
HighSpeedToolStripMenuItem = new ToolStripMenuItem();
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).BeginInit();
|
|
||||||
menuStrip1.SuspendLayout();
|
menuStrip1.SuspendLayout();
|
||||||
SuspendLayout();
|
SuspendLayout();
|
||||||
//
|
//
|
||||||
// picScreen
|
|
||||||
//
|
|
||||||
picScreen.BackColor = Color.Black;
|
|
||||||
picScreen.Location = new Point(10, 26);
|
|
||||||
picScreen.Name = "picScreen";
|
|
||||||
picScreen.Size = new Size(720, 576);
|
|
||||||
picScreen.SizeMode = PictureBoxSizeMode.Zoom;
|
|
||||||
picScreen.TabIndex = 0;
|
|
||||||
picScreen.TabStop = false;
|
|
||||||
//
|
|
||||||
// menuStrip1
|
// menuStrip1
|
||||||
//
|
//
|
||||||
menuStrip1.ImageScalingSize = new Size(24, 24);
|
menuStrip1.ImageScalingSize = new Size(24, 24);
|
||||||
@@ -163,7 +151,7 @@
|
|||||||
// HighSpeedToolStripMenuItem
|
// HighSpeedToolStripMenuItem
|
||||||
//
|
//
|
||||||
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
|
HighSpeedToolStripMenuItem.Name = "HighSpeedToolStripMenuItem";
|
||||||
HighSpeedToolStripMenuItem.Size = new Size(224, 26);
|
HighSpeedToolStripMenuItem.Size = new Size(170, 26);
|
||||||
HighSpeedToolStripMenuItem.Text = "High Speed";
|
HighSpeedToolStripMenuItem.Text = "High Speed";
|
||||||
HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click;
|
HighSpeedToolStripMenuItem.Click += btnHighSpeedToggle_Click;
|
||||||
//
|
//
|
||||||
@@ -172,13 +160,11 @@
|
|||||||
AutoScaleDimensions = new SizeF(8F, 20F);
|
AutoScaleDimensions = new SizeF(8F, 20F);
|
||||||
AutoScaleMode = AutoScaleMode.Font;
|
AutoScaleMode = AutoScaleMode.Font;
|
||||||
ClientSize = new Size(738, 608);
|
ClientSize = new Size(738, 608);
|
||||||
Controls.Add(picScreen);
|
|
||||||
Controls.Add(menuStrip1);
|
Controls.Add(menuStrip1);
|
||||||
KeyPreview = true;
|
KeyPreview = true;
|
||||||
MainMenuStrip = menuStrip1;
|
MainMenuStrip = menuStrip1;
|
||||||
Name = "Form1";
|
Name = "Form1";
|
||||||
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
Text = "Parsons Sinclair ZX Spectrum 48K - 2026";
|
||||||
((System.ComponentModel.ISupportInitialize)picScreen).EndInit();
|
|
||||||
menuStrip1.ResumeLayout(false);
|
menuStrip1.ResumeLayout(false);
|
||||||
menuStrip1.PerformLayout();
|
menuStrip1.PerformLayout();
|
||||||
ResumeLayout(false);
|
ResumeLayout(false);
|
||||||
@@ -186,8 +172,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private PictureBox picScreen;
|
|
||||||
private MenuStrip menuStrip1;
|
private MenuStrip menuStrip1;
|
||||||
private ToolStripMenuItem fileToolStripMenuItem;
|
private ToolStripMenuItem fileToolStripMenuItem;
|
||||||
private ToolStripMenuItem openToolStripMenuItem;
|
private ToolStripMenuItem openToolStripMenuItem;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace Desktop
|
|||||||
private TapManager _tapManager = null!;
|
private TapManager _tapManager = null!;
|
||||||
private DebuggerForm? _debugger = null;
|
private DebuggerForm? _debugger = null;
|
||||||
private BeeperDevice _beeper = null!;
|
private BeeperDevice _beeper = null!;
|
||||||
|
private Bitmap _screenBitmap = null!;
|
||||||
private string _baseTitle = "";
|
private string _baseTitle = "";
|
||||||
private bool _isRunning = false;
|
private bool _isRunning = false;
|
||||||
private bool _isPaused = false;
|
private bool _isPaused = false;
|
||||||
@@ -34,6 +35,8 @@ namespace Desktop
|
|||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
this.DoubleBuffered = true;
|
||||||
|
this.ResizeRedraw = true;
|
||||||
InitializeEmulator();
|
InitializeEmulator();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +176,10 @@ namespace Desktop
|
|||||||
{
|
{
|
||||||
this.BeginInvoke((MethodInvoker)delegate
|
this.BeginInvoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
UpdateScreenBitmap();
|
UpdateScreenBitmap(); // Your existing method that writes the ULA data to _screenBitmap
|
||||||
|
|
||||||
|
this.Invalidate(); // Tells Windows: "The bitmap changed, please run OnPaint()!"
|
||||||
|
|
||||||
this.Text = $"{_baseTitle} - FPS: {FramesPerSecond:F1}";
|
this.Text = $"{_baseTitle} - FPS: {FramesPerSecond:F1}";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -181,10 +187,13 @@ namespace Desktop
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.Invoke((MethodInvoker)delegate
|
this.BeginInvoke((MethodInvoker)delegate
|
||||||
{
|
{
|
||||||
UpdateScreenBitmap();
|
UpdateScreenBitmap(); // Your existing method that writes the ULA data to _screenBitmap
|
||||||
this.Text = $"{_baseTitle} - FPS: {FramesPerSecond:F1} - Tape Loaded: {tapeLoaded.ToString()}";
|
|
||||||
|
this.Invalidate(); // Tells Windows: "The bitmap changed, please run OnPaint()!"
|
||||||
|
|
||||||
|
this.Text = $"{_baseTitle} - FPS: {FramesPerSecond:F1}";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +234,40 @@ namespace Desktop
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnPaint(PaintEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnPaint(e);
|
||||||
|
|
||||||
|
// If we don't have a screen bitmap yet, just paint the background black and exit
|
||||||
|
if (_screenBitmap == null)
|
||||||
|
{
|
||||||
|
e.Graphics.Clear(Color.Black);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Calculate the scaling factor to fit the window while preserving aspect ratio
|
||||||
|
float scaleX = (float)this.ClientSize.Width / _screenBitmap.Width;
|
||||||
|
float scaleY = (float)this.ClientSize.Height / _screenBitmap.Height;
|
||||||
|
float scale = Math.Min(scaleX, scaleY); // Pick the smallest scale so it fits inside
|
||||||
|
|
||||||
|
int newWidth = (int)(_screenBitmap.Width * scale);
|
||||||
|
int newHeight = (int)(_screenBitmap.Height * scale);
|
||||||
|
|
||||||
|
// 2. Center the image horizontally and vertically (Pillarboxing/Letterboxing)
|
||||||
|
int posX = (this.ClientSize.Width - newWidth) / 2;
|
||||||
|
int posY = (this.ClientSize.Height - newHeight) / 2;
|
||||||
|
|
||||||
|
// 3. RETRO MAGIC: Force Nearest-Neighbor interpolation for razor-sharp pixels!
|
||||||
|
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
|
||||||
|
|
||||||
|
// PixelOffsetMode.Half is a GDI+ trick required to make NearestNeighbor perfectly accurate
|
||||||
|
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
|
||||||
|
|
||||||
|
// 4. Paint the black borders (if any) and draw the screen
|
||||||
|
e.Graphics.Clear(Color.Black);
|
||||||
|
e.Graphics.DrawImage(_screenBitmap, posX, posY, newWidth, newHeight);
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleInstantTapeLoad()
|
private void HandleInstantTapeLoad()
|
||||||
{
|
{
|
||||||
byte[] block = _tapManager.GetNextBlock(); // Your original Queue.Dequeue() method
|
byte[] block = _tapManager.GetNextBlock(); // Your original Queue.Dequeue() method
|
||||||
@@ -271,7 +314,7 @@ namespace Desktop
|
|||||||
_cpu.SP++;
|
_cpu.SP++;
|
||||||
_cpu.PC = (ushort)((pcHigh << 8) | pcLow);
|
_cpu.PC = (ushort)((pcHigh << 8) | pcLow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void UpdateScreenBitmap()
|
private void UpdateScreenBitmap()
|
||||||
{
|
{
|
||||||
@@ -286,8 +329,14 @@ namespace Desktop
|
|||||||
Marshal.Copy(_ula.FrameBuffer, 0, bmpData.Scan0, _ula.FrameBuffer.Length);
|
Marshal.Copy(_ula.FrameBuffer, 0, bmpData.Scan0, _ula.FrameBuffer.Length);
|
||||||
bmp.UnlockBits(bmpData);
|
bmp.UnlockBits(bmpData);
|
||||||
|
|
||||||
if (picScreen.Image != null) picScreen.Image.Dispose();
|
// Dispose of the old frame to prevent massive RAM leaks!
|
||||||
picScreen.Image = bmp;
|
if (_screenBitmap != null)
|
||||||
|
{
|
||||||
|
_screenBitmap.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the new frame so OnPaint() can draw it
|
||||||
|
_screenBitmap = bmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadTAPToolStripMenuItem_Click(object sender, EventArgs e)
|
private void loadTAPToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
|
|||||||
Reference in New Issue
Block a user