/* * Copyright (C) 2022 Umut İnan Erdoğan * * 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/. */ 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, brush: Option, 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>(&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::>(), &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>(&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() } }