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