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