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