pub mod d2d1;
pub mod d3d11;
pub mod d3d12;
use crate::*;
use std::sync::Arc;
use windows::Win32::{
Graphics::Direct2D::*,
Graphics::DirectWrite::*,
Graphics::Imaging::D2D::*,
Graphics::Imaging::*,
System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance},
};
pub trait Target {
fn bitmap(&self) -> &ID2D1Bitmap1;
fn size(&self) -> Size<f32>;
fn pixel_size(&self) -> Size<u32>;
}
pub trait Backend {
type RenderTarget: Target;
fn d2d1_factory(&self) -> &ID2D1Factory6;
fn d2d1_device(&self) -> &ID2D1Device5;
fn begin_draw(&self, _target: &Self::RenderTarget) {}
fn end_draw(&self, _target: &Self::RenderTarget) -> Result<()> {
Ok(())
}
}
pub(crate) struct FontFileLoader {
factory: IDWriteFactory6,
loader: IDWriteInMemoryFontFileLoader,
}
impl FontFileLoader {
fn new(factory: &IDWriteFactory6) -> Result<Self> {
let loader = unsafe { factory.CreateInMemoryFontFileLoader()? };
unsafe {
factory.RegisterFontFileLoader(&loader)?;
}
Ok(Self {
factory: factory.clone(),
loader,
})
}
pub(crate) fn handle(&self) -> &IDWriteInMemoryFontFileLoader {
&self.loader
}
}
impl Drop for FontFileLoader {
fn drop(&mut self) {
unsafe {
self.factory.UnregisterFontFileLoader(&self.loader).ok();
}
}
}
#[derive(Clone)]
pub struct Context<T: Backend> {
pub(crate) backend: Arc<T>,
pub(crate) d2d1_device_context: ID2D1DeviceContext5,
pub(crate) dwrite_factory: IDWriteFactory6,
pub(crate) font_file_loader: Arc<FontFileLoader>,
pub(crate) wic_imaging_factory: IWICImagingFactory2,
pub(crate) default_text_format: TextFormat,
}
impl<T> Context<T>
where
T: Backend,
{
#[inline]
pub fn new(backend: T) -> Result<Self> {
let d2d1_device_context = unsafe {
backend.d2d1_device().CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
)?
};
let dwrite_factory: IDWriteFactory6 =
unsafe { DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)? };
let wic_imaging_factory =
unsafe { CoCreateInstance(&CLSID_WICImagingFactory2, None, CLSCTX_INPROC_SERVER)? };
let font_file_loader = Arc::new(FontFileLoader::new(&dwrite_factory)?);
let default_text_format =
TextFormatBuilder::new_private(&dwrite_factory, &font_file_loader)
.font(Font::System(""))
.size(FontPoint(14.0))
.build()?;
Ok(Self {
backend: Arc::new(backend),
d2d1_device_context,
dwrite_factory,
font_file_loader,
wic_imaging_factory,
default_text_format,
})
}
#[inline]
pub fn set_dpi(&mut self, dpi_x: f32, dpi_y: f32) {
unsafe {
self.d2d1_device_context.SetDpi(dpi_x, dpi_y);
}
}
#[inline]
pub fn set_scale_factor(&mut self, scale: f32) {
let scale = scale * 96.0;
self.set_dpi(scale, scale);
}
#[inline]
pub fn set_default_text_format(&mut self, format: &TextFormat) {
self.default_text_format = format.clone();
}
#[inline]
pub fn draw<R>(
&self,
target: &T::RenderTarget,
f: impl FnOnce(DrawCommand<T>) -> R,
) -> Result<R> {
let ctx = &self.d2d1_device_context;
self.backend.begin_draw(target);
unsafe {
ctx.SetTarget(target.bitmap());
ctx.BeginDraw();
}
let ret = f(DrawCommand::new(self));
unsafe {
ctx.EndDraw(None, None)?;
ctx.SetTarget(None);
}
self.backend.end_draw(target)?;
Ok(ret)
}
}