Thank you so much @Huajiang that makes total sense now!!
I have made code below which uses the winform to screenshot the visible area and then crop it to just the deck:
- Uses percentage to crop the right control bar
- Uses color to set background to transparent and crop to just the device
using Hamilton.HxSys3DView;
using HxSysDeckLib;
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Test
{
[Guid("9654cd04-832b-4bfd-93ce-70423c3c3713")]
[ComVisible(true)]
public interface IDeckLayoutView
{
string CaptureDeckImage(string file, string savePath);
}
[Guid("d163dfbb-de10-44aa-b1ad-b73fc24424e0")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("Test.DeckLayoutView")]
[ComVisible(true)]
public class DeckLayoutView : IDeckLayoutView
{
private static Form CreateHiddenDeckForm(string file, out HxInstrument3DView view)
{
var form = new Form
{
Width = 1920,
Height = 1080,
Text = "Hidden Deck Capture",
Visible = false
};
var HxSystemDeck = new SystemDeck();
HxSystemDeck.InitSystemFromFile(file);
view = new HxInstrument3DView();
view.Initialize(form.Handle.ToInt32(), HxSystemDeck, "ML_STAR");
view.Mode = HxSys3DViewMode.RunVisualization;
view.ModifyEnable = false;
return form;
}
public string CaptureDeckImage(string file, string savePath)
{
string finalPath = savePath;
Thread t = new Thread(() =>
{
Form form = CreateHiddenDeckForm(file, out HxInstrument3DView view);
form.Show();
form.Refresh();
Thread.Sleep(2000);
for (int i = 0; i < 5; i++)
{
SendKeys.SendWait("^{+}");
Thread.Sleep(500);
}
Thread.Sleep(2000);
Bitmap deckImage = CaptureClientArea(form.Handle);
Bitmap processedImage = ProcessImage(deckImage);
processedImage.Save(savePath, ImageFormat.Png);
Console.WriteLine($"Deck layout image saved at: {savePath}");
form.Close();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
t.Join();
return finalPath;
}
private static Bitmap CaptureClientArea(IntPtr hwnd)
{
RECT rect;
GetClientRect(hwnd, out rect);
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
Bitmap bitmap = new Bitmap(width, height);
using (Graphics g = Graphics.FromImage(bitmap))
{
IntPtr hdcBitmap = g.GetHdc();
IntPtr hdcWindow = GetDC(hwnd);
BitBlt(hdcBitmap, 0, 0, width, height, hdcWindow, 0, 0, SRCCOPY);
ReleaseDC(hwnd, hdcWindow);
g.ReleaseHdc(hdcBitmap);
}
// Crop 5% from the right
int cropWidth = (int)(width * 0.95);
Rectangle cropArea = new Rectangle(0, 0, cropWidth, height);
Bitmap croppedBitmap = bitmap.Clone(cropArea, bitmap.PixelFormat);
return croppedBitmap;
}
private static Bitmap ProcessImage(Bitmap image)
{
Color transparentColor = Color.FromArgb(0xF5, 0xEB, 0xD7);
image.MakeTransparent(transparentColor);
return CropToContent(image);
}
private static Bitmap CropToContent(Bitmap source)
{
int minX = source.Width, minY = source.Height, maxX = 0, maxY = 0;
for (int y = 0; y < source.Height; y++)
{
for (int x = 0; x < source.Width; x++)
{
if (source.GetPixel(x, y).A != 0)
{
if (x < minX) minX = x;
if (y < minY) minY = y;
if (x > maxX) maxX = x;
if (y > maxY) maxY = y;
}
}
}
int cropWidth = maxX - minX + 1;
int cropHeight = maxY - minY + 1;
Rectangle cropRect = new Rectangle(minX, minY, cropWidth, cropHeight);
return source.Clone(cropRect, source.PixelFormat);
}
#region Windows API
private const int SRCCOPY = 0x00CC0020;
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int w, int h,
IntPtr hdcSrc, int xSrc, int ySrc, int rop);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
#endregion
}
}
Maybe you have some ideas on how to make this process more efficient/ how to keep the winform hidden during the process?
If getting the .x directx file is possible maybe that would allow for the resultant image be a vector such as an svg vs just a bitmap which would be even more ideal.
Let me know your thoughts!