1495 lines · 50.9 KB · Rust
RawDownload
use std::{borrow::Cow, sync::Arc}; use ::util::ResultExt;use anyhow::{anyhow, Result};use collections::HashMap;use itertools::Itertools;use parking_lot::{RwLock, RwLockUpgradableReadGuard};use smallvec::SmallVec;use windows::{    core::*,    Win32::{        Foundation::*,        Globalization::GetUserDefaultLocaleName,        Graphics::{            Direct2D::{Common::*, *},            DirectWrite::*,            Dxgi::Common::*,            Gdi::LOGFONTW,            Imaging::*,        },        System::SystemServices::LOCALE_NAME_MAX_LENGTH,        UI::WindowsAndMessaging::*,    },}; use crate::*; #[derive(Debug)]struct FontInfo {    font_family: String,    font_face: IDWriteFontFace3,    features: IDWriteTypography,    fallbacks: Option<IDWriteFontFallback>,    is_system_font: bool,    is_emoji: bool,} pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>); struct DirectWriteComponent {    locale: String,    factory: IDWriteFactory5,    bitmap_factory: AgileReference<IWICImagingFactory>,    d2d1_factory: ID2D1Factory,    in_memory_loader: IDWriteInMemoryFontFileLoader,    builder: IDWriteFontSetBuilder1,    text_renderer: Arc<TextRendererWrapper>,    render_context: GlyphRenderContext,} struct GlyphRenderContext {    params: IDWriteRenderingParams3,    dc_target: ID2D1DeviceContext4,} 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>,} #[derive(Debug, Clone, Hash, PartialEq, Eq)]struct FontIdentifier {    postscript_name: String,    weight: i32,    style: i32,} impl DirectWriteComponent {    pub fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {        unsafe {            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;            let bitmap_factory = AgileReference::new(bitmap_factory)?;            let d2d1_factory: ID2D1Factory =                D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;            // 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 render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;             Ok(DirectWriteComponent {                locale,                factory,                bitmap_factory,                d2d1_factory,                in_memory_loader,                builder,                text_renderer,                render_context,            })        }    }} impl GlyphRenderContext {    pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {        unsafe {            let default_params: IDWriteRenderingParams3 =                factory.CreateRenderingParams()?.cast()?;            let gamma = default_params.GetGamma();            let enhanced_contrast = default_params.GetEnhancedContrast();            let gray_contrast = default_params.GetGrayscaleEnhancedContrast();            let cleartype_level = default_params.GetClearTypeLevel();            let grid_fit_mode = default_params.GetGridFitMode();             let params = factory.CreateCustomRenderingParams(                gamma,                enhanced_contrast,                gray_contrast,                cleartype_level,                DWRITE_PIXEL_GEOMETRY_RGB,                DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,                grid_fit_mode,            )?;            let dc_target = {                let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(                    DXGI_FORMAT_B8G8R8A8_UNORM,                    D2D1_ALPHA_MODE_PREMULTIPLIED,                ))?;                let target = target.cast::<ID2D1DeviceContext4>()?;                target.SetTextRenderingParams(&params);                target            };             Ok(Self { params, dc_target })        }    }} impl DirectWriteTextSystem {    pub(crate) fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> {        let components = DirectWriteComponent::new(bitmap_factory)?;        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();         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(),        })))    }} 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);            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()            })    }} 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 = 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 = collection.GetFontSet().log_err()?;        let font = fontset            .GetMatchingFonts(                &HSTRING::from(family_name),                font_weight.into(),                DWRITE_FONT_STRETCH_NORMAL,                font_style.into(),            )            .log_err()?;        let total_number = font.GetFontCount();        for index in 0..total_number {            let Some(font_face_ref) = font.GetFontFaceReference(index).log_err() else {                continue;            };            let Some(font_face) = font_face_ref.CreateFontFace().log_err() else {                continue;            };            let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {                continue;            };            let is_emoji = font_face.IsColorFont().as_bool();            let Some(direct_write_features) = 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,                is_emoji,            };            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 = std::mem::zeroed();        if 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) -> 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(),                )                .unwrap()            } else {                self.find_font_id(                    target_font.family.as_ref(),                    target_font.weight,                    target_font.style,                    &target_font.features,                    target_font.fallbacks.as_ref(),                )                .unwrap_or_else(|| {                    let family = self.system_ui_font_name.clone();                    log::error!("{} not found, use {} instead.", target_font.family, family);                    self.get_font_id_from_font_collection(                        family.as_ref(),                        target_font.weight,                        target_font.style,                        &target_font.features,                        target_font.fallbacks.as_ref(),                        true,                    )                    .unwrap()                })            }        }    }     unsafe fn find_font_id(        &mut self,        family_name: &str,        weight: FontWeight,        style: FontStyle,        features: &FontFeatures,        fallbacks: Option<&FontFallbacks>,    ) -> Option<FontId> {        // try to find target font in custom font collection first        self.get_font_id_from_font_collection(            family_name,            weight,            style,            features,            fallbacks,            false,        )        .or_else(|| {            self.get_font_id_from_font_collection(                family_name,                weight,                style,                features,                fallbacks,                true,            )        })        .or_else(|| {            self.update_system_font_collection();            self.get_font_id_from_font_collection(                family_name,                weight,                style,                features,                fallbacks,                true,            )        })    }     fn layout_line(        &mut self,        text: &str,        font_size: Pixels,        font_runs: &[FontRun],    ) -> Result<LineLayout> {        if font_runs.is_empty() {            return Ok(LineLayout {                font_size,                ..Default::default()            });        }        unsafe {            let text_renderer = self.components.text_renderer.clone();

Lines 1–500 of 1495.