using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using BizHawk.Common.NumberExtensions;
using BizHawk.Client.Common;
using BizHawk.Emulation.Cores.Nintendo.Gameboy;
using BizHawk.Client.EmuHawk.WinFormExtensions;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy;
using BizHawk.Common;
namespace BizHawk.Client.EmuHawk
{
public partial class GBPrinterView : Form, IToolFormAutoConfig
{
const int PaperWidth = 160;
// the bg color
private static readonly uint PaperColor = (uint)Color.AntiqueWhite.ToArgb();
private ColorMatrix PaperAdjustment;
[RequiredService]
public IGameboyCommon Gb { get; private set; }
// If we've connected the printer yet
bool connected = false;
// the entire bitmap
Bitmap printerHistory;
public GBPrinterView()
{
InitializeComponent();
// adjust the color of the printed output to be more papery
PaperAdjustment = new ColorMatrix();
PaperAdjustment.Matrix00 = (0xFA - 0x10) / 255F;
PaperAdjustment.Matrix40 = 0x10 / 255F;
PaperAdjustment.Matrix11 = (0xEB - 0x10) / 255F;
PaperAdjustment.Matrix41 = 0x10 / 255F;
PaperAdjustment.Matrix22 = (0xD7 - 0x18) / 255F;
PaperAdjustment.Matrix42 = 0x18 / 255F;
paperView.ChangeBitmapSize(PaperWidth, PaperWidth);
ClearPaper();
}
private void GBPrinterView_FormClosed(object sender, FormClosedEventArgs e)
{
if (Gb != null)
{
Gb.SetPrinterCallback(null);
}
}
public bool UpdateBefore => false;
public bool AskSaveChanges() => true;
public void FastUpdate()
{
}
public void NewUpdate(ToolFormUpdateType type)
{
}
public void Restart()
{
// Really, there's not necessarilly a reason to clear it at all,
// since the paper would still be there,
// but it just seems right to get a blank slate on reset.
ClearPaper();
connected = false;
}
public void UpdateValues()
{
// Automatically connect once the game is running
if (!connected)
{
Gb.SetPrinterCallback(OnPrint);
connected = true;
}
}
///
/// The printer callback that . See PrinterCallback for details.
///
void OnPrint(IntPtr image, byte height, byte topMargin, byte bottomMargin, byte exposure)
{
// In this implementation:
// the bottom margin and top margin are just white lines at the top and bottom
// exposure is ignored
// The page received image
Bitmap page = new Bitmap(PaperWidth, height);
var bmp = page.LockBits(new Rectangle(0, 0, PaperWidth, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < PaperWidth; x++)
{
uint pixel;
unsafe
{
// Pixel of the image; it's just sent from the core as a big bitmap that's 160xheight
pixel = *(uint*)(image + (x + y * PaperWidth) * sizeof(uint));
}
SetPixel(bmp, x, y, pixel);
}
}
page.UnlockBits(bmp);
// add it to the bottom of the history
int oldHeight = printerHistory.Height;
ResizeHistory(printerHistory.Height + page.Height + topMargin + bottomMargin);
using (var g = Graphics.FromImage(printerHistory))
{
// Make it brown
ImageAttributes a = new ImageAttributes();
a.SetColorMatrix(PaperAdjustment);
g.DrawImage(page, new Rectangle(0, oldHeight + topMargin, page.Width, page.Height), 0F, 0F, page.Width, page.Height, GraphicsUnit.Pixel, a);
g.Flush();
}
RefreshView();
}
///
/// Set a 2x pixel
///
/// The bitmap data to draw to
/// X position
/// Y position
/// The ARGB color to set that pixel to
unsafe void SetPixel(BitmapData bmp, int x, int y, uint c)
{
uint* pixel = (uint*)(bmp.Scan0 + x * 4 + y * bmp.Stride);
*pixel = c;
}
void ClearPaper()
{
ResizeHistory(8);
RefreshView();
}
void ResizeHistory(int height)
{
// copy to a new image of height
var newHistory = new Bitmap(PaperWidth, height);
using (var g = Graphics.FromImage(newHistory))
{
g.Clear(Color.FromArgb((int)PaperColor));
if (printerHistory != null)
g.DrawImage(printerHistory, Point.Empty);
g.Flush();
}
if (printerHistory != null)
printerHistory.Dispose();
printerHistory = newHistory;
// Update scrollbar, viewport is a square
paperScroll.Maximum = Math.Max(0, height);
}
void RefreshView()
{
using (Graphics g = Graphics.FromImage(paperView.BMP))
{
g.Clear(Color.FromArgb((int)PaperColor));
g.DrawImage(printerHistory, new Point(0, -paperScroll.Value));
g.Flush();
}
paperView.Refresh();
}
private void saveImageToolStripMenuItem_Click(object sender, EventArgs e)
{
// slight hack to use the nice SaveFile() feature of a BmpView
BmpView toSave = new BmpView();
toSave.ChangeBitmapSize(printerHistory.Size);
using (var g = Graphics.FromImage(toSave.BMP))
{
g.DrawImage(printerHistory, Point.Empty);
g.Flush();
}
toSave.SaveFile();
}
private void copyToolStripMenuItem_Click(object sender, EventArgs e)
{
Clipboard.SetImage(printerHistory);
}
private void PaperScroll_ValueChanged(object sender, System.EventArgs e)
{
RefreshView();
}
}
}