Implement perceptual gamma / contrast correction (#37167)

localcc created

Closes #36023 

This improves font rendering quality by doing perceptual gamma+contrast
correction which makes font edges look nicer and more legible.

A comparison image: (left is old, right is new)
<img width="1638" height="854" alt="Screenshot 2025-08-29 140015"
src="https://github.com/user-attachments/assets/85ca9818-0d55-4af0-a796-19e8cf9ed36b"
/>

This is most noticeable on smaller fonts / low-dpi displays

Release Notes:

- Improved font rendering quality

Change summary

Cargo.toml                                              |   1 
crates/gpui/src/platform/windows/alpha_correction.hlsl  |  28 ++
crates/gpui/src/platform/windows/color_text_raster.hlsl |  14 
crates/gpui/src/platform/windows/direct_write.rs        | 141 ++--------
crates/gpui/src/platform/windows/directx_renderer.rs    |  76 +++++
crates/gpui/src/platform/windows/platform.rs            |  15 -
crates/gpui/src/platform/windows/shaders.hlsl           |   9 
7 files changed, 157 insertions(+), 127 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -696,6 +696,7 @@ features = [
     "Win32_Graphics_Dxgi_Common",
     "Win32_Graphics_Gdi",
     "Win32_Graphics_Imaging",
+    "Win32_Graphics_Hlsl",
     "Win32_Networking_WinSock",
     "Win32_Security",
     "Win32_Security_Credentials",

crates/gpui/src/platform/windows/alpha_correction.hlsl 🔗

@@ -0,0 +1,28 @@
+float color_brightness(float3 color) {
+    // REC. 601 luminance coefficients for percieved brightness
+    return dot(color, float3(0.30f, 0.59f, 0.11f));
+}
+
+float light_on_dark_contrast(float enhancedContrast, float3 color) {
+    float brightness = color_brightness(color);
+    float multiplier = saturate(4.0f * (0.75f - brightness));
+    return enhancedContrast * multiplier;
+}
+
+float enhance_contrast(float alpha, float k) {
+    return alpha * (k + 1.0f) / (alpha * k + 1.0f);
+}
+
+float apply_alpha_correction(float a, float b, float4 g) {
+    float brightness_adjustment = g.x * b + g.y;
+    float correction = brightness_adjustment * a + (g.z * b + g.w);
+    return a + a * (1.0f - a) * correction;
+}
+
+float apply_contrast_and_gamma_correction(float sample, float3 color, float enhanced_contrast_factor, float4 gamma_ratios) {
+    float enhanced_contrast = light_on_dark_contrast(enhanced_contrast_factor, color);
+    float brightness = color_brightness(color);
+
+    float contrasted = enhance_contrast(sample, enhanced_contrast);
+    return apply_alpha_correction(contrasted, brightness, gamma_ratios);
+}

crates/gpui/src/platform/windows/color_text_raster.hlsl 🔗

@@ -1,3 +1,5 @@
+#include "alpha_correction.hlsl"
+
 struct RasterVertexOutput {
     float4 position : SV_Position;
     float2 texcoord : TEXCOORD0;
@@ -23,17 +25,19 @@ struct Bounds {
     int2 size;
 };
 
-Texture2D<float4> t_layer : register(t0);
+Texture2D<float> t_layer : register(t0);
 SamplerState s_layer : register(s0);
 
 cbuffer GlyphLayerTextureParams : register(b0) {
     Bounds bounds;
     float4 run_color;
+    float4 gamma_ratios;
+    float grayscale_enhanced_contrast;
+    float3 _pad;
 };
 
 float4 emoji_rasterization_fragment(PixelInput input): SV_Target {
-    float3 sampled = t_layer.Sample(s_layer, input.texcoord.xy).rgb;
-    float alpha = (sampled.r + sampled.g + sampled.b) / 3;
-
-    return float4(run_color.rgb, alpha);
+    float sample = t_layer.Sample(s_layer, input.texcoord.xy).r;
+    float alpha_corrected = apply_contrast_and_gamma_correction(sample, run_color.rgb, grayscale_enhanced_contrast, gamma_ratios);
+    return float4(run_color.rgb, alpha_corrected * run_color.a);
 }

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

@@ -10,12 +10,8 @@ use windows::{
         Foundation::*,
         Globalization::GetUserDefaultLocaleName,
         Graphics::{
-            Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
-            Direct3D11::*,
-            DirectWrite::*,
-            Dxgi::Common::*,
-            Gdi::{IsRectEmpty, LOGFONTW},
-            Imaging::*,
+            Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*,
+            Dxgi::Common::*, Gdi::LOGFONTW,
         },
         System::SystemServices::LOCALE_NAME_MAX_LENGTH,
         UI::WindowsAndMessaging::*,
@@ -40,12 +36,10 @@ pub(crate) struct DirectWriteTextSystem(RwLock<DirectWriteState>);
 struct DirectWriteComponent {
     locale: String,
     factory: IDWriteFactory5,
-    bitmap_factory: AgileReference<IWICImagingFactory>,
     in_memory_loader: IDWriteInMemoryFontFileLoader,
     builder: IDWriteFontSetBuilder1,
     text_renderer: Arc<TextRendererWrapper>,
 
-    render_params: IDWriteRenderingParams3,
     gpu_state: GPUState,
 }
 
@@ -76,11 +70,10 @@ struct FontIdentifier {
 }
 
 impl DirectWriteComponent {
-    pub fn new(bitmap_factory: &IWICImagingFactory, gpu_context: &DirectXDevices) -> Result<Self> {
+    pub fn new(gpu_context: &DirectXDevices) -> Result<Self> {
         // todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
         unsafe {
             let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
-            let bitmap_factory = AgileReference::new(bitmap_factory)?;
             // The `IDWriteInMemoryFontFileLoader` here is supported starting from
             // Windows 10 Creators Update, which consequently requires the entire
             // `DirectWriteTextSystem` to run on `win10 1703`+.
@@ -92,36 +85,14 @@ impl DirectWriteComponent {
             let locale = String::from_utf16_lossy(&locale_vec);
             let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
 
-            let render_params = {
-                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();
-
-                factory.CreateCustomRenderingParams(
-                    gamma,
-                    enhanced_contrast,
-                    gray_contrast,
-                    cleartype_level,
-                    DWRITE_PIXEL_GEOMETRY_RGB,
-                    DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
-                    grid_fit_mode,
-                )?
-            };
-
             let gpu_state = GPUState::new(gpu_context)?;
 
             Ok(DirectWriteComponent {
                 locale,
                 factory,
-                bitmap_factory,
                 in_memory_loader,
                 builder,
                 text_renderer,
-                render_params,
                 gpu_state,
             })
         }
@@ -212,11 +183,8 @@ impl GPUState {
 }
 
 impl DirectWriteTextSystem {
-    pub(crate) fn new(
-        gpu_context: &DirectXDevices,
-        bitmap_factory: &IWICImagingFactory,
-    ) -> Result<Self> {
-        let components = DirectWriteComponent::new(bitmap_factory, gpu_context)?;
+    pub(crate) fn new(gpu_context: &DirectXDevices) -> Result<Self> {
+        let components = DirectWriteComponent::new(gpu_context)?;
         let system_font_collection = unsafe {
             let mut result = std::mem::zeroed();
             components
@@ -762,14 +730,14 @@ impl DirectWriteState {
         unsafe {
             font.font_face.GetRecommendedRenderingMode(
                 params.font_size.0,
-                // The dpi here seems that it has the same effect with `Some(&transform)`
-                1.0,
-                1.0,
+                // Using 96 as scale is applied by the transform
+                96.0,
+                96.0,
                 Some(&transform),
                 false,
                 DWRITE_OUTLINE_THRESHOLD_ANTIALIASED,
                 DWRITE_MEASURING_MODE_NATURAL,
-                &self.components.render_params,
+                None,
                 &mut rendering_mode,
                 &mut grid_fit_mode,
             )?;
@@ -782,8 +750,7 @@ impl DirectWriteState {
                 rendering_mode,
                 DWRITE_MEASURING_MODE_NATURAL,
                 grid_fit_mode,
-                // We're using cleartype not grayscale for monochrome is because it provides better quality
-                DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
+                DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE,
                 baseline_origin_x,
                 baseline_origin_y,
             )
@@ -794,10 +761,14 @@ impl DirectWriteState {
     fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
         let glyph_analysis = self.create_glyph_run_analysis(params)?;
 
-        let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1)? };
-        // Some glyphs cannot be drawn with ClearType, such as bitmap fonts. In that case
-        // GetAlphaTextureBounds() supposedly returns an empty RECT, but I haven't tested that yet.
-        if !unsafe { IsRectEmpty(&bounds) }.as_bool() {
+        let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1)? };
+
+        if bounds.right < bounds.left {
+            Ok(Bounds {
+                origin: point(0.into(), 0.into()),
+                size: size(0.into(), 0.into()),
+            })
+        } else {
             Ok(Bounds {
                 origin: point(bounds.left.into(), bounds.top.into()),
                 size: size(
@@ -805,25 +776,6 @@ impl DirectWriteState {
                     (bounds.bottom - bounds.top).into(),
                 ),
             })
-        } else {
-            // If it's empty, retry with grayscale AA.
-            let bounds =
-                unsafe { glyph_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1)? };
-
-            if bounds.right < bounds.left {
-                Ok(Bounds {
-                    origin: point(0.into(), 0.into()),
-                    size: size(0.into(), 0.into()),
-                })
-            } else {
-                Ok(Bounds {
-                    origin: point(bounds.left.into(), bounds.top.into()),
-                    size: size(
-                        (bounds.right - bounds.left).into(),
-                        (bounds.bottom - bounds.top).into(),
-                    ),
-                })
-            }
         }
     }
 
@@ -872,13 +824,12 @@ impl DirectWriteState {
         glyph_bounds: Bounds<DevicePixels>,
     ) -> Result<Vec<u8>> {
         let mut bitmap_data =
-            vec![0u8; glyph_bounds.size.width.0 as usize * glyph_bounds.size.height.0 as usize * 3];
+            vec![0u8; glyph_bounds.size.width.0 as usize * glyph_bounds.size.height.0 as usize];
 
         let glyph_analysis = self.create_glyph_run_analysis(params)?;
         unsafe {
             glyph_analysis.CreateAlphaTexture(
-                // We're using cleartype not grayscale for monochrome is because it provides better quality
-                DWRITE_TEXTURE_CLEARTYPE_3x1,
+                DWRITE_TEXTURE_ALIASED_1x1,
                 &RECT {
                     left: glyph_bounds.origin.x.0,
                     top: glyph_bounds.origin.y.0,
@@ -889,30 +840,6 @@ impl DirectWriteState {
             )?;
         }
 
-        let bitmap_factory = self.components.bitmap_factory.resolve()?;
-        let bitmap = unsafe {
-            bitmap_factory.CreateBitmapFromMemory(
-                glyph_bounds.size.width.0 as u32,
-                glyph_bounds.size.height.0 as u32,
-                &GUID_WICPixelFormat24bppRGB,
-                glyph_bounds.size.width.0 as u32 * 3,
-                &bitmap_data,
-            )
-        }?;
-
-        let grayscale_bitmap =
-            unsafe { WICConvertBitmapSource(&GUID_WICPixelFormat8bppGray, &bitmap) }?;
-
-        let mut bitmap_data =
-            vec![0u8; glyph_bounds.size.width.0 as usize * glyph_bounds.size.height.0 as usize];
-        unsafe {
-            grayscale_bitmap.CopyPixels(
-                std::ptr::null() as _,
-                glyph_bounds.size.width.0 as u32,
-                &mut bitmap_data,
-            )
-        }?;
-
         Ok(bitmap_data)
     }
 
@@ -981,25 +908,24 @@ impl DirectWriteState {
                         DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
                         DWRITE_MEASURING_MODE_NATURAL,
                         DWRITE_GRID_FIT_MODE_DEFAULT,
-                        DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
+                        DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE,
                         baseline_origin_x,
                         baseline_origin_y,
                     )
                 }?;
 
                 let color_bounds =
-                    unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
+                    unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1) }?;
 
                 let color_size = size(
                     color_bounds.right - color_bounds.left,
                     color_bounds.bottom - color_bounds.top,
                 );
                 if color_size.width > 0 && color_size.height > 0 {
-                    let mut alpha_data =
-                        vec![0u8; (color_size.width * color_size.height * 3) as usize];
+                    let mut alpha_data = vec![0u8; (color_size.width * color_size.height) as usize];
                     unsafe {
                         color_analysis.CreateAlphaTexture(
-                            DWRITE_TEXTURE_CLEARTYPE_3x1,
+                            DWRITE_TEXTURE_ALIASED_1x1,
                             &color_bounds,
                             &mut alpha_data,
                         )
@@ -1015,10 +941,6 @@ impl DirectWriteState {
                         }
                     };
                     let bounds = bounds(point(color_bounds.left, color_bounds.top), color_size);
-                    let alpha_data = alpha_data
-                        .chunks_exact(3)
-                        .flat_map(|chunk| [chunk[0], chunk[1], chunk[2], 255])
-                        .collect::<Vec<_>>();
                     glyph_layers.push(GlyphLayerTexture::new(
                         &self.components.gpu_state,
                         run_color,
@@ -1135,10 +1057,18 @@ impl DirectWriteState {
         unsafe { device_context.PSSetSamplers(0, Some(&gpu_state.sampler)) };
         unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) };
 
+        let crate::FontInfo {
+            gamma_ratios,
+            grayscale_enhanced_contrast,
+        } = DirectXRenderer::get_font_info();
+
         for layer in glyph_layers {
             let params = GlyphLayerTextureParams {
                 run_color: layer.run_color,
                 bounds: layer.bounds,
+                gamma_ratios: *gamma_ratios,
+                grayscale_enhanced_contrast: *grayscale_enhanced_contrast,
+                _pad: [0f32; 3],
             };
             unsafe {
                 let mut dest = std::mem::zeroed();
@@ -1298,7 +1228,7 @@ impl GlyphLayerTexture {
             Height: texture_size.height as u32,
             MipLevels: 1,
             ArraySize: 1,
-            Format: DXGI_FORMAT_R8G8B8A8_UNORM,
+            Format: DXGI_FORMAT_R8_UNORM,
             SampleDesc: DXGI_SAMPLE_DESC {
                 Count: 1,
                 Quality: 0,
@@ -1334,7 +1264,7 @@ impl GlyphLayerTexture {
                 0,
                 None,
                 alpha_data.as_ptr() as _,
-                (texture_size.width * 4) as u32,
+                texture_size.width as u32,
                 0,
             )
         };
@@ -1352,6 +1282,9 @@ impl GlyphLayerTexture {
 struct GlyphLayerTextureParams {
     bounds: Bounds<i32>,
     run_color: Rgba,
+    gamma_ratios: [f32; 4],
+    grayscale_enhanced_contrast: f32,
+    _pad: [f32; 3],
 }
 
 struct TextRendererWrapper(pub IDWriteTextRenderer);

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

@@ -1,4 +1,7 @@
-use std::{mem::ManuallyDrop, sync::Arc};
+use std::{
+    mem::ManuallyDrop,
+    sync::{Arc, OnceLock},
+};
 
 use ::util::ResultExt;
 use anyhow::{Context, Result};
@@ -9,6 +12,7 @@ use windows::{
             Direct3D::*,
             Direct3D11::*,
             DirectComposition::*,
+            DirectWrite::*,
             Dxgi::{Common::*, *},
         },
     },
@@ -27,6 +31,11 @@ const RENDER_TARGET_FORMAT: DXGI_FORMAT = DXGI_FORMAT_B8G8R8A8_UNORM;
 // This configuration is used for MSAA rendering on paths only, and it's guaranteed to be supported by DirectX 11.
 const PATH_MULTISAMPLE_COUNT: u32 = 4;
 
+pub(crate) struct FontInfo {
+    pub gamma_ratios: [f32; 4],
+    pub grayscale_enhanced_contrast: f32,
+}
+
 pub(crate) struct DirectXRenderer {
     hwnd: HWND,
     atlas: Arc<DirectXAtlas>,
@@ -35,6 +44,7 @@ pub(crate) struct DirectXRenderer {
     globals: DirectXGlobalElements,
     pipelines: DirectXRenderPipelines,
     direct_composition: Option<DirectComposition>,
+    font_info: &'static FontInfo,
 }
 
 /// Direct3D objects
@@ -171,6 +181,7 @@ impl DirectXRenderer {
             globals,
             pipelines,
             direct_composition,
+            font_info: Self::get_font_info(),
         })
     }
 
@@ -183,10 +194,12 @@ impl DirectXRenderer {
             &self.devices.device_context,
             self.globals.global_params_buffer[0].as_ref().unwrap(),
             &[GlobalParams {
+                gamma_ratios: self.font_info.gamma_ratios,
                 viewport_size: [
                     self.resources.viewport[0].Width,
                     self.resources.viewport[0].Height,
                 ],
+                grayscale_enhanced_contrast: self.font_info.grayscale_enhanced_contrast,
                 _pad: 0,
             }],
         )?;
@@ -617,6 +630,52 @@ impl DirectXRenderer {
             driver_info: driver_version,
         })
     }
+
+    pub(crate) fn get_font_info() -> &'static FontInfo {
+        static CACHED_FONT_INFO: OnceLock<FontInfo> = OnceLock::new();
+        CACHED_FONT_INFO.get_or_init(|| unsafe {
+            let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED).unwrap();
+            let render_params: IDWriteRenderingParams1 =
+                factory.CreateRenderingParams().unwrap().cast().unwrap();
+            FontInfo {
+                gamma_ratios: Self::get_gamma_ratios(render_params.GetGamma()),
+                grayscale_enhanced_contrast: render_params.GetGrayscaleEnhancedContrast(),
+            }
+        })
+    }
+
+    // Gamma ratios for brightening/darkening edges for better contrast
+    // https://github.com/microsoft/terminal/blob/1283c0f5b99a2961673249fa77c6b986efb5086c/src/renderer/atlas/dwrite.cpp#L50
+    fn get_gamma_ratios(gamma: f32) -> [f32; 4] {
+        const GAMMA_INCORRECT_TARGET_RATIOS: [[f32; 4]; 13] = [
+            [0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0, 0.0000 / 4.0], // gamma = 1.0
+            [0.0166 / 4.0, -0.0807 / 4.0, 0.2227 / 4.0, -0.0751 / 4.0], // gamma = 1.1
+            [0.0350 / 4.0, -0.1760 / 4.0, 0.4325 / 4.0, -0.1370 / 4.0], // gamma = 1.2
+            [0.0543 / 4.0, -0.2821 / 4.0, 0.6302 / 4.0, -0.1876 / 4.0], // gamma = 1.3
+            [0.0739 / 4.0, -0.3963 / 4.0, 0.8167 / 4.0, -0.2287 / 4.0], // gamma = 1.4
+            [0.0933 / 4.0, -0.5161 / 4.0, 0.9926 / 4.0, -0.2616 / 4.0], // gamma = 1.5
+            [0.1121 / 4.0, -0.6395 / 4.0, 1.1588 / 4.0, -0.2877 / 4.0], // gamma = 1.6
+            [0.1300 / 4.0, -0.7649 / 4.0, 1.3159 / 4.0, -0.3080 / 4.0], // gamma = 1.7
+            [0.1469 / 4.0, -0.8911 / 4.0, 1.4644 / 4.0, -0.3234 / 4.0], // gamma = 1.8
+            [0.1627 / 4.0, -1.0170 / 4.0, 1.6051 / 4.0, -0.3347 / 4.0], // gamma = 1.9
+            [0.1773 / 4.0, -1.1420 / 4.0, 1.7385 / 4.0, -0.3426 / 4.0], // gamma = 2.0
+            [0.1908 / 4.0, -1.2652 / 4.0, 1.8650 / 4.0, -0.3476 / 4.0], // gamma = 2.1
+            [0.2031 / 4.0, -1.3864 / 4.0, 1.9851 / 4.0, -0.3501 / 4.0], // gamma = 2.2
+        ];
+
+        const NORM13: f32 = ((0x10000 as f64) / (255.0 * 255.0) * 4.0) as f32;
+        const NORM24: f32 = ((0x100 as f64) / (255.0) * 4.0) as f32;
+
+        let index = ((gamma * 10.0).round() as usize).clamp(10, 22) - 10;
+        let ratios = GAMMA_INCORRECT_TARGET_RATIOS[index];
+
+        [
+            ratios[0] * NORM13,
+            ratios[1] * NORM24,
+            ratios[2] * NORM13,
+            ratios[3] * NORM24,
+        ]
+    }
 }
 
 impl DirectXResources {
@@ -822,8 +881,10 @@ impl DirectXGlobalElements {
 #[derive(Debug, Default)]
 #[repr(C)]
 struct GlobalParams {
+    gamma_ratios: [f32; 4],
     viewport_size: [f32; 2],
-    _pad: u64,
+    grayscale_enhanced_contrast: f32,
+    _pad: u32,
 }
 
 struct PipelineState<T> {
@@ -1544,6 +1605,10 @@ pub(crate) mod shader_resources {
     #[cfg(debug_assertions)]
     pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> {
         unsafe {
+            use windows::Win32::Graphics::{
+                Direct3D::ID3DInclude, Hlsl::D3D_COMPILE_STANDARD_FILE_INCLUDE,
+            };
+
             let shader_name = if matches!(entry, ShaderModule::EmojiRasterization) {
                 "color_text_raster.hlsl"
             } else {
@@ -1572,10 +1637,15 @@ pub(crate) mod shader_resources {
             let entry_point = PCSTR::from_raw(entry.as_ptr());
             let target_cstr = PCSTR::from_raw(target.as_ptr());
 
+            // really dirty trick because winapi bindings are unhappy otherwise
+            let include_handler = &std::mem::transmute::<usize, ID3DInclude>(
+                D3D_COMPILE_STANDARD_FILE_INCLUDE as usize,
+            );
+
             let ret = D3DCompileFromFile(
                 &HSTRING::from(shader_path.to_str().unwrap()),
                 None,
-                None,
+                include_handler,
                 entry_point,
                 target_cstr,
                 D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION,

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

@@ -1,7 +1,6 @@
 use std::{
     cell::RefCell,
     ffi::OsStr,
-    mem::ManuallyDrop,
     path::{Path, PathBuf},
     rc::Rc,
     sync::Arc,
@@ -18,10 +17,7 @@ use windows::{
     UI::ViewManagement::UISettings,
     Win32::{
         Foundation::*,
-        Graphics::{
-            Gdi::*,
-            Imaging::{CLSID_WICImagingFactory, IWICImagingFactory},
-        },
+        Graphics::Gdi::*,
         Security::Credentials::*,
         System::{Com::*, LibraryLoader::*, Ole::*, SystemInformation::*, Threading::*},
         UI::{Input::KeyboardAndMouse::*, Shell::*, WindowsAndMessaging::*},
@@ -41,7 +37,6 @@ pub(crate) struct WindowsPlatform {
     foreground_executor: ForegroundExecutor,
     text_system: Arc<DirectWriteTextSystem>,
     windows_version: WindowsVersion,
-    bitmap_factory: ManuallyDrop<IWICImagingFactory>,
     drop_target_helper: IDropTargetHelper,
     validation_number: usize,
     main_thread_id_win32: u32,
@@ -101,12 +96,8 @@ impl WindowsPlatform {
         let foreground_executor = ForegroundExecutor::new(dispatcher);
         let directx_devices = DirectXDevices::new(disable_direct_composition)
             .context("Unable to init directx devices.")?;
-        let bitmap_factory = ManuallyDrop::new(unsafe {
-            CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
-                .context("Error creating bitmap factory.")?
-        });
         let text_system = Arc::new(
-            DirectWriteTextSystem::new(&directx_devices, &bitmap_factory)
+            DirectWriteTextSystem::new(&directx_devices)
                 .context("Error creating DirectWriteTextSystem")?,
         );
         let drop_target_helper: IDropTargetHelper = unsafe {
@@ -128,7 +119,6 @@ impl WindowsPlatform {
             text_system,
             disable_direct_composition,
             windows_version,
-            bitmap_factory,
             drop_target_helper,
             validation_number,
             main_thread_id_win32,
@@ -716,7 +706,6 @@ impl Platform for WindowsPlatform {
 impl Drop for WindowsPlatform {
     fn drop(&mut self) {
         unsafe {
-            ManuallyDrop::drop(&mut self.bitmap_factory);
             OleUninitialize();
         }
     }

crates/gpui/src/platform/windows/shaders.hlsl 🔗

@@ -1,6 +1,10 @@
+#include "alpha_correction.hlsl"
+
 cbuffer GlobalParams: register(b0) {
+    float4 gamma_ratios;
     float2 global_viewport_size;
-    uint2 _pad;
+    float grayscale_enhanced_contrast;
+    uint _pad;
 };
 
 Texture2D<float4> t_sprite: register(t0);
@@ -1098,7 +1102,8 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI
 
 float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target {
     float sample = t_sprite.Sample(s_sprite, input.tile_position).r;
-    return float4(input.color.rgb, input.color.a * sample);
+    float alpha_corrected = apply_contrast_and_gamma_correction(sample, input.color.rgb, grayscale_enhanced_contrast, gamma_ratios);
+    return float4(input.color.rgb, input.color.a * alpha_corrected);
 }
 
 /*