abletk/abletk-direct2d/src/lib.rs

219 lines
6.1 KiB
Rust
Executable File

use std::mem;
use raw_window_handle::Win32Handle;
use windows::{
core::Interface, Foundation::Numerics::*, Win32::Foundation::*,
Win32::Graphics::Direct2D::Common::*, Win32::Graphics::Direct2D::*,
Win32::Graphics::DirectWrite::*, Win32::UI::WindowsAndMessaging::*,
};
/// The default brush color is black.
const DEFAULT_BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
r: 0.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
const DEFAULT_BRUSH_PROPERTIES: D2D1_BRUSH_PROPERTIES = D2D1_BRUSH_PROPERTIES {
opacity: 1.0,
// Matrix3x2::identity() is not a const fn but it could be
//
// I (TheOddGarlic) sent windows-rs a PR and it got merged but now we wait
// for it to be included in the next release
transform: Matrix3x2 {
M11: 1.0, M12: 0.0, M21: 0.0, M22: 1.0, M31: 0.0, M32: 0.0
}
};
pub struct Renderer {
handle: Win32Handle,
factory: ID2D1Factory1,
dw_factory: IDWriteFactory,
target: Option<ID2D1HwndRenderTarget>,
brush: Option<ID2D1SolidColorBrush>,
stroke_style: ID2D1StrokeStyle,
system_fonts: IDWriteFontCollection,
text_format: IDWriteTextFormat
}
impl Renderer {
pub fn new(handle: Win32Handle) -> Self {
let factory = create_factory();
let dw_factory = create_dw_factory();
let system_fonts = create_system_font_collection(&dw_factory);
let text_format = create_text_format(&dw_factory, &system_fonts);
let stroke_style = unsafe {
factory.CreateStrokeStyle(&Default::default(), &[])
.unwrap()
};
Self {
handle,
factory,
dw_factory,
target: None,
brush: None,
stroke_style,
system_fonts,
text_format,
}
}
pub fn begin_draw(&mut self) {
self.setup_target();
unsafe { self.target.as_ref().unwrap().BeginDraw() }
}
pub fn clear<C: Into<D2D1_COLOR_F>>(&self, color: C) {
unsafe { self.target.as_ref().unwrap().Clear(&color.into()) }
}
pub fn draw_text(&self, text: &str) {
unsafe {
self.target.as_ref().unwrap().DrawText(
// fixme: make this not hacky
&text.as_bytes().iter().map(|b| *b as u16).collect::<Vec<_>>(),
&self.text_format,
// fixme: unhardcode this (needs layout stuff probably)
&D2D_RECT_F {
left: 0.0,
top: 0.0,
right: 100.0,
bottom: 100.0,
},
self.brush.as_ref().unwrap(),
D2D1_DRAW_TEXT_OPTIONS_NONE,
DWRITE_MEASURING_MODE_NATURAL,
);
}
}
pub fn end_draw(&self) {
unsafe {
self.target.as_ref().unwrap()
.EndDraw(std::ptr::null_mut(), std::ptr::null_mut()).unwrap()
}
}
pub fn fill_rect<R: Into<D2D_RECT_F>>(&self, rect: R) {
unsafe {
self.target.as_ref().unwrap()
.FillRectangle(
&rect.into(),
self.brush.as_ref().unwrap())
}
}
pub fn resized(&mut self, width: u32, height: u32) {
unsafe {
if let Some(target) = self.target.as_ref() {
target.Resize(&D2D_SIZE_U {
width,
height
}).unwrap()
}
}
}
pub fn size(&self) -> (f32, f32) {
let size = unsafe {
self.target.as_ref().unwrap().GetSize()
};
(size.width, size.height)
}
fn setup_target(&mut self) {
if self.target.is_none() {
let mut rect = RECT::default();
unsafe {
GetClientRect(
mem::transmute::<_, HWND>(self.handle.hwnd),
&mut rect
);
}
let d2d_rect = D2D_SIZE_U {
width: (rect.right - rect.left) as u32,
height: (rect.bottom - rect.top) as u32,
};
let render_properties = D2D1_RENDER_TARGET_PROPERTIES::default();
let hwnd_render_properties = D2D1_HWND_RENDER_TARGET_PROPERTIES {
hwnd: unsafe { mem::transmute(self.handle.hwnd) },
pixelSize: d2d_rect,
presentOptions: D2D1_PRESENT_OPTIONS_NONE,
};
let target = unsafe {
self.factory
.CreateHwndRenderTarget(
&render_properties,
&hwnd_render_properties)
.unwrap()
};
let brush = unsafe {
target.CreateSolidColorBrush(&DEFAULT_BRUSH_COLOR, &DEFAULT_BRUSH_PROPERTIES)
.unwrap()
};
self.target = Some(target);
self.brush = Some(brush);
}
}
}
fn create_factory() -> ID2D1Factory1 {
let mut options = D2D1_FACTORY_OPTIONS::default();
if cfg!(debug_assertions) {
options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
}
let mut result = None;
unsafe {
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&ID2D1Factory1::IID,
&options,
mem::transmute(&mut result)
).map(|()| result.unwrap())
}.unwrap()
}
fn create_dw_factory() -> IDWriteFactory {
unsafe {
mem::transmute(DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
&IDWriteFactory::IID
).unwrap())
}
}
fn create_system_font_collection(factory: &IDWriteFactory) -> IDWriteFontCollection {
unsafe {
let mut result = None;
factory.GetSystemFontCollection(&mut result, false)
.map(|_| result.unwrap())
.unwrap()
}
}
fn create_text_format(factory: &IDWriteFactory, fonts: &IDWriteFontCollection)
-> IDWriteTextFormat
{
unsafe {
factory.CreateTextFormat(
"Arial",
fonts,
DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
10.0 * 96.0 / 72.0,
"en-US",
).unwrap()
}
}