Rasterize glyphs without D2D (#35376)

This allows debugging Zed with Renderdoc, and also fixes an issue where
glyphs' bounds were miscalculated for certain sizes and scale factors.

Release Notes:

- N/A

---------

Co-authored-by: Kate <kate@zed.dev>
Co-authored-by: Julia <julia@zed.dev>
Co-authored-by: Junkui Zhang <364772080@qq.com>
This commit is contained in:
Max Brunsfeld 2025-08-01 10:46:09 -07:00 committed by GitHub
parent f888f3fc0b
commit b31f893408
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 698 additions and 287 deletions

View file

@ -678,8 +678,6 @@ features = [
"UI_ViewManagement", "UI_ViewManagement",
"Wdk_System_SystemServices", "Wdk_System_SystemServices",
"Win32_Globalization", "Win32_Globalization",
"Win32_Graphics_Direct2D",
"Win32_Graphics_Direct2D_Common",
"Win32_Graphics_Direct3D", "Win32_Graphics_Direct3D",
"Win32_Graphics_Direct3D11", "Win32_Graphics_Direct3D11",
"Win32_Graphics_Direct3D_Fxc", "Win32_Graphics_Direct3D_Fxc",
@ -690,7 +688,6 @@ features = [
"Win32_Graphics_Dxgi_Common", "Win32_Graphics_Dxgi_Common",
"Win32_Graphics_Gdi", "Win32_Graphics_Gdi",
"Win32_Graphics_Imaging", "Win32_Graphics_Imaging",
"Win32_Graphics_Imaging_D2D",
"Win32_Networking_WinSock", "Win32_Networking_WinSock",
"Win32_Security", "Win32_Security",
"Win32_Security_Credentials", "Win32_Security_Credentials",

View file

@ -310,6 +310,18 @@ mod windows {
&rust_binding_path, &rust_binding_path,
); );
} }
{
let shader_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap())
.join("src/platform/windows/color_text_raster.hlsl");
compile_shader_for_module(
"emoji_rasterization",
&out_dir,
&fxc_path,
shader_path.to_str().unwrap(),
&rust_binding_path,
);
}
} }
/// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler. /// You can set the `GPUI_FXC_PATH` environment variable to specify the path to the fxc.exe compiler.

View file

@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid {
"χ", "ψ", "", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р", "χ", "ψ", "", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",
"У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*", "У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",
"_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "", "µ", "_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "", "µ",
"", "<=", "!=", "==", "--", "++", "=>", "->", "", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎",
]; ];
let columns = 11; let columns = 11;

View file

