1use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
2
3use ::util::ResultExt;
4use anyhow::Result;
5use collections::HashMap;
6use itertools::Itertools;
7use parking_lot::{RwLock, RwLockUpgradableReadGuard};
8use windows::{
9 Win32::{
10 Foundation::*,
11 Globalization::GetUserDefaultLocaleName,
12 Graphics::{
13 Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*,
14 Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*,
15 },
16 System::SystemServices::LOCALE_NAME_MAX_LENGTH,
17 UI::WindowsAndMessaging::*,
18 },
19 core::*,
20};
21use windows_numerics::Vector2;
22
23use crate::*;
24
25#[derive(Debug)]
26struct FontInfo {
27 font_family: String,
28 font_face: IDWriteFontFace3,
29 features: IDWriteTypography,
30 fallbacks: Option<IDWriteFontFallback>,
31 is_system_font: bool,
32}
33
34pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
35
36struct DirectWriteComponent {
37 locale: String,
38 factory: IDWriteFactory5,
39 bitmap_factory: AgileReference<IWICImagingFactory>,
40 in_memory_loader: IDWriteInMemoryFontFileLoader,
41 builder: IDWriteFontSetBuilder1,
42 text_renderer: Arc<TextRendererWrapper>,
43
44 render_params: IDWriteRenderingParams3,
45 gpu_state: GPUState,
46}
47
48struct GPUState {
49 device: ID3D11Device,
50 device_context: ID3D11DeviceContext,
51 sampler: [Option<ID3D11SamplerState>; 1],
52 blend_state: ID3D11BlendState,
53 vertex_shader: ID3D11VertexShader,
54 pixel_shader: ID3D11PixelShader,
55}
56
57struct DirectWriteState {
58 components: DirectWriteComponent,
59 system_ui_font_name: SharedString,
60 system_font_collection: IDWriteFontCollection1,
61 custom_font_collection: IDWriteFontCollection1,
62 fonts: Vec<FontInfo>,
63 font_selections: HashMap<Font, FontId>,
64 font_id_by_identifier: HashMap<FontIdentifier, FontId>,
65}
66
67#[derive(Debug, Clone, Hash, PartialEq, Eq)]
68struct FontIdentifier {
69 postscript_name: String,
70 weight: i32,
71 style: i32,
72}
73
74impl DirectWriteComponent {
75 pub fn new(bitmap_factory: &IWICImagingFactory, gpu_context: &DirectXDevices) -> Result<Self> {
76 // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
77 unsafe {
78 let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
79 let bitmap_factory = AgileReference::new(bitmap_factory)?;
80 // The `IDWriteInMemoryFontFileLoader` here is supported starting from
81 // Windows 10 Creators Update, which consequently requires the entire
82 // `DirectWriteTextSystem` to run on `win10 1703`+.
83 let in_memory_loader = factory.CreateInMemoryFontFileLoader()?;
84 factory.RegisterFontFileLoader(&in_memory_loader)?;
85 let builder = factory.CreateFontSetBuilder()?;
86 let mut locale_vec = vec![0u16; LOCALE_NAME_MAX_LENGTH as usize];
87 GetUserDefaultLocaleName(&mut locale_vec);
88 let locale = String::from_utf16_lossy(&locale_vec);
89 let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
90
91 let render_params = {
92 let default_params: IDWriteRenderingParams3 =
93 factory.CreateRenderingParams()?.cast()?;
94 let gamma = default_params.GetGamma();
95 let enhanced_contrast = default_params.GetEnhancedContrast();
96 let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
97 let cleartype_level = default_params.GetClearTypeLevel();
98 let grid_fit_mode = default_params.GetGridFitMode();
99
100 factory.CreateCustomRenderingParams(
101 gamma,
102 enhanced_contrast,
103 gray_contrast,
104 cleartype_level,
105 DWRITE_PIXEL_GEOMETRY_RGB,
106 DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
107 grid_fit_mode,
108 )?
109 };
110
111 let gpu_state = GPUState::new(gpu_context)?;
112
113 Ok(DirectWriteComponent {
114 locale,
115 factory,
116 bitmap_factory,
117 in_memory_loader,
118 builder,
119 text_renderer,
120 render_params,
121 gpu_state,
122 })
123 }
124 }
125}
126
127impl GPUState {
128 fn new(gpu_context: &DirectXDevices) -> Result<Self> {
129 // todo: safety comments
130 let device = gpu_context.device.clone();
131 let device_context = gpu_context.device_context.clone();
132
133 let blend_state = {
134 let mut blend_state = None;
135 let desc = D3D11_BLEND_DESC {
136 AlphaToCoverageEnable: false.into(),
137 IndependentBlendEnable: false.into(),
138 RenderTarget: [
139 D3D11_RENDER_TARGET_BLEND_DESC {
140 BlendEnable: true.into(),
141 SrcBlend: D3D11_BLEND_SRC_ALPHA,
142 DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
143 BlendOp: D3D11_BLEND_OP_ADD,
144 SrcBlendAlpha: D3D11_BLEND_SRC_ALPHA,
145 DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
146 BlendOpAlpha: D3D11_BLEND_OP_ADD,
147 RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
148 },
149 Default::default(),
150 Default::default(),
151 Default::default(),
152 Default::default(),
153 Default::default(),
154 Default::default(),
155 Default::default(),
156 ],
157 };
158 unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?;
159 blend_state.unwrap()
160 };
161
162 let sampler = {
163 let mut sampler = None;
164 let desc = D3D11_SAMPLER_DESC {
165 Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,
166 AddressU: D3D11_TEXTURE_ADDRESS_BORDER,
167 AddressV: D3D11_TEXTURE_ADDRESS_BORDER,
168 AddressW: D3D11_TEXTURE_ADDRESS_BORDER,
169 MipLODBias: 0.0,
170 MaxAnisotropy: 1,
171 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
172 BorderColor: [0.0, 0.0, 0.0, 0.0],
173 MinLOD: 0.0,
174 MaxLOD: 0.0,
175 };
176 unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?;
177 [sampler]
178 };
179
180 let vertex_shader = {
181 let source = shader_resources::RawShaderBytes::new(
182 shader_resources::ShaderModule::EmojiRasterization,
183 shader_resources::ShaderTarget::Vertex,
184 )?;
185 let mut shader = None;
186 unsafe { device.CreateVertexShader(source.as_bytes(), None, Some(&mut shader)) }?;
187 shader.unwrap()
188 };
189
190 let pixel_shader = {
191 let source = shader_resources::RawShaderBytes::new(
192 shader_resources::ShaderModule::EmojiRasterization,
193 shader_resources::ShaderTarget::Fragment,
194 )?;
195 let mut shader = None;
196 unsafe { device.CreatePixelShader(source.as_bytes(), None, Some(&mut shader)) }?;
197 shader.unwrap()
198 };
199
200 Ok(Self {
201 device,
202 device_context,
203 sampler,
204 blend_state,
205 vertex_shader,
206 pixel_shader,
207 })
208 }
209}
210
211impl DirectWriteTextSystem {
212 pub(crate) fn new(
213 gpu_context: &DirectXDevices,
214 bitmap_factory: &IWICImagingFactory,
215 ) -> Result<Self> {
216 let components = DirectWriteComponent::new(bitmap_factory, gpu_context)?;
217 let system_font_collection = unsafe {
218 let mut result = std::mem::zeroed();
219 components
220 .factory
221 .GetSystemFontCollection(false, &mut result, true)?;
222 result.unwrap()
223 };
224 let custom_font_set = unsafe { components.builder.CreateFontSet()? };
225 let custom_font_collection = unsafe {
226 components
227 .factory
228 .CreateFontCollectionFromFontSet(&custom_font_set)?
229 };
230 let system_ui_font_name = get_system_ui_font_name();
231
232 Ok(Self(RwLock::new(DirectWriteState {
233 components,
234 system_ui_font_name,
235 system_font_collection,
236 custom_font_collection,
237 fonts: Vec::new(),
238 font_selections: HashMap::default(),
239 font_id_by_identifier: HashMap::default(),
240 })))
241 }
242}
243
244impl PlatformTextSystem for DirectWriteTextSystem {
245 fn add_fonts(&self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
246 self.0.write().add_fonts(fonts)
247 }
248
249 fn all_font_names(&self) -> Vec<String> {
250 self.0.read().all_font_names()
251 }
252
253 fn font_id(&self, font: &Font) -> Result<FontId> {
254 let lock = self.0.upgradable_read();
255 if let Some(font_id) = lock.font_selections.get(font) {
256 Ok(*font_id)
257 } else {
258 let mut lock = RwLockUpgradableReadGuard::upgrade(lock);
259 let font_id = lock.select_font(font);
260 lock.font_selections.insert(font.clone(), font_id);
261 Ok(font_id)
262 }
263 }
264
265 fn font_metrics(&self, font_id: FontId) -> FontMetrics {
266 self.0.read().font_metrics(font_id)
267 }
268
269 fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
270 self.0.read().get_typographic_bounds(font_id, glyph_id)
271 }
272
273 fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> anyhow::Result<Size<f32>> {
274 self.0.read().get_advance(font_id, glyph_id)
275 }
276
277 fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
278 self.0.read().glyph_for_char(font_id, ch)
279 }
280
281 fn glyph_raster_bounds(
282 &self,
283 params: &RenderGlyphParams,
284 ) -> anyhow::Result<Bounds<DevicePixels>> {
285 self.0.read().raster_bounds(params)
286 }
287
288 fn rasterize_glyph(
289 &self,
290 params: &RenderGlyphParams,
291 raster_bounds: Bounds<DevicePixels>,
292 ) -> anyhow::Result<(Size<DevicePixels>, Vec<u8>)> {
293 self.0.read().rasterize_glyph(params, raster_bounds)
294 }
295
296 fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout {
297 self.0
298 .write()
299 .layout_line(text, font_size, runs)
300 .log_err()
301 .unwrap_or(LineLayout {
302 font_size,
303 ..Default::default()
304 })
305 }
306}
307
308impl DirectWriteState {
309 fn add_fonts(&mut self, fonts: Vec<Cow<'static, [u8]>>) -> Result<()> {
310 for font_data in fonts {
311 match font_data {
312 Cow::Borrowed(data) => unsafe {
313 let font_file = self
314 .components
315 .in_memory_loader
316 .CreateInMemoryFontFileReference(
317 &self.components.factory,
318 data.as_ptr() as _,
319 data.len() as _,
320 None,
321 )?;
322 self.components.builder.AddFontFile(&font_file)?;
323 },
324 Cow::Owned(data) => unsafe {
325 let font_file = self
326 .components
327 .in_memory_loader
328 .CreateInMemoryFontFileReference(
329 &self.components.factory,
330 data.as_ptr() as _,
331 data.len() as _,
332 None,
333 )?;
334 self.components.builder.AddFontFile(&font_file)?;
335 },
336 }
337 }
338 let set = unsafe { self.components.builder.CreateFontSet()? };
339 let collection = unsafe {
340 self.components
341 .factory
342 .CreateFontCollectionFromFontSet(&set)?
343 };
344 self.custom_font_collection = collection;
345
346 Ok(())
347 }
348
349 fn generate_font_fallbacks(
350 &self,
351 fallbacks: &FontFallbacks,
352 ) -> Result<Option<IDWriteFontFallback>> {
353 if fallbacks.fallback_list().is_empty() {
354 return Ok(None);
355 }
356 unsafe {
357 let builder = self.components.factory.CreateFontFallbackBuilder()?;
358 let font_set = &self.system_font_collection.GetFontSet()?;
359 for family_name in fallbacks.fallback_list() {
360 let Some(fonts) = font_set
361 .GetMatchingFonts(
362 &HSTRING::from(family_name),
363 DWRITE_FONT_WEIGHT_NORMAL,
364 DWRITE_FONT_STRETCH_NORMAL,
365 DWRITE_FONT_STYLE_NORMAL,
366 )
367 .log_err()
368 else {
369 continue;
370 };
371 if fonts.GetFontCount() == 0 {
372 log::error!("No matching font found for {}", family_name);
373 continue;
374 }
375 let font = fonts.GetFontFaceReference(0)?.CreateFontFace()?;
376 let mut count = 0;
377 font.GetUnicodeRanges(None, &mut count).ok();
378 if count == 0 {
379 continue;
380 }
381 let mut unicode_ranges = vec![DWRITE_UNICODE_RANGE::default(); count as usize];
382 let Some(_) = font
383 .GetUnicodeRanges(Some(&mut unicode_ranges), &mut count)
384 .log_err()
385 else {
386 continue;
387 };
388 let target_family_name = HSTRING::from(family_name);
389 builder.AddMapping(
390 &unicode_ranges,
391 &[target_family_name.as_ptr()],
392 None,
393 None,
394 None,
395 1.0,
396 )?;
397 }
398 let system_fallbacks = self.components.factory.GetSystemFontFallback()?;
399 builder.AddMappings(&system_fallbacks)?;
400 Ok(Some(builder.CreateFontFallback()?))
401 }
402 }
403
404 unsafe fn generate_font_features(
405 &self,
406 font_features: &FontFeatures,
407 ) -> Result<IDWriteTypography> {
408 let direct_write_features = unsafe { self.components.factory.CreateTypography()? };
409 apply_font_features(&direct_write_features, font_features)?;
410 Ok(direct_write_features)
411 }
412
413 unsafe fn get_font_id_from_font_collection(
414 &mut self,
415 family_name: &str,
416 font_weight: FontWeight,
417 font_style: FontStyle,
418 font_features: &FontFeatures,
419 font_fallbacks: Option<&FontFallbacks>,
420 is_system_font: bool,
421 ) -> Option<FontId> {
422 let collection = if is_system_font {
423 &self.system_font_collection
424 } else {
425 &self.custom_font_collection
426 };
427 let fontset = unsafe { collection.GetFontSet().log_err()? };
428 let font = unsafe {
429 fontset
430 .GetMatchingFonts(
431 &HSTRING::from(family_name),
432 font_weight.into(),
433 DWRITE_FONT_STRETCH_NORMAL,
434 font_style.into(),
435 )
436 .log_err()?
437 };
438 let total_number = unsafe { font.GetFontCount() };
439 for index in 0..total_number {
440 let Some(font_face_ref) = (unsafe { font.GetFontFaceReference(index).log_err() })
441 else {
442 continue;
443 };
444 let Some(font_face) = (unsafe { font_face_ref.CreateFontFace().log_err() }) else {
445 continue;
446 };
447 let Some(identifier) = get_font_identifier(&font_face, &self.components.locale) else {
448 continue;
449 };
450 let Some(direct_write_features) =
451 (unsafe { self.generate_font_features(font_features).log_err() })
452 else {
453 continue;
454 };
455 let fallbacks = font_fallbacks
456 .and_then(|fallbacks| self.generate_font_fallbacks(fallbacks).log_err().flatten());
457 let font_info = FontInfo {
458 font_family: family_name.to_owned(),
459 font_face,
460 features: direct_write_features,
461 fallbacks,
462 is_system_font,
463 };
464 let font_id = FontId(self.fonts.len());
465 self.fonts.push(font_info);
466 self.font_id_by_identifier.insert(identifier, font_id);
467 return Some(font_id);
468 }
469 None
470 }
471
472 unsafe fn update_system_font_collection(&mut self) {
473 let mut collection = unsafe { std::mem::zeroed() };
474 if unsafe {
475 self.components
476 .factory
477 .GetSystemFontCollection(false, &mut collection, true)
478 .log_err()
479 .is_some()
480 } {
481 self.system_font_collection = collection.unwrap();
482 }
483 }
484
485 fn select_font(&mut self, target_font: &Font) -> FontId {
486 unsafe {
487 if target_font.family == ".SystemUIFont" {
488 let family = self.system_ui_font_name.clone();
489 self.find_font_id(
490 family.as_ref(),
491 target_font.weight,
492 target_font.style,
493 &target_font.features,
494 target_font.fallbacks.as_ref(),
495 )
496 .unwrap()
497 } else {
498 self.find_font_id(
499 target_font.family.as_ref(),
500 target_font.weight,
501 target_font.style,
502 &target_font.features,
503 target_font.fallbacks.as_ref(),
504 )
505 .unwrap_or_else(|| {
506 #[cfg(any(test, feature = "test-support"))]
507 {
508 panic!("ERROR: {} font not found!", target_font.family);
509 }
510 #[cfg(not(any(test, feature = "test-support")))]
511 {
512 let family = self.system_ui_font_name.clone();
513 log::error!("{} not found, use {} instead.", target_font.family, family);
514 self.get_font_id_from_font_collection(
515 family.as_ref(),
516 target_font.weight,
517 target_font.style,
518 &target_font.features,
519 target_font.fallbacks.as_ref(),
520 true,
521 )
522 .unwrap()
523 }
524 })
525 }
526 }
527 }
528
529 unsafe fn find_font_id(
530 &mut self,
531 family_name: &str,
532 weight: FontWeight,
533 style: FontStyle,
534 features: &FontFeatures,
535 fallbacks: Option<&FontFallbacks>,
536 ) -> Option<FontId> {
537 // try to find target font in custom font collection first
538 unsafe {
539 self.get_font_id_from_font_collection(
540 family_name,
541 weight,
542 style,
543 features,
544 fallbacks,
545 false,
546 )
547 .or_else(|| {
548 self.get_font_id_from_font_collection(
549 family_name,
550 weight,
551 style,
552 features,
553 fallbacks,
554 true,
555 )
556 })
557 .or_else(|| {
558 self.update_system_font_collection();
559 self.get_font_id_from_font_collection(
560 family_name,
561 weight,
562 style,
563 features,
564 fallbacks,
565 true,
566 )
567 })
568 }
569 }
570
571 fn layout_line(
572 &mut self,
573 text: &str,
574 font_size: Pixels,
575 font_runs: &[FontRun],
576 ) -> Result<LineLayout> {
577 if font_runs.is_empty() {
578 return Ok(LineLayout {
579 font_size,
580 ..Default::default()
581 });
582 }
583 unsafe {
584 let text_renderer = self.components.text_renderer.clone();
585 let text_wide = text.encode_utf16().collect_vec();
586
587 let mut utf8_offset = 0usize;
588 let mut utf16_offset = 0u32;
589 let text_layout = {
590 let first_run = &font_runs[0];
591 let font_info = &self.fonts[first_run.font_id.0];
592 let collection = if font_info.is_system_font {
593 &self.system_font_collection
594 } else {
595 &self.custom_font_collection
596 };
597 let format: IDWriteTextFormat1 = self
598 .components
599 .factory
600 .CreateTextFormat(
601 &HSTRING::from(&font_info.font_family),
602 collection,
603 font_info.font_face.GetWeight(),
604 font_info.font_face.GetStyle(),
605 DWRITE_FONT_STRETCH_NORMAL,
606 font_size.0,
607 &HSTRING::from(&self.components.locale),
608 )?
609 .cast()?;
610 if let Some(ref fallbacks) = font_info.fallbacks {
611 format.SetFontFallback(fallbacks)?;
612 }
613
614 let layout = self.components.factory.CreateTextLayout(
615 &text_wide,
616 &format,
617 f32::INFINITY,
618 f32::INFINITY,
619 )?;
620 let current_text = &text[utf8_offset..(utf8_offset + first_run.len)];
621 utf8_offset += first_run.len;
622 let current_text_utf16_length = current_text.encode_utf16().count() as u32;
623 let text_range = DWRITE_TEXT_RANGE {
624 startPosition: utf16_offset,
625 length: current_text_utf16_length,
626 };
627 layout.SetTypography(&font_info.features, text_range)?;
628 utf16_offset += current_text_utf16_length;
629
630 layout
631 };
632
633 let mut first_run = true;
634 let mut ascent = Pixels::default();
635 let mut descent = Pixels::default();
636 for run in font_runs {
637 if first_run {
638 first_run = false;
639 let mut metrics = vec![DWRITE_LINE_METRICS::default(); 4];
640 let mut line_count = 0u32;
641 text_layout.GetLineMetrics(Some(&mut metrics), &mut line_count as _)?;
642 ascent = px(metrics[0].baseline);
643 descent = px(metrics[0].height - metrics[0].baseline);
644 continue;
645 }
646 let font_info = &self.fonts[run.font_id.0];
647 let current_text = &text[utf8_offset..(utf8_offset + run.len)];
648 utf8_offset += run.len;
649 let current_text_utf16_length = current_text.encode_utf16().count() as u32;
650
651 let collection = if font_info.is_system_font {
652 &self.system_font_collection
653 } else {
654 &self.custom_font_collection
655 };
656 let text_range = DWRITE_TEXT_RANGE {
657 startPosition: utf16_offset,
658 length: current_text_utf16_length,
659 };
660 utf16_offset += current_text_utf16_length;
661 text_layout.SetFontCollection(collection, text_range)?;
662 text_layout
663 .SetFontFamilyName(&HSTRING::from(&font_info.font_family), text_range)?;
664 text_layout.SetFontSize(font_size.0, text_range)?;
665 text_layout.SetFontStyle(font_info.font_face.GetStyle(), text_range)?;
666 text_layout.SetFontWeight(font_info.font_face.GetWeight(), text_range)?;
667 text_layout.SetTypography(&font_info.features, text_range)?;
668 }
669
670 let mut runs = Vec::new();
671 let renderer_context = RendererContext {
672 text_system: self,
673 index_converter: StringIndexConverter::new(text),
674 runs: &mut runs,
675 width: 0.0,
676 };
677 text_layout.Draw(
678 Some(&renderer_context as *const _ as _),
679 &text_renderer.0,
680 0.0,
681 0.0,
682 )?;
683 let width = px(renderer_context.width);
684
685 Ok(LineLayout {
686 font_size,
687 width,
688 ascent,
689 descent,
690 runs,
691 len: text.len(),
692 })
693 }
694 }
695
696 fn font_metrics(&self, font_id: FontId) -> FontMetrics {
697 unsafe {
698 let font_info = &self.fonts[font_id.0];
699 let mut metrics = std::mem::zeroed();
700 font_info.font_face.GetMetrics(&mut metrics);
701
702 FontMetrics {
703 units_per_em: metrics.Base.designUnitsPerEm as _,
704 ascent: metrics.Base.ascent as _,
705 descent: -(metrics.Base.descent as f32),
706 line_gap: metrics.Base.lineGap as _,
707 underline_position: metrics.Base.underlinePosition as _,
708 underline_thickness: metrics.Base.underlineThickness as _,
709 cap_height: metrics.Base.capHeight as _,
710 x_height: metrics.Base.xHeight as _,
711 bounding_box: Bounds {
712 origin: Point {
713 x: metrics.glyphBoxLeft as _,
714 y: metrics.glyphBoxBottom as _,
715 },
716 size: Size {
717 width: (metrics.glyphBoxRight - metrics.glyphBoxLeft) as _,
718 height: (metrics.glyphBoxTop - metrics.glyphBoxBottom) as _,
719 },
720 },
721 }
722 }
723 }
724
725 fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
726 let font = &self.fonts[params.font_id.0];
727 let glyph_id = [params.glyph_id.0 as u16];
728 let advance = [0.0f32];
729 let offset = [DWRITE_GLYPH_OFFSET::default()];
730 let glyph_run = DWRITE_GLYPH_RUN {
731 fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
732 fontEmSize: params.font_size.0,
733 glyphCount: 1,
734 glyphIndices: glyph_id.as_ptr(),
735 glyphAdvances: advance.as_ptr(),
736 glyphOffsets: offset.as_ptr(),
737 isSideways: BOOL(0),
738 bidiLevel: 0,
739 };
740
741 let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC;
742 let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
743 let baseline_origin_x = 0.0;
744 let baseline_origin_y = 0.0;
745
746 let transform = DWRITE_MATRIX {
747 m11: params.scale_factor,
748 m12: 0.0,
749 m21: 0.0,
750 m22: params.scale_factor,
751 dx: 0.0,
752 dy: 0.0,
753 };
754
755 let glyph_analysis = unsafe {
756 self.components.factory.CreateGlyphRunAnalysis(
757 &glyph_run,
758 Some(&transform),
759 rendering_mode,
760 measuring_mode,
761 DWRITE_GRID_FIT_MODE_DEFAULT,
762 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
763 baseline_origin_x,
764 baseline_origin_y,
765 )?
766 };
767
768 let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
769 let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
770
771 if bounds.right < bounds.left {
772 Ok(Bounds {
773 origin: point(0.into(), 0.into()),
774 size: size(0.into(), 0.into()),
775 })
776 } else {
777 Ok(Bounds {
778 origin: point((bounds.left as i32).into(), (bounds.top as i32).into()),
779 size: size(
780 (bounds.right - bounds.left).into(),
781 (bounds.bottom - bounds.top).into(),
782 ),
783 })
784 }
785 }
786
787 fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option<GlyphId> {
788 let font_info = &self.fonts[font_id.0];
789 let codepoints = [ch as u32];
790 let mut glyph_indices = vec![0u16; 1];
791 unsafe {
792 font_info
793 .font_face
794 .GetGlyphIndices(codepoints.as_ptr(), 1, glyph_indices.as_mut_ptr())
795 .log_err()
796 }
797 .map(|_| GlyphId(glyph_indices[0] as u32))
798 }
799
800 fn rasterize_glyph(
801 &self,
802 params: &RenderGlyphParams,
803 glyph_bounds: Bounds<DevicePixels>,
804 ) -> Result<(Size<DevicePixels>, Vec<u8>)> {
805 if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 {
806 anyhow::bail!("glyph bounds are empty");
807 }
808
809 let font_info = &self.fonts[params.font_id.0];
810 let glyph_id = [params.glyph_id.0 as u16];
811 let advance = [glyph_bounds.size.width.0 as f32];
812 let offset = [DWRITE_GLYPH_OFFSET {
813 advanceOffset: -glyph_bounds.origin.x.0 as f32 / params.scale_factor,
814 ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
815 }];
816 let glyph_run = DWRITE_GLYPH_RUN {
817 fontFace: ManuallyDrop::new(Some(font_info.font_face.cast()?)),
818 fontEmSize: params.font_size.0,
819 glyphCount: 1,
820 glyphIndices: glyph_id.as_ptr(),
821 glyphAdvances: advance.as_ptr(),
822 glyphOffsets: offset.as_ptr(),
823 isSideways: BOOL(0),
824 bidiLevel: 0,
825 };
826
827 // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing.
828 let mut bitmap_size = glyph_bounds.size;
829 if params.subpixel_variant.x > 0 {
830 bitmap_size.width += DevicePixels(1);
831 }
832 if params.subpixel_variant.y > 0 {
833 bitmap_size.height += DevicePixels(1);
834 }
835 let bitmap_size = bitmap_size;
836
837 let subpixel_shift = params
838 .subpixel_variant
839 .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
840 let baseline_origin_x = subpixel_shift.x / params.scale_factor;
841 let baseline_origin_y = subpixel_shift.y / params.scale_factor;
842
843 let transform = DWRITE_MATRIX {
844 m11: params.scale_factor,
845 m12: 0.0,
846 m21: 0.0,
847 m22: params.scale_factor,
848 dx: 0.0,
849 dy: 0.0,
850 };
851
852 let rendering_mode = if params.is_emoji {
853 DWRITE_RENDERING_MODE1_NATURAL
854 } else {
855 DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC
856 };
857
858 let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
859
860 let glyph_analysis = unsafe {
861 self.components.factory.CreateGlyphRunAnalysis(
862 &glyph_run,
863 Some(&transform),
864 rendering_mode,
865 measuring_mode,
866 DWRITE_GRID_FIT_MODE_DEFAULT,
867 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
868 baseline_origin_x,
869 baseline_origin_y,
870 )?
871 };
872
873 let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
874 let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
875 let texture_width = (texture_bounds.right - texture_bounds.left) as u32;
876 let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32;
877
878 if texture_width == 0 || texture_height == 0 {
879 return Ok((
880 bitmap_size,
881 vec![
882 0u8;
883 bitmap_size.width.0 as usize
884 * bitmap_size.height.0 as usize
885 * if params.is_emoji { 4 } else { 1 }
886 ],
887 ));
888 }
889
890 let mut bitmap_data: Vec<u8>;
891 if params.is_emoji {
892 if let Ok(color) = self.rasterize_color(
893 &glyph_run,
894 rendering_mode,
895 measuring_mode,
896 &transform,
897 point(baseline_origin_x, baseline_origin_y),
898 bitmap_size,
899 ) {
900 bitmap_data = color;
901 } else {
902 let monochrome = Self::rasterize_monochrome(
903 &glyph_analysis,
904 bitmap_size,
905 size(texture_width, texture_height),
906 &texture_bounds,
907 )?;
908 bitmap_data = monochrome
909 .into_iter()
910 .flat_map(|pixel| [0, 0, 0, pixel])
911 .collect::<Vec<_>>();
912 }
913 } else {
914 bitmap_data = Self::rasterize_monochrome(
915 &glyph_analysis,
916 bitmap_size,
917 size(texture_width, texture_height),
918 &texture_bounds,
919 )?;
920 }
921
922 Ok((bitmap_size, bitmap_data))
923 }
924
925 fn rasterize_monochrome(
926 glyph_analysis: &IDWriteGlyphRunAnalysis,
927 bitmap_size: Size<DevicePixels>,
928 texture_size: Size<u32>,
929 texture_bounds: &RECT,
930 ) -> Result<Vec<u8>> {
931 let mut bitmap_data =
932 vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
933
934 let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize];
935
936 unsafe {
937 glyph_analysis.CreateAlphaTexture(
938 DWRITE_TEXTURE_CLEARTYPE_3x1,
939 texture_bounds,
940 &mut alpha_data,
941 )?;
942 }
943
944 // Convert ClearType RGB data to grayscale and place in bitmap
945 let offset_x = texture_bounds.left.max(0) as usize;
946 let offset_y = texture_bounds.top.max(0) as usize;
947
948 for y in 0..texture_size.height as usize {
949 for x in 0..texture_size.width as usize {
950 let bitmap_x = offset_x + x;
951 let bitmap_y = offset_y + y;
952
953 if bitmap_x < bitmap_size.width.0 as usize
954 && bitmap_y < bitmap_size.height.0 as usize
955 {
956 let texture_idx = (y * texture_size.width as usize + x) * 3;
957 let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x;
958
959 if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() {
960 let max_value = alpha_data[texture_idx]
961 .max(alpha_data[texture_idx + 1])
962 .max(alpha_data[texture_idx + 2]);
963 bitmap_data[bitmap_idx] = max_value;
964 }
965 }
966 }
967 }
968
969 Ok(bitmap_data)
970 }
971
972 fn rasterize_color(
973 &self,
974 glyph_run: &DWRITE_GLYPH_RUN,
975 rendering_mode: DWRITE_RENDERING_MODE1,
976 measuring_mode: DWRITE_MEASURING_MODE,
977 transform: &DWRITE_MATRIX,
978 baseline_origin: Point<f32>,
979 bitmap_size: Size<DevicePixels>,
980 ) -> Result<Vec<u8>> {
981 // todo: support formats other than COLR
982 let color_enumerator = unsafe {
983 self.components.factory.TranslateColorGlyphRun(
984 Vector2::new(baseline_origin.x, baseline_origin.y),
985 glyph_run,
986 None,
987 DWRITE_GLYPH_IMAGE_FORMATS_COLR,
988 measuring_mode,
989 Some(transform),
990 0,
991 )
992 }?;
993
994 let mut glyph_layers = Vec::new();
995 loop {
996 let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
997 let color_run = unsafe { &*color_run };
998 let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
999 if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR {
1000 let color_analysis = unsafe {
1001 self.components.factory.CreateGlyphRunAnalysis(
1002 &color_run.Base.glyphRun as *const _,
1003 Some(transform),
1004 rendering_mode,
1005 measuring_mode,
1006 DWRITE_GRID_FIT_MODE_DEFAULT,
1007 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1008 baseline_origin.x,
1009 baseline_origin.y,
1010 )
1011 }?;
1012
1013 let color_bounds =
1014 unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
1015
1016 let color_size = size(
1017 color_bounds.right - color_bounds.left,
1018 color_bounds.bottom - color_bounds.top,
1019 );
1020 if color_size.width > 0 && color_size.height > 0 {
1021 let mut alpha_data =
1022 vec![0u8; (color_size.width * color_size.height * 3) as usize];
1023 unsafe {
1024 color_analysis.CreateAlphaTexture(
1025 DWRITE_TEXTURE_CLEARTYPE_3x1,
1026 &color_bounds,
1027 &mut alpha_data,
1028 )
1029 }?;
1030
1031 let run_color = {
1032 let run_color = color_run.Base.runColor;
1033 Rgba {
1034 r: run_color.r,
1035 g: run_color.g,
1036 b: run_color.b,
1037 a: run_color.a,
1038 }
1039 };
1040 let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size);
1041 let alpha_data = alpha_data
1042 .chunks_exact(3)
1043 .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255])
1044 .collect::<Vec<_>>();
1045 glyph_layers.push(GlyphLayerTexture::new(
1046 &self.components.gpu_state,
1047 run_color,
1048 bounds,
1049 &alpha_data,
1050 )?);
1051 }
1052 }
1053
1054 let has_next = unsafe { color_enumerator.MoveNext() }
1055 .map(|e| e.as_bool())
1056 .unwrap_or(false);
1057 if !has_next {
1058 break;
1059 }
1060 }
1061
1062 let gpu_state = &self.components.gpu_state;
1063 let params_buffer = {
1064 let desc = D3D11_BUFFER_DESC {
1065 ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
1066 Usage: D3D11_USAGE_DYNAMIC,
1067 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
1068 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1069 MiscFlags: 0,
1070 StructureByteStride: 0,
1071 };
1072
1073 let mut buffer = None;
1074 unsafe {
1075 gpu_state
1076 .device
1077 .CreateBuffer(&desc, None, Some(&mut buffer))
1078 }?;
1079 [buffer]
1080 };
1081
1082 let render_target_texture = {
1083 let mut texture = None;
1084 let desc = D3D11_TEXTURE2D_DESC {
1085 Width: bitmap_size.width.0 as u32,
1086 Height: bitmap_size.height.0 as u32,
1087 MipLevels: 1,
1088 ArraySize: 1,
1089 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1090 SampleDesc: DXGI_SAMPLE_DESC {
1091 Count: 1,
1092 Quality: 0,
1093 },
1094 Usage: D3D11_USAGE_DEFAULT,
1095 BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1096 CPUAccessFlags: 0,
1097 MiscFlags: 0,
1098 };
1099 unsafe {
1100 gpu_state
1101 .device
1102 .CreateTexture2D(&desc, None, Some(&mut texture))
1103 }?;
1104 texture.unwrap()
1105 };
1106
1107 let render_target_view = {
1108 let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1109 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1110 ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1111 Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 {
1112 Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 },
1113 },
1114 };
1115 let mut rtv = None;
1116 unsafe {
1117 gpu_state.device.CreateRenderTargetView(
1118 &render_target_texture,
1119 Some(&desc),
1120 Some(&mut rtv),
1121 )
1122 }?;
1123 [rtv]
1124 };
1125
1126 let staging_texture = {
1127 let mut texture = None;
1128 let desc = D3D11_TEXTURE2D_DESC {
1129 Width: bitmap_size.width.0 as u32,
1130 Height: bitmap_size.height.0 as u32,
1131 MipLevels: 1,
1132 ArraySize: 1,
1133 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1134 SampleDesc: DXGI_SAMPLE_DESC {
1135 Count: 1,
1136 Quality: 0,
1137 },
1138 Usage: D3D11_USAGE_STAGING,
1139 BindFlags: 0,
1140 CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
1141 MiscFlags: 0,
1142 };
1143 unsafe {
1144 gpu_state
1145 .device
1146 .CreateTexture2D(&desc, None, Some(&mut texture))
1147 }?;
1148 texture.unwrap()
1149 };
1150
1151 let device_context = &gpu_state.device_context;
1152 unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) };
1153 unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) };
1154 unsafe { device_context.PSSetShader(&gpu_state.pixel_shader, None) };
1155 unsafe { device_context.VSSetConstantBuffers(0, Some(¶ms_buffer)) };
1156 unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_buffer)) };
1157 unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) };
1158 unsafe { device_context.PSSetSamplers(0, Some(&gpu_state.sampler)) };
1159 unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) };
1160
1161 for layer in glyph_layers {
1162 let params = GlyphLayerTextureParams {
1163 run_color: layer.run_color,
1164 bounds: layer.bounds,
1165 };
1166 unsafe {
1167 let mut dest = std::mem::zeroed();
1168 gpu_state.device_context.Map(
1169 params_buffer[0].as_ref().unwrap(),
1170 0,
1171 D3D11_MAP_WRITE_DISCARD,
1172 0,
1173 Some(&mut dest),
1174 )?;
1175 std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1);
1176 gpu_state
1177 .device_context
1178 .Unmap(params_buffer[0].as_ref().unwrap(), 0);
1179 };
1180
1181 let texture = [Some(layer.texture_view)];
1182 unsafe { device_context.PSSetShaderResources(0, Some(&texture)) };
1183
1184 let viewport = [D3D11_VIEWPORT {
1185 TopLeftX: layer.bounds.origin.x as f32,
1186 TopLeftY: layer.bounds.origin.y as f32,
1187 Width: layer.bounds.size.width as f32,
1188 Height: layer.bounds.size.height as f32,
1189 MinDepth: 0.0,
1190 MaxDepth: 1.0,
1191 }];
1192 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1193
1194 unsafe { device_context.Draw(4, 0) };
1195 }
1196
1197 unsafe { device_context.CopyResource(&staging_texture, &render_target_texture) };
1198
1199 let mapped_data = {
1200 let mut mapped_data = D3D11_MAPPED_SUBRESOURCE::default();
1201 unsafe {
1202 device_context.Map(
1203 &staging_texture,
1204 0,
1205 D3D11_MAP_READ,
1206 0,
1207 Some(&mut mapped_data),
1208 )
1209 }?;
1210 mapped_data
1211 };
1212 let mut rasterized =
1213 vec![0u8; (bitmap_size.width.0 as u32 * bitmap_size.height.0 as u32 * 4) as usize];
1214
1215 for y in 0..bitmap_size.height.0 as usize {
1216 let width = bitmap_size.width.0 as usize;
1217 unsafe {
1218 std::ptr::copy_nonoverlapping::<u8>(
1219 (mapped_data.pData as *const u8).byte_add(mapped_data.RowPitch as usize * y),
1220 rasterized
1221 .as_mut_ptr()
1222 .byte_add(width * y * std::mem::size_of::<u32>()),
1223 width * std::mem::size_of::<u32>(),
1224 )
1225 };
1226 }
1227
1228 Ok(rasterized)
1229 }
1230
1231 fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
1232 unsafe {
1233 let font = &self.fonts[font_id.0].font_face;
1234 let glyph_indices = [glyph_id.0 as u16];
1235 let mut metrics = [DWRITE_GLYPH_METRICS::default()];
1236 font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
1237
1238 let metrics = &metrics[0];
1239 let advance_width = metrics.advanceWidth as i32;
1240 let advance_height = metrics.advanceHeight as i32;
1241 let left_side_bearing = metrics.leftSideBearing;
1242 let right_side_bearing = metrics.rightSideBearing;
1243 let top_side_bearing = metrics.topSideBearing;
1244 let bottom_side_bearing = metrics.bottomSideBearing;
1245 let vertical_origin_y = metrics.verticalOriginY;
1246
1247 let y_offset = vertical_origin_y + bottom_side_bearing - advance_height;
1248 let width = advance_width - (left_side_bearing + right_side_bearing);
1249 let height = advance_height - (top_side_bearing + bottom_side_bearing);
1250
1251 Ok(Bounds {
1252 origin: Point {
1253 x: left_side_bearing as f32,
1254 y: y_offset as f32,
1255 },
1256 size: Size {
1257 width: width as f32,
1258 height: height as f32,
1259 },
1260 })
1261 }
1262 }
1263
1264 fn get_advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
1265 unsafe {
1266 let font = &self.fonts[font_id.0].font_face;
1267 let glyph_indices = [glyph_id.0 as u16];
1268 let mut metrics = [DWRITE_GLYPH_METRICS::default()];
1269 font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
1270
1271 let metrics = &metrics[0];
1272
1273 Ok(Size {
1274 width: metrics.advanceWidth as f32,
1275 height: 0.0,
1276 })
1277 }
1278 }
1279
1280 fn all_font_names(&self) -> Vec<String> {
1281 let mut result =
1282 get_font_names_from_collection(&self.system_font_collection, &self.components.locale);
1283 result.extend(get_font_names_from_collection(
1284 &self.custom_font_collection,
1285 &self.components.locale,
1286 ));
1287 result
1288 }
1289}
1290
1291impl Drop for DirectWriteState {
1292 fn drop(&mut self) {
1293 unsafe {
1294 let _ = self
1295 .components
1296 .factory
1297 .UnregisterFontFileLoader(&self.components.in_memory_loader);
1298 }
1299 }
1300}
1301
1302struct GlyphLayerTexture {
1303 run_color: Rgba,
1304 bounds: Bounds<i32>,
1305 texture: ID3D11Texture2D,
1306 texture_view: ID3D11ShaderResourceView,
1307}
1308
1309impl GlyphLayerTexture {
1310 pub fn new(
1311 gpu_state: &GPUState,
1312 run_color: Rgba,
1313 bounds: Bounds<i32>,
1314 alpha_data: &[u8],
1315 ) -> Result<Self> {
1316 // todo: ideally we would have a nice texture wrapper
1317 let texture_size = bounds.size;
1318
1319 let desc = D3D11_TEXTURE2D_DESC {
1320 Width: texture_size.width as u32,
1321 Height: texture_size.height as u32,
1322 MipLevels: 1,
1323 ArraySize: 1,
1324 Format: DXGI_FORMAT_R8G8B8A8_UNORM,
1325 SampleDesc: DXGI_SAMPLE_DESC {
1326 Count: 1,
1327 Quality: 0,
1328 },
1329 Usage: D3D11_USAGE_DEFAULT,
1330 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1331 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1332 MiscFlags: 0,
1333 };
1334
1335 let texture = {
1336 let mut texture: Option<ID3D11Texture2D> = None;
1337 unsafe {
1338 gpu_state
1339 .device
1340 .CreateTexture2D(&desc, None, Some(&mut texture))?
1341 };
1342 texture.unwrap()
1343 };
1344 let texture_view = {
1345 let mut view: Option<ID3D11ShaderResourceView> = None;
1346 unsafe {
1347 gpu_state
1348 .device
1349 .CreateShaderResourceView(&texture, None, Some(&mut view))?
1350 };
1351 view.unwrap()
1352 };
1353
1354 unsafe {
1355 gpu_state.device_context.UpdateSubresource(
1356 &texture,
1357 0,
1358 None,
1359 alpha_data.as_ptr() as _,
1360 (texture_size.width * 4) as u32,
1361 0,
1362 )
1363 };
1364
1365 Ok(GlyphLayerTexture {
1366 run_color,
1367 bounds,
1368 texture,
1369 texture_view,
1370 })
1371 }
1372}
1373
1374#[repr(C)]
1375struct GlyphLayerTextureParams {
1376 bounds: Bounds<i32>,
1377 run_color: Rgba,
1378}
1379
1380struct TextRendererWrapper(pub IDWriteTextRenderer);
1381
1382impl TextRendererWrapper {
1383 pub fn new(locale_str: &str) -> Self {
1384 let inner = TextRenderer::new(locale_str);
1385 TextRendererWrapper(inner.into())
1386 }
1387}
1388
1389#[implement(IDWriteTextRenderer)]
1390struct TextRenderer {
1391 locale: String,
1392}
1393
1394impl TextRenderer {
1395 pub fn new(locale_str: &str) -> Self {
1396 TextRenderer {
1397 locale: locale_str.to_owned(),
1398 }
1399 }
1400}
1401
1402struct RendererContext<'t, 'a, 'b> {
1403 text_system: &'t mut DirectWriteState,
1404 index_converter: StringIndexConverter<'a>,
1405 runs: &'b mut Vec<ShapedRun>,
1406 width: f32,
1407}
1408
1409#[derive(Debug)]
1410struct ClusterAnalyzer<'t> {
1411 utf16_idx: usize,
1412 glyph_idx: usize,
1413 glyph_count: usize,
1414 cluster_map: &'t [u16],
1415}
1416
1417impl<'t> ClusterAnalyzer<'t> {
1418 pub fn new(cluster_map: &'t [u16], glyph_count: usize) -> Self {
1419 ClusterAnalyzer {
1420 utf16_idx: 0,
1421 glyph_idx: 0,
1422 glyph_count,
1423 cluster_map,
1424 }
1425 }
1426}
1427
1428impl Iterator for ClusterAnalyzer<'_> {
1429 type Item = (usize, usize);
1430
1431 fn next(&mut self) -> Option<(usize, usize)> {
1432 if self.utf16_idx >= self.cluster_map.len() {
1433 return None; // No more clusters
1434 }
1435 let start_utf16_idx = self.utf16_idx;
1436 let current_glyph = self.cluster_map[start_utf16_idx] as usize;
1437
1438 // Find the end of current cluster (where glyph index changes)
1439 let mut end_utf16_idx = start_utf16_idx + 1;
1440 while end_utf16_idx < self.cluster_map.len()
1441 && self.cluster_map[end_utf16_idx] as usize == current_glyph
1442 {
1443 end_utf16_idx += 1;
1444 }
1445
1446 let utf16_len = end_utf16_idx - start_utf16_idx;
1447
1448 // Calculate glyph count for this cluster
1449 let next_glyph = if end_utf16_idx < self.cluster_map.len() {
1450 self.cluster_map[end_utf16_idx] as usize
1451 } else {
1452 self.glyph_count
1453 };
1454
1455 let glyph_count = next_glyph - current_glyph;
1456
1457 // Update state for next call
1458 self.utf16_idx = end_utf16_idx;
1459 self.glyph_idx = next_glyph;
1460
1461 Some((utf16_len, glyph_count))
1462 }
1463}
1464
1465#[allow(non_snake_case)]
1466impl IDWritePixelSnapping_Impl for TextRenderer_Impl {
1467 fn IsPixelSnappingDisabled(
1468 &self,
1469 _clientdrawingcontext: *const ::core::ffi::c_void,
1470 ) -> windows::core::Result<BOOL> {
1471 Ok(BOOL(0))
1472 }
1473
1474 fn GetCurrentTransform(
1475 &self,
1476 _clientdrawingcontext: *const ::core::ffi::c_void,
1477 transform: *mut DWRITE_MATRIX,
1478 ) -> windows::core::Result<()> {
1479 unsafe {
1480 *transform = DWRITE_MATRIX {
1481 m11: 1.0,
1482 m12: 0.0,
1483 m21: 0.0,
1484 m22: 1.0,
1485 dx: 0.0,
1486 dy: 0.0,
1487 };
1488 }
1489 Ok(())
1490 }
1491
1492 fn GetPixelsPerDip(
1493 &self,
1494 _clientdrawingcontext: *const ::core::ffi::c_void,
1495 ) -> windows::core::Result<f32> {
1496 Ok(1.0)
1497 }
1498}
1499
1500#[allow(non_snake_case)]
1501impl IDWriteTextRenderer_Impl for TextRenderer_Impl {
1502 fn DrawGlyphRun(
1503 &self,
1504 clientdrawingcontext: *const ::core::ffi::c_void,
1505 _baselineoriginx: f32,
1506 _baselineoriginy: f32,
1507 _measuringmode: DWRITE_MEASURING_MODE,
1508 glyphrun: *const DWRITE_GLYPH_RUN,
1509 glyphrundescription: *const DWRITE_GLYPH_RUN_DESCRIPTION,
1510 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1511 ) -> windows::core::Result<()> {
1512 let glyphrun = unsafe { &*glyphrun };
1513 let glyph_count = glyphrun.glyphCount as usize;
1514 if glyph_count == 0 || glyphrun.fontFace.is_none() {
1515 return Ok(());
1516 }
1517 let desc = unsafe { &*glyphrundescription };
1518 let context = unsafe {
1519 &mut *(clientdrawingcontext as *const RendererContext as *mut RendererContext)
1520 };
1521 let font_face = glyphrun.fontFace.as_ref().unwrap();
1522 // This `cast()` action here should never fail since we are running on Win10+, and
1523 // `IDWriteFontFace3` requires Win10
1524 let font_face = &font_face.cast::<IDWriteFontFace3>().unwrap();
1525 let Some((font_identifier, font_struct, color_font)) =
1526 get_font_identifier_and_font_struct(font_face, &self.locale)
1527 else {
1528 return Ok(());
1529 };
1530
1531 let font_id = if let Some(id) = context
1532 .text_system
1533 .font_id_by_identifier
1534 .get(&font_identifier)
1535 {
1536 *id
1537 } else {
1538 context.text_system.select_font(&font_struct)
1539 };
1540
1541 let glyph_ids = unsafe { std::slice::from_raw_parts(glyphrun.glyphIndices, glyph_count) };
1542 let glyph_advances =
1543 unsafe { std::slice::from_raw_parts(glyphrun.glyphAdvances, glyph_count) };
1544 let glyph_offsets =
1545 unsafe { std::slice::from_raw_parts(glyphrun.glyphOffsets, glyph_count) };
1546 let cluster_map =
1547 unsafe { std::slice::from_raw_parts(desc.clusterMap, desc.stringLength as usize) };
1548
1549 let mut cluster_analyzer = ClusterAnalyzer::new(cluster_map, glyph_count);
1550 let mut utf16_idx = desc.textPosition as usize;
1551 let mut glyph_idx = 0;
1552 let mut glyphs = Vec::with_capacity(glyph_count);
1553 for (cluster_utf16_len, cluster_glyph_count) in cluster_analyzer {
1554 context.index_converter.advance_to_utf16_ix(utf16_idx);
1555 utf16_idx += cluster_utf16_len;
1556 for (cluster_glyph_idx, glyph_id) in glyph_ids
1557 [glyph_idx..(glyph_idx + cluster_glyph_count)]
1558 .iter()
1559 .enumerate()
1560 {
1561 let id = GlyphId(*glyph_id as u32);
1562 let is_emoji = color_font
1563 && is_color_glyph(font_face, id, &context.text_system.components.factory);
1564 let this_glyph_idx = glyph_idx + cluster_glyph_idx;
1565 glyphs.push(ShapedGlyph {
1566 id,
1567 position: point(
1568 px(context.width + glyph_offsets[this_glyph_idx].advanceOffset),
1569 px(0.0),
1570 ),
1571 index: context.index_converter.utf8_ix,
1572 is_emoji,
1573 });
1574 context.width += glyph_advances[this_glyph_idx];
1575 }
1576 glyph_idx += cluster_glyph_count;
1577 }
1578 context.runs.push(ShapedRun { font_id, glyphs });
1579 Ok(())
1580 }
1581
1582 fn DrawUnderline(
1583 &self,
1584 _clientdrawingcontext: *const ::core::ffi::c_void,
1585 _baselineoriginx: f32,
1586 _baselineoriginy: f32,
1587 _underline: *const DWRITE_UNDERLINE,
1588 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1589 ) -> windows::core::Result<()> {
1590 Err(windows::core::Error::new(
1591 E_NOTIMPL,
1592 "DrawUnderline unimplemented",
1593 ))
1594 }
1595
1596 fn DrawStrikethrough(
1597 &self,
1598 _clientdrawingcontext: *const ::core::ffi::c_void,
1599 _baselineoriginx: f32,
1600 _baselineoriginy: f32,
1601 _strikethrough: *const DWRITE_STRIKETHROUGH,
1602 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1603 ) -> windows::core::Result<()> {
1604 Err(windows::core::Error::new(
1605 E_NOTIMPL,
1606 "DrawStrikethrough unimplemented",
1607 ))
1608 }
1609
1610 fn DrawInlineObject(
1611 &self,
1612 _clientdrawingcontext: *const ::core::ffi::c_void,
1613 _originx: f32,
1614 _originy: f32,
1615 _inlineobject: windows::core::Ref<IDWriteInlineObject>,
1616 _issideways: BOOL,
1617 _isrighttoleft: BOOL,
1618 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1619 ) -> windows::core::Result<()> {
1620 Err(windows::core::Error::new(
1621 E_NOTIMPL,
1622 "DrawInlineObject unimplemented",
1623 ))
1624 }
1625}
1626
1627struct StringIndexConverter<'a> {
1628 text: &'a str,
1629 utf8_ix: usize,
1630 utf16_ix: usize,
1631}
1632
1633impl<'a> StringIndexConverter<'a> {
1634 fn new(text: &'a str) -> Self {
1635 Self {
1636 text,
1637 utf8_ix: 0,
1638 utf16_ix: 0,
1639 }
1640 }
1641
1642 #[allow(dead_code)]
1643 fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
1644 for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1645 if self.utf8_ix + ix >= utf8_target {
1646 self.utf8_ix += ix;
1647 return;
1648 }
1649 self.utf16_ix += c.len_utf16();
1650 }
1651 self.utf8_ix = self.text.len();
1652 }
1653
1654 fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
1655 for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1656 if self.utf16_ix >= utf16_target {
1657 self.utf8_ix += ix;
1658 return;
1659 }
1660 self.utf16_ix += c.len_utf16();
1661 }
1662 self.utf8_ix = self.text.len();
1663 }
1664}
1665
1666impl Into<DWRITE_FONT_STYLE> for FontStyle {
1667 fn into(self) -> DWRITE_FONT_STYLE {
1668 match self {
1669 FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
1670 FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
1671 FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
1672 }
1673 }
1674}
1675
1676impl From<DWRITE_FONT_STYLE> for FontStyle {
1677 fn from(value: DWRITE_FONT_STYLE) -> Self {
1678 match value.0 {
1679 0 => FontStyle::Normal,
1680 1 => FontStyle::Italic,
1681 2 => FontStyle::Oblique,
1682 _ => unreachable!(),
1683 }
1684 }
1685}
1686
1687impl Into<DWRITE_FONT_WEIGHT> for FontWeight {
1688 fn into(self) -> DWRITE_FONT_WEIGHT {
1689 DWRITE_FONT_WEIGHT(self.0 as i32)
1690 }
1691}
1692
1693impl From<DWRITE_FONT_WEIGHT> for FontWeight {
1694 fn from(value: DWRITE_FONT_WEIGHT) -> Self {
1695 FontWeight(value.0 as f32)
1696 }
1697}
1698
1699fn get_font_names_from_collection(
1700 collection: &IDWriteFontCollection1,
1701 locale: &str,
1702) -> Vec<String> {
1703 unsafe {
1704 let mut result = Vec::new();
1705 let family_count = collection.GetFontFamilyCount();
1706 for index in 0..family_count {
1707 let Some(font_family) = collection.GetFontFamily(index).log_err() else {
1708 continue;
1709 };
1710 let Some(localized_family_name) = font_family.GetFamilyNames().log_err() else {
1711 continue;
1712 };
1713 let Some(family_name) = get_name(localized_family_name, locale).log_err() else {
1714 continue;
1715 };
1716 result.push(family_name);
1717 }
1718
1719 result
1720 }
1721}
1722
1723fn get_font_identifier_and_font_struct(
1724 font_face: &IDWriteFontFace3,
1725 locale: &str,
1726) -> Option<(FontIdentifier, Font, bool)> {
1727 let postscript_name = get_postscript_name(font_face, locale).log_err()?;
1728 let localized_family_name = unsafe { font_face.GetFamilyNames().log_err() }?;
1729 let family_name = get_name(localized_family_name, locale).log_err()?;
1730 let weight = unsafe { font_face.GetWeight() };
1731 let style = unsafe { font_face.GetStyle() };
1732 let identifier = FontIdentifier {
1733 postscript_name,
1734 weight: weight.0,
1735 style: style.0,
1736 };
1737 let font_struct = Font {
1738 family: family_name.into(),
1739 features: FontFeatures::default(),
1740 weight: weight.into(),
1741 style: style.into(),
1742 fallbacks: None,
1743 };
1744 let is_emoji = unsafe { font_face.IsColorFont().as_bool() };
1745 Some((identifier, font_struct, is_emoji))
1746}
1747
1748#[inline]
1749fn get_font_identifier(font_face: &IDWriteFontFace3, locale: &str) -> Option<FontIdentifier> {
1750 let weight = unsafe { font_face.GetWeight().0 };
1751 let style = unsafe { font_face.GetStyle().0 };
1752 get_postscript_name(font_face, locale)
1753 .log_err()
1754 .map(|postscript_name| FontIdentifier {
1755 postscript_name,
1756 weight,
1757 style,
1758 })
1759}
1760
1761#[inline]
1762fn get_postscript_name(font_face: &IDWriteFontFace3, locale: &str) -> Result<String> {
1763 let mut info = None;
1764 let mut exists = BOOL(0);
1765 unsafe {
1766 font_face.GetInformationalStrings(
1767 DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
1768 &mut info,
1769 &mut exists,
1770 )?
1771 };
1772 if !exists.as_bool() || info.is_none() {
1773 anyhow::bail!("No postscript name found for font face");
1774 }
1775
1776 get_name(info.unwrap(), locale)
1777}
1778
1779// https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_feature_tag
1780fn apply_font_features(
1781 direct_write_features: &IDWriteTypography,
1782 features: &FontFeatures,
1783) -> Result<()> {
1784 let tag_values = features.tag_value_list();
1785 if tag_values.is_empty() {
1786 return Ok(());
1787 }
1788
1789 // All of these features are enabled by default by DirectWrite.
1790 // If you want to (and can) peek into the source of DirectWrite
1791 let mut feature_liga = make_direct_write_feature("liga", 1);
1792 let mut feature_clig = make_direct_write_feature("clig", 1);
1793 let mut feature_calt = make_direct_write_feature("calt", 1);
1794
1795 for (tag, value) in tag_values {
1796 if tag.as_str() == "liga" && *value == 0 {
1797 feature_liga.parameter = 0;
1798 continue;
1799 }
1800 if tag.as_str() == "clig" && *value == 0 {
1801 feature_clig.parameter = 0;
1802 continue;
1803 }
1804 if tag.as_str() == "calt" && *value == 0 {
1805 feature_calt.parameter = 0;
1806 continue;
1807 }
1808
1809 unsafe {
1810 direct_write_features.AddFontFeature(make_direct_write_feature(&tag, *value))?;
1811 }
1812 }
1813 unsafe {
1814 direct_write_features.AddFontFeature(feature_liga)?;
1815 direct_write_features.AddFontFeature(feature_clig)?;
1816 direct_write_features.AddFontFeature(feature_calt)?;
1817 }
1818
1819 Ok(())
1820}
1821
1822#[inline]
1823const fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_FEATURE {
1824 let tag = make_direct_write_tag(feature_name);
1825 DWRITE_FONT_FEATURE {
1826 nameTag: tag,
1827 parameter,
1828 }
1829}
1830
1831#[inline]
1832const fn make_open_type_tag(tag_name: &str) -> u32 {
1833 let bytes = tag_name.as_bytes();
1834 debug_assert!(bytes.len() == 4);
1835 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
1836}
1837
1838#[inline]
1839const fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG {
1840 DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name))
1841}
1842
1843#[inline]
1844fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
1845 let mut locale_name_index = 0u32;
1846 let mut exists = BOOL(0);
1847 unsafe {
1848 string.FindLocaleName(
1849 &HSTRING::from(locale),
1850 &mut locale_name_index,
1851 &mut exists as _,
1852 )?
1853 };
1854 if !exists.as_bool() {
1855 unsafe {
1856 string.FindLocaleName(
1857 DEFAULT_LOCALE_NAME,
1858 &mut locale_name_index as _,
1859 &mut exists as _,
1860 )?
1861 };
1862 anyhow::ensure!(exists.as_bool(), "No localised string for {locale}");
1863 }
1864
1865 let name_length = unsafe { string.GetStringLength(locale_name_index) }? as usize;
1866 let mut name_vec = vec![0u16; name_length + 1];
1867 unsafe {
1868 string.GetString(locale_name_index, &mut name_vec)?;
1869 }
1870
1871 Ok(String::from_utf16_lossy(&name_vec[..name_length]))
1872}
1873
1874fn get_system_ui_font_name() -> SharedString {
1875 unsafe {
1876 let mut info: LOGFONTW = std::mem::zeroed();
1877 let font_family = if SystemParametersInfoW(
1878 SPI_GETICONTITLELOGFONT,
1879 std::mem::size_of::<LOGFONTW>() as u32,
1880 Some(&mut info as *mut _ as _),
1881 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
1882 )
1883 .log_err()
1884 .is_none()
1885 {
1886 // https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-fonts
1887 // Segoe UI is the Windows font intended for user interface text strings.
1888 "Segoe UI".into()
1889 } else {
1890 let font_name = String::from_utf16_lossy(&info.lfFaceName);
1891 font_name.trim_matches(char::from(0)).to_owned().into()
1892 };
1893 log::info!("Use {} as UI font.", font_family);
1894 font_family
1895 }
1896}
1897
1898// One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats
1899// but that doesn't seem to work for some glyphs, say โค
1900fn is_color_glyph(
1901 font_face: &IDWriteFontFace3,
1902 glyph_id: GlyphId,
1903 factory: &IDWriteFactory5,
1904) -> bool {
1905 let glyph_run = DWRITE_GLYPH_RUN {
1906 fontFace: unsafe { std::mem::transmute_copy(font_face) },
1907 fontEmSize: 14.0,
1908 glyphCount: 1,
1909 glyphIndices: &(glyph_id.0 as u16),
1910 glyphAdvances: &0.0,
1911 glyphOffsets: &DWRITE_GLYPH_OFFSET {
1912 advanceOffset: 0.0,
1913 ascenderOffset: 0.0,
1914 },
1915 isSideways: BOOL(0),
1916 bidiLevel: 0,
1917 };
1918 unsafe {
1919 factory.TranslateColorGlyphRun(
1920 Vector2::default(),
1921 &glyph_run as _,
1922 None,
1923 DWRITE_GLYPH_IMAGE_FORMATS_COLR
1924 | DWRITE_GLYPH_IMAGE_FORMATS_SVG
1925 | DWRITE_GLYPH_IMAGE_FORMATS_PNG
1926 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
1927 | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
1928 DWRITE_MEASURING_MODE_NATURAL,
1929 None,
1930 0,
1931 )
1932 }
1933 .is_ok()
1934}
1935
1936const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
1937
1938#[cfg(test)]
1939mod tests {
1940 use crate::platform::windows::direct_write::ClusterAnalyzer;
1941
1942 #[test]
1943 fn test_cluster_map() {
1944 let cluster_map = [0];
1945 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1);
1946 let next = analyzer.next();
1947 assert_eq!(next, Some((1, 1)));
1948 let next = analyzer.next();
1949 assert_eq!(next, None);
1950
1951 let cluster_map = [0, 1, 2];
1952 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 3);
1953 let next = analyzer.next();
1954 assert_eq!(next, Some((1, 1)));
1955 let next = analyzer.next();
1956 assert_eq!(next, Some((1, 1)));
1957 let next = analyzer.next();
1958 assert_eq!(next, Some((1, 1)));
1959 let next = analyzer.next();
1960 assert_eq!(next, None);
1961 // ๐จโ๐ฉโ๐งโ๐ฆ๐ฉโ๐ป
1962 let cluster_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4];
1963 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 5);
1964 let next = analyzer.next();
1965 assert_eq!(next, Some((11, 4)));
1966 let next = analyzer.next();
1967 assert_eq!(next, Some((5, 1)));
1968 let next = analyzer.next();
1969 assert_eq!(next, None);
1970 // ๐ฉโ๐ป
1971 let cluster_map = [0, 0, 0, 0, 0];
1972 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1);
1973 let next = analyzer.next();
1974 assert_eq!(next, Some((5, 1)));
1975 let next = analyzer.next();
1976 assert_eq!(next, None);
1977 }
1978}