2022-04-16 17:25:56 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2022 Umut İnan Erdoğan <umutinanerdogan@pm.me>
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
2022-04-16 17:18:23 +00:00
|
|
|
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()
|
|
|
|
}
|
|
|
|
}
|