/* * 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/. */ pub mod brush; use std::mem; use brush::{Brush, DEFAULT_BRUSH_COLOR}; use raw_window_handle::Win32Handle; use windows::{ core::Interface, Win32::Foundation::*, Win32::Graphics::Direct2D::Common::*, Win32::Graphics::Direct2D::*, Win32::Graphics::DirectWrite::*, Win32::UI::WindowsAndMessaging::*, }; 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, layoutrect: R) { 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, &layoutrect.into(), self.brush.as_ref().unwrap(), D2D1_DRAW_TEXT_OPTIONS_NO_SNAP, 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 set_brush(&mut self, brush: Brush) { self.brush = Some(brush.to_brush(self.target.as_ref().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() }; self.brush = Some(Brush::Solid(DEFAULT_BRUSH_COLOR).to_brush(&target)); self.target = Some(target); } } } 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 { let format = 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(); format.SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP).unwrap(); format } }