direct_write.rs

   1use std::{borrow::Cow, sync::Arc};
   2
   3use ::util::ResultExt;
   4use anyhow::{anyhow, Result};
   5use collections::HashMap;
   6use itertools::Itertools;
   7use parking_lot::{RwLock, RwLockUpgradableReadGuard};
   8use smallvec::SmallVec;
   9use windows::{
  10    core::*,
  11    Win32::{
  12        Foundation::*,
  13        Globalization::GetUserDefaultLocaleName,
  14        Graphics::{
  15            Direct2D::{Common::*, *},
  16            DirectWrite::*,
  17            Dxgi::Common::*,
  18            Gdi::LOGFONTW,
  19            Imaging::{D2D::IWICImagingFactory2, *},
  20        },
  21        System::{Com::*, SystemServices::LOCALE_NAME_MAX_LENGTH},
  22        UI::WindowsAndMessaging::*,
  23    },
  24};
  25
  26use crate::*;
  27
  28#[derive(Debug)]
  29struct FontInfo {
  30    font_family: String,
  31    font_face: IDWriteFontFace3,
  32    features: IDWriteTypography,
  33    is_system_font: bool,
  34    is_emoji: bool,
  35}
  36
  37pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
  38
  39struct DirectWriteComponent {
  40    locale: String,
  41    factory: IDWriteFactory5,
  42    bitmap_factory: IWICImagingFactory2,
  43    d2d1_factory: ID2D1Factory,
  44    in_memory_loader: IDWriteInMemoryFontFileLoader,
  45    builder: IDWriteFontSetBuilder1,
  46    text_renderer: Arc<TextRendererWrapper>,
  47    render_context: GlyphRenderContext,
  48}
  49
  50struct GlyphRenderContext {
  51    params: IDWriteRenderingParams3,
  52    normal_dc_target: ID2D1DeviceContext4,
  53    emoji_dc_target: ID2D1DeviceContext4,
  54}
  55
  56// All use of the IUnknown methods should be "thread-safe".
  57unsafe impl Sync for DirectWriteComponent {}
  58unsafe impl Send for DirectWriteComponent {}
  59
  60struct DirectWriteState {
  61    components: DirectWriteComponent,
  62    system_ui_font_name: SharedString,
  63    system_font_collection: IDWriteFontCollection1,
  64    custom_font_collection: IDWriteFontCollection1,
  65    fonts: Vec<FontInfo>,
  66    font_selections: HashMap<Font, FontId>,
  67    font_id_by_identifier: HashMap<FontIdentifier, FontId>,
  68}
  69
  70#[derive(Debug, Clone, Hash, PartialEq, Eq)]
  71struct FontIdentifier {
  72    postscript_name: String,
  73    weight: i32,
  74    style: i32,
  75}
  76
  77impl DirectWriteComponent {
  78    pub fn new() -> Result<Self> {
  79        unsafe {
  80            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
  81            let bitmap_factory: IWICImagingFactory2 =
  82                CoCreateInstance(&CLSID_WICImagingFactory2, None, CLSCTX_INPROC_SERVER)?;
  83            let d2d1_factory: ID2D1Factory =
  84                D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;
  85            // The `IDWriteInMemoryFontFileLoader` here is supported starting from
  86            // Windows 10 Creators Update, which consequently requires the entire
  87            // `DirectWriteTextSystem` to run on `win10 1703`+.
  88            let in_memory_loader = factory.CreateInMemoryFontFileLoader()?;
  89            factory.RegisterFontFileLoader(&in_memory_loader)?;
  90            let builder = factory.CreateFontSetBuilder()?;
  91            let mut locale_vec = vec![0u16; LOCALE_NAME_MAX_LENGTH as usize];
  92            GetUserDefaultLocaleName(&mut locale_vec);
  93            let locale = String::from_utf16_lossy(&locale_vec);
  94            let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
  95            let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
  96
  97            Ok(DirectWriteComponent {
  98                locale,
  99                factory,
 100                bitmap_factory,
 101                d2d1_factory,
 102                in_memory_loader,
 103                builder,
 104                text_renderer,
 105                render_context,
 106            })
 107        }
 108    }
 109}
 110
 111impl GlyphRenderContext {
 112    pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {
 113        unsafe {
 114            let default_params: IDWriteRenderingParams3 =
 115                factory.CreateRenderingParams()?.cast()?;
 116            let gamma = default_params.GetGamma();
 117            let enhanced_contrast = default_params.GetEnhancedContrast();
 118            let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
 119            let cleartype_level = default_params.GetClearTypeLevel();
 120            let grid_fit_mode = default_params.GetGridFitMode();
 121
 122            let params = factory.CreateCustomRenderingParams(
 123                gamma,
 124                enhanced_contrast,
 125                gray_contrast,
 126                cleartype_level,
 127                DWRITE_PIXEL_GEOMETRY_RGB,
 128                DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
 129                grid_fit_mode,
 130            )?;
 131            let normal_dc_target = {
 132                let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
 133                    DXGI_FORMAT_A8_UNORM,
 134                    D2D1_ALPHA_MODE_STRAIGHT,
 135                ))?;
 136                let target = target.cast::<ID2D1DeviceContext4>()?;
 137                target.SetTextRenderingParams(&params);
 138                target
 139            };
 140            let emoji_dc_target = {
 141                let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
 142                    DXGI_FORMAT_B8G8R8A8_UNORM,
 143                    D2D1_ALPHA_MODE_PREMULTIPLIED,
 144                ))?;
 145                let target = target.cast::<ID2D1DeviceContext4>()?;
 146                target.SetTextRenderingParams(&params);
 147                target
 148            };
 149
 150            Ok(Self {
 151                params,
 152                normal_dc_target,
 153                emoji_dc_target,
 154            })
 155        }
 156    }
 157}
 158
 159impl DirectWriteTextSystem {
 160    pub(crate) fn new() -> Result<Self> {
 161        let components = DirectWriteComponent::new()?;
 162        let system_font_collection = unsafe {
 163            let mut result = std::mem::zeroed();
 164            components
 165                .factory
 166                .GetSystemFontCollection(false, &mut result, true)?;
 167            result.unwrap()
 168        };
 169        let custom_font_set = unsafe { components.builder.CreateFontSet()? };
 170        let custom_font_collection = unsafe {
 171            components
 172                .factory
 173                .CreateFontCollectionFromFontSet(&custom_font_set)?
 174        };
 175        let system_ui_font_name = get_system_ui_font_name();
 176
 177        Ok(Self(RwLock::new(DirectWriteState {
 178            components,
 179            system_ui_font_name,
 180            system_font_collection,
 181            custom_font_collection,
 182            fonts: Vec::new(),
 183            font_selections: HashMap::default(),
 184            font_id_by_identifier: HashMap::default(),
 185        })))
 186    }
 187}
 188
 189impl PlatformTextSystem for DirectWriteTextSystem {
 190    fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 191        self.0.write().add_fonts(fonts)
 192    }
 193
 194    fn all_font_names(&self) -> Vec<String> {
 195        self.0.read().all_font_names()
 196    }
 197
 198    fn all_font_families(&self) -> Vec<String> {
 199        self.0.read().all_font_families()
 200    }
 201
 202    fn font_id(&self, font: &Font) -> Result<FontId> {
 203        let lock = self.0.upgradable_read();
 204        if let Some(font_id) = lock.font_selections.get(font) {
 205            Ok(*font_id)
 206        } else {
 207            let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
 208            let font_id = lock.select_font(font);
 209            lock.font_selections.insert(font.clone(), font_id);
 210            Ok(font_id)
 211        }
 212    }
 213
 214    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
 215        self.0.read().font_metrics(font_id)
 216    }
 217
 218    fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
 219        self.0.read().get_typographic_bounds(font_id, glyph_id)
 220    }
 221
 222    fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {
 223        self.0.read().get_advance(font_id, glyph_id)
 224    }
 225
 226    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
 227        self.0.read().glyph_for_char(font_id, ch)
 228    }
 229
 230    fn glyph_raster_bounds(
 231        &self,
 232        params: &RenderGlyphParams,
 233    ) -> anyhow::Result<Bounds<DevicePixels>> {
 234        self.0.read().raster_bounds(params)
 235    }
 236
 237    fn rasterize_glyph(
 238        &self,
 239        params: &RenderGlyphParams,
 240        raster_bounds: Bounds<DevicePixels>,
 241    ) -> anyhow::Result<(Size<DevicePixels>, Vec<u8>)> {
 242        self.0.read().rasterize_glyph(params, raster_bounds)
 243    }
 244
 245    fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
 246        self.0
 247            .write()
 248            .layout_line(text, font_size, runs)
 249            .log_err()
 250            .unwrap_or(LineLayout {
 251                font_size,
 252                ..Default::default()
 253            })
 254    }
 255}
 256
 257impl DirectWriteState {
 258    fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
 259        for font_data in fonts {
 260            match font_data {
 261                Cow::Borrowed(data) => unsafe {
 262                    let font_file = self
 263                        .components
 264                        .in_memory_loader
 265                        .CreateInMemoryFontFileReference(
 266                            &self.components.factory,
 267                            data.as_ptr() as _,
 268                            data.len() as _,
 269                            None,
 270                        )?;
 271                    self.components.builder.AddFontFile(&font_file)?;
 272                },
 273                Cow::Owned(data) => unsafe {
 274                    let font_file = self
 275                        .components
 276                        .in_memory_loader
 277                        .CreateInMemoryFontFileReference(
 278                            &self.components.factory,
 279                            data.as_ptr() as _,
 280                            data.len() as _,
 281                            None,
 282                        )?;
 283                    self.components.builder.AddFontFile(&font_file)?;
 284                },
 285            }
 286        }
 287        let set = unsafe { self.components.builder.CreateFontSet()? };
 288        let collection = unsafe {
 289            self.components
 290                .factory
 291                .CreateFontCollectionFromFontSet(&set)?
 292        };
 293        self.custom_font_collection = collection;
 294
 295        Ok(())
 296    }
 297
 298    unsafe fn generate_font_features(
 299        &self,
 300        font_features: &FontFeatures,
 301    ) -> Result<IDWriteTypography> {
 302        let direct_write_features = self.components.factory.CreateTypography()?;
 303        apply_font_features(&direct_write_features, font_features)?;
 304        Ok(direct_write_features)
 305    }
 306
 307    unsafe fn get_font_id_from_font_collection(
 308        &mut self,
 309        family_name: &str,
 310        font_weight: FontWeight,
 311        font_style: FontStyle,
 312        font_features: &FontFeatures,
 313        is_system_font: bool,
 314    ) -> Option<FontId> {
 315        let collection = if is_system_font {
 316            &self.system_font_collection
 317        } else {
 318            &self.custom_font_collection
 319        };
 320        let fontset = collection.GetFontSet().log_err()?;
 321        let font = fontset
 322            .GetMatchingFonts(
 323                &HSTRING::from(family_name),
 324                font_weight.into(),
 325                DWRITE_FONT_STRETCH_NORMAL,
 326                font_style.into(),
 327            )
 328            .log_err()?;
 329        let total_number = font.GetFontCount();
 330        for index in 0..total_number {
 331            let Some(font_face_ref) = font.GetFontFaceReference(index).log_err() else {
 332                continue;
 333            };
 334            let Some(font_face) = font_face_ref.CreateFontFace().log_err() else {
 335                continue;
 336            };
 337            let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {
 338                continue;
 339            };
 340            let is_emoji = font_face.IsColorFont().as_bool();
 341            let Some(direct_write_features) = self.generate_font_features(font_features).log_err()
 342            else {
 343                continue;
 344            };
 345            let font_info = FontInfo {
 346                font_family: family_name.to_owned(),
 347                font_face,
 348                is_system_font,
 349                features: direct_write_features,
 350                is_emoji,
 351            };
 352            let font_id = FontId(self.fonts.len());
 353            self.fonts.push(font_info);
 354            self.font_id_by_identifier.insert(identifier, font_id);
 355            return Some(font_id);
 356        }
 357        None
 358    }
 359
 360    unsafe fn update_system_font_collection(&mut self) {
 361        let mut collection = std::mem::zeroed();
 362        if self
 363            .components
 364            .factory
 365            .GetSystemFontCollection(false, &mut collection, true)
 366            .log_err()
 367            .is_some()
 368        {
 369            self.system_font_collection = collection.unwrap();
 370        }
 371    }
 372
 373    fn select_font(&mut self, target_font: &Font) -> FontId {
 374        unsafe {
 375            if target_font.family == ".SystemUIFont" {
 376                let family = self.system_ui_font_name.clone();
 377                self.find_font_id(
 378                    family.as_ref(),
 379                    target_font.weight,
 380                    target_font.style,
 381                    &target_font.features,
 382                )
 383                .unwrap()
 384            } else {
 385                self.find_font_id(
 386                    target_font.family.as_ref(),
 387                    target_font.weight,
 388                    target_font.style,
 389                    &target_font.features,
 390                )
 391                .unwrap_or_else(|| {
 392                    let family = self.system_ui_font_name.clone();
 393                    log::error!("{} not found, use {} instead.", target_font.family, family);
 394                    self.get_font_id_from_font_collection(
 395                        family.as_ref(),
 396                        target_font.weight,
 397                        target_font.style,
 398                        &target_font.features,
 399                        true,
 400                    )
 401                    .unwrap()
 402                })
 403            }
 404        }
 405    }
 406
 407    unsafe fn find_font_id(
 408        &mut self,
 409        family_name: &str,
 410        weight: FontWeight,
 411        style: FontStyle,
 412        features: &FontFeatures,
 413    ) -> Option<FontId> {
 414        // try to find target font in custom font collection first
 415        self.get_font_id_from_font_collection(family_name, weight, style, features, false)
 416            .or_else(|| {
 417                self.get_font_id_from_font_collection(family_name, weight, style, features, true)
 418            })
 419            .or_else(|| {
 420                self.update_system_font_collection();
 421                self.get_font_id_from_font_collection(family_name, weight, style, features, true)
 422            })
 423    }
 424
 425    fn layout_line(
 426        &mut self,
 427        text: &str,
 428        font_size: Pixels,
 429        font_runs: &[FontRun],
 430    ) -> Result<LineLayout> {
 431        if font_runs.is_empty() {
 432            return Ok(LineLayout {
 433                font_size,
 434                ..Default::default()
 435            });
 436        }
 437        unsafe {
 438            let text_renderer = self.components.text_renderer.clone();
 439            let text_wide = text.encode_utf16().collect_vec();
 440
 441            let mut utf8_offset = 0usize;
 442            let mut utf16_offset = 0u32;
 443            let text_layout = {
 444                let first_run = &font_runs[0];
 445                let font_info = &self.fonts[first_run.font_id.0];
 446                let collection = if font_info.is_system_font {
 447                    &self.system_font_collection
 448                } else {
 449                    &self.custom_font_collection
 450                };
 451                let format = self.components.factory.CreateTextFormat(
 452                    &HSTRING::from(&font_info.font_family),
 453                    collection,
 454                    font_info.font_face.GetWeight(),
 455                    font_info.font_face.GetStyle(),
 456                    DWRITE_FONT_STRETCH_NORMAL,
 457                    font_size.0,
 458                    &HSTRING::from(&self.components.locale),
 459                )?;
 460
 461                let layout = self.components.factory.CreateTextLayout(
 462                    &text_wide,
 463                    &format,
 464                    f32::INFINITY,
 465                    f32::INFINITY,
 466                )?;
 467                let current_text = &text[utf8_offset..(utf8_offset + first_run.len)];
 468                utf8_offset += first_run.len;
 469                let current_text_utf16_length = current_text.encode_utf16().count() as u32;
 470                let text_range = DWRITE_TEXT_RANGE {
 471                    startPosition: utf16_offset,
 472                    length: current_text_utf16_length,
 473                };
 474                layout.SetTypography(&font_info.features, text_range)?;
 475                utf16_offset += current_text_utf16_length;
 476
 477                layout
 478            };
 479
 480            let mut first_run = true;
 481            let mut ascent = Pixels::default();
 482            let mut descent = Pixels::default();
 483            for run in font_runs {
 484                if first_run {
 485                    first_run = false;
 486                    let mut metrics = vec![DWRITE_LINE_METRICS::default(); 4];
 487                    let mut line_count = 0u32;
 488                    text_layout.GetLineMetrics(Some(&mut metrics), &mut line_count as _)?;
 489                    ascent = px(metrics[0].baseline);
 490                    descent = px(metrics[0].height - metrics[0].baseline);
 491                    continue;
 492                }
 493                let font_info = &self.fonts[run.font_id.0];
 494                let current_text = &text[utf8_offset..(utf8_offset + run.len)];
 495                utf8_offset += run.len;
 496                let current_text_utf16_length = current_text.encode_utf16().count() as u32;
 497
 498                let collection = if font_info.is_system_font {
 499                    &self.system_font_collection
 500                } else {
 501                    &self.custom_font_collection
 502                };
 503                let text_range = DWRITE_TEXT_RANGE {
 504                    startPosition: utf16_offset,
 505                    length: current_text_utf16_length,
 506                };
 507                utf16_offset += current_text_utf16_length;
 508                text_layout.SetFontCollection(collection, text_range)?;
 509                text_layout
 510                    .SetFontFamilyName(&HSTRING::from(&font_info.font_family), text_range)?;
 511                text_layout.SetFontSize(font_size.0, text_range)?;
 512                text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?;
 513                text_layout.SetFontWeight(font_info.font_face.GetWeight(), text_range)?;
 514                text_layout.SetTypography(&font_info.features, text_range)?;
 515            }
 516
 517            let mut runs = Vec::new();
 518            let renderer_context = RendererContext {
 519                text_system: self,
 520                index_converter: StringIndexConverter::new(text),
 521                runs: &mut runs,
 522                utf16_index: 0,
 523                width: 0.0,
 524            };
 525            text_layout.Draw(
 526                Some(&renderer_context as *const _ as _),
 527                &text_renderer.0,
 528                0.0,
 529                0.0,
 530            )?;
 531            let width = px(renderer_context.width);
 532
 533            Ok(LineLayout {
 534                font_size,
 535                width,
 536                ascent,
 537                descent,
 538                runs,
 539                len: text.len(),
 540            })
 541        }
 542    }
 543
 544    fn font_metrics(&self, font_id: FontId) -> FontMetrics {
 545        unsafe {
 546            let font_info = &self.fonts[font_id.0];
 547            let mut metrics = std::mem::zeroed();
 548            font_info.font_face.GetMetrics(&mut metrics);
 549
 550            FontMetrics {
 551                units_per_em: metrics.Base.designUnitsPerEm as _,
 552                ascent: metrics.Base.ascent as _,
 553                descent: -(metrics.Base.descent as f32),
 554                line_gap: metrics.Base.lineGap as _,
 555                underline_position: metrics.Base.underlinePosition as _,
 556                underline_thickness: metrics.Base.underlineThickness as _,
 557                cap_height: metrics.Base.capHeight as _,
 558                x_height: metrics.Base.xHeight as _,
 559                bounding_box: Bounds {
 560                    origin: Point {
 561                        x: metrics.glyphBoxLeft as _,
 562                        y: metrics.glyphBoxBottom as _,
 563                    },
 564                    size: Size {
 565                        width: (metrics.glyphBoxRight - metrics.glyphBoxLeft) as _,
 566                        height: (metrics.glyphBoxTop - metrics.glyphBoxBottom) as _,
 567                    },
 568                },
 569            }
 570        }
 571    }
 572
 573    fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
 574        let render_target = if params.is_emoji {
 575            &self.components.render_context.emoji_dc_target
 576        } else {
 577            &self.components.render_context.normal_dc_target
 578        };
 579        unsafe {
 580            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
 581            render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
 582        }
 583        let font = &self.fonts[params.font_id.0];
 584        let glyph_id = [params.glyph_id.0 as u16];
 585        let advance = [0.0f32];
 586        let offset = [DWRITE_GLYPH_OFFSET::default()];
 587        let glyph_run = DWRITE_GLYPH_RUN {
 588            fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
 589            fontEmSize: params.font_size.0,
 590            glyphCount: 1,
 591            glyphIndices: glyph_id.as_ptr(),
 592            glyphAdvances: advance.as_ptr(),
 593            glyphOffsets: offset.as_ptr(),
 594            isSideways: BOOL(0),
 595            bidiLevel: 0,
 596        };
 597        let bounds = unsafe {
 598            render_target.GetGlyphRunWorldBounds(
 599                D2D_POINT_2F { x: 0.0, y: 0.0 },
 600                &glyph_run,
 601                DWRITE_MEASURING_MODE_NATURAL,
 602            )?
 603        };
 604
 605        if bounds.right < bounds.left {
 606            Ok(Bounds {
 607                origin: point(0.into(), 0.into()),
 608                size: size(0.into(), 0.into()),
 609            })
 610        } else {
 611            Ok(Bounds {
 612                origin: point(
 613                    ((bounds.left * params.scale_factor).ceil() as i32).into(),
 614                    ((bounds.top * params.scale_factor).ceil() as i32).into(),
 615                ),
 616                size: size(
 617                    (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
 618                    (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32).into(),
 619                ),
 620            })
 621        }
 622    }
 623
 624    fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
 625        let font_info = &self.fonts[font_id.0];
 626        let codepoints = [ch as u32];
 627        let mut glyph_indices = vec![0u16; 1];
 628        unsafe {
 629            font_info
 630                .font_face
 631                .GetGlyphIndices(codepoints.as_ptr(), 1, glyph_indices.as_mut_ptr())
 632                .log_err()
 633        }
 634        .map(|_| GlyphId(glyph_indices[0] as u32))
 635    }
 636
 637    fn rasterize_glyph(
 638        &self,
 639        params: &RenderGlyphParams,
 640        glyph_bounds: Bounds<DevicePixels>,
 641    ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
 642        if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
 643            return Err(anyhow!("glyph bounds are empty"));
 644        }
 645
 646        let font_info = &self.fonts[params.font_id.0];
 647        let glyph_id = [params.glyph_id.0 as u16];
 648        let advance = [glyph_bounds.size.width.0 as f32];
 649        let offset = [DWRITE_GLYPH_OFFSET {
 650            advanceOffset: -glyph_bounds.origin.x.0 as f32 / params.scale_factor,
 651            ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
 652        }];
 653        let glyph_run = DWRITE_GLYPH_RUN {
 654            fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) },
 655            fontEmSize: params.font_size.0,
 656            glyphCount: 1,
 657            glyphIndices: glyph_id.as_ptr(),
 658            glyphAdvances: advance.as_ptr(),
 659            glyphOffsets: offset.as_ptr(),
 660            isSideways: BOOL(0),
 661            bidiLevel: 0,
 662        };
 663
 664        // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing.
 665        let mut bitmap_size = glyph_bounds.size;
 666        if params.subpixel_variant.x > 0 {
 667            bitmap_size.width += DevicePixels(1);
 668        }
 669        if params.subpixel_variant.y > 0 {
 670            bitmap_size.height += DevicePixels(1);
 671        }
 672        let bitmap_size = bitmap_size;
 673
 674        let total_bytes;
 675        let bitmap_format;
 676        let render_target_property;
 677        let bitmap_width;
 678        let bitmap_height;
 679        let bitmap_stride;
 680        let bitmap_dpi;
 681        if params.is_emoji {
 682            total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
 683            bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
 684            render_target_property = get_render_target_property(
 685                DXGI_FORMAT_B8G8R8A8_UNORM,
 686                D2D1_ALPHA_MODE_PREMULTIPLIED,
 687            );
 688            bitmap_width = bitmap_size.width.0 as u32;
 689            bitmap_height = bitmap_size.height.0 as u32;
 690            bitmap_stride = bitmap_size.width.0 as u32 * 4;
 691            bitmap_dpi = 96.0;
 692        } else {
 693            total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
 694            bitmap_format = &GUID_WICPixelFormat8bppAlpha;
 695            render_target_property =
 696                get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
 697            bitmap_width = bitmap_size.width.0 as u32 * 2;
 698            bitmap_height = bitmap_size.height.0 as u32 * 2;
 699            bitmap_stride = bitmap_size.width.0 as u32;
 700            bitmap_dpi = 192.0;
 701        }
 702
 703        unsafe {
 704            let bitmap = self.components.bitmap_factory.CreateBitmap(
 705                bitmap_width,
 706                bitmap_height,
 707                bitmap_format,
 708                WICBitmapCacheOnLoad,
 709            )?;
 710            let render_target = self
 711                .components
 712                .d2d1_factory
 713                .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
 714            let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
 715            let subpixel_shift = params
 716                .subpixel_variant
 717                .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
 718            let baseline_origin = D2D_POINT_2F {
 719                x: subpixel_shift.x / params.scale_factor,
 720                y: subpixel_shift.y / params.scale_factor,
 721            };
 722
 723            // This `cast()` action here should never fail since we are running on Win10+, and
 724            // ID2D1DeviceContext4 requires Win8+
 725            let render_target = render_target.cast::<ID2D1DeviceContext4>().unwrap();
 726            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
 727            render_target.SetDpi(
 728                bitmap_dpi * params.scale_factor,
 729                bitmap_dpi * params.scale_factor,
 730            );
 731            render_target.SetTextRenderingParams(&self.components.render_context.params);
 732            render_target.BeginDraw();
 733
 734            if params.is_emoji {
 735                // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested
 736                let enumerator = self.components.factory.TranslateColorGlyphRun(
 737                    baseline_origin,
 738                    &glyph_run as _,
 739                    None,
 740                    DWRITE_GLYPH_IMAGE_FORMATS_COLR
 741                        | DWRITE_GLYPH_IMAGE_FORMATS_SVG
 742                        | DWRITE_GLYPH_IMAGE_FORMATS_PNG
 743                        | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
 744                        | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
 745                    DWRITE_MEASURING_MODE_NATURAL,
 746                    None,
 747                    0,
 748                )?;
 749                while enumerator.MoveNext().is_ok() {
 750                    let Ok(color_glyph) = enumerator.GetCurrentRun() else {
 751                        break;
 752                    };
 753                    let color_glyph = &*color_glyph;
 754                    let brush_color = translate_color(&color_glyph.Base.runColor);
 755                    brush.SetColor(&brush_color);
 756                    match color_glyph.glyphImageFormat {
 757                        DWRITE_GLYPH_IMAGE_FORMATS_PNG
 758                        | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
 759                        | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target
 760                            .DrawColorBitmapGlyphRun(
 761                                color_glyph.glyphImageFormat,
 762                                baseline_origin,
 763                                &color_glyph.Base.glyphRun,
 764                                color_glyph.measuringMode,
 765                                D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT,
 766                            ),
 767                        DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun(
 768                            baseline_origin,
 769                            &color_glyph.Base.glyphRun,
 770                            &brush,
 771                            None,
 772                            color_glyph.Base.paletteIndex as u32,
 773                            color_glyph.measuringMode,
 774                        ),
 775                        _ => render_target.DrawGlyphRun(
 776                            baseline_origin,
 777                            &color_glyph.Base.glyphRun,
 778                            Some(color_glyph.Base.glyphRunDescription as *const _),
 779                            &brush,
 780                            color_glyph.measuringMode,
 781                        ),
 782                    }
 783                }
 784            } else {
 785                render_target.DrawGlyphRun(
 786                    baseline_origin,
 787                    &glyph_run,
 788                    None,
 789                    &brush,
 790                    DWRITE_MEASURING_MODE_NATURAL,
 791                );
 792            }
 793            render_target.EndDraw(None, None)?;
 794
 795            let mut raw_data = vec![0u8; total_bytes];
 796            if params.is_emoji {
 797                bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
 798                // Convert from BGRA with premultiplied alpha to BGRA with straight alpha.
 799                for pixel in raw_data.chunks_exact_mut(4) {
 800                    let a = pixel[3] as f32 / 255.;
 801                    pixel[0] = (pixel[0] as f32 / a) as u8;
 802                    pixel[1] = (pixel[1] as f32 / a) as u8;
 803                    pixel[2] = (pixel[2] as f32 / a) as u8;
 804                }
 805            } else {
 806                let scaler = self.components.bitmap_factory.CreateBitmapScaler()?;
 807                scaler.Initialize(
 808                    &bitmap,
 809                    bitmap_size.width.0 as u32,
 810                    bitmap_size.height.0 as u32,
 811                    WICBitmapInterpolationModeHighQualityCubic,
 812                )?;
 813                scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
 814            }
 815            Ok((bitmap_size, raw_data))
 816        }
 817    }
 818
 819    fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
 820        unsafe {
 821            let font = &self.fonts[font_id.0].font_face;
 822            let glyph_indices = [glyph_id.0 as u16];
 823            let mut metrics = [DWRITE_GLYPH_METRICS::default()];
 824            font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
 825
 826            let metrics = &metrics[0];
 827            let advance_width = metrics.advanceWidth as i32;
 828            let advance_height = metrics.advanceHeight as i32;
 829            let left_side_bearing = metrics.leftSideBearing;
 830            let right_side_bearing = metrics.rightSideBearing;
 831            let top_side_bearing = metrics.topSideBearing;
 832            let bottom_side_bearing = metrics.bottomSideBearing;
 833            let vertical_origin_y = metrics.verticalOriginY;
 834
 835            let y_offset = vertical_origin_y + bottom_side_bearing - advance_height;
 836            let width = advance_width - (left_side_bearing + right_side_bearing);
 837            let height = advance_height - (top_side_bearing + bottom_side_bearing);
 838
 839            Ok(Bounds {
 840                origin: Point {
 841                    x: left_side_bearing as f32,
 842                    y: y_offset as f32,
 843                },
 844                size: Size {
 845                    width: width as f32,
 846                    height: height as f32,
 847                },
 848            })
 849        }
 850    }
 851
 852    fn get_advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
 853        unsafe {
 854            let font = &self.fonts[font_id.0].font_face;
 855            let glyph_indices = [glyph_id.0 as u16];
 856            let mut metrics = [DWRITE_GLYPH_METRICS::default()];
 857            font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
 858
 859            let metrics = &metrics[0];
 860
 861            Ok(Size {
 862                width: metrics.advanceWidth as f32,
 863                height: 0.0,
 864            })
 865        }
 866    }
 867
 868    fn all_font_names(&self) -> Vec<String> {
 869        let mut result =
 870            get_font_names_from_collection(&self.system_font_collection, &self.components.locale);
 871        result.extend(get_font_names_from_collection(
 872            &self.custom_font_collection,
 873            &self.components.locale,
 874        ));
 875        result
 876    }
 877
 878    fn all_font_families(&self) -> Vec<String> {
 879        get_font_names_from_collection(&self.system_font_collection, &self.components.locale)
 880    }
 881}
 882
 883impl Drop for DirectWriteState {
 884    fn drop(&mut self) {
 885        unsafe {
 886            let _ = self
 887                .components
 888                .factory
 889                .UnregisterFontFileLoader(&self.components.in_memory_loader);
 890        }
 891    }
 892}
 893
 894struct TextRendererWrapper(pub IDWriteTextRenderer);
 895
 896impl TextRendererWrapper {
 897    pub fn new(locale_str: &str) -> Self {
 898        let inner = TextRenderer::new(locale_str);
 899        TextRendererWrapper(inner.into())
 900    }
 901}
 902
 903#[implement(IDWriteTextRenderer)]
 904struct TextRenderer {
 905    locale: String,
 906}
 907
 908impl TextRenderer {
 909    pub fn new(locale_str: &str) -> Self {
 910        TextRenderer {
 911            locale: locale_str.to_owned(),
 912        }
 913    }
 914}
 915
 916struct RendererContext<'t, 'a, 'b> {
 917    text_system: &'t mut DirectWriteState,
 918    index_converter: StringIndexConverter<'a>,
 919    runs: &'b mut Vec<ShapedRun>,
 920    utf16_index: usize,
 921    width: f32,
 922}
 923
 924#[allow(non_snake_case)]
 925impl IDWritePixelSnapping_Impl for TextRenderer {
 926    fn IsPixelSnappingDisabled(
 927        &self,
 928        _clientdrawingcontext: *const ::core::ffi::c_void,
 929    ) -> windows::core::Result<BOOL> {
 930        Ok(BOOL(0))
 931    }
 932
 933    fn GetCurrentTransform(
 934        &self,
 935        _clientdrawingcontext: *const ::core::ffi::c_void,
 936        transform: *mut DWRITE_MATRIX,
 937    ) -> windows::core::Result<()> {
 938        unsafe {
 939            *transform = DWRITE_MATRIX {
 940                m11: 1.0,
 941                m12: 0.0,
 942                m21: 0.0,
 943                m22: 1.0,
 944                dx: 0.0,
 945                dy: 0.0,
 946            };
 947        }
 948        Ok(())
 949    }
 950
 951    fn GetPixelsPerDip(
 952        &self,
 953        _clientdrawingcontext: *const ::core::ffi::c_void,
 954    ) -> windows::core::Result<f32> {
 955        Ok(1.0)
 956    }
 957}
 958
 959#[allow(non_snake_case)]
 960impl IDWriteTextRenderer_Impl for TextRenderer {
 961    fn DrawGlyphRun(
 962        &self,
 963        clientdrawingcontext: *const ::core::ffi::c_void,
 964        _baselineoriginx: f32,
 965        _baselineoriginy: f32,
 966        _measuringmode: DWRITE_MEASURING_MODE,
 967        glyphrun: *const DWRITE_GLYPH_RUN,
 968        glyphrundescription: *const DWRITE_GLYPH_RUN_DESCRIPTION,
 969        _clientdrawingeffect: Option<&windows::core::IUnknown>,
 970    ) -> windows::core::Result<()> {
 971        unsafe {
 972            let glyphrun = &*glyphrun;
 973            let glyph_count = glyphrun.glyphCount as usize;
 974            if glyph_count == 0 {
 975                return Ok(());
 976            }
 977            let desc = &*glyphrundescription;
 978            let utf16_length_per_glyph = desc.stringLength as usize / glyph_count;
 979            let context =
 980                &mut *(clientdrawingcontext as *const RendererContext as *mut RendererContext);
 981
 982            if glyphrun.fontFace.is_none() {
 983                return Ok(());
 984            }
 985
 986            let font_face = glyphrun.fontFace.as_ref().unwrap();
 987            // This `cast()` action here should never fail since we are running on Win10+, and
 988            // `IDWriteFontFace3` requires Win10
 989            let font_face = &font_face.cast::<IDWriteFontFace3>().unwrap();
 990            let Some((font_identifier, font_struct, is_emoji)) =
 991                get_font_identifier_and_font_struct(font_face, &self.locale)
 992            else {
 993                return Ok(());
 994            };
 995
 996            let font_id = if let Some(id) = context
 997                .text_system
 998                .font_id_by_identifier
 999                .get(&font_identifier)
1000            {
1001                *id
1002            } else {
1003                context.text_system.select_font(&font_struct)
1004            };
1005            let mut glyphs = SmallVec::new();
1006            for index in 0..glyph_count {
1007                let id = GlyphId(*glyphrun.glyphIndices.add(index) as u32);
1008                context
1009                    .index_converter
1010                    .advance_to_utf16_ix(context.utf16_index);
1011                glyphs.push(ShapedGlyph {
1012                    id,
1013                    position: point(px(context.width), px(0.0)),
1014                    index: context.index_converter.utf8_ix,
1015                    is_emoji,
1016                });
1017                context.utf16_index += utf16_length_per_glyph;
1018                context.width += *glyphrun.glyphAdvances.add(index);
1019            }
1020            context.runs.push(ShapedRun { font_id, glyphs });
1021        }
1022        Ok(())
1023    }
1024
1025    fn DrawUnderline(
1026        &self,
1027        _clientdrawingcontext: *const ::core::ffi::c_void,
1028        _baselineoriginx: f32,
1029        _baselineoriginy: f32,
1030        _underline: *const DWRITE_UNDERLINE,
1031        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1032    ) -> windows::core::Result<()> {
1033        Err(windows::core::Error::new(
1034            E_NOTIMPL,
1035            "DrawUnderline unimplemented",
1036        ))
1037    }
1038
1039    fn DrawStrikethrough(
1040        &self,
1041        _clientdrawingcontext: *const ::core::ffi::c_void,
1042        _baselineoriginx: f32,
1043        _baselineoriginy: f32,
1044        _strikethrough: *const DWRITE_STRIKETHROUGH,
1045        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1046    ) -> windows::core::Result<()> {
1047        Err(windows::core::Error::new(
1048            E_NOTIMPL,
1049            "DrawStrikethrough unimplemented",
1050        ))
1051    }
1052
1053    fn DrawInlineObject(
1054        &self,
1055        _clientdrawingcontext: *const ::core::ffi::c_void,
1056        _originx: f32,
1057        _originy: f32,
1058        _inlineobject: Option<&IDWriteInlineObject>,
1059        _issideways: BOOL,
1060        _isrighttoleft: BOOL,
1061        _clientdrawingeffect: Option<&windows::core::IUnknown>,
1062    ) -> windows::core::Result<()> {
1063        Err(windows::core::Error::new(
1064            E_NOTIMPL,
1065            "DrawInlineObject unimplemented",
1066        ))
1067    }
1068}
1069
1070struct StringIndexConverter<'a> {
1071    text: &'a str,
1072    utf8_ix: usize,
1073    utf16_ix: usize,
1074}
1075
1076impl<'a> StringIndexConverter<'a> {
1077    fn new(text: &'a str) -> Self {
1078        Self {
1079            text,
1080            utf8_ix: 0,
1081            utf16_ix: 0,
1082        }
1083    }
1084
1085    fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
1086        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1087            if self.utf8_ix + ix >= utf8_target {
1088                self.utf8_ix += ix;
1089                return;
1090            }
1091            self.utf16_ix += c.len_utf16();
1092        }
1093        self.utf8_ix = self.text.len();
1094    }
1095
1096    fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
1097        for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1098            if self.utf16_ix >= utf16_target {
1099                self.utf8_ix += ix;
1100                return;
1101            }
1102            self.utf16_ix += c.len_utf16();
1103        }
1104        self.utf8_ix = self.text.len();
1105    }
1106}
1107
1108impl Into<DWRITE_FONT_STYLE> for FontStyle {
1109    fn into(self) -> DWRITE_FONT_STYLE {
1110        match self {
1111            FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
1112            FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
1113            FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
1114        }
1115    }
1116}
1117
1118impl From<DWRITE_FONT_STYLE> for FontStyle {
1119    fn from(value: DWRITE_FONT_STYLE) -> Self {
1120        match value.0 {
1121            0 => FontStyle::Normal,
1122            1 => FontStyle::Italic,
1123            2 => FontStyle::Oblique,
1124            _ => unreachable!(),
1125        }
1126    }
1127}
1128
1129impl Into<DWRITE_FONT_WEIGHT> for FontWeight {
1130    fn into(self) -> DWRITE_FONT_WEIGHT {
1131        DWRITE_FONT_WEIGHT(self.0 as i32)
1132    }
1133}
1134
1135impl From<DWRITE_FONT_WEIGHT> for FontWeight {
1136    fn from(value: DWRITE_FONT_WEIGHT) -> Self {
1137        FontWeight(value.0 as f32)
1138    }
1139}
1140
1141fn get_font_names_from_collection(
1142    collection: &IDWriteFontCollection1,
1143    locale: &str,
1144) -> Vec<String> {
1145    unsafe {
1146        let mut result = Vec::new();
1147        let family_count = collection.GetFontFamilyCount();
1148        for index in 0..family_count {
1149            let Some(font_family) = collection.GetFontFamily(index).log_err() else {
1150                continue;
1151            };
1152            let Some(localized_family_name) = font_family.GetFamilyNames().log_err() else {
1153                continue;
1154            };
1155            let Some(family_name) = get_name(localized_family_name, locale).log_err() else {
1156                continue;
1157            };
1158            result.push(family_name);
1159        }
1160
1161        result
1162    }
1163}
1164
1165fn get_font_identifier_and_font_struct(
1166    font_face: &IDWriteFontFace3,
1167    locale: &str,
1168) -> Option<(FontIdentifier, Font, bool)> {
1169    let postscript_name = get_postscript_name(font_face, locale).log_err()?;
1170    let localized_family_name = unsafe { font_face.GetFamilyNames().log_err() }?;
1171    let family_name = get_name(localized_family_name, locale).log_err()?;
1172    let weight = unsafe { font_face.GetWeight() };
1173    let style = unsafe { font_face.GetStyle() };
1174    let identifier = FontIdentifier {
1175        postscript_name,
1176        weight: weight.0,
1177        style: style.0,
1178    };
1179    let font_struct = Font {
1180        family: family_name.into(),
1181        features: FontFeatures::default(),
1182        weight: weight.into(),
1183        style: style.into(),
1184    };
1185    let is_emoji = unsafe { font_face.IsColorFont().as_bool() };
1186    Some((identifier, font_struct, is_emoji))
1187}
1188
1189#[inline]
1190fn get_font_identifier(font_face: &IDWriteFontFace3, locale: &str) -> Option<FontIdentifier> {
1191    let weight = unsafe { font_face.GetWeight().0 };
1192    let style = unsafe { font_face.GetStyle().0 };
1193    get_postscript_name(font_face, locale)
1194        .log_err()
1195        .map(|postscript_name| FontIdentifier {
1196            postscript_name,
1197            weight,
1198            style,
1199        })
1200}
1201
1202#[inline]
1203fn get_postscript_name(font_face: &IDWriteFontFace3, locale: &str) -> Result<String> {
1204    let mut info = None;
1205    let mut exists = BOOL(0);
1206    unsafe {
1207        font_face.GetInformationalStrings(
1208            DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
1209            &mut info,
1210            &mut exists,
1211        )?
1212    };
1213    if !exists.as_bool() || info.is_none() {
1214        return Err(anyhow!("No postscript name found for font face"));
1215    }
1216
1217    get_name(info.unwrap(), locale)
1218}
1219
1220// https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_feature_tag
1221fn apply_font_features(
1222    direct_write_features: &IDWriteTypography,
1223    features: &FontFeatures,
1224) -> Result<()> {
1225    let tag_values = features.tag_value_list();
1226    if tag_values.is_empty() {
1227        return Ok(());
1228    }
1229
1230    // All of these features are enabled by default by DirectWrite.
1231    // If you want to (and can) peek into the source of DirectWrite
1232    let mut feature_liga = make_direct_write_feature("liga", 1);
1233    let mut feature_clig = make_direct_write_feature("clig", 1);
1234    let mut feature_calt = make_direct_write_feature("calt", 1);
1235
1236    for (tag, value) in tag_values {
1237        if tag.as_str() == "liga" && *value == 0 {
1238            feature_liga.parameter = 0;
1239            continue;
1240        }
1241        if tag.as_str() == "clig" && *value == 0 {
1242            feature_clig.parameter = 0;
1243            continue;
1244        }
1245        if tag.as_str() == "calt" && *value == 0 {
1246            feature_calt.parameter = 0;
1247            continue;
1248        }
1249
1250        unsafe {
1251            direct_write_features.AddFontFeature(make_direct_write_feature(&tag, *value))?;
1252        }
1253    }
1254    unsafe {
1255        direct_write_features.AddFontFeature(feature_liga)?;
1256        direct_write_features.AddFontFeature(feature_clig)?;
1257        direct_write_features.AddFontFeature(feature_calt)?;
1258    }
1259
1260    Ok(())
1261}
1262
1263#[inline]
1264fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_FEATURE {
1265    let tag = make_direct_write_tag(feature_name);
1266    DWRITE_FONT_FEATURE {
1267        nameTag: tag,
1268        parameter,
1269    }
1270}
1271
1272#[inline]
1273fn make_open_type_tag(tag_name: &str) -> u32 {
1274    let bytes = tag_name.bytes().collect_vec();
1275    assert_eq!(bytes.len(), 4);
1276    ((bytes[3] as u32) << 24)
1277        | ((bytes[2] as u32) << 16)
1278        | ((bytes[1] as u32) << 8)
1279        | (bytes[0] as u32)
1280}
1281
1282#[inline]
1283fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG {
1284    DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name))
1285}
1286
1287#[inline]
1288fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
1289    let mut locale_name_index = 0u32;
1290    let mut exists = BOOL(0);
1291    unsafe {
1292        string.FindLocaleName(
1293            &HSTRING::from(locale),
1294            &mut locale_name_index,
1295            &mut exists as _,
1296        )?
1297    };
1298    if !exists.as_bool() {
1299        unsafe {
1300            string.FindLocaleName(
1301                DEFAULT_LOCALE_NAME,
1302                &mut locale_name_index as _,
1303                &mut exists as _,
1304            )?
1305        };
1306        if !exists.as_bool() {
1307            return Err(anyhow!("No localised string for {}", locale));
1308        }
1309    }
1310
1311    let name_length = unsafe { string.GetStringLength(locale_name_index) }? as usize;
1312    let mut name_vec = vec![0u16; name_length + 1];
1313    unsafe {
1314        string.GetString(locale_name_index, &mut name_vec)?;
1315    }
1316
1317    Ok(String::from_utf16_lossy(&name_vec[..name_length]))
1318}
1319
1320#[inline]
1321fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F {
1322    D2D1_COLOR_F {
1323        r: color.r,
1324        g: color.g,
1325        b: color.b,
1326        a: color.a,
1327    }
1328}
1329
1330fn get_system_ui_font_name() -> SharedString {
1331    unsafe {
1332        let mut info: LOGFONTW = std::mem::zeroed();
1333        let font_family = if SystemParametersInfoW(
1334            SPI_GETICONTITLELOGFONT,
1335            std::mem::size_of::<LOGFONTW>() as u32,
1336            Some(&mut info as *mut _ as _),
1337            SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
1338        )
1339        .log_err()
1340        .is_none()
1341        {
1342            // https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-fonts
1343            // Segoe UI is the Windows font intended for user interface text strings.
1344            "Segoe UI".into()
1345        } else {
1346            let font_name = String::from_utf16_lossy(&info.lfFaceName);
1347            font_name.trim_matches(char::from(0)).to_owned().into()
1348        };
1349        log::info!("Use {} as UI font.", font_family);
1350        font_family
1351    }
1352}
1353
1354#[inline]
1355fn get_render_target_property(
1356    pixel_format: DXGI_FORMAT,
1357    alpha_mode: D2D1_ALPHA_MODE,
1358) -> D2D1_RENDER_TARGET_PROPERTIES {
1359    D2D1_RENDER_TARGET_PROPERTIES {
1360        r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
1361        pixelFormat: D2D1_PIXEL_FORMAT {
1362            format: pixel_format,
1363            alphaMode: alpha_mode,
1364        },
1365        dpiX: 96.0,
1366        dpiY: 96.0,
1367        usage: D2D1_RENDER_TARGET_USAGE_NONE,
1368        minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
1369    }
1370}
1371
1372const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
1373const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
1374    r: 1.0,
1375    g: 1.0,
1376    b: 1.0,
1377    a: 1.0,
1378};