mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Implement perceptual gamma / contrast correction (#37167)
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
This commit is contained in:
parent
61175ab9cd
commit
d910feac1d
7 changed files with 157 additions and 127 deletions
|
|
@ -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",
|
||||
|
|
|
|||
28
crates/gpui/src/platform/windows/alpha_correction.hlsl
Normal file
28
crates/gpui/src/platform/windows/alpha_correction.hlsl
Normal file
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue