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 self.rasterize_color(
837 &glyph_run,
838 rendering_mode,
839 measuring_mode,
840 &transform,
841 point(baseline_origin_x, baseline_origin_y),
842 bitmap_size,
843 size(texture_width, texture_height),
844 );
845 // // todo: support more glyph image formats for more exotic fonts, for now it should fallback to monochrome rendering
846 // let color_enumerator = unsafe {
847 // self.components.factory.TranslateColorGlyphRun(
848 // Vector2::new(baseline_origin_x, baseline_origin_y),
849 // &glyph_run,
850 // None,
851 // DWRITE_GLYPH_IMAGE_FORMATS_COLR
852 // | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
853 // measuring_mode,
854 // Some(&transform),
855 // 0,
856 // )
857 // };
858
859 // if let Ok(color_enumerator) = color_enumerator {
860 // loop {
861 // let color_run = unsafe { color_enumerator.GetCurrentRun() };
862 // if let Ok(color_run) = color_run {
863 // let color_glyph_run = unsafe { &*color_run };
864 // let color_value = color_glyph_run.Base.runColor;
865
866 // // Create analysis for this color layer
867 // let color_analysis = unsafe {
868 // self.components.factory.CreateGlyphRunAnalysis(
869 // &color_glyph_run.Base.glyphRun as *const _,
870 // Some(&transform),
871 // rendering_mode,
872 // measuring_mode,
873 // DWRITE_GRID_FIT_MODE_DEFAULT,
874 // DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
875 // baseline_origin_x,
876 // baseline_origin_y,
877 // )
878 // };
879
880 // // todo: move this block completely to the gpu
881 // // this is important because fonts can bundle quite large icons
882 // // and compositing them on the cpu is quite expensive
883 // // also the code is ugly
884 // if let Ok(color_analysis) = color_analysis {
885 // let color_bounds =
886 // unsafe { color_analysis.GetAlphaTextureBounds(texture_type) };
887 // if let Ok(color_bounds) = color_bounds {
888 // let color_width = (color_bounds.right - color_bounds.left) as u32;
889 // let color_height = (color_bounds.bottom - color_bounds.top) as u32;
890
891 // if color_width > 0 && color_height > 0 {
892 // let mut alpha_data =
893 // vec![0u8; (color_width * color_height * 3) as usize];
894 // if unsafe {
895 // color_analysis.CreateAlphaTexture(
896 // texture_type,
897 // &color_bounds,
898 // &mut alpha_data,
899 // )
900 // }
901 // .is_ok()
902 // {
903 // let r = (color_value.r * 255.0) as u8;
904 // let g = (color_value.g * 255.0) as u8;
905 // let b = (color_value.b * 255.0) as u8;
906 // let a = (color_value.a * 255.0) as u8;
907
908 // let offset_x = color_bounds.left.max(0) as usize;
909 // let offset_y = color_bounds.top.max(0) as usize;
910
911 // for y in 0..color_height as usize {
912 // for x in 0..color_width as usize {
913 // let bitmap_x = offset_x + x;
914 // let bitmap_y = offset_y + y;
915
916 // if bitmap_x < bitmap_size.width.0 as usize
917 // && bitmap_y < bitmap_size.height.0 as usize
918 // {
919 // let alpha_idx =
920 // (y * color_width as usize + x) * 3;
921 // let bitmap_idx = (bitmap_y
922 // * bitmap_size.width.0 as usize
923 // + bitmap_x)
924 // * 4;
925
926 // if alpha_idx + 2 < alpha_data.len()
927 // && bitmap_idx + 3 < bitmap_data.len()
928 // {
929 // let alpha_value = (alpha_data[alpha_idx]
930 // as u32
931 // + alpha_data[alpha_idx + 1] as u32
932 // + alpha_data[alpha_idx + 2] as u32)
933 // / 3;
934 // let final_alpha =
935 // ((alpha_value * a as u32) / 255) as u8;
936
937 // if final_alpha > 0 {
938 // let existing_r =
939 // bitmap_data[bitmap_idx];
940 // let existing_g =
941 // bitmap_data[bitmap_idx + 1];
942 // let existing_b =
943 // bitmap_data[bitmap_idx + 2];
944 // let existing_a =
945 // bitmap_data[bitmap_idx + 3];
946
947 // let src_alpha =
948 // final_alpha as f32 / 255.0;
949 // let dst_alpha =
950 // existing_a as f32 / 255.0;
951 // let out_alpha = src_alpha
952 // + dst_alpha * (1.0 - src_alpha);
953
954 // if out_alpha > 0.0 {
955 // bitmap_data[bitmap_idx] =
956 // ((r as f32 * src_alpha
957 // + existing_r as f32
958 // * dst_alpha
959 // * (1.0 - src_alpha))
960 // / out_alpha)
961 // as u8;
962 // bitmap_data[bitmap_idx + 1] =
963 // ((g as f32 * src_alpha
964 // + existing_g as f32
965 // * dst_alpha
966 // * (1.0 - src_alpha))
967 // / out_alpha)
968 // as u8;
969 // bitmap_data[bitmap_idx + 2] =
970 // ((b as f32 * src_alpha
971 // + existing_b as f32
972 // * dst_alpha
973 // * (1.0 - src_alpha))
974 // / out_alpha)
975 // as u8;
976 // bitmap_data[bitmap_idx + 3] =
977 // (out_alpha * 255.0) as u8;
978 // }
979 // }
980 // }
981 // }
982 // }
983 // }
984 // }
985 // }
986 // }
987 // }
988 // }
989
990 // if !unsafe { color_enumerator.MoveNext() }?.as_bool() {
991 // break;
992 // }
993 // }
994
995 // // bitmap_data.chunks_mut(4).for_each(|chunk| {
996 // // let tmp = chunk[2];
997 // // chunk[2] = chunk[0];
998 // // chunk[0] = tmp;
999 // // });
1000
1001 // std::fs::write(
1002 // &format!(
1003 // "{}x{}_{}_color.raw",
1004 // texture_width, texture_height, params.glyph_id.0
1005 // ),
1006 // &bitmap_data,
1007 // )
1008 // .unwrap();
1009 // } else {
1010 // let monochrome_data = Self::rasterize_monochrome(
1011 // &glyph_analysis,
1012 // bitmap_size,
1013 // size(texture_width, texture_height),
1014 // &texture_bounds,
1015 // )?;
1016 // // todo: monochrome emojis should be handled gracefully by the renderer
1017 // // currently they just get drawn as their reported color because it assumes they are always colored
1018 // // but in reality monochrome emojis should be colored the same as text is
1019 // bitmap_data = monochrome_data
1020 // .into_iter()
1021 // .flat_map(|e| [0, 0, 0, e])
1022 // .collect::<Vec<u8>>();
1023 // }
1024 } else {
1025 bitmap_data = Self::rasterize_monochrome(
1026 &glyph_analysis,
1027 bitmap_size,
1028 size(texture_width, texture_height),
1029 &texture_bounds,
1030 )?;
1031 }
1032
1033 Ok((bitmap_size, bitmap_data))
1034 }
1035
1036 fn rasterize_monochrome(
1037 glyph_analysis: &IDWriteGlyphRunAnalysis,
1038 bitmap_size: Size<DevicePixels>,
1039 texture_size: Size<u32>,
1040 texture_bounds: &RECT,
1041 ) -> Result<Vec<u8>> {
1042 let mut bitmap_data =
1043 vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
1044
1045 let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize];
1046 unsafe {
1047 glyph_analysis.CreateAlphaTexture(
1048 DWRITE_TEXTURE_CLEARTYPE_3x1,
1049 texture_bounds,
1050 &mut alpha_data,
1051 )?;
1052 }
1053
1054 // Convert ClearType RGB data to grayscale and place in bitmap
1055 let offset_x = texture_bounds.left.max(0) as usize;
1056 let offset_y = texture_bounds.top.max(0) as usize;
1057
1058 for y in 0..texture_size.height as usize {
1059 for x in 0..texture_size.width as usize {
1060 let bitmap_x = offset_x + x;
1061 let bitmap_y = offset_y + y;
1062
1063 if bitmap_x < bitmap_size.width.0 as usize
1064 && bitmap_y < bitmap_size.height.0 as usize
1065 {
1066 let texture_idx = (y * texture_size.width as usize + x) * 3;
1067 let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x;
1068
1069 if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() {
1070 let avg = (alpha_data[texture_idx] as u32
1071 + alpha_data[texture_idx + 1] as u32
1072 + alpha_data[texture_idx + 2] as u32)
1073 / 3;
1074 bitmap_data[bitmap_idx] = avg as u8;
1075 }
1076 }
1077 }
1078 }
1079
1080 Ok(bitmap_data)
1081 }
1082
1083 fn rasterize_color(
1084 &self,
1085 glyph_run: &DWRITE_GLYPH_RUN,
1086 rendering_mode: DWRITE_RENDERING_MODE1,
1087 measuring_mode: DWRITE_MEASURING_MODE,
1088 transform: &DWRITE_MATRIX,
1089 baseline_origin: Point<f32>,
1090 bitmap_size: Size<DevicePixels>,
1091 texture_size: Size<u32>,
1092 ) -> Result<Vec<u8>> {
1093 let color_enumerator = unsafe {
1094 self.components.factory.TranslateColorGlyphRun(
1095 Vector2::new(baseline_origin.x, baseline_origin.y),
1096 glyph_run,
1097 None,
1098 DWRITE_GLYPH_IMAGE_FORMATS_COLR | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
1099 measuring_mode,
1100 Some(transform),
1101 0,
1102 )
1103 }?;
1104
1105 let mut glyph_layers = Vec::new();
1106 loop {
1107 let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
1108 let color_run = unsafe { &*color_run };
1109
1110 let color_analysis = unsafe {
1111 self.components.factory.CreateGlyphRunAnalysis(
1112 &color_run.Base.glyphRun as *const _,
1113 Some(transform),
1114 rendering_mode,
1115 measuring_mode,
1116 DWRITE_GRID_FIT_MODE_DEFAULT,
1117 DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
1118 baseline_origin.x,
1119 baseline_origin.y,
1120 )
1121 }?;
1122
1123 let color_bounds =
1124 unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
1125
1126 let color_size = size(
1127 color_bounds.right - color_bounds.left,
1128 color_bounds.bottom - color_bounds.top,
1129 );
1130 if color_size.width > 0 && color_size.height > 0 {
1131 let mut alpha_data = vec![0u8; (color_size.width * color_size.height * 3) as usize];
1132 unsafe {
1133 color_analysis.CreateAlphaTexture(
1134 DWRITE_TEXTURE_CLEARTYPE_3x1,
1135 &color_bounds,
1136 &mut alpha_data,
1137 )
1138 }?;
1139
1140 let run_color = {
1141 let run_color = color_run.Base.runColor;
1142 Rgba {
1143 r: run_color.r,
1144 g: run_color.g,
1145 b: run_color.b,
1146 a: run_color.a,
1147 }
1148 };
1149 let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size);
1150 let alpha_data = alpha_data
1151 .chunks_exact(3)
1152 .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255])
1153 .collect::<Vec<_>>();
1154 glyph_layers.push(GlyphLayerTexture::new(
1155 &self.gpu_state,
1156 run_color,
1157 bounds,
1158 &alpha_data,
1159 )?);
1160 }
1161
1162 let has_next = unsafe { color_enumerator.MoveNext() }
1163 .map(|e| e.as_bool())
1164 .unwrap_or(false);
1165 if !has_next {
1166 break;
1167 }
1168 }
1169
1170 let params_buffer = {
1171 let desc = D3D11_BUFFER_DESC {
1172 ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
1173 Usage: D3D11_USAGE_DYNAMIC,
1174 BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
1175 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1176 MiscFlags: 0,
1177 StructureByteStride: 0,
1178 };
1179
1180 let mut buffer = None;
1181 unsafe {
1182 self.gpu_state
1183 .device
1184 .CreateBuffer(&desc, None, Some(&mut buffer))
1185 }?;
1186 [buffer]
1187 };
1188
1189 let vertex_shader = {
1190 let source =
1191 shader_resources::build_shader_blob("color_text_raster", "vertex", "vs_5_0")?;
1192 let bytes = unsafe {
1193 std::slice::from_raw_parts(
1194 source.GetBufferPointer() as *mut u8,
1195 source.GetBufferSize(),
1196 )
1197 };
1198 let mut shader = None;
1199 unsafe {
1200 self.gpu_state
1201 .device
1202 .CreateVertexShader(bytes, None, Some(&mut shader))
1203 }?;
1204 shader.unwrap()
1205 };
1206
1207 let render_target_texture = {
1208 let mut texture = None;
1209 let desc = D3D11_TEXTURE2D_DESC {
1210 Width: bitmap_size.width.0 as u32,
1211 Height: bitmap_size.height.0 as u32,
1212 MipLevels: 1,
1213 ArraySize: 1,
1214 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1215 SampleDesc: DXGI_SAMPLE_DESC {
1216 Count: 1,
1217 Quality: 0,
1218 },
1219 Usage: D3D11_USAGE_DEFAULT,
1220 BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
1221 CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
1222 MiscFlags: 0,
1223 };
1224 unsafe {
1225 self.gpu_state
1226 .device
1227 .CreateTexture2D(&desc, None, Some(&mut texture))
1228 }?;
1229 texture.unwrap()
1230 };
1231
1232 let render_target_view = {
1233 let desc = D3D11_RENDER_TARGET_VIEW_DESC {
1234 Format: DXGI_FORMAT_B8G8R8A8_UNORM,
1235 ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
1236 Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 {
1237 Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 },
1238 },
1239 };
1240 let mut rtv = None;
1241 unsafe {
1242 self.gpu_state.device.CreateRenderTargetView(
1243 &render_target_texture,
1244 Some(&desc),
1245 Some(&mut rtv),
1246 )
1247 }?;
1248 [rtv]
1249 };
1250
1251 let blend_state = {
1252 let mut blend_state = None;
1253 let desc = D3D11_BLEND_DESC {
1254 AlphaToCoverageEnable: false.into(),
1255 IndependentBlendEnable: false.into(),
1256 RenderTarget: [
1257 D3D11_RENDER_TARGET_BLEND_DESC {
1258 BlendEnable: true.into(),
1259 SrcBlend: D3D11_BLEND_SRC_ALPHA,
1260 DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
1261 BlendOp: D3D11_BLEND_OP_ADD,
1262 SrcBlendAlpha: D3D11_BLEND_ONE,
1263 DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
1264 BlendOpAlpha: D3D11_BLEND_OP_ADD,
1265 RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
1266 },
1267 Default::default(),
1268 Default::default(),
1269 Default::default(),
1270 Default::default(),
1271 Default::default(),
1272 Default::default(),
1273 Default::default(),
1274 ],
1275 };
1276 unsafe {
1277 self.gpu_state
1278 .device
1279 .CreateBlendState(&desc, Some(&mut blend_state))
1280 }?;
1281 blend_state.unwrap()
1282 };
1283
1284 let sampler = {
1285 let mut sampler = None;
1286 let desc = D3D11_SAMPLER_DESC {
1287 Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,
1288 AddressU: D3D11_TEXTURE_ADDRESS_BORDER,
1289 AddressV: D3D11_TEXTURE_ADDRESS_BORDER,
1290 AddressW: D3D11_TEXTURE_ADDRESS_BORDER,
1291 MipLODBias: 0.0,
1292 MaxAnisotropy: 1,
1293 ComparisonFunc: D3D11_COMPARISON_ALWAYS,
1294 BorderColor: [0.0, 0.0, 0.0, 0.0],
1295 MinLOD: 0.0,
1296 MaxLOD: 0.0,
1297 };
1298 unsafe {
1299 self.gpu_state
1300 .device
1301 .CreateSamplerState(&desc, Some(&mut sampler))
1302 }?;
1303 [sampler]
1304 };
1305
1306 let pixel_shader = {
1307 let source =
1308 shader_resources::build_shader_blob("color_text_raster", "pixel", "ps_5_0")?;
1309 let bytes = unsafe {
1310 std::slice::from_raw_parts(
1311 source.GetBufferPointer() as *mut u8,
1312 source.GetBufferSize(),
1313 )
1314 };
1315 let mut shader = None;
1316 unsafe {
1317 self.gpu_state
1318 .device
1319 .CreatePixelShader(bytes, None, Some(&mut shader))
1320 }?;
1321 shader.unwrap()
1322 };
1323
1324 #[cfg(feature = "enable-renderdoc")]
1325 self.renderdoc
1326 .0
1327 .write()
1328 .start_frame_capture(std::ptr::null(), std::ptr::null());
1329
1330 let device_context = &self.gpu_state.device_context;
1331 unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) };
1332 unsafe { device_context.VSSetShader(&vertex_shader, None) };
1333 unsafe { device_context.PSSetShader(&pixel_shader, None) };
1334 unsafe { device_context.VSSetConstantBuffers(0, Some(¶ms_buffer)) };
1335 unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_buffer)) };
1336 unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) };
1337 unsafe { device_context.PSSetSamplers(0, Some(&sampler)) };
1338 unsafe { device_context.OMSetBlendState(&blend_state, None, 0xffffffff) };
1339
1340 for layer in glyph_layers {
1341 let params = GlyphLayerTextureParams {
1342 run_color: layer.run_color,
1343 bounds: layer.bounds,
1344 };
1345 unsafe {
1346 let mut dest = std::mem::zeroed();
1347 self.gpu_state.device_context.Map(
1348 params_buffer[0].as_ref().unwrap(),
1349 0,
1350 D3D11_MAP_WRITE_DISCARD,
1351 0,
1352 Some(&mut dest),
1353 )?;
1354 std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1);
1355 self.gpu_state
1356 .device_context
1357 .Unmap(params_buffer[0].as_ref().unwrap(), 0);
1358 };
1359
1360 let texture = [Some(layer.texture_view)];
1361 unsafe { device_context.PSSetShaderResources(0, Some(&texture)) };
1362
1363 let viewport = [D3D11_VIEWPORT {
1364 TopLeftX: layer.bounds.origin.x as f32,
1365 TopLeftY: layer.bounds.origin.y as f32,
1366 Width: layer.bounds.size.width as f32,
1367 Height: layer.bounds.size.height as f32,
1368 MinDepth: 0.0,
1369 MaxDepth: 1.0,
1370 }];
1371 unsafe { device_context.RSSetViewports(Some(&viewport)) };
1372
1373 unsafe { device_context.Draw(4, 0) };
1374 }
1375
1376 #[cfg(feature = "enable-renderdoc")]
1377 self.renderdoc
1378 .0
1379 .write()
1380 .end_frame_capture(std::ptr::null(), std::ptr::null());
1381
1382 println!("render finished");
1383
1384 Ok(Vec::new())
1385 }
1386
1387 fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
1388 unsafe {
1389 let font = &self.fonts[font_id.0].font_face;
1390 let glyph_indices = [glyph_id.0 as u16];
1391 let mut metrics = [DWRITE_GLYPH_METRICS::default()];
1392 font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
1393
1394 let metrics = &metrics[0];
1395 let advance_width = metrics.advanceWidth as i32;
1396 let advance_height = metrics.advanceHeight as i32;
1397 let left_side_bearing = metrics.leftSideBearing;
1398 let right_side_bearing = metrics.rightSideBearing;
1399 let top_side_bearing = metrics.topSideBearing;
1400 let bottom_side_bearing = metrics.bottomSideBearing;
1401 let vertical_origin_y = metrics.verticalOriginY;
1402
1403 let y_offset = vertical_origin_y + bottom_side_bearing - advance_height;
1404 let width = advance_width - (left_side_bearing + right_side_bearing);
1405 let height = advance_height - (top_side_bearing + bottom_side_bearing);
1406
1407 Ok(Bounds {
1408 origin: Point {
1409 x: left_side_bearing as f32,
1410 y: y_offset as f32,
1411 },
1412 size: Size {
1413 width: width as f32,
1414 height: height as f32,
1415 },
1416 })
1417 }
1418 }
1419
1420 fn get_advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Size<f32>> {
1421 unsafe {
1422 let font = &self.fonts[font_id.0].font_face;
1423 let glyph_indices = [glyph_id.0 as u16];
1424 let mut metrics = [DWRITE_GLYPH_METRICS::default()];
1425 font.GetDesignGlyphMetrics(glyph_indices.as_ptr(), 1, metrics.as_mut_ptr(), false)?;
1426
1427 let metrics = &metrics[0];
1428
1429 Ok(Size {
1430 width: metrics.advanceWidth as f32,
1431 height: 0.0,
1432 })
1433 }
1434 }
1435
1436 fn all_font_names(&self) -> Vec<String> {
1437 let mut result =
1438 get_font_names_from_collection(&self.system_font_collection, &self.components.locale);
1439 result.extend(get_font_names_from_collection(
1440 &self.custom_font_collection,
1441 &self.components.locale,
1442 ));
1443 result
1444 }
1445}
1446
1447impl Drop for DirectWriteState {
1448 fn drop(&mut self) {
1449 unsafe {
1450 let _ = self
1451 .components
1452 .factory
1453 .UnregisterFontFileLoader(&self.components.in_memory_loader);
1454 }
1455 }
1456}
1457
1458struct GlyphLayerTexture {
1459 run_color: Rgba,
1460 bounds: Bounds<i32>,
1461 texture: ID3D11Texture2D,
1462 texture_view: ID3D11ShaderResourceView,
1463}
1464
1465impl GlyphLayerTexture {
1466 pub fn new(
1467 gpu_state: &GPUState,
1468 run_color: Rgba,
1469 bounds: Bounds<i32>,
1470 alpha_data: &[u8],
1471 ) -> Result<Self> {
1472 let texture_size = bounds.size;
1473
1474 let desc = D3D11_TEXTURE2D_DESC {
1475 Width: texture_size.width as u32,
1476 Height: texture_size.height as u32,
1477 MipLevels: 1,
1478 ArraySize: 1,
1479 Format: DXGI_FORMAT_R8G8B8A8_UNORM,
1480 SampleDesc: DXGI_SAMPLE_DESC {
1481 Count: 1,
1482 Quality: 0,
1483 },
1484 Usage: D3D11_USAGE_DEFAULT,
1485 BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
1486 CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
1487 MiscFlags: 0,
1488 };
1489
1490 let texture = {
1491 let mut texture: Option<ID3D11Texture2D> = None;
1492 unsafe {
1493 gpu_state
1494 .device
1495 .CreateTexture2D(&desc, None, Some(&mut texture))?
1496 };
1497 texture.unwrap()
1498 };
1499 let texture_view = {
1500 let mut view: Option<ID3D11ShaderResourceView> = None;
1501 unsafe {
1502 gpu_state
1503 .device
1504 .CreateShaderResourceView(&texture, None, Some(&mut view))?
1505 };
1506 view.unwrap()
1507 };
1508
1509 unsafe {
1510 gpu_state.device_context.UpdateSubresource(
1511 &texture,
1512 0,
1513 None,
1514 alpha_data.as_ptr() as _,
1515 (texture_size.width * 4) as u32,
1516 0,
1517 )
1518 };
1519
1520 Ok(GlyphLayerTexture {
1521 run_color,
1522 bounds,
1523 texture,
1524 texture_view,
1525 })
1526 }
1527}
1528
1529#[repr(C)]
1530struct GlyphLayerTextureParams {
1531 bounds: Bounds<i32>,
1532 run_color: Rgba,
1533}
1534
1535struct TextRendererWrapper(pub IDWriteTextRenderer);
1536
1537impl TextRendererWrapper {
1538 pub fn new(locale_str: &str) -> Self {
1539 let inner = TextRenderer::new(locale_str);
1540 TextRendererWrapper(inner.into())
1541 }
1542}
1543
1544#[implement(IDWriteTextRenderer)]
1545struct TextRenderer {
1546 locale: String,
1547}
1548
1549impl TextRenderer {
1550 pub fn new(locale_str: &str) -> Self {
1551 TextRenderer {
1552 locale: locale_str.to_owned(),
1553 }
1554 }
1555}
1556
1557struct RendererContext<'t, 'a, 'b> {
1558 text_system: &'t mut DirectWriteState,
1559 index_converter: StringIndexConverter<'a>,
1560 runs: &'b mut Vec<ShapedRun>,
1561 width: f32,
1562}
1563
1564#[derive(Debug)]
1565struct ClusterAnalyzer<'t> {
1566 utf16_idx: usize,
1567 glyph_idx: usize,
1568 glyph_count: usize,
1569 cluster_map: &'t [u16],
1570}
1571
1572impl<'t> ClusterAnalyzer<'t> {
1573 pub fn new(cluster_map: &'t [u16], glyph_count: usize) -> Self {
1574 ClusterAnalyzer {
1575 utf16_idx: 0,
1576 glyph_idx: 0,
1577 glyph_count,
1578 cluster_map,
1579 }
1580 }
1581}
1582
1583impl Iterator for ClusterAnalyzer<'_> {
1584 type Item = (usize, usize);
1585
1586 fn next(&mut self) -> Option<(usize, usize)> {
1587 if self.utf16_idx >= self.cluster_map.len() {
1588 return None; // No more clusters
1589 }
1590 let start_utf16_idx = self.utf16_idx;
1591 let current_glyph = self.cluster_map[start_utf16_idx] as usize;
1592
1593 // Find the end of current cluster (where glyph index changes)
1594 let mut end_utf16_idx = start_utf16_idx + 1;
1595 while end_utf16_idx < self.cluster_map.len()
1596 && self.cluster_map[end_utf16_idx] as usize == current_glyph
1597 {
1598 end_utf16_idx += 1;
1599 }
1600
1601 let utf16_len = end_utf16_idx - start_utf16_idx;
1602
1603 // Calculate glyph count for this cluster
1604 let next_glyph = if end_utf16_idx < self.cluster_map.len() {
1605 self.cluster_map[end_utf16_idx] as usize
1606 } else {
1607 self.glyph_count
1608 };
1609
1610 let glyph_count = next_glyph - current_glyph;
1611
1612 // Update state for next call
1613 self.utf16_idx = end_utf16_idx;
1614 self.glyph_idx = next_glyph;
1615
1616 Some((utf16_len, glyph_count))
1617 }
1618}
1619
1620#[allow(non_snake_case)]
1621impl IDWritePixelSnapping_Impl for TextRenderer_Impl {
1622 fn IsPixelSnappingDisabled(
1623 &self,
1624 _clientdrawingcontext: *const ::core::ffi::c_void,
1625 ) -> windows::core::Result<BOOL> {
1626 Ok(BOOL(0))
1627 }
1628
1629 fn GetCurrentTransform(
1630 &self,
1631 _clientdrawingcontext: *const ::core::ffi::c_void,
1632 transform: *mut DWRITE_MATRIX,
1633 ) -> windows::core::Result<()> {
1634 unsafe {
1635 *transform = DWRITE_MATRIX {
1636 m11: 1.0,
1637 m12: 0.0,
1638 m21: 0.0,
1639 m22: 1.0,
1640 dx: 0.0,
1641 dy: 0.0,
1642 };
1643 }
1644 Ok(())
1645 }
1646
1647 fn GetPixelsPerDip(
1648 &self,
1649 _clientdrawingcontext: *const ::core::ffi::c_void,
1650 ) -> windows::core::Result<f32> {
1651 Ok(1.0)
1652 }
1653}
1654
1655#[allow(non_snake_case)]
1656impl IDWriteTextRenderer_Impl for TextRenderer_Impl {
1657 fn DrawGlyphRun(
1658 &self,
1659 clientdrawingcontext: *const ::core::ffi::c_void,
1660 _baselineoriginx: f32,
1661 _baselineoriginy: f32,
1662 _measuringmode: DWRITE_MEASURING_MODE,
1663 glyphrun: *const DWRITE_GLYPH_RUN,
1664 glyphrundescription: *const DWRITE_GLYPH_RUN_DESCRIPTION,
1665 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1666 ) -> windows::core::Result<()> {
1667 let glyphrun = unsafe { &*glyphrun };
1668 let glyph_count = glyphrun.glyphCount as usize;
1669 if glyph_count == 0 || glyphrun.fontFace.is_none() {
1670 return Ok(());
1671 }
1672 let desc = unsafe { &*glyphrundescription };
1673 let context = unsafe {
1674 &mut *(clientdrawingcontext as *const RendererContext as *mut RendererContext)
1675 };
1676 let font_face = glyphrun.fontFace.as_ref().unwrap();
1677 // This `cast()` action here should never fail since we are running on Win10+, and
1678 // `IDWriteFontFace3` requires Win10
1679 let font_face = &font_face.cast::<IDWriteFontFace3>().unwrap();
1680 let Some((font_identifier, font_struct, color_font)) =
1681 get_font_identifier_and_font_struct(font_face, &self.locale)
1682 else {
1683 return Ok(());
1684 };
1685
1686 let font_id = if let Some(id) = context
1687 .text_system
1688 .font_id_by_identifier
1689 .get(&font_identifier)
1690 {
1691 *id
1692 } else {
1693 context.text_system.select_font(&font_struct)
1694 };
1695
1696 let glyph_ids = unsafe { std::slice::from_raw_parts(glyphrun.glyphIndices, glyph_count) };
1697 let glyph_advances =
1698 unsafe { std::slice::from_raw_parts(glyphrun.glyphAdvances, glyph_count) };
1699 let glyph_offsets =
1700 unsafe { std::slice::from_raw_parts(glyphrun.glyphOffsets, glyph_count) };
1701 let cluster_map =
1702 unsafe { std::slice::from_raw_parts(desc.clusterMap, desc.stringLength as usize) };
1703
1704 let mut cluster_analyzer = ClusterAnalyzer::new(cluster_map, glyph_count);
1705 let mut utf16_idx = desc.textPosition as usize;
1706 let mut glyph_idx = 0;
1707 let mut glyphs = Vec::with_capacity(glyph_count);
1708 for (cluster_utf16_len, cluster_glyph_count) in cluster_analyzer {
1709 context.index_converter.advance_to_utf16_ix(utf16_idx);
1710 utf16_idx += cluster_utf16_len;
1711 for (cluster_glyph_idx, glyph_id) in glyph_ids
1712 [glyph_idx..(glyph_idx + cluster_glyph_count)]
1713 .iter()
1714 .enumerate()
1715 {
1716 let id = GlyphId(*glyph_id as u32);
1717 let is_emoji = color_font
1718 && is_color_glyph(font_face, id, &context.text_system.components.factory);
1719 let this_glyph_idx = glyph_idx + cluster_glyph_idx;
1720 glyphs.push(ShapedGlyph {
1721 id,
1722 position: point(
1723 px(context.width + glyph_offsets[this_glyph_idx].advanceOffset),
1724 px(0.0),
1725 ),
1726 index: context.index_converter.utf8_ix,
1727 is_emoji,
1728 });
1729 context.width += glyph_advances[this_glyph_idx];
1730 }
1731 glyph_idx += cluster_glyph_count;
1732 }
1733 context.runs.push(ShapedRun { font_id, glyphs });
1734 Ok(())
1735 }
1736
1737 fn DrawUnderline(
1738 &self,
1739 _clientdrawingcontext: *const ::core::ffi::c_void,
1740 _baselineoriginx: f32,
1741 _baselineoriginy: f32,
1742 _underline: *const DWRITE_UNDERLINE,
1743 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1744 ) -> windows::core::Result<()> {
1745 Err(windows::core::Error::new(
1746 E_NOTIMPL,
1747 "DrawUnderline unimplemented",
1748 ))
1749 }
1750
1751 fn DrawStrikethrough(
1752 &self,
1753 _clientdrawingcontext: *const ::core::ffi::c_void,
1754 _baselineoriginx: f32,
1755 _baselineoriginy: f32,
1756 _strikethrough: *const DWRITE_STRIKETHROUGH,
1757 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1758 ) -> windows::core::Result<()> {
1759 Err(windows::core::Error::new(
1760 E_NOTIMPL,
1761 "DrawStrikethrough unimplemented",
1762 ))
1763 }
1764
1765 fn DrawInlineObject(
1766 &self,
1767 _clientdrawingcontext: *const ::core::ffi::c_void,
1768 _originx: f32,
1769 _originy: f32,
1770 _inlineobject: windows::core::Ref<IDWriteInlineObject>,
1771 _issideways: BOOL,
1772 _isrighttoleft: BOOL,
1773 _clientdrawingeffect: windows::core::Ref<windows::core::IUnknown>,
1774 ) -> windows::core::Result<()> {
1775 Err(windows::core::Error::new(
1776 E_NOTIMPL,
1777 "DrawInlineObject unimplemented",
1778 ))
1779 }
1780}
1781
1782struct StringIndexConverter<'a> {
1783 text: &'a str,
1784 utf8_ix: usize,
1785 utf16_ix: usize,
1786}
1787
1788impl<'a> StringIndexConverter<'a> {
1789 fn new(text: &'a str) -> Self {
1790 Self {
1791 text,
1792 utf8_ix: 0,
1793 utf16_ix: 0,
1794 }
1795 }
1796
1797 #[allow(dead_code)]
1798 fn advance_to_utf8_ix(&mut self, utf8_target: usize) {
1799 for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1800 if self.utf8_ix + ix >= utf8_target {
1801 self.utf8_ix += ix;
1802 return;
1803 }
1804 self.utf16_ix += c.len_utf16();
1805 }
1806 self.utf8_ix = self.text.len();
1807 }
1808
1809 fn advance_to_utf16_ix(&mut self, utf16_target: usize) {
1810 for (ix, c) in self.text[self.utf8_ix..].char_indices() {
1811 if self.utf16_ix >= utf16_target {
1812 self.utf8_ix += ix;
1813 return;
1814 }
1815 self.utf16_ix += c.len_utf16();
1816 }
1817 self.utf8_ix = self.text.len();
1818 }
1819}
1820
1821impl Into<DWRITE_FONT_STYLE> for FontStyle {
1822 fn into(self) -> DWRITE_FONT_STYLE {
1823 match self {
1824 FontStyle::Normal => DWRITE_FONT_STYLE_NORMAL,
1825 FontStyle::Italic => DWRITE_FONT_STYLE_ITALIC,
1826 FontStyle::Oblique => DWRITE_FONT_STYLE_OBLIQUE,
1827 }
1828 }
1829}
1830
1831impl From<DWRITE_FONT_STYLE> for FontStyle {
1832 fn from(value: DWRITE_FONT_STYLE) -> Self {
1833 match value.0 {
1834 0 => FontStyle::Normal,
1835 1 => FontStyle::Italic,
1836 2 => FontStyle::Oblique,
1837 _ => unreachable!(),
1838 }
1839 }
1840}
1841
1842impl Into<DWRITE_FONT_WEIGHT> for FontWeight {
1843 fn into(self) -> DWRITE_FONT_WEIGHT {
1844 DWRITE_FONT_WEIGHT(self.0 as i32)
1845 }
1846}
1847
1848impl From<DWRITE_FONT_WEIGHT> for FontWeight {
1849 fn from(value: DWRITE_FONT_WEIGHT) -> Self {
1850 FontWeight(value.0 as f32)
1851 }
1852}
1853
1854fn get_font_names_from_collection(
1855 collection: &IDWriteFontCollection1,
1856 locale: &str,
1857) -> Vec<String> {
1858 unsafe {
1859 let mut result = Vec::new();
1860 let family_count = collection.GetFontFamilyCount();
1861 for index in 0..family_count {
1862 let Some(font_family) = collection.GetFontFamily(index).log_err() else {
1863 continue;
1864 };
1865 let Some(localized_family_name) = font_family.GetFamilyNames().log_err() else {
1866 continue;
1867 };
1868 let Some(family_name) = get_name(localized_family_name, locale).log_err() else {
1869 continue;
1870 };
1871 result.push(family_name);
1872 }
1873
1874 result
1875 }
1876}
1877
1878fn get_font_identifier_and_font_struct(
1879 font_face: &IDWriteFontFace3,
1880 locale: &str,
1881) -> Option<(FontIdentifier, Font, bool)> {
1882 let postscript_name = get_postscript_name(font_face, locale).log_err()?;
1883 let localized_family_name = unsafe { font_face.GetFamilyNames().log_err() }?;
1884 let family_name = get_name(localized_family_name, locale).log_err()?;
1885 let weight = unsafe { font_face.GetWeight() };
1886 let style = unsafe { font_face.GetStyle() };
1887 let identifier = FontIdentifier {
1888 postscript_name,
1889 weight: weight.0,
1890 style: style.0,
1891 };
1892 let font_struct = Font {
1893 family: family_name.into(),
1894 features: FontFeatures::default(),
1895 weight: weight.into(),
1896 style: style.into(),
1897 fallbacks: None,
1898 };
1899 let is_emoji = unsafe { font_face.IsColorFont().as_bool() };
1900 Some((identifier, font_struct, is_emoji))
1901}
1902
1903#[inline]
1904fn get_font_identifier(font_face: &IDWriteFontFace3, locale: &str) -> Option<FontIdentifier> {
1905 let weight = unsafe { font_face.GetWeight().0 };
1906 let style = unsafe { font_face.GetStyle().0 };
1907 get_postscript_name(font_face, locale)
1908 .log_err()
1909 .map(|postscript_name| FontIdentifier {
1910 postscript_name,
1911 weight,
1912 style,
1913 })
1914}
1915
1916#[inline]
1917fn get_postscript_name(font_face: &IDWriteFontFace3, locale: &str) -> Result<String> {
1918 let mut info = None;
1919 let mut exists = BOOL(0);
1920 unsafe {
1921 font_face.GetInformationalStrings(
1922 DWRITE_INFORMATIONAL_STRING_POSTSCRIPT_NAME,
1923 &mut info,
1924 &mut exists,
1925 )?
1926 };
1927 if !exists.as_bool() || info.is_none() {
1928 anyhow::bail!("No postscript name found for font face");
1929 }
1930
1931 get_name(info.unwrap(), locale)
1932}
1933
1934// https://learn.microsoft.com/en-us/windows/win32/api/dwrite/ne-dwrite-dwrite_font_feature_tag
1935fn apply_font_features(
1936 direct_write_features: &IDWriteTypography,
1937 features: &FontFeatures,
1938) -> Result<()> {
1939 let tag_values = features.tag_value_list();
1940 if tag_values.is_empty() {
1941 return Ok(());
1942 }
1943
1944 // All of these features are enabled by default by DirectWrite.
1945 // If you want to (and can) peek into the source of DirectWrite
1946 let mut feature_liga = make_direct_write_feature("liga", 1);
1947 let mut feature_clig = make_direct_write_feature("clig", 1);
1948 let mut feature_calt = make_direct_write_feature("calt", 1);
1949
1950 for (tag, value) in tag_values {
1951 if tag.as_str() == "liga" && *value == 0 {
1952 feature_liga.parameter = 0;
1953 continue;
1954 }
1955 if tag.as_str() == "clig" && *value == 0 {
1956 feature_clig.parameter = 0;
1957 continue;
1958 }
1959 if tag.as_str() == "calt" && *value == 0 {
1960 feature_calt.parameter = 0;
1961 continue;
1962 }
1963
1964 unsafe {
1965 direct_write_features.AddFontFeature(make_direct_write_feature(&tag, *value))?;
1966 }
1967 }
1968 unsafe {
1969 direct_write_features.AddFontFeature(feature_liga)?;
1970 direct_write_features.AddFontFeature(feature_clig)?;
1971 direct_write_features.AddFontFeature(feature_calt)?;
1972 }
1973
1974 Ok(())
1975}
1976
1977#[inline]
1978const fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_FEATURE {
1979 let tag = make_direct_write_tag(feature_name);
1980 DWRITE_FONT_FEATURE {
1981 nameTag: tag,
1982 parameter,
1983 }
1984}
1985
1986#[inline]
1987const fn make_open_type_tag(tag_name: &str) -> u32 {
1988 let bytes = tag_name.as_bytes();
1989 debug_assert!(bytes.len() == 4);
1990 u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
1991}
1992
1993#[inline]
1994const fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG {
1995 DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name))
1996}
1997
1998#[inline]
1999fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
2000 let mut locale_name_index = 0u32;
2001 let mut exists = BOOL(0);
2002 unsafe {
2003 string.FindLocaleName(
2004 &HSTRING::from(locale),
2005 &mut locale_name_index,
2006 &mut exists as _,
2007 )?
2008 };
2009 if !exists.as_bool() {
2010 unsafe {
2011 string.FindLocaleName(
2012 DEFAULT_LOCALE_NAME,
2013 &mut locale_name_index as _,
2014 &mut exists as _,
2015 )?
2016 };
2017 anyhow::ensure!(exists.as_bool(), "No localised string for {locale}");
2018 }
2019
2020 let name_length = unsafe { string.GetStringLength(locale_name_index) }? as usize;
2021 let mut name_vec = vec![0u16; name_length + 1];
2022 unsafe {
2023 string.GetString(locale_name_index, &mut name_vec)?;
2024 }
2025
2026 Ok(String::from_utf16_lossy(&name_vec[..name_length]))
2027}
2028
2029#[inline]
2030fn translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] {
2031 [color.r, color.g, color.b, color.a]
2032}
2033
2034fn get_system_ui_font_name() -> SharedString {
2035 unsafe {
2036 let mut info: LOGFONTW = std::mem::zeroed();
2037 let font_family = if SystemParametersInfoW(
2038 SPI_GETICONTITLELOGFONT,
2039 std::mem::size_of::<LOGFONTW>() as u32,
2040 Some(&mut info as *mut _ as _),
2041 SYSTEM_PARAMETERS_INFO_UPDATE_FLAGS(0),
2042 )
2043 .log_err()
2044 .is_none()
2045 {
2046 // https://learn.microsoft.com/en-us/windows/win32/uxguide/vis-fonts
2047 // Segoe UI is the Windows font intended for user interface text strings.
2048 "Segoe UI".into()
2049 } else {
2050 let font_name = String::from_utf16_lossy(&info.lfFaceName);
2051 font_name.trim_matches(char::from(0)).to_owned().into()
2052 };
2053 log::info!("Use {} as UI font.", font_family);
2054 font_family
2055 }
2056}
2057
2058// One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats
2059// but that doesn't seem to work for some glyphs, say โค
2060fn is_color_glyph(
2061 font_face: &IDWriteFontFace3,
2062 glyph_id: GlyphId,
2063 factory: &IDWriteFactory5,
2064) -> bool {
2065 let glyph_run = DWRITE_GLYPH_RUN {
2066 fontFace: unsafe { std::mem::transmute_copy(font_face) },
2067 fontEmSize: 14.0,
2068 glyphCount: 1,
2069 glyphIndices: &(glyph_id.0 as u16),
2070 glyphAdvances: &0.0,
2071 glyphOffsets: &DWRITE_GLYPH_OFFSET {
2072 advanceOffset: 0.0,
2073 ascenderOffset: 0.0,
2074 },
2075 isSideways: BOOL(0),
2076 bidiLevel: 0,
2077 };
2078 unsafe {
2079 factory.TranslateColorGlyphRun(
2080 Vector2::default(),
2081 &glyph_run as _,
2082 None,
2083 DWRITE_GLYPH_IMAGE_FORMATS_COLR
2084 | DWRITE_GLYPH_IMAGE_FORMATS_SVG
2085 | DWRITE_GLYPH_IMAGE_FORMATS_PNG
2086 | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
2087 | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
2088 DWRITE_MEASURING_MODE_NATURAL,
2089 None,
2090 0,
2091 )
2092 }
2093 .is_ok()
2094}
2095
2096const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
2097
2098#[cfg(test)]
2099mod tests {
2100 use crate::platform::windows::direct_write::ClusterAnalyzer;
2101
2102 #[test]
2103 fn test_cluster_map() {
2104 let cluster_map = [0];
2105 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1);
2106 let next = analyzer.next();
2107 assert_eq!(next, Some((1, 1)));
2108 let next = analyzer.next();
2109 assert_eq!(next, None);
2110
2111 let cluster_map = [0, 1, 2];
2112 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 3);
2113 let next = analyzer.next();
2114 assert_eq!(next, Some((1, 1)));
2115 let next = analyzer.next();
2116 assert_eq!(next, Some((1, 1)));
2117 let next = analyzer.next();
2118 assert_eq!(next, Some((1, 1)));
2119 let next = analyzer.next();
2120 assert_eq!(next, None);
2121 // ๐จโ๐ฉโ๐งโ๐ฆ๐ฉโ๐ป
2122 let cluster_map = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4];
2123 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 5);
2124 let next = analyzer.next();
2125 assert_eq!(next, Some((11, 4)));
2126 let next = analyzer.next();
2127 assert_eq!(next, Some((5, 1)));
2128 let next = analyzer.next();
2129 assert_eq!(next, None);
2130 // ๐ฉโ๐ป
2131 let cluster_map = [0, 0, 0, 0, 0];
2132 let mut analyzer = ClusterAnalyzer::new(&cluster_map, 1);
2133 let next = analyzer.next();
2134 assert_eq!(next, Some((5, 1)));
2135 let next = analyzer.next();
2136 assert_eq!(next, None);
2137 }
2138}