
Adds a Fonts type the render interface. Here's an example of how you could use this in `gui.zig`: ```zig const fonts = render.Fonts.init(); defer fonts.deinit(); const count = fonts.count(); std.log.info("{} fonts", .{count}); for (0..count) |font_index| { const name = fonts.getName(font_index); std.log.info("font {} '{}'", .{ font_index, std.unicode.fmtUtf16Le(name.slice()) }); } ```
117 lines
3.4 KiB
Zig
117 lines
3.4 KiB
Zig
const DwriteRenderer = @This();
|
|
|
|
const std = @import("std");
|
|
const win32 = @import("win32").everything;
|
|
const win32ext = @import("win32ext.zig");
|
|
|
|
const dwrite = @import("dwrite.zig");
|
|
const XY = @import("xy.zig").XY;
|
|
|
|
pub const Font = dwrite.Font;
|
|
pub const Fonts = dwrite.Fonts;
|
|
|
|
pub const needs_direct2d = true;
|
|
|
|
render_target: *win32.ID2D1RenderTarget,
|
|
white_brush: *win32.ID2D1SolidColorBrush,
|
|
pub fn init(
|
|
d2d_factory: *win32.ID2D1Factory,
|
|
texture: *win32.ID3D11Texture2D,
|
|
) DwriteRenderer {
|
|
const dxgi_surface = win32ext.queryInterface(texture, win32.IDXGISurface);
|
|
defer _ = dxgi_surface.IUnknown.Release();
|
|
|
|
var render_target: *win32.ID2D1RenderTarget = undefined;
|
|
{
|
|
const props = win32.D2D1_RENDER_TARGET_PROPERTIES{
|
|
.type = .DEFAULT,
|
|
.pixelFormat = .{
|
|
.format = .A8_UNORM,
|
|
.alphaMode = .PREMULTIPLIED,
|
|
},
|
|
.dpiX = 0,
|
|
.dpiY = 0,
|
|
.usage = .{},
|
|
.minLevel = .DEFAULT,
|
|
};
|
|
const hr = d2d_factory.CreateDxgiSurfaceRenderTarget(
|
|
dxgi_surface,
|
|
&props,
|
|
&render_target,
|
|
);
|
|
if (hr < 0) fatalHr("CreateDxgiSurfaceRenderTarget", hr);
|
|
}
|
|
errdefer _ = render_target.IUnknown.Release();
|
|
|
|
{
|
|
const dc = win32ext.queryInterface(render_target, win32.ID2D1DeviceContext);
|
|
defer _ = dc.IUnknown.Release();
|
|
dc.SetUnitMode(win32.D2D1_UNIT_MODE_PIXELS);
|
|
}
|
|
|
|
var white_brush: *win32.ID2D1SolidColorBrush = undefined;
|
|
{
|
|
const hr = render_target.CreateSolidColorBrush(
|
|
&.{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 1.0 },
|
|
null,
|
|
&white_brush,
|
|
);
|
|
if (hr < 0) fatalHr("CreateSolidColorBrush", hr);
|
|
}
|
|
errdefer _ = white_brush.IUnknown.Release();
|
|
|
|
return .{
|
|
.render_target = render_target,
|
|
.white_brush = white_brush,
|
|
};
|
|
}
|
|
pub fn deinit(self: *DwriteRenderer) void {
|
|
_ = self.white_brush.IUnknown.Release();
|
|
_ = self.render_target.IUnknown.Release();
|
|
self.* = undefined;
|
|
}
|
|
|
|
pub fn render(
|
|
self: *const DwriteRenderer,
|
|
font: Font,
|
|
utf8: []const u8,
|
|
) void {
|
|
var utf16_buf: [10]u16 = undefined;
|
|
const utf16_len = std.unicode.utf8ToUtf16Le(&utf16_buf, utf8) catch unreachable;
|
|
const utf16 = utf16_buf[0..utf16_len];
|
|
std.debug.assert(utf16.len <= 2);
|
|
|
|
{
|
|
const rect: win32.D2D_RECT_F = .{
|
|
.left = 0,
|
|
.top = 0,
|
|
.right = @floatFromInt(font.cell_size.x),
|
|
.bottom = @floatFromInt(font.cell_size.y),
|
|
};
|
|
self.render_target.BeginDraw();
|
|
{
|
|
const color: win32.D2D_COLOR_F = .{ .r = 0, .g = 0, .b = 0, .a = 0 };
|
|
self.render_target.Clear(&color);
|
|
}
|
|
self.render_target.DrawText(
|
|
@ptrCast(utf16.ptr),
|
|
@intCast(utf16.len),
|
|
font.text_format,
|
|
&rect,
|
|
&self.white_brush.ID2D1Brush,
|
|
.{},
|
|
.NATURAL,
|
|
);
|
|
var tag1: u64 = undefined;
|
|
var tag2: u64 = undefined;
|
|
const hr = self.render_target.EndDraw(&tag1, &tag2);
|
|
if (hr < 0) std.debug.panic(
|
|
"D2D DrawText failed, hresult=0x{x}, tag1={}, tag2={}",
|
|
.{ @as(u32, @bitCast(hr)), tag1, tag2 },
|
|
);
|
|
}
|
|
}
|
|
|
|
fn fatalHr(what: []const u8, hresult: win32.HRESULT) noreturn {
|
|
std.debug.panic("{s} failed, hresult=0x{x}", .{ what, @as(u32, @bitCast(hresult)) });
|
|
}
|