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