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