windows: Slightly improve font rendering quality (#12015)

张小白 created

Upper before this PR, lower after.

![Screenshot 2024-05-20
144852](https://github.com/zed-industries/zed/assets/14981363/88995482-3a98-41be-9c2c-6b781bef6ad2)

This PR manually applies a MSAA to the font atlas. Before this PR, the
font may seem aliased ( espeacially on low DPI monitors ), that's
because `DirectWrite` and `CoreText` ( on which currently `Zed` built
the whole text system ) uses different anti-aliasing strategy. The
different anti-aliasing approach used by `DirectWrite` and `CoreText`:

![Screenshot 2024-05-20
151114](https://github.com/zed-industries/zed/assets/14981363/21a2fc1e-48a2-4cff-a9d1-41602eff3658)

The upper is `VSCode` font rendering result, middle `macOS`, lower this
PR ( pic captured with same font face, same font size, same DPI, same
physical resolution, same editor theme, and same magnification rate ).

This PR brings a quality similiar to `CoreText`. What's more, from the
`VSCode` image, you can see how `DirectWrite` sub-pixel anti-aliasing is
performed on the edge of the glyph. Can we achieve the same rendering
quality? Currently, No. `Zed` use a grayscale image to render glyph, and
a sub-pixel anti-aliasing `DirectWrite` requires all RGB channels and
the foreground color of the rendering glyph, which `Zed` dose not
provide.

So, to achieve the quality of `VSCode` font rendering, the text system
of `Zed` needs much much more efforts to refactor the codes.


Release Notes:

- N/A

Change summary

crates/gpui/src/platform/windows/direct_write.rs | 208 ++++++++++-------
1 file changed, 125 insertions(+), 83 deletions(-)

Detailed changes

crates/gpui/src/platform/windows/direct_write.rs 🔗

@@ -8,7 +8,6 @@ use parking_lot::{RwLock, RwLockUpgradableReadGuard};
 use smallvec::SmallVec;
 use windows::{
     core::*,
-    Foundation::Numerics::Matrix3x2,
     Win32::{
         Foundation::*,
         Globalization::GetUserDefaultLocaleName,
@@ -45,6 +44,12 @@ struct DirectWriteComponent {
     in_memory_loader: IDWriteInMemoryFontFileLoader,
     builder: IDWriteFontSetBuilder1,
     text_renderer: Arc<TextRendererWrapper>,
+    render_context: GlyphRenderContext,
+}
+
+struct GlyphRenderContext {
+    params: IDWriteRenderingParams3,
+    dc_target: ID2D1DeviceContext4,
 }
 
 // All use of the IUnknown methods should be "thread-safe".
@@ -86,6 +91,7 @@ impl DirectWriteComponent {
             GetUserDefaultLocaleName(&mut locale_vec);
             let locale = String::from_utf16_lossy(&locale_vec);
             let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
+            let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
 
             Ok(DirectWriteComponent {
                 locale,
@@ -95,11 +101,47 @@ impl DirectWriteComponent {
                 in_memory_loader,
                 builder,
                 text_renderer,
+                render_context,
             })
         }
     }
 }
 
+impl GlyphRenderContext {
+    pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {
+        unsafe {
+            let default_params: IDWriteRenderingParams3 =
+                factory.CreateRenderingParams()?.cast()?;
+            let gamma = default_params.GetGamma();
+            let enhanced_contrast = default_params.GetEnhancedContrast();
+            let gray_contrast = default_params.GetGrayscaleEnhancedContrast();
+            let cleartype_level = default_params.GetClearTypeLevel();
+            let grid_fit_mode = default_params.GetGridFitMode();
+
+            let params = factory.CreateCustomRenderingParams(
+                gamma,
+                enhanced_contrast,
+                gray_contrast,
+                cleartype_level,
+                DWRITE_PIXEL_GEOMETRY_RGB,
+                DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
+                grid_fit_mode,
+            )?;
+            let dc_target = {
+                let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
+                    DXGI_FORMAT_B8G8R8A8_UNORM,
+                    D2D1_ALPHA_MODE_PREMULTIPLIED,
+                ))?;
+                let target = target.cast::<ID2D1DeviceContext4>()?;
+                target.SetTextRenderingParams(&params);
+                target
+            };
+
+            Ok(Self { params, dc_target })
+        }
+    }
+}
+
 impl DirectWriteTextSystem {
     pub(crate) fn new() -> Result<Self> {
         let components = DirectWriteComponent::new()?;
@@ -521,10 +563,12 @@ impl DirectWriteState {
         }
     }
 
-    unsafe fn get_glyphrun_analysis(
-        &self,
-        params: &RenderGlyphParams,
-    ) -> windows::core::Result<IDWriteGlyphRunAnalysis> {
+    fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
+        let render_target = &self.components.render_context.dc_target;
+        unsafe {
+            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
+            render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
+        }
         let font = &self.fonts[params.font_id.0];
         let glyph_id = [params.glyph_id.0 as u16];
         let advance = [0.0f32];
@@ -539,40 +583,29 @@ impl DirectWriteState {
             isSideways: BOOL(0),
             bidiLevel: 0,
         };
-        let transform = DWRITE_MATRIX {
-            m11: params.scale_factor,
-            m12: 0.0,
-            m21: 0.0,
-            m22: params.scale_factor,
-            dx: 0.0,
-            dy: 0.0,
+        let bounds = unsafe {
+            render_target.GetGlyphRunWorldBounds(
+                D2D_POINT_2F { x: 0.0, y: 0.0 },
+                &glyph_run,
+                DWRITE_MEASURING_MODE_NATURAL,
+            )?
         };
-        self.components.factory.CreateGlyphRunAnalysis(
-            &glyph_run as _,
-            Some(&transform as _),
-            DWRITE_RENDERING_MODE1_NATURAL,
-            DWRITE_MEASURING_MODE_NATURAL,
-            DWRITE_GRID_FIT_MODE_DEFAULT,
-            DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
-            0.0,
-            0.0,
-        )
-    }
-
-    fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
-        unsafe {
-            let glyph_run_analysis = self.get_glyphrun_analysis(params)?;
-            let bounds = glyph_run_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1)?;
 
+        if bounds.right < bounds.left {
             Ok(Bounds {
-                origin: Point {
-                    x: DevicePixels(bounds.left),
-                    y: DevicePixels(bounds.top),
-                },
-                size: Size {
-                    width: DevicePixels(bounds.right - bounds.left),
-                    height: DevicePixels(bounds.bottom - bounds.top),
-                },
+                origin: point(0.into(), 0.into()),
+                size: size(0.into(), 0.into()),
+            })
+        } else {
+            Ok(Bounds {
+                origin: point(
+                    ((bounds.left * params.scale_factor).ceil() as i32).into(),
+                    ((bounds.top * params.scale_factor).ceil() as i32).into(),
+                ),
+                size: size(
+                    (((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
+                    (((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32).into(),
+                ),
             })
         }
     }
@@ -626,66 +659,40 @@ impl DirectWriteState {
             bitmap_size.height += DevicePixels(1);
         }
         let bitmap_size = bitmap_size;
-        let transform = DWRITE_MATRIX {
-            m11: params.scale_factor,
-            m12: 0.0,
-            m21: 0.0,
-            m22: params.scale_factor,
-            dx: 0.0,
-            dy: 0.0,
-        };
-        let brush_property = D2D1_BRUSH_PROPERTIES {
-            opacity: 1.0,
-            transform: Matrix3x2 {
-                M11: params.scale_factor,
-                M12: 0.0,
-                M21: 0.0,
-                M22: params.scale_factor,
-                M31: 0.0,
-                M32: 0.0,
-            },
-        };
 
         let total_bytes;
         let bitmap_format;
         let render_target_property;
+        let bitmap_width;
+        let bitmap_height;
         let bitmap_stride;
+        let bitmap_dpi;
         if params.is_emoji {
             total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
             bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
-            render_target_property = D2D1_RENDER_TARGET_PROPERTIES {
-                r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
-                pixelFormat: D2D1_PIXEL_FORMAT {
-                    format: DXGI_FORMAT_B8G8R8A8_UNORM,
-                    alphaMode: D2D1_ALPHA_MODE_PREMULTIPLIED,
-                },
-                dpiX: params.scale_factor * 96.0,
-                dpiY: params.scale_factor * 96.0,
-                usage: D2D1_RENDER_TARGET_USAGE_NONE,
-                minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
-            };
+            render_target_property = get_render_target_property(
+                DXGI_FORMAT_B8G8R8A8_UNORM,
+                D2D1_ALPHA_MODE_PREMULTIPLIED,
+            );
+            bitmap_width = bitmap_size.width.0 as u32;
+            bitmap_height = bitmap_size.height.0 as u32;
             bitmap_stride = bitmap_size.width.0 as u32 * 4;
+            bitmap_dpi = 96.0;
         } else {
             total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
             bitmap_format = &GUID_WICPixelFormat8bppAlpha;
-            render_target_property = D2D1_RENDER_TARGET_PROPERTIES {
-                r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
-                pixelFormat: D2D1_PIXEL_FORMAT {
-                    format: DXGI_FORMAT_A8_UNORM,
-                    alphaMode: D2D1_ALPHA_MODE_STRAIGHT,
-                },
-                dpiX: params.scale_factor * 96.0,
-                dpiY: params.scale_factor * 96.0,
-                usage: D2D1_RENDER_TARGET_USAGE_NONE,
-                minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
-            };
+            render_target_property =
+                get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
+            bitmap_width = bitmap_size.width.0 as u32 * 2;
+            bitmap_height = bitmap_size.height.0 as u32 * 2;
             bitmap_stride = bitmap_size.width.0 as u32;
+            bitmap_dpi = 192.0;
         }
 
         unsafe {
             let bitmap = self.components.bitmap_factory.CreateBitmap(
-                bitmap_size.width.0 as u32,
-                bitmap_size.height.0 as u32,
+                bitmap_width,
+                bitmap_height,
                 bitmap_format,
                 WICBitmapCacheOnLoad,
             )?;
@@ -693,7 +700,7 @@ impl DirectWriteState {
                 .components
                 .d2d1_factory
                 .CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
-            let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, Some(&brush_property))?;
+            let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
             let subpixel_shift = params
                 .subpixel_variant
                 .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
@@ -705,7 +712,14 @@ impl DirectWriteState {
             // This `cast()` action here should never fail since we are running on Win10+, and
             // ID2D1DeviceContext4 requires Win8+
             let render_target = render_target.cast::<ID2D1DeviceContext4>().unwrap();
+            render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
+            render_target.SetDpi(
+                bitmap_dpi * params.scale_factor,
+                bitmap_dpi * params.scale_factor,
+            );
+            render_target.SetTextRenderingParams(&self.components.render_context.params);
             render_target.BeginDraw();
+
             if params.is_emoji {
                 // WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested
                 let enumerator = self.components.factory.TranslateColorGlyphRun(
@@ -718,7 +732,7 @@ impl DirectWriteState {
                         | DWRITE_GLYPH_IMAGE_FORMATS_JPEG
                         | DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
                     DWRITE_MEASURING_MODE_NATURAL,
-                    Some(&transform as _),
+                    None,
                     0,
                 )?;
                 while enumerator.MoveNext().is_ok() {
@@ -766,9 +780,10 @@ impl DirectWriteState {
                 );
             }
             render_target.EndDraw(None, None)?;
+
             let mut raw_data = vec![0u8; total_bytes];
-            bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
             if params.is_emoji {
+                bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
                 // Convert from BGRA with premultiplied alpha to BGRA with straight alpha.
                 for pixel in raw_data.chunks_exact_mut(4) {
                     let a = pixel[3] as f32 / 255.;
@@ -776,6 +791,15 @@ impl DirectWriteState {
                     pixel[1] = (pixel[1] as f32 / a) as u8;
                     pixel[2] = (pixel[2] as f32 / a) as u8;
                 }
+            } else {
+                let scaler = self.components.bitmap_factory.CreateBitmapScaler()?;
+                scaler.Initialize(
+                    &bitmap,
+                    bitmap_size.width.0 as u32,
+                    bitmap_size.height.0 as u32,
+                    WICBitmapInterpolationModeHighQualityCubic,
+                )?;
+                scaler.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
             }
             Ok((bitmap_size, raw_data))
         }
@@ -1243,8 +1267,8 @@ fn make_direct_write_feature(feature_name: &str, parameter: u32) -> DWRITE_FONT_
 
 #[inline]
 fn make_open_type_tag(tag_name: &str) -> u32 {
-    assert_eq!(tag_name.chars().count(), 4);
     let bytes = tag_name.bytes().collect_vec();
+    assert_eq!(bytes.len(), 4);
     ((bytes[3] as u32) << 24)
         | ((bytes[2] as u32) << 16)
         | ((bytes[1] as u32) << 8)
@@ -1327,6 +1351,24 @@ fn get_system_ui_font_name() -> SharedString {
     }
 }
 
+#[inline]
+fn get_render_target_property(
+    pixel_format: DXGI_FORMAT,
+    alpha_mode: D2D1_ALPHA_MODE,
+) -> D2D1_RENDER_TARGET_PROPERTIES {
+    D2D1_RENDER_TARGET_PROPERTIES {
+        r#type: D2D1_RENDER_TARGET_TYPE_DEFAULT,
+        pixelFormat: D2D1_PIXEL_FORMAT {
+            format: pixel_format,
+            alphaMode: alpha_mode,
+        },
+        dpiX: 96.0,
+        dpiY: 96.0,
+        usage: D2D1_RENDER_TARGET_USAGE_NONE,
+        minLevel: D2D1_FEATURE_LEVEL_DEFAULT,
+    }
+}
+
 const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
 const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
     r: 1.0,