2017 lines · 67.7 KB · Rust
RawDownload
use std::{    borrow::Cow,    ffi::{c_uint, c_void},    sync::Arc,}; use ::util::ResultExt;use anyhow::{Context, Result};use collections::HashMap;use itertools::Itertools;use parking_lot::{RwLock, RwLockUpgradableReadGuard};use windows::{    Win32::{        Foundation::*,        Globalization::GetUserDefaultLocaleName,        Graphics::{            Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*,            Dxgi::Common::*, Gdi::LOGFONTW,        },        System::SystemServices::LOCALE_NAME_MAX_LENGTH,        UI::WindowsAndMessaging::*,    },    core::*,};use windows_numerics::Vector2; use crate::*; #[derive(Debug)]struct FontInfo {    font_family: String,    font_face: IDWriteFontFace3,    features: IDWriteTypography,    fallbacks: Option<IDWriteFontFallback>,    is_system_font: bool,} pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>); struct DirectWriteComponent {    locale: String,    factory: IDWriteFactory5,    in_memory_loader: IDWriteInMemoryFontFileLoader,    builder: IDWriteFontSetBuilder1,    text_renderer: Arc<TextRendererWrapper>,     gpu_state: GPUState,} struct GPUState {    device: ID3D11Device,    device_context: ID3D11DeviceContext,    sampler: [Option<ID3D11SamplerState>; 1],    blend_state: ID3D11BlendState,    vertex_shader: ID3D11VertexShader,    pixel_shader: ID3D11PixelShader,} struct DirectWriteState {    components: DirectWriteComponent,    system_ui_font_name: SharedString,    system_font_collection: IDWriteFontCollection1,    custom_font_collection: IDWriteFontCollection1,    fonts: Vec<FontInfo>,    font_selections: HashMap<Font, FontId>,    font_id_by_identifier: HashMap<FontIdentifier, FontId>,    system_subpixel_rendering: bool,} #[derive(Debug, Clone, Hash, PartialEq, Eq)]struct FontIdentifier {    postscript_name: String,    weight: i32,    style: i32,} impl DirectWriteComponent {    pub fn new(directx_devices: &DirectXDevices) -> Result<Self> {        // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing        unsafe {            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;            // The `IDWriteInMemoryFontFileLoader` here is supported starting from            // Windows 10 Creators Update, which consequently requires the entire            // `DirectWriteTextSystem` to run on `win10 1703`+.            let in_memory_loader = factory.CreateInMemoryFontFileLoader()?;            factory.RegisterFontFileLoader(&in_memory_loader)?;            let builder = factory.CreateFontSetBuilder()?;            let mut locale_vec = vec![0u16; LOCALE_NAME_MAX_LENGTH as usize];            GetUserDefaultLocaleName(&mut locale_vec);            let locale = String::from_utf16_lossy(&locale_vec);            let text_renderer = Arc::new(TextRendererWrapper::new(&locale));             let gpu_state = GPUState::new(directx_devices)?;             Ok(DirectWriteComponent {                locale,                factory,                in_memory_loader,                builder,                text_renderer,                gpu_state,            })        }    }} impl GPUState {    fn new(directx_devices: &DirectXDevices) -> Result<Self> {        let device = directx_devices.device.clone();        let device_context = directx_devices.device_context.clone();         let blend_state = {            let mut blend_state = None;            let desc = D3D11_BLEND_DESC {                AlphaToCoverageEnable: false.into(),                IndependentBlendEnable: false.into(),                RenderTarget: [                    D3D11_RENDER_TARGET_BLEND_DESC {                        BlendEnable: true.into(),                        SrcBlend: D3D11_BLEND_ONE,                        DestBlend: D3D11_BLEND_INV_SRC_ALPHA,                        BlendOp: D3D11_BLEND_OP_ADD,                        SrcBlendAlpha: D3D11_BLEND_ONE,                        DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,                        BlendOpAlpha: D3D11_BLEND_OP_ADD,                        RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,                    },                    Default::default(),                    Default::default(),                    Default::default(),                    Default::default(),                    Default::default(),                    Default::default(),                    Default::default(),                ],            };            unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?;            blend_state.unwrap()        };         let sampler = {            let mut sampler = None;            let desc = D3D11_SAMPLER_DESC {                Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,                AddressU: D3D11_TEXTURE_ADDRESS_BORDER,                AddressV: D3D11_TEXTURE_ADDRESS_BORDER,                AddressW: D3D11_TEXTURE_ADDRESS_BORDER,                MipLODBias: 0.0,                MaxAnisotropy: 1,                ComparisonFunc: D3D11_COMPARISON_ALWAYS,                BorderColor: [0.0, 0.0, 0.0, 0.0],                MinLOD: 0.0,                MaxLOD: 0.0,            };            unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?;            [sampler]        };         let vertex_shader = {            let source = shader_resources::RawShaderBytes::new(                shader_resources::ShaderModule::EmojiRasterization,                shader_resources::ShaderTarget::Vertex,            )?;            let mut shader = None;            unsafe { device.CreateVertexShader(source.as_bytes(), None, Some(&mut shader)) }?;            shader.unwrap()        };         let pixel_shader = {            let source = shader_resources::RawShaderBytes::new(                shader_resources::ShaderModule::EmojiRasterization,                shader_resources::ShaderTarget::Fragment,            )?;            let mut shader = None;            unsafe { device.CreatePixelShader(source.as_bytes(), None, Some(&mut shader)) }?;            shader.unwrap()        };         Ok(Self {            device,            device_context,            sampler,            blend_state,            vertex_shader,            pixel_shader,        })    }} impl DirectWriteTextSystem {    pub(crate) fn new(directx_devices: &DirectXDevices) -> Result<Self> {        let components = DirectWriteComponent::new(directx_devices)?;        let system_font_collection = unsafe {            let mut result = std::mem::zeroed();            components                .factory                .GetSystemFontCollection(false, &mut result, true)?;            result.unwrap()        };        let custom_font_set = unsafe { components.builder.CreateFontSet()? };        let custom_font_collection = unsafe {            components                .factory                .CreateFontCollectionFromFontSet(&custom_font_set)?        };        let system_ui_font_name = get_system_ui_font_name();        let system_subpixel_rendering = get_system_subpixel_rendering();         Ok(Self(RwLock::new(DirectWriteState {            components,            system_ui_font_name,            system_font_collection,            custom_font_collection,            fonts: Vec::new(),            font_selections: HashMap::default(),            font_id_by_identifier: HashMap::default(),            system_subpixel_rendering,        })))    }     pub(crate) fn handle_gpu_lost(&self, directx_devices: &DirectXDevices) -> Result<()> {        self.0.write().handle_gpu_lost(directx_devices)    }} impl PlatformTextSystem for DirectWriteTextSystem {    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {        self.0.write().add_fonts(fonts)    }     fn all_font_names(&self) -> Vec<String> {        self.0.read().all_font_names()    }     fn font_id(&self, font: &Font) -> Result<FontId> {        let lock = self.0.upgradable_read();        if let Some(font_id) = lock.font_selections.get(font) {            Ok(*font_id)        } else {            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);            let font_id = lock                .select_font(font)                .with_context(|| format!("Failed to select font: {:?}", font))?;            lock.font_selections.insert(font.clone(), font_id);            Ok(font_id)        }    }     fn font_metrics(&self, font_id: FontId) -> FontMetrics {        self.0.read().font_metrics(font_id)    }     fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {        self.0.read().get_typographic_bounds(font_id, glyph_id)    }     fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {        self.0.read().get_advance(font_id, glyph_id)    }     fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {        self.0.read().glyph_for_char(font_id, ch)    }     fn glyph_raster_bounds(        &self,        params: &RenderGlyphParams,    ) -> anyhow::Result<Bounds<DevicePixels>> {        self.0.read().raster_bounds(params)    }     fn rasterize_glyph(        &self,        params: &RenderGlyphParams,        raster_bounds: Bounds<DevicePixels>,    ) -> anyhow::Result<(Size<DevicePixels>, Vec<u8>)> {        self.0.read().rasterize_glyph(params, raster_bounds)    }     fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {        self.0            .write()            .layout_line(text, font_size, runs)            .log_err()            .unwrap_or(LineLayout {                font_size,                ..Default::default()            })    }     fn recommended_rendering_mode(        &self,        _font_id: FontId,        _font_size: Pixels,    ) -> TextRenderingMode {        if self.0.read().system_subpixel_rendering {            TextRenderingMode::Subpixel        } else {            TextRenderingMode::Grayscale        }    }} impl DirectWriteState {    fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {        for font_data in fonts {            match font_data {                Cow::Borrowed(data) => unsafe {                    let font_file = self                        .components                        .in_memory_loader                        .CreateInMemoryFontFileReference(                            &self.components.factory,                            data.as_ptr() as _,                            data.len() as _,                            None,                        )?;                    self.components.builder.AddFontFile(&font_file)?;                },                Cow::Owned(data) => unsafe {                    let font_file = self                        .components                        .in_memory_loader                        .CreateInMemoryFontFileReference(                            &self.components.factory,                            data.as_ptr() as _,                            data.len() as _,                            None,                        )?;                    self.components.builder.AddFontFile(&font_file)?;                },            }        }        let set = unsafe { self.components.builder.CreateFontSet()? };        let collection = unsafe {            self.components                .factory                .CreateFontCollectionFromFontSet(&set)?        };        self.custom_font_collection = collection;         Ok(())    }     fn generate_font_fallbacks(        &self,        fallbacks: &FontFallbacks,    ) -> Result<Option<IDWriteFontFallback>> {        if fallbacks.fallback_list().is_empty() {            return Ok(None);        }        unsafe {            let builder = self.components.factory.CreateFontFallbackBuilder()?;            let font_set = &self.system_font_collection.GetFontSet()?;            for family_name in fallbacks.fallback_list() {                let Some(fonts) = font_set                    .GetMatchingFonts(                        &HSTRING::from(family_name),                        DWRITE_FONT_WEIGHT_NORMAL,                        DWRITE_FONT_STRETCH_NORMAL,                        DWRITE_FONT_STYLE_NORMAL,                    )                    .log_err()                else {                    continue;                };                if fonts.GetFontCount() == 0 {                    log::error!("No matching font found for {}", family_name);                    continue;                }                let font = fonts.GetFontFaceReference(0)?.CreateFontFace()?;                let mut count = 0;                font.GetUnicodeRanges(None, &mut count).ok();                if count == 0 {                    continue;                }                let mut unicode_ranges = vec![DWRITE_UNICODE_RANGE::default(); count as usize];                let Some(_) = font                    .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count)                    .log_err()                else {                    continue;                };                let target_family_name = HSTRING::from(family_name);                builder.AddMapping(                    &unicode_ranges,                    &[target_family_name.as_ptr()],                    None,                    None,                    None,                    1.0,                )?;            }            let system_fallbacks = self.components.factory.GetSystemFontFallback()?;            builder.AddMappings(&system_fallbacks)?;            Ok(Some(builder.CreateFontFallback()?))        }    }     unsafe fn generate_font_features(        &self,        font_features: &FontFeatures,    ) -> Result<IDWriteTypography> {        let direct_write_features = unsafe { self.components.factory.CreateTypography()? };        apply_font_features(&direct_write_features, font_features)?;        Ok(direct_write_features)    }     unsafe fn get_font_id_from_font_collection(        &mut self,        family_name: &str,        font_weight: FontWeight,        font_style: FontStyle,        font_features: &FontFeatures,        font_fallbacks: Option<&FontFallbacks>,        is_system_font: bool,    ) -> Option<FontId> {        let collection = if is_system_font {            &self.system_font_collection        } else {            &self.custom_font_collection        };        let fontset = unsafe { collection.GetFontSet().log_err()? };        let font = unsafe {            fontset                .GetMatchingFonts(                    &HSTRING::from(family_name),                    font_weight.into(),                    DWRITE_FONT_STRETCH_NORMAL,                    font_style.into(),                )                .log_err()?        };        let total_number = unsafe { font.GetFontCount() };        for index in 0..total_number {            let Some(font_face_ref) = (unsafe { font.GetFontFaceReference(index).log_err() })            else {                continue;            };            let Some(font_face) = (unsafe { font_face_ref.CreateFontFace().log_err() }) else {                continue;            };            let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {                continue;            };            let Some(direct_write_features) =                (unsafe { self.generate_font_features(font_features).log_err() })            else {                continue;            };            let fallbacks = font_fallbacks                .and_then(|fallbacks| self.generate_font_fallbacks(fallbacks).log_err().flatten());            let font_info = FontInfo {                font_family: family_name.to_owned(),                font_face,                features: direct_write_features,                fallbacks,                is_system_font,            };            let font_id = FontId(self.fonts.len());            self.fonts.push(font_info);            self.font_id_by_identifier.insert(identifier, font_id);            return Some(font_id);        }        None    }     unsafe fn update_system_font_collection(&mut self) {        let mut collection = unsafe { std::mem::zeroed() };        if unsafe {            self.components                .factory                .GetSystemFontCollection(false, &mut collection, true)                .log_err()                .is_some()        } {            self.system_font_collection = collection.unwrap();        }    }     fn select_font(&mut self, target_font: &Font) -> Option<FontId> {        unsafe {            if target_font.family == ".SystemUIFont" {                let family = self.system_ui_font_name.clone();                self.find_font_id(                    family.as_ref(),                    target_font.weight,                    target_font.style,                    &target_font.features,                    target_font.fallbacks.as_ref(),                )            } else {                let family = self.system_ui_font_name.clone();                self.find_font_id(                    font_name_with_fallbacks(target_font.family.as_ref(), family.as_ref()),                    target_font.weight,                    target_font.style,                    &target_font.features,                    target_font.fallbacks.as_ref(),                )

Lines 1–500 of 2017.