poe2-bot/src/Automata.Ui/Overlay/D2dRenderContext.cs
2026-03-06 00:38:30 -05:00

158 lines
6.8 KiB
C#

using Vortice.Direct2D1;
using Vortice.DirectWrite;
using Vortice.DXGI;
using Vortice.Mathematics;
using DWriteFactory = Vortice.DirectWrite.IDWriteFactory;
using D2dFactoryType = Vortice.Direct2D1.FactoryType;
using DwFactoryType = Vortice.DirectWrite.FactoryType;
namespace Automata.Ui.Overlay;
public sealed class D2dRenderContext : IDisposable
{
private readonly nint _hwnd;
private readonly int _width;
private readonly int _height;
// Factories
public ID2D1Factory1 D2dFactory { get; }
public DWriteFactory DWriteFactory { get; }
// Render target (recreated on device loss)
public ID2D1HwndRenderTarget RenderTarget { get; private set; } = null!;
// Pre-created brushes
public ID2D1SolidColorBrush Red { get; private set; } = null!;
public ID2D1SolidColorBrush Yellow { get; private set; } = null!;
public ID2D1SolidColorBrush Cyan { get; private set; } = null!;
public ID2D1SolidColorBrush Green { get; private set; } = null!;
public ID2D1SolidColorBrush White { get; private set; } = null!;
public ID2D1SolidColorBrush Gray { get; private set; } = null!;
public ID2D1SolidColorBrush LifeBrush { get; private set; } = null!;
public ID2D1SolidColorBrush ManaBrush { get; private set; } = null!;
public ID2D1SolidColorBrush ShieldBrush { get; private set; } = null!;
public ID2D1SolidColorBrush BarBgBrush { get; private set; } = null!;
public ID2D1SolidColorBrush LabelBgBrush { get; private set; } = null!;
public ID2D1SolidColorBrush DebugTextBrush { get; private set; } = null!;
public ID2D1SolidColorBrush TimingBrush { get; private set; } = null!;
public ID2D1SolidColorBrush DebugBgBrush { get; private set; } = null!;
public ID2D1SolidColorBrush ProfilerBrush { get; private set; } = null!;
// Rarity brushes for entity labels
public ID2D1SolidColorBrush MagicBrush { get; private set; } = null!;
public ID2D1SolidColorBrush RareBrush { get; private set; } = null!;
public ID2D1SolidColorBrush UniqueBrush { get; private set; } = null!;
// Text formats
public IDWriteTextFormat LabelFormat { get; } // 12pt — enemy labels
public IDWriteTextFormat BarValueFormat { get; } // 11pt — bar values
public IDWriteTextFormat DebugFormat { get; } // 13pt — debug overlay
public D2dRenderContext(nint hwnd, int width, int height)
{
_hwnd = hwnd;
_width = width;
_height = height;
D2dFactory = D2D1.D2D1CreateFactory<ID2D1Factory1>(D2dFactoryType.SingleThreaded);
DWriteFactory = Vortice.DirectWrite.DWrite.DWriteCreateFactory<DWriteFactory>(DwFactoryType.Shared);
CreateRenderTarget();
CreateBrushes();
// Text formats (these survive device loss — they're DWrite, not D2D)
LabelFormat = DWriteFactory.CreateTextFormat("Consolas", 12f);
BarValueFormat = DWriteFactory.CreateTextFormat("Consolas", 11f);
DebugFormat = DWriteFactory.CreateTextFormat("Consolas", 13f);
}
private void CreateRenderTarget()
{
var rtProps = new RenderTargetProperties
{
Type = RenderTargetType.Software,
PixelFormat = new Vortice.DCommon.PixelFormat(Format.B8G8R8A8_UNorm, Vortice.DCommon.AlphaMode.Premultiplied),
};
var hwndProps = new HwndRenderTargetProperties
{
Hwnd = _hwnd,
PixelSize = new SizeI(_width, _height),
PresentOptions = PresentOptions.Immediately,
};
RenderTarget = D2dFactory.CreateHwndRenderTarget(rtProps, hwndProps);
RenderTarget.TextAntialiasMode = Vortice.Direct2D1.TextAntialiasMode.Grayscale;
}
private void CreateBrushes()
{
Red = RenderTarget.CreateSolidColorBrush(new Color4(1f, 0f, 0f, 1f));
Yellow = RenderTarget.CreateSolidColorBrush(new Color4(1f, 1f, 0f, 1f));
Cyan = RenderTarget.CreateSolidColorBrush(new Color4(0f, 1f, 1f, 1f));
Green = RenderTarget.CreateSolidColorBrush(new Color4(0.31f, 1f, 0.31f, 1f)); // 80,255,80
White = RenderTarget.CreateSolidColorBrush(new Color4(1f, 1f, 1f, 1f));
Gray = RenderTarget.CreateSolidColorBrush(new Color4(0.5f, 0.5f, 0.5f, 1f));
LifeBrush = RenderTarget.CreateSolidColorBrush(new Color4(200 / 255f, 40 / 255f, 40 / 255f, 1f));
ManaBrush = RenderTarget.CreateSolidColorBrush(new Color4(40 / 255f, 80 / 255f, 200 / 255f, 1f));
ShieldBrush = RenderTarget.CreateSolidColorBrush(new Color4(100 / 255f, 180 / 255f, 220 / 255f, 1f));
BarBgBrush = RenderTarget.CreateSolidColorBrush(new Color4(20 / 255f, 20 / 255f, 20 / 255f, 140 / 255f));
LabelBgBrush = RenderTarget.CreateSolidColorBrush(new Color4(0f, 0f, 0f, 160 / 255f));
DebugTextBrush = RenderTarget.CreateSolidColorBrush(new Color4(80 / 255f, 1f, 80 / 255f, 1f));
TimingBrush = RenderTarget.CreateSolidColorBrush(new Color4(1f, 200 / 255f, 80 / 255f, 1f));
DebugBgBrush = RenderTarget.CreateSolidColorBrush(new Color4(0f, 0f, 0f, 160 / 255f));
ProfilerBrush = RenderTarget.CreateSolidColorBrush(new Color4(180 / 255f, 140 / 255f, 1f, 1f)); // light purple
MagicBrush = RenderTarget.CreateSolidColorBrush(new Color4(0.4f, 0.53f, 1f, 1f)); // #6688FF
RareBrush = RenderTarget.CreateSolidColorBrush(new Color4(1f, 0.93f, 0.34f, 1f)); // #FFEE57
UniqueBrush = RenderTarget.CreateSolidColorBrush(new Color4(1f, 0.55f, 0f, 1f)); // #FF8C00
}
private void DisposeBrushes()
{
Red?.Dispose();
Yellow?.Dispose();
Cyan?.Dispose();
Green?.Dispose();
White?.Dispose();
Gray?.Dispose();
LifeBrush?.Dispose();
ManaBrush?.Dispose();
ShieldBrush?.Dispose();
BarBgBrush?.Dispose();
LabelBgBrush?.Dispose();
DebugTextBrush?.Dispose();
TimingBrush?.Dispose();
DebugBgBrush?.Dispose();
ProfilerBrush?.Dispose();
MagicBrush?.Dispose();
RareBrush?.Dispose();
UniqueBrush?.Dispose();
}
/// <summary>
/// Call after EndDraw returns D2DERR_RECREATE_TARGET.
/// Recreates the render target and all device-dependent resources.
/// </summary>
public void RecreateTarget()
{
DisposeBrushes();
RenderTarget?.Dispose();
CreateRenderTarget();
CreateBrushes();
}
/// <summary>Create a text layout for measurement + drawing.</summary>
public IDWriteTextLayout CreateTextLayout(string text, IDWriteTextFormat format, float maxWidth = 4096f, float maxHeight = 4096f)
{
return DWriteFactory.CreateTextLayout(text, format, maxWidth, maxHeight);
}
public void Dispose()
{
DisposeBrushes();
RenderTarget?.Dispose();
LabelFormat?.Dispose();
BarValueFormat?.Dispose();
DebugFormat?.Dispose();
DWriteFactory?.Dispose();
D2dFactory?.Dispose();
}
}