@ -35,6 +35,7 @@ pub(crate) fn swap_rgba_pa_to_bgra(color: &mut [u8]) {
/// An RGBA color /// An RGBA color
#[derive(PartialEq, Clone, Copy, Default)] #[derive(PartialEq, Clone, Copy, Default)]
#[repr(C)]
pub struct Rgba { pub struct Rgba {
/// The red component of the color, in the range 0.0 to 1.0 /// The red component of the color, in the range 0.0 to 1.0
pub r: f32, pub r: f32,

View file

@ -0,0 +1,39 @@
struct RasterVertexOutput {
float4 position : SV_Position;
float2 texcoord : TEXCOORD0;
};
RasterVertexOutput emoji_rasterization_vertex(uint vertexID : SV_VERTEXID)
{
RasterVertexOutput output;
output.texcoord = float2((vertexID << 1) & 2, vertexID & 2);
output.position = float4(output.texcoord * 2.0f - 1.0f, 0.0f, 1.0f);
output.position.y = -output.position.y;
return output;
}
struct PixelInput {
float4 position: SV_Position;
float2 texcoord : TEXCOORD0;
};
struct Bounds {
int2 origin;
int2 size;
};
Texture2D<float4> t_layer : register(t0);
SamplerState s_layer : register(s0);
cbuffer GlyphLayerTextureParams : register(b0) {
Bounds bounds;
float4 run_color;
};
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);
}

View file

@ -10,10 +10,11 @@ use windows::{
Foundation::*, Foundation::*,
Globalization::GetUserDefaultLocaleName, Globalization::GetUserDefaultLocaleName,
Graphics::{ Graphics::{
Direct2D::{Common::*, *}, Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP,
Direct3D11::*,
DirectWrite::*, DirectWrite::*,
Dxgi::Common::*, Dxgi::Common::*,
Gdi::LOGFONTW, Gdi::{IsRectEmpty, LOGFONTW},
Imaging::*, Imaging::*,
}, },
System::SystemServices::LOCALE_NAME_MAX_LENGTH, System::SystemServices::LOCALE_NAME_MAX_LENGTH,
@ -40,16 +41,21 @@ struct DirectWriteComponent {
locale: String, locale: String,
factory: IDWriteFactory5, factory: IDWriteFactory5,
bitmap_factory: AgileReference<IWICImagingFactory>, bitmap_factory: AgileReference<IWICImagingFactory>,
d2d1_factory: ID2D1Factory,
in_memory_loader: IDWriteInMemoryFontFileLoader, in_memory_loader: IDWriteInMemoryFontFileLoader,
builder: IDWriteFontSetBuilder1, builder: IDWriteFontSetBuilder1,
text_renderer: Arc<TextRendererWrapper>, text_renderer: Arc<TextRendererWrapper>,
render_context: GlyphRenderContext,
render_params: IDWriteRenderingParams3,
gpu_state: GPUState,
} }
struct GlyphRenderContext { struct GPUState {
params: IDWriteRenderingParams3, device: ID3D11Device,
dc_target: ID2D1DeviceContext4, device_context: ID3D11DeviceContext,
sampler: [Option<ID3D11SamplerState>; 1],
blend_state: ID3D11BlendState,
vertex_shader: ID3D11VertexShader,
pixel_shader: ID3D11PixelShader,
} }
struct DirectWriteState { struct DirectWriteState {
@ -70,12 +76,11 @@ struct FontIdentifier {
} }
impl DirectWriteComponent { impl DirectWriteComponent {
pub fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> { pub fn new(bitmap_factory: &IWICImagingFactory, gpu_context: &DirectXDevices) -> Result<Self> {
// todo: ideally this would not be a large unsafe block but smaller isolated ones for easier auditing
unsafe { unsafe {
let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?; let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
let bitmap_factory = AgileReference::new(bitmap_factory)?; let bitmap_factory = AgileReference::new(bitmap_factory)?;
let d2d1_factory: ID2D1Factory =
D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, None)?;
// The `IDWriteInMemoryFontFileLoader` here is supported starting from // The `IDWriteInMemoryFontFileLoader` here is supported starting from
// Windows 10 Creators Update, which consequently requires the entire // Windows 10 Creators Update, which consequently requires the entire
// `DirectWriteTextSystem` to run on `win10 1703`+. // `DirectWriteTextSystem` to run on `win10 1703`+.
@ -86,60 +91,132 @@ impl DirectWriteComponent {
GetUserDefaultLocaleName(&mut locale_vec); GetUserDefaultLocaleName(&mut locale_vec);
let locale = String::from_utf16_lossy(&locale_vec); let locale = String::from_utf16_lossy(&locale_vec);
let text_renderer = Arc::new(TextRendererWrapper::new(&locale)); let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
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 { Ok(DirectWriteComponent {
locale, locale,
factory, factory,
bitmap_factory, bitmap_factory,
d2d1_factory,
in_memory_loader, in_memory_loader,
builder, builder,
text_renderer, text_renderer,
render_context, render_params,
gpu_state,
}) })
} }
} }
} }
impl GlyphRenderContext { impl GPUState {
pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> { fn new(gpu_context: &DirectXDevices) -> Result<Self> {
unsafe { let device = gpu_context.device.clone();
let default_params: IDWriteRenderingParams3 = let device_context = gpu_context.device_context.clone();
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( let blend_state = {
gamma, let mut blend_state = None;
enhanced_contrast, let desc = D3D11_BLEND_DESC {
gray_contrast, AlphaToCoverageEnable: false.into(),
cleartype_level, IndependentBlendEnable: false.into(),
DWRITE_PIXEL_GEOMETRY_RGB, RenderTarget: [
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC, D3D11_RENDER_TARGET_BLEND_DESC {
grid_fit_mode, BlendEnable: true.into(),
)?; SrcBlend: D3D11_BLEND_SRC_ALPHA,
let dc_target = { DestBlend: D3D11_BLEND_INV_SRC_ALPHA,
let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property( BlendOp: D3D11_BLEND_OP_ADD,
DXGI_FORMAT_B8G8R8A8_UNORM, SrcBlendAlpha: D3D11_BLEND_SRC_ALPHA,
D2D1_ALPHA_MODE_PREMULTIPLIED, DestBlendAlpha: D3D11_BLEND_INV_SRC_ALPHA,
))?; BlendOpAlpha: D3D11_BLEND_OP_ADD,
let target = target.cast::<ID2D1DeviceContext4>()?; RenderTargetWriteMask: D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8,
target.SetTextRenderingParams(&params); },
target Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
Default::default(),
],
}; };
unsafe { device.CreateBlendState(&desc, Some(&mut blend_state)) }?;
blend_state.unwrap()
};
Ok(Self { params, dc_target }) let sampler = {
} let mut sampler = None;
let desc = D3D11_SAMPLER_DESC {
Filter: D3D11_FILTER_MIN_MAG_MIP_POINT,
AddressU: D3D11_TEXTURE_ADDRESS_BORDER,
AddressV: D3D11_TEXTURE_ADDRESS_BORDER,
AddressW: D3D11_TEXTURE_ADDRESS_BORDER,
MipLODBias: 0.0,
MaxAnisotropy: 1,
ComparisonFunc: D3D11_COMPARISON_ALWAYS,
BorderColor: [0.0, 0.0, 0.0, 0.0],
MinLOD: 0.0,
MaxLOD: 0.0,
};
unsafe { device.CreateSamplerState(&desc, Some(&mut sampler)) }?;
[sampler]
};
let vertex_shader = {
let source = shader_resources::RawShaderBytes::new(
shader_resources::ShaderModule::EmojiRasterization,
shader_resources::ShaderTarget::Vertex,
)?;
let mut shader = None;
unsafe { device.CreateVertexShader(source.as_bytes(), None, Some(&mut shader)) }?;
shader.unwrap()
};
let pixel_shader = {
let source = shader_resources::RawShaderBytes::new(
shader_resources::ShaderModule::EmojiRasterization,
shader_resources::ShaderTarget::Fragment,
)?;
let mut shader = None;
unsafe { device.CreatePixelShader(source.as_bytes(), None, Some(&mut shader)) }?;
shader.unwrap()
};
Ok(Self {
device,
device_context,
sampler,
blend_state,
vertex_shader,
pixel_shader,
})
} }
} }
impl DirectWriteTextSystem { impl DirectWriteTextSystem {
pub(crate) fn new(bitmap_factory: &IWICImagingFactory) -> Result<Self> { pub(crate) fn new(
let components = DirectWriteComponent::new(bitmap_factory)?; gpu_context: &DirectXDevices,
bitmap_factory: &IWICImagingFactory,
) -> Result<Self> {
let components = DirectWriteComponent::new(bitmap_factory, gpu_context)?;
let system_font_collection = unsafe { let system_font_collection = unsafe {
let mut result = std::mem::zeroed(); let mut result = std::mem::zeroed();
components components
@ -648,15 +725,13 @@ impl DirectWriteState {
} }
} }
fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> { fn create_glyph_run_analysis(
let render_target = &self.components.render_context.dc_target; &self,
unsafe { params: &RenderGlyphParams,
render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); ) -> Result<IDWriteGlyphRunAnalysis> {
render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
}
let font = &self.fonts[params.font_id.0]; let font = &self.fonts[params.font_id.0];
let glyph_id = [params.glyph_id.0 as u16]; let glyph_id = [params.glyph_id.0 as u16];
let advance = [0.0f32]; let advance = [0.0];
let offset = [DWRITE_GLYPH_OFFSET::default()]; let offset = [DWRITE_GLYPH_OFFSET::default()];
let glyph_run = DWRITE_GLYPH_RUN { let glyph_run = DWRITE_GLYPH_RUN {
fontFace: unsafe { std::mem::transmute_copy(&font.font_face) }, fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
@ -668,44 +743,87 @@ impl DirectWriteState {
isSideways: BOOL(0), isSideways: BOOL(0),
bidiLevel: 0, bidiLevel: 0,
}; };
let bounds = unsafe { let transform = DWRITE_MATRIX {
render_target.GetGlyphRunWorldBounds( m11: params.scale_factor,
Vector2 { X: 0.0, Y: 0.0 }, m12: 0.0,
&glyph_run, m21: 0.0,
DWRITE_MEASURING_MODE_NATURAL, m22: params.scale_factor,
)? dx: 0.0,
dy: 0.0,
}; };
// todo(windows) let subpixel_shift = params
// This is a walkaround, deleted when figured out. .subpixel_variant
let y_offset; .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
let extra_height; let baseline_origin_x = subpixel_shift.x / params.scale_factor;
if params.is_emoji { let baseline_origin_y = subpixel_shift.y / params.scale_factor;
y_offset = 0;
extra_height = 0; let mut rendering_mode = DWRITE_RENDERING_MODE1::default();
} else { let mut grid_fit_mode = DWRITE_GRID_FIT_MODE::default();
// make some room for scaler. unsafe {
y_offset = -1; font.font_face.GetRecommendedRenderingMode(
extra_height = 2; params.font_size.0,
// The dpi here seems that it has the same effect with `Some(&transform)`
1.0,
1.0,
Some(&transform),
false,
DWRITE_OUTLINE_THRESHOLD_ANTIALIASED,
DWRITE_MEASURING_MODE_NATURAL,
&self.components.render_params,
&mut rendering_mode,
&mut grid_fit_mode,
)?;
} }
if bounds.right < bounds.left { let glyph_analysis = unsafe {
self.components.factory.CreateGlyphRunAnalysis(
&glyph_run,
Some(&transform),
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,
baseline_origin_x,
baseline_origin_y,
)
}?;
Ok(glyph_analysis)
}
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() {
Ok(Bounds { Ok(Bounds {
origin: point(0.into(), 0.into()), origin: point(bounds.left.into(), bounds.top.into()),
size: size(0.into(), 0.into()), size: size(
(bounds.right - bounds.left).into(),
(bounds.bottom - bounds.top).into(),
),
}) })
} else { } else {
Ok(Bounds { // If it's empty, retry with grayscale AA.
origin: point( let bounds =
((bounds.left * params.scale_factor).ceil() as i32).into(), unsafe { glyph_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_ALIASED_1x1)? };
((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(),
), if bounds.right < bounds.left {
size: size( Ok(Bounds {
(((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(), origin: point(0.into(), 0.into()),
(((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32 size: size(0.into(), 0.into()),
+ extra_height) })
.into(), } else {
), Ok(Bounds {
}) origin: point(bounds.left.into(), bounds.top.into()),
size: size(
(bounds.right - bounds.left).into(),
(bounds.bottom - bounds.top).into(),
),
})
}
} }
} }
@ -731,7 +849,95 @@ impl DirectWriteState {
anyhow::bail!("glyph bounds are empty"); anyhow::bail!("glyph bounds are empty");
} }
let font_info = &self.fonts[params.font_id.0]; let bitmap_data = if params.is_emoji {
if let Ok(color) = self.rasterize_color(&params, glyph_bounds) {
color
} else {
let monochrome = self.rasterize_monochrome(params, glyph_bounds)?;
monochrome
.into_iter()
.flat_map(|pixel| [0, 0, 0, pixel])
.collect::<Vec<_>>()
}
} else {
self.rasterize_monochrome(params, glyph_bounds)?
};
Ok((glyph_bounds.size, bitmap_data))
}
fn rasterize_monochrome(
&self,
params: &RenderGlyphParams,
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];
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,
&RECT {
left: glyph_bounds.origin.x.0,
top: glyph_bounds.origin.y.0,
right: glyph_bounds.size.width.0 + glyph_bounds.origin.x.0,
bottom: glyph_bounds.size.height.0 + glyph_bounds.origin.y.0,
},
&mut bitmap_data,
)?;
}
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)
}
fn rasterize_color(
&self,
params: &RenderGlyphParams,
glyph_bounds: Bounds<DevicePixels>,
) -> Result<Vec<u8>> {
let bitmap_size = glyph_bounds.size;
let subpixel_shift = params
.subpixel_variant
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
let baseline_origin_x = subpixel_shift.x / params.scale_factor;
let baseline_origin_y = subpixel_shift.y / params.scale_factor;
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 font = &self.fonts[params.font_id.0];
let glyph_id = [params.glyph_id.0 as u16]; let glyph_id = [params.glyph_id.0 as u16];
let advance = [glyph_bounds.size.width.0 as f32]; let advance = [glyph_bounds.size.width.0 as f32];
let offset = [DWRITE_GLYPH_OFFSET { let offset = [DWRITE_GLYPH_OFFSET {
@ -739,7 +945,7 @@ impl DirectWriteState {
ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor, ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
}]; }];
let glyph_run = DWRITE_GLYPH_RUN { let glyph_run = DWRITE_GLYPH_RUN {
fontFace: unsafe { std::mem::transmute_copy(&font_info.font_face) }, fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
fontEmSize: params.font_size.0, fontEmSize: params.font_size.0,
glyphCount: 1, glyphCount: 1,
glyphIndices: glyph_id.as_ptr(), glyphIndices: glyph_id.as_ptr(),
@ -749,160 +955,254 @@ impl DirectWriteState {
bidiLevel: 0, bidiLevel: 0,
}; };
// Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing. // todo: support formats other than COLR
let mut bitmap_size = glyph_bounds.size; let color_enumerator = unsafe {
if params.subpixel_variant.x > 0 { self.components.factory.TranslateColorGlyphRun(
bitmap_size.width += DevicePixels(1); Vector2::new(baseline_origin_x, baseline_origin_y),
} &glyph_run,
if params.subpixel_variant.y > 0 { None,
bitmap_size.height += DevicePixels(1); DWRITE_GLYPH_IMAGE_FORMATS_COLR,
} DWRITE_MEASURING_MODE_NATURAL,
let bitmap_size = bitmap_size; Some(&transform),
0,
)
}?;
let total_bytes; let mut glyph_layers = Vec::new();
let bitmap_format; loop {
let render_target_property; let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
let bitmap_width; let color_run = unsafe { &*color_run };
let bitmap_height; let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
let bitmap_stride; if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR {
let bitmap_dpi; let color_analysis = unsafe {
if params.is_emoji { self.components.factory.CreateGlyphRunAnalysis(
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4; &color_run.Base.glyphRun as *const _,
bitmap_format = &GUID_WICPixelFormat32bppPBGRA; Some(&transform),
render_target_property = get_render_target_property( DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
DXGI_FORMAT_B8G8R8A8_UNORM, DWRITE_MEASURING_MODE_NATURAL,
D2D1_ALPHA_MODE_PREMULTIPLIED, DWRITE_GRID_FIT_MODE_DEFAULT,
); DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
bitmap_width = bitmap_size.width.0 as u32; baseline_origin_x,
bitmap_height = bitmap_size.height.0 as u32; baseline_origin_y,
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; let color_bounds =
bitmap_format = &GUID_WICPixelFormat8bppAlpha; unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
render_target_property =
get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT); let color_size = size(
bitmap_width = bitmap_size.width.0 as u32 * 2; color_bounds.right - color_bounds.left,
bitmap_height = bitmap_size.height.0 as u32 * 2; color_bounds.bottom - color_bounds.top,
bitmap_stride = bitmap_size.width.0 as u32; );
bitmap_dpi = 192.0; if color_size.width > 0 && color_size.height > 0 {
let mut alpha_data =
vec![0u8; (color_size.width * color_size.height * 3) as usize];
unsafe {
color_analysis.CreateAlphaTexture(
DWRITE_TEXTURE_CLEARTYPE_3x1,
&color_bounds,
&mut alpha_data,
)
}?;
let run_color = {
let run_color = color_run.Base.runColor;
Rgba {
r: run_color.r,
g: run_color.g,
b: run_color.b,
a: run_color.a,
}
};
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,
bounds,
&alpha_data,
)?);
}
}
let has_next = unsafe { color_enumerator.MoveNext() }
.map(|e| e.as_bool())
.unwrap_or(false);
if !has_next {
break;
}
} }
let bitmap_factory = self.components.bitmap_factory.resolve()?; let gpu_state = &self.components.gpu_state;
unsafe { let params_buffer = {
let bitmap = bitmap_factory.CreateBitmap( let desc = D3D11_BUFFER_DESC {
bitmap_width, ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
bitmap_height, Usage: D3D11_USAGE_DYNAMIC,
bitmap_format, BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
WICBitmapCacheOnLoad, CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
)?; MiscFlags: 0,
let render_target = self StructureByteStride: 0,
.components
.d2d1_factory
.CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
let subpixel_shift = params
.subpixel_variant
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
let baseline_origin = Vector2 {
X: subpixel_shift.x / params.scale_factor,
Y: subpixel_shift.y / params.scale_factor,
}; };
// This `cast()` action here should never fail since we are running on Win10+, and let mut buffer = None;
// ID2D1DeviceContext4 requires Win8+ unsafe {
let render_target = render_target.cast::<ID2D1DeviceContext4>().unwrap(); gpu_state
render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS); .device
render_target.SetDpi( .CreateBuffer(&desc, None, Some(&mut buffer))
bitmap_dpi * params.scale_factor, }?;
bitmap_dpi * params.scale_factor, [buffer]
); };
render_target.SetTextRenderingParams(&self.components.render_context.params);
render_target.BeginDraw();
if params.is_emoji { let render_target_texture = {
// WARN: only DWRITE_GLYPH_IMAGE_FORMATS_COLR has been tested let mut texture = None;
let enumerator = self.components.factory.TranslateColorGlyphRun( let desc = D3D11_TEXTURE2D_DESC {
baseline_origin, Width: bitmap_size.width.0 as u32,
&glyph_run as _, Height: bitmap_size.height.0 as u32,
None, MipLevels: 1,
DWRITE_GLYPH_IMAGE_FORMATS_COLR ArraySize: 1,
| DWRITE_GLYPH_IMAGE_FORMATS_SVG Format: DXGI_FORMAT_B8G8R8A8_UNORM,
| DWRITE_GLYPH_IMAGE_FORMATS_PNG SampleDesc: DXGI_SAMPLE_DESC {
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG Count: 1,
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8, Quality: 0,
DWRITE_MEASURING_MODE_NATURAL, },
None, Usage: D3D11_USAGE_DEFAULT,
BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32,
CPUAccessFlags: 0,
MiscFlags: 0,
};
unsafe {
gpu_state
.device
.CreateTexture2D(&desc, None, Some(&mut texture))
}?;
texture.unwrap()
};
let render_target_view = {
let desc = D3D11_RENDER_TARGET_VIEW_DESC {
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
ViewDimension: D3D11_RTV_DIMENSION_TEXTURE2D,
Anonymous: D3D11_RENDER_TARGET_VIEW_DESC_0 {
Texture2D: D3D11_TEX2D_RTV { MipSlice: 0 },
},
};
let mut rtv = None;
unsafe {
gpu_state.device.CreateRenderTargetView(
&render_target_texture,
Some(&desc),
Some(&mut rtv),
)
}?;
[rtv]
};
let staging_texture = {
let mut texture = None;
let desc = D3D11_TEXTURE2D_DESC {
Width: bitmap_size.width.0 as u32,
Height: bitmap_size.height.0 as u32,
MipLevels: 1,
ArraySize: 1,
Format: DXGI_FORMAT_B8G8R8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Usage: D3D11_USAGE_STAGING,
BindFlags: 0,
CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
MiscFlags: 0,
};
unsafe {
gpu_state
.device
.CreateTexture2D(&desc, None, Some(&mut texture))
}?;
texture.unwrap()
};
let device_context = &gpu_state.device_context;
unsafe { device_context.IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP) };
unsafe { device_context.VSSetShader(&gpu_state.vertex_shader, None) };
unsafe { device_context.PSSetShader(&gpu_state.pixel_shader, None) };
unsafe { device_context.VSSetConstantBuffers(0, Some(&params_buffer)) };
unsafe { device_context.PSSetConstantBuffers(0, Some(&params_buffer)) };
unsafe { device_context.OMSetRenderTargets(Some(&render_target_view), None) };
unsafe { device_context.PSSetSamplers(0, Some(&gpu_state.sampler)) };
unsafe { device_context.OMSetBlendState(&gpu_state.blend_state, None, 0xffffffff) };
for layer in glyph_layers {
let params = GlyphLayerTextureParams {
run_color: layer.run_color,
bounds: layer.bounds,
};
unsafe {
let mut dest = std::mem::zeroed();
gpu_state.device_context.Map(
params_buffer[0].as_ref().unwrap(),
0, 0,
D3D11_MAP_WRITE_DISCARD,
0,
Some(&mut dest),
)?; )?;
while enumerator.MoveNext().is_ok() { std::ptr::copy_nonoverlapping(&params as *const _, dest.pData as *mut _, 1);
let Ok(color_glyph) = enumerator.GetCurrentRun() else { gpu_state
break; .device_context
}; .Unmap(params_buffer[0].as_ref().unwrap(), 0);
let color_glyph = &*color_glyph; };
let brush_color = translate_color(&color_glyph.Base.runColor);
brush.SetColor(&brush_color);
match color_glyph.glyphImageFormat {
DWRITE_GLYPH_IMAGE_FORMATS_PNG
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target
.DrawColorBitmapGlyphRun(
color_glyph.glyphImageFormat,
baseline_origin,
&color_glyph.Base.glyphRun,
color_glyph.measuringMode,
D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT,
),
DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun(
baseline_origin,
&color_glyph.Base.glyphRun,
&brush,
None,
color_glyph.Base.paletteIndex as u32,
color_glyph.measuringMode,
),
_ => render_target.DrawGlyphRun(
baseline_origin,
&color_glyph.Base.glyphRun,
Some(color_glyph.Base.glyphRunDescription as *const _),
&brush,
color_glyph.measuringMode,
),
}
}
} else {
render_target.DrawGlyphRun(
baseline_origin,
&glyph_run,
None,
&brush,
DWRITE_MEASURING_MODE_NATURAL,
);
}
render_target.EndDraw(None, None)?;
let mut raw_data = vec![0u8; total_bytes]; let texture = [Some(layer.texture_view)];
if params.is_emoji { unsafe { device_context.PSSetShaderResources(0, Some(&texture)) };
bitmap.CopyPixels(std::ptr::null() as _, bitmap_stride, &mut raw_data)?;
// Convert from BGRA with premultiplied alpha to BGRA with straight alpha. let viewport = [D3D11_VIEWPORT {
for pixel in raw_data.chunks_exact_mut(4) { TopLeftX: layer.bounds.origin.x as f32,
let a = pixel[3] as f32 / 255.; TopLeftY: layer.bounds.origin.y as f32,
pixel[0] = (pixel[0] as f32 / a) as u8; Width: layer.bounds.size.width as f32,
pixel[1] = (pixel[1] as f32 / a) as u8; Height: layer.bounds.size.height as f32,
pixel[2] = (pixel[2] as f32 / a) as u8; MinDepth: 0.0,
} MaxDepth: 1.0,
} else { }];
let scaler = bitmap_factory.CreateBitmapScaler()?; unsafe { device_context.RSSetViewports(Some(&viewport)) };
scaler.Initialize(
&bitmap, unsafe { device_context.Draw(4, 0) };
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))
} }
unsafe { device_context.CopyResource(&staging_texture, &render_target_texture) };
let mapped_data = {
let mut mapped_data = D3D11_MAPPED_SUBRESOURCE::default();
unsafe {
device_context.Map(
&staging_texture,
0,
D3D11_MAP_READ,
0,
Some(&mut mapped_data),
)
}?;
mapped_data
};
let mut rasterized =
vec![0u8; (bitmap_size.width.0 as u32 * bitmap_size.height.0 as u32 * 4) as usize];
for y in 0..bitmap_size.height.0 as usize {
let width = bitmap_size.width.0 as usize;
unsafe {
std::ptr::copy_nonoverlapping::<u8>(
(mapped_data.pData as *const u8).byte_add(mapped_data.RowPitch as usize * y),
rasterized
.as_mut_ptr()
.byte_add(width * y * std::mem::size_of::<u32>()),
width * std::mem::size_of::<u32>(),
)
};
}
Ok(rasterized)
} }
fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> { fn get_typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result<Bounds<f32>> {
@ -976,6 +1276,84 @@ impl Drop for DirectWriteState {
} }
} }
struct GlyphLayerTexture {
run_color: Rgba,
bounds: Bounds<i32>,
texture_view: ID3D11ShaderResourceView,
// holding on to the texture to not RAII drop it
_texture: ID3D11Texture2D,
}
impl GlyphLayerTexture {
pub fn new(
gpu_state: &GPUState,
run_color: Rgba,
bounds: Bounds<i32>,
alpha_data: &[u8],
) -> Result<Self> {
let texture_size = bounds.size;
let desc = D3D11_TEXTURE2D_DESC {
Width: texture_size.width as u32,
Height: texture_size.height as u32,
MipLevels: 1,
ArraySize: 1,
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Usage: D3D11_USAGE_DEFAULT,
BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32,
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
MiscFlags: 0,
};
let texture = {
let mut texture: Option<ID3D11Texture2D> = None;
unsafe {
gpu_state
.device
.CreateTexture2D(&desc, None, Some(&mut texture))?
};
texture.unwrap()
};
let texture_view = {
let mut view: Option<ID3D11ShaderResourceView> = None;
unsafe {
gpu_state
.device
.CreateShaderResourceView(&texture, None, Some(&mut view))?
};
view.unwrap()
};
unsafe {
gpu_state.device_context.UpdateSubresource(
&texture,
0,
None,
alpha_data.as_ptr() as _,
(texture_size.width * 4) as u32,
0,
)
};
Ok(GlyphLayerTexture {
run_color,
bounds,
texture_view,
_texture: texture,
})
}
}
#[repr(C)]
struct GlyphLayerTextureParams {
bounds: Bounds<i32>,
run_color: Rgba,
}
struct TextRendererWrapper(pub IDWriteTextRenderer); struct TextRendererWrapper(pub IDWriteTextRenderer);
impl TextRendererWrapper { impl TextRendererWrapper {
@ -1470,16 +1848,6 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
Ok(String::from_utf16_lossy(&name_vec[..name_length])) Ok(String::from_utf16_lossy(&name_vec[..name_length]))
} }
#[inline]
fn translate_color(color: &DWRITE_COLOR_F) -> D2D1_COLOR_F {
D2D1_COLOR_F {
r: color.r,
g: color.g,
b: color.b,
a: color.a,
}
}
fn get_system_ui_font_name() -> SharedString { fn get_system_ui_font_name() -> SharedString {
unsafe { unsafe {
let mut info: LOGFONTW = std::mem::zeroed(); let mut info: LOGFONTW = std::mem::zeroed();
@ -1504,24 +1872,6 @@ 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,
}
}
// One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats // One would think that with newer DirectWrite method: IDWriteFontFace4::GetGlyphImageFormats
// but that doesn't seem to work for some glyphs, say ❤ // but that doesn't seem to work for some glyphs, say ❤
fn is_color_glyph( fn is_color_glyph(
@ -1561,12 +1911,6 @@ fn is_color_glyph(
} }
const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US"); const DEFAULT_LOCALE_NAME: PCWSTR = windows::core::w!("en-US");
const BRUSH_COLOR: D2D1_COLOR_F = D2D1_COLOR_F {
r: 1.0,
g: 1.0,
b: 1.0,
a: 1.0,
};
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {

View file

@ -7,7 +7,7 @@ use windows::Win32::Graphics::{
D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView, D3D11_USAGE_DEFAULT, ID3D11Device, ID3D11DeviceContext, ID3D11ShaderResourceView,
ID3D11Texture2D, ID3D11Texture2D,
}, },
Dxgi::Common::{DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}, Dxgi::Common::*,
}; };
use crate::{ use crate::{
@ -167,7 +167,7 @@ impl DirectXAtlasState {
let bytes_per_pixel; let bytes_per_pixel;
match kind { match kind {
AtlasTextureKind::Monochrome => { AtlasTextureKind::Monochrome => {
pixel_format = DXGI_FORMAT_A8_UNORM; pixel_format = DXGI_FORMAT_R8_UNORM;
bind_flag = D3D11_BIND_SHADER_RESOURCE; bind_flag = D3D11_BIND_SHADER_RESOURCE;
bytes_per_pixel = 1; bytes_per_pixel = 1;
} }

View file

@ -42,8 +42,8 @@ pub(crate) struct DirectXRenderer {
pub(crate) struct DirectXDevices { pub(crate) struct DirectXDevices {
adapter: IDXGIAdapter1, adapter: IDXGIAdapter1,
dxgi_factory: IDXGIFactory6, dxgi_factory: IDXGIFactory6,
device: ID3D11Device, pub(crate) device: ID3D11Device,
device_context: ID3D11DeviceContext, pub(crate) device_context: ID3D11DeviceContext,
dxgi_device: Option<IDXGIDevice>, dxgi_device: Option<IDXGIDevice>,
} }
@ -187,7 +187,7 @@ impl DirectXRenderer {
self.resources.viewport[0].Width, self.resources.viewport[0].Width,
self.resources.viewport[0].Height, self.resources.viewport[0].Height,
], ],
..Default::default() _pad: 0,
}], }],
)?; )?;
unsafe { unsafe {
@ -1441,7 +1441,7 @@ fn report_live_objects(device: &ID3D11Device) -> Result<()> {
const BUFFER_COUNT: usize = 3; const BUFFER_COUNT: usize = 3;
mod shader_resources { pub(crate) mod shader_resources {
use anyhow::Result; use anyhow::Result;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -1454,7 +1454,7 @@ mod shader_resources {
}; };
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(super) enum ShaderModule { pub(crate) enum ShaderModule {
Quad, Quad,
Shadow, Shadow,
Underline, Underline,
@ -1462,15 +1462,16 @@ mod shader_resources {
PathSprite, PathSprite,
MonochromeSprite, MonochromeSprite,
PolychromeSprite, PolychromeSprite,
EmojiRasterization,
} }
#[derive(Copy, Clone, Debug, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(super) enum ShaderTarget { pub(crate) enum ShaderTarget {
Vertex, Vertex,
Fragment, Fragment,
} }
pub(super) struct RawShaderBytes<'t> { pub(crate) struct RawShaderBytes<'t> {
inner: &'t [u8], inner: &'t [u8],
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@ -1478,7 +1479,7 @@ mod shader_resources {
} }
impl<'t> RawShaderBytes<'t> { impl<'t> RawShaderBytes<'t> {
pub(super) fn new(module: ShaderModule, target: ShaderTarget) -> Result<Self> { pub(crate) fn new(module: ShaderModule, target: ShaderTarget) -> Result<Self> {
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
{ {
Ok(Self::from_bytes(module, target)) Ok(Self::from_bytes(module, target))
@ -1496,7 +1497,7 @@ mod shader_resources {
} }
} }
pub(super) fn as_bytes(&'t self) -> &'t [u8] { pub(crate) fn as_bytes(&'t self) -> &'t [u8] {
self.inner self.inner
} }
@ -1531,6 +1532,10 @@ mod shader_resources {
ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES, ShaderTarget::Vertex => POLYCHROME_SPRITE_VERTEX_BYTES,
ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES, ShaderTarget::Fragment => POLYCHROME_SPRITE_FRAGMENT_BYTES,
}, },
ShaderModule::EmojiRasterization => match target {
ShaderTarget::Vertex => EMOJI_RASTERIZATION_VERTEX_BYTES,
ShaderTarget::Fragment => EMOJI_RASTERIZATION_FRAGMENT_BYTES,
},
}; };
Self { inner: bytes } Self { inner: bytes }
} }
@ -1539,6 +1544,12 @@ mod shader_resources {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> { pub(super) fn build_shader_blob(entry: ShaderModule, target: ShaderTarget) -> Result<ID3DBlob> {
unsafe { unsafe {
let shader_name = if matches!(entry, ShaderModule::EmojiRasterization) {
"color_text_raster.hlsl"
} else {
"shaders.hlsl"
};
let entry = format!( let entry = format!(
"{}_{}\0", "{}_{}\0",
entry.as_str(), entry.as_str(),
@ -1555,7 +1566,7 @@ mod shader_resources {
let mut compile_blob = None; let mut compile_blob = None;
let mut error_blob = None; let mut error_blob = None;
let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) let shader_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src/platform/windows/shaders.hlsl") .join(&format!("src/platform/windows/{}", shader_name))
.canonicalize()?; .canonicalize()?;
let entry_point = PCSTR::from_raw(entry.as_ptr()); let entry_point = PCSTR::from_raw(entry.as_ptr());
@ -1601,6 +1612,7 @@ mod shader_resources {
ShaderModule::PathSprite => "path_sprite", ShaderModule::PathSprite => "path_sprite",
ShaderModule::MonochromeSprite => "monochrome_sprite", ShaderModule::MonochromeSprite => "monochrome_sprite",
ShaderModule::PolychromeSprite => "polychrome_sprite", ShaderModule::PolychromeSprite => "polychrome_sprite",
ShaderModule::EmojiRasterization => "emoji_rasterization",
} }
} }
} }

View file

@ -44,6 +44,7 @@ pub(crate) struct WindowsPlatform {
drop_target_helper: IDropTargetHelper, drop_target_helper: IDropTargetHelper,
validation_number: usize, validation_number: usize,
main_thread_id_win32: u32, main_thread_id_win32: u32,
disable_direct_composition: bool,
} }
pub(crate) struct WindowsPlatformState { pub(crate) struct WindowsPlatformState {
@ -93,14 +94,18 @@ impl WindowsPlatform {
main_thread_id_win32, main_thread_id_win32,
validation_number, validation_number,
)); ));
let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
.is_ok_and(|value| value == "true" || value == "1");
let background_executor = BackgroundExecutor::new(dispatcher.clone()); let background_executor = BackgroundExecutor::new(dispatcher.clone());
let foreground_executor = ForegroundExecutor::new(dispatcher); 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 { let bitmap_factory = ManuallyDrop::new(unsafe {
CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER) CoCreateInstance(&CLSID_WICImagingFactory, None, CLSCTX_INPROC_SERVER)
.context("Error creating bitmap factory.")? .context("Error creating bitmap factory.")?
}); });
let text_system = Arc::new( let text_system = Arc::new(
DirectWriteTextSystem::new(&bitmap_factory) DirectWriteTextSystem::new(&directx_devices, &bitmap_factory)
.context("Error creating DirectWriteTextSystem")?, .context("Error creating DirectWriteTextSystem")?,
); );
let drop_target_helper: IDropTargetHelper = unsafe { let drop_target_helper: IDropTargetHelper = unsafe {
@ -120,6 +125,7 @@ impl WindowsPlatform {
background_executor, background_executor,
foreground_executor, foreground_executor,
text_system, text_system,
disable_direct_composition,
windows_version, windows_version,
bitmap_factory, bitmap_factory,
drop_target_helper, drop_target_helper,
@ -184,6 +190,7 @@ impl WindowsPlatform {
validation_number: self.validation_number, validation_number: self.validation_number,
main_receiver: self.main_receiver.clone(), main_receiver: self.main_receiver.clone(),
main_thread_id_win32: self.main_thread_id_win32, main_thread_id_win32: self.main_thread_id_win32,
disable_direct_composition: self.disable_direct_composition,
} }
} }
@ -715,6 +722,7 @@ pub(crate) struct WindowCreationInfo {
pub(crate) validation_number: usize, pub(crate) validation_number: usize,
pub(crate) main_receiver: flume::Receiver<Runnable>, pub(crate) main_receiver: flume::Receiver<Runnable>,
pub(crate) main_thread_id_win32: u32, pub(crate) main_thread_id_win32: u32,
pub(crate) disable_direct_composition: bool,
} }
fn open_target(target: &str) { fn open_target(target: &str) {

View file

@ -1,6 +1,6 @@
cbuffer GlobalParams: register(b0) { cbuffer GlobalParams: register(b0) {
float2 global_viewport_size; float2 global_viewport_size;
uint2 _global_pad; uint2 _pad;
}; };
Texture2D<float4> t_sprite: register(t0); Texture2D<float4> t_sprite: register(t0);
@ -1069,6 +1069,7 @@ struct MonochromeSpriteFragmentInput {
float4 position: SV_Position; float4 position: SV_Position;
float2 tile_position: POSITION; float2 tile_position: POSITION;
nointerpolation float4 color: COLOR; nointerpolation float4 color: COLOR;
float4 clip_distance: SV_ClipDistance;
}; };
StructuredBuffer<MonochromeSprite> mono_sprites: register(t1); StructuredBuffer<MonochromeSprite> mono_sprites: register(t1);
@ -1091,10 +1092,8 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI
} }
float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target { float4 monochrome_sprite_fragment(MonochromeSpriteFragmentInput input): SV_Target {
float4 sample = t_sprite.Sample(s_sprite, input.tile_position); float sample = t_sprite.Sample(s_sprite, input.tile_position).r;
float4 color = input.color; return float4(input.color.rgb, input.color.a * sample);
color.a *= sample.a;
return color;
} }
/* /*

View file

@ -360,6 +360,7 @@ impl WindowsWindow {
validation_number, validation_number,
main_receiver, main_receiver,
main_thread_id_win32, main_thread_id_win32,
disable_direct_composition,
} = creation_info; } = creation_info;
let classname = register_wnd_class(icon); let classname = register_wnd_class(icon);
let hide_title_bar = params let hide_title_bar = params
@ -375,8 +376,6 @@ impl WindowsWindow {
.map(|title| title.as_ref()) .map(|title| title.as_ref())
.unwrap_or(""), .unwrap_or(""),
); );
let disable_direct_composition = std::env::var(DISABLE_DIRECT_COMPOSITION)
.is_ok_and(|value| value == "true" || value == "1");
let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp { let (mut dwexstyle, dwstyle) = if params.kind == WindowKind::PopUp {
(WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0)) (WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0))