Rasterize glyphs without D2D
Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com> Co-authored-by: Julia <julia@zed.dev>
This commit is contained in:
parent
bb1a7ccbba
commit
da83011fda
10 changed files with 729 additions and 270 deletions
|
@ -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.
|
||||||
|
|
|
@ -198,7 +198,7 @@ impl RenderOnce for CharacterGrid {
|
||||||
"χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",
|
"χ", "ψ", "∂", "а", "в", "Ж", "ж", "З", "з", "К", "к", "л", "м", "Н", "н", "Р", "р",
|
||||||
"У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",
|
"У", "у", "ф", "ч", "ь", "ы", "Э", "э", "Я", "я", "ij", "öẋ", ".,", "⣝⣑", "~", "*",
|
||||||
"_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ",
|
"_", "^", "`", "'", "(", "{", "«", "#", "&", "@", "$", "¢", "%", "|", "?", "¶", "µ",
|
||||||
"❮", "<=", "!=", "==", "--", "++", "=>", "->",
|
"❮", "<=", "!=", "==", "--", "++", "=>", "->", "🏀", "🎊", "😍", "❤️", "👍", "👎",
|
||||||
];
|
];
|
||||||
|
|
||||||
let columns = 11;
|
let columns = 11;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
39
crates/gpui/src/platform/windows/color_text_raster.hlsl
Normal file
39
crates/gpui/src/platform/windows/color_text_raster.hlsl
Normal 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);
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{borrow::Cow, sync::Arc};
|
use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
|
||||||
|
|
||||||
use ::util::ResultExt;
|
use ::util::ResultExt;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
@ -10,11 +10,8 @@ use windows::{
|
||||||
Foundation::*,
|
Foundation::*,
|
||||||
Globalization::GetUserDefaultLocaleName,
|
Globalization::GetUserDefaultLocaleName,
|
||||||
Graphics::{
|
Graphics::{
|
||||||
Direct2D::{Common::*, *},
|
Direct3D::D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP, Direct3D11::*, DirectWrite::*,
|
||||||
DirectWrite::*,
|
Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*,
|
||||||
Dxgi::Common::*,
|
|
||||||
Gdi::LOGFONTW,
|
|
||||||
Imaging::*,
|
|
||||||
},
|
},
|
||||||
System::SystemServices::LOCALE_NAME_MAX_LENGTH,
|
System::SystemServices::LOCALE_NAME_MAX_LENGTH,
|
||||||
UI::WindowsAndMessaging::*,
|
UI::WindowsAndMessaging::*,
|
||||||
|
@ -40,16 +37,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 +72,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 +87,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(¶ms);
|
},
|
||||||
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
|
||||||
|
@ -649,11 +722,6 @@ impl DirectWriteState {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
|
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 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.0f32];
|
||||||
|
@ -668,25 +736,36 @@ impl DirectWriteState {
|
||||||
isSideways: BOOL(0),
|
isSideways: BOOL(0),
|
||||||
bidiLevel: 0,
|
bidiLevel: 0,
|
||||||
};
|
};
|
||||||
let bounds = unsafe {
|
|
||||||
render_target.GetGlyphRunWorldBounds(
|
let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC;
|
||||||
Vector2 { X: 0.0, Y: 0.0 },
|
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
|
||||||
|
let baseline_origin_x = 0.0;
|
||||||
|
let baseline_origin_y = 0.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 glyph_analysis = unsafe {
|
||||||
|
self.components.factory.CreateGlyphRunAnalysis(
|
||||||
&glyph_run,
|
&glyph_run,
|
||||||
DWRITE_MEASURING_MODE_NATURAL,
|
Some(&transform),
|
||||||
|
rendering_mode,
|
||||||
|
measuring_mode,
|
||||||
|
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||||
|
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||||
|
baseline_origin_x,
|
||||||
|
baseline_origin_y,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
// todo(windows)
|
|
||||||
// This is a walkaround, deleted when figured out.
|
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||||
let y_offset;
|
let bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
|
||||||
let extra_height;
|
|
||||||
if params.is_emoji {
|
|
||||||
y_offset = 0;
|
|
||||||
extra_height = 0;
|
|
||||||
} else {
|
|
||||||
// make some room for scaler.
|
|
||||||
y_offset = -1;
|
|
||||||
extra_height = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if bounds.right < bounds.left {
|
if bounds.right < bounds.left {
|
||||||
Ok(Bounds {
|
Ok(Bounds {
|
||||||
|
@ -695,15 +774,10 @@ impl DirectWriteState {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Bounds {
|
Ok(Bounds {
|
||||||
origin: point(
|
origin: point((bounds.left as i32).into(), (bounds.top as i32).into()),
|
||||||
((bounds.left * params.scale_factor).ceil() as i32).into(),
|
|
||||||
((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(),
|
|
||||||
),
|
|
||||||
size: size(
|
size: size(
|
||||||
(((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
|
(bounds.right - bounds.left).into(),
|
||||||
(((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32
|
(bounds.bottom - bounds.top).into(),
|
||||||
+ extra_height)
|
|
||||||
.into(),
|
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -739,7 +813,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: ManuallyDrop::new(Some(font_info.font_face.cast()?)),
|
||||||
fontEmSize: params.font_size.0,
|
fontEmSize: params.font_size.0,
|
||||||
glyphCount: 1,
|
glyphCount: 1,
|
||||||
glyphIndices: glyph_id.as_ptr(),
|
glyphIndices: glyph_id.as_ptr(),
|
||||||
|
@ -759,150 +833,398 @@ impl DirectWriteState {
|
||||||
}
|
}
|
||||||
let bitmap_size = bitmap_size;
|
let bitmap_size = bitmap_size;
|
||||||
|
|
||||||
let total_bytes;
|
let subpixel_shift = params
|
||||||
let bitmap_format;
|
.subpixel_variant
|
||||||
let render_target_property;
|
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
|
||||||
let bitmap_width;
|
let baseline_origin_x = subpixel_shift.x / params.scale_factor;
|
||||||
let bitmap_height;
|
let baseline_origin_y = subpixel_shift.y / params.scale_factor;
|
||||||
let bitmap_stride;
|
|
||||||
let bitmap_dpi;
|
let transform = DWRITE_MATRIX {
|
||||||
if params.is_emoji {
|
m11: params.scale_factor,
|
||||||
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
|
m12: 0.0,
|
||||||
bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
|
m21: 0.0,
|
||||||
render_target_property = get_render_target_property(
|
m22: params.scale_factor,
|
||||||
DXGI_FORMAT_B8G8R8A8_UNORM,
|
dx: 0.0,
|
||||||
D2D1_ALPHA_MODE_PREMULTIPLIED,
|
dy: 0.0,
|
||||||
);
|
};
|
||||||
bitmap_width = bitmap_size.width.0 as u32;
|
|
||||||
bitmap_height = bitmap_size.height.0 as u32;
|
let rendering_mode = if params.is_emoji {
|
||||||
bitmap_stride = bitmap_size.width.0 as u32 * 4;
|
DWRITE_RENDERING_MODE1_NATURAL
|
||||||
bitmap_dpi = 96.0;
|
|
||||||
} else {
|
} else {
|
||||||
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
|
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC
|
||||||
bitmap_format = &GUID_WICPixelFormat8bppAlpha;
|
};
|
||||||
render_target_property =
|
|
||||||
get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
|
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
|
||||||
bitmap_width = bitmap_size.width.0 as u32 * 2;
|
|
||||||
bitmap_height = bitmap_size.height.0 as u32 * 2;
|
let glyph_analysis = unsafe {
|
||||||
bitmap_stride = bitmap_size.width.0 as u32;
|
self.components.factory.CreateGlyphRunAnalysis(
|
||||||
bitmap_dpi = 192.0;
|
&glyph_run,
|
||||||
|
Some(&transform),
|
||||||
|
rendering_mode,
|
||||||
|
measuring_mode,
|
||||||
|
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||||
|
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||||
|
baseline_origin_x,
|
||||||
|
baseline_origin_y,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
|
||||||
|
let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
|
||||||
|
let texture_width = (texture_bounds.right - texture_bounds.left) as u32;
|
||||||
|
let texture_height = (texture_bounds.bottom - texture_bounds.top) as u32;
|
||||||
|
|
||||||
|
if texture_width == 0 || texture_height == 0 {
|
||||||
|
return Ok((
|
||||||
|
bitmap_size,
|
||||||
|
vec![
|
||||||
|
0u8;
|
||||||
|
bitmap_size.width.0 as usize
|
||||||
|
* bitmap_size.height.0 as usize
|
||||||
|
* if params.is_emoji { 4 } else { 1 }
|
||||||
|
],
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let bitmap_factory = self.components.bitmap_factory.resolve()?;
|
let mut bitmap_data: Vec<u8>;
|
||||||
unsafe {
|
if params.is_emoji {
|
||||||
let bitmap = bitmap_factory.CreateBitmap(
|
if let Ok(color) = self.rasterize_color(
|
||||||
bitmap_width,
|
&glyph_run,
|
||||||
bitmap_height,
|
rendering_mode,
|
||||||
bitmap_format,
|
measuring_mode,
|
||||||
WICBitmapCacheOnLoad,
|
&transform,
|
||||||
)?;
|
point(baseline_origin_x, baseline_origin_y),
|
||||||
let render_target = self
|
bitmap_size,
|
||||||
.components
|
) {
|
||||||
.d2d1_factory
|
bitmap_data = color;
|
||||||
.CreateWicBitmapRenderTarget(&bitmap, &render_target_property)?;
|
} else {
|
||||||
let brush = render_target.CreateSolidColorBrush(&BRUSH_COLOR, None)?;
|
let monochrome = Self::rasterize_monochrome(
|
||||||
let subpixel_shift = params
|
&glyph_analysis,
|
||||||
.subpixel_variant
|
bitmap_size,
|
||||||
.map(|v| v as f32 / SUBPIXEL_VARIANTS as f32);
|
size(texture_width, texture_height),
|
||||||
let baseline_origin = Vector2 {
|
&texture_bounds,
|
||||||
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
|
|
||||||
// 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(
|
|
||||||
baseline_origin,
|
|
||||||
&glyph_run as _,
|
|
||||||
None,
|
|
||||||
DWRITE_GLYPH_IMAGE_FORMATS_COLR
|
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_SVG
|
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG
|
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8,
|
|
||||||
DWRITE_MEASURING_MODE_NATURAL,
|
|
||||||
None,
|
|
||||||
0,
|
|
||||||
)?;
|
)?;
|
||||||
while enumerator.MoveNext().is_ok() {
|
bitmap_data = monochrome
|
||||||
let Ok(color_glyph) = enumerator.GetCurrentRun() else {
|
.into_iter()
|
||||||
break;
|
.flat_map(|pixel| [0, 0, 0, pixel])
|
||||||
};
|
.collect::<Vec<_>>();
|
||||||
let color_glyph = &*color_glyph;
|
}
|
||||||
let brush_color = translate_color(&color_glyph.Base.runColor);
|
} else {
|
||||||
brush.SetColor(&brush_color);
|
bitmap_data = Self::rasterize_monochrome(
|
||||||
match color_glyph.glyphImageFormat {
|
&glyph_analysis,
|
||||||
DWRITE_GLYPH_IMAGE_FORMATS_PNG
|
bitmap_size,
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_JPEG
|
size(texture_width, texture_height),
|
||||||
| DWRITE_GLYPH_IMAGE_FORMATS_PREMULTIPLIED_B8G8R8A8 => render_target
|
&texture_bounds,
|
||||||
.DrawColorBitmapGlyphRun(
|
)?;
|
||||||
color_glyph.glyphImageFormat,
|
}
|
||||||
baseline_origin,
|
|
||||||
&color_glyph.Base.glyphRun,
|
Ok((bitmap_size, bitmap_data))
|
||||||
color_glyph.measuringMode,
|
}
|
||||||
D2D1_COLOR_BITMAP_GLYPH_SNAP_OPTION_DEFAULT,
|
|
||||||
),
|
fn rasterize_monochrome(
|
||||||
DWRITE_GLYPH_IMAGE_FORMATS_SVG => render_target.DrawSvgGlyphRun(
|
glyph_analysis: &IDWriteGlyphRunAnalysis,
|
||||||
baseline_origin,
|
bitmap_size: Size<DevicePixels>,
|
||||||
&color_glyph.Base.glyphRun,
|
texture_size: Size<u32>,
|
||||||
&brush,
|
texture_bounds: &RECT,
|
||||||
None,
|
) -> Result<Vec<u8>> {
|
||||||
color_glyph.Base.paletteIndex as u32,
|
let mut bitmap_data =
|
||||||
color_glyph.measuringMode,
|
vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize];
|
||||||
),
|
|
||||||
_ => render_target.DrawGlyphRun(
|
let mut alpha_data = vec![0u8; (texture_size.width * texture_size.height * 3) as usize];
|
||||||
baseline_origin,
|
|
||||||
&color_glyph.Base.glyphRun,
|
unsafe {
|
||||||
Some(color_glyph.Base.glyphRunDescription as *const _),
|
glyph_analysis.CreateAlphaTexture(
|
||||||
&brush,
|
DWRITE_TEXTURE_CLEARTYPE_3x1,
|
||||||
color_glyph.measuringMode,
|
texture_bounds,
|
||||||
),
|
&mut alpha_data,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert ClearType RGB data to grayscale and place in bitmap
|
||||||
|
let offset_x = texture_bounds.left.max(0) as usize;
|
||||||
|
let offset_y = texture_bounds.top.max(0) as usize;
|
||||||
|
|
||||||
|
for y in 0..texture_size.height as usize {
|
||||||
|
for x in 0..texture_size.width as usize {
|
||||||
|
let bitmap_x = offset_x + x;
|
||||||
|
let bitmap_y = offset_y + y;
|
||||||
|
|
||||||
|
if bitmap_x < bitmap_size.width.0 as usize
|
||||||
|
&& bitmap_y < bitmap_size.height.0 as usize
|
||||||
|
{
|
||||||
|
let texture_idx = (y * texture_size.width as usize + x) * 3;
|
||||||
|
let bitmap_idx = bitmap_y * bitmap_size.width.0 as usize + bitmap_x;
|
||||||
|
|
||||||
|
if texture_idx + 2 < alpha_data.len() && bitmap_idx < bitmap_data.len() {
|
||||||
|
let max_value = alpha_data[texture_idx]
|
||||||
|
.max(alpha_data[texture_idx + 1])
|
||||||
|
.max(alpha_data[texture_idx + 2]);
|
||||||
|
bitmap_data[bitmap_idx] = max_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} 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];
|
|
||||||
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.;
|
|
||||||
pixel[0] = (pixel[0] as f32 / a) as u8;
|
|
||||||
pixel[1] = (pixel[1] as f32 / a) as u8;
|
|
||||||
pixel[2] = (pixel[2] as f32 / a) as u8;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let scaler = 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))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(bitmap_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rasterize_color(
|
||||||
|
&self,
|
||||||
|
glyph_run: &DWRITE_GLYPH_RUN,
|
||||||
|
rendering_mode: DWRITE_RENDERING_MODE1,
|
||||||
|
measuring_mode: DWRITE_MEASURING_MODE,
|
||||||
|
transform: &DWRITE_MATRIX,
|
||||||
|
baseline_origin: Point<f32>,
|
||||||
|
bitmap_size: Size<DevicePixels>,
|
||||||
|
) -> Result<Vec<u8>> {
|
||||||
|
// todo: support formats other than COLR
|
||||||
|
let color_enumerator = unsafe {
|
||||||
|
self.components.factory.TranslateColorGlyphRun(
|
||||||
|
Vector2::new(baseline_origin.x, baseline_origin.y),
|
||||||
|
glyph_run,
|
||||||
|
None,
|
||||||
|
DWRITE_GLYPH_IMAGE_FORMATS_COLR,
|
||||||
|
measuring_mode,
|
||||||
|
Some(transform),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let mut glyph_layers = Vec::new();
|
||||||
|
loop {
|
||||||
|
let color_run = unsafe { color_enumerator.GetCurrentRun() }?;
|
||||||
|
let color_run = unsafe { &*color_run };
|
||||||
|
let image_format = color_run.glyphImageFormat & !DWRITE_GLYPH_IMAGE_FORMATS_TRUETYPE;
|
||||||
|
if image_format == DWRITE_GLYPH_IMAGE_FORMATS_COLR {
|
||||||
|
let color_analysis = unsafe {
|
||||||
|
self.components.factory.CreateGlyphRunAnalysis(
|
||||||
|
&color_run.Base.glyphRun as *const _,
|
||||||
|
Some(transform),
|
||||||
|
rendering_mode,
|
||||||
|
measuring_mode,
|
||||||
|
DWRITE_GRID_FIT_MODE_DEFAULT,
|
||||||
|
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
|
||||||
|
baseline_origin.x,
|
||||||
|
baseline_origin.y,
|
||||||
|
)
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let color_bounds =
|
||||||
|
unsafe { color_analysis.GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1) }?;
|
||||||
|
|
||||||
|
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];
|
||||||
|
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 gpu_state = &self.components.gpu_state;
|
||||||
|
let params_buffer = {
|
||||||
|
let desc = D3D11_BUFFER_DESC {
|
||||||
|
ByteWidth: std::mem::size_of::<GlyphLayerTextureParams>() as u32,
|
||||||
|
Usage: D3D11_USAGE_DYNAMIC,
|
||||||
|
BindFlags: D3D11_BIND_CONSTANT_BUFFER.0 as u32,
|
||||||
|
CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32,
|
||||||
|
MiscFlags: 0,
|
||||||
|
StructureByteStride: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = None;
|
||||||
|
unsafe {
|
||||||
|
gpu_state
|
||||||
|
.device
|
||||||
|
.CreateBuffer(&desc, None, Some(&mut buffer))
|
||||||
|
}?;
|
||||||
|
[buffer]
|
||||||
|
};
|
||||||
|
|
||||||
|
let render_target_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_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(¶ms_buffer)) };
|
||||||
|
unsafe { device_context.PSSetConstantBuffers(0, Some(¶ms_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,
|
||||||
|
D3D11_MAP_WRITE_DISCARD,
|
||||||
|
0,
|
||||||
|
Some(&mut dest),
|
||||||
|
)?;
|
||||||
|
std::ptr::copy_nonoverlapping(¶ms as *const _, dest.pData as *mut _, 1);
|
||||||
|
gpu_state
|
||||||
|
.device_context
|
||||||
|
.Unmap(params_buffer[0].as_ref().unwrap(), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
let texture = [Some(layer.texture_view)];
|
||||||
|
unsafe { device_context.PSSetShaderResources(0, Some(&texture)) };
|
||||||
|
|
||||||
|
let viewport = [D3D11_VIEWPORT {
|
||||||
|
TopLeftX: layer.bounds.origin.x as f32,
|
||||||
|
TopLeftY: layer.bounds.origin.y as f32,
|
||||||
|
Width: layer.bounds.size.width as f32,
|
||||||
|
Height: layer.bounds.size.height as f32,
|
||||||
|
MinDepth: 0.0,
|
||||||
|
MaxDepth: 1.0,
|
||||||
|
}];
|
||||||
|
unsafe { device_context.RSSetViewports(Some(&viewport)) };
|
||||||
|
|
||||||
|
unsafe { device_context.Draw(4, 0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
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 +1298,83 @@ impl Drop for DirectWriteState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GlyphLayerTexture {
|
||||||
|
run_color: Rgba,
|
||||||
|
bounds: Bounds<i32>,
|
||||||
|
texture: ID3D11Texture2D,
|
||||||
|
texture_view: ID3D11ShaderResourceView,
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
texture_view,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct GlyphLayerTextureParams {
|
||||||
|
bounds: Bounds<i32>,
|
||||||
|
run_color: Rgba,
|
||||||
|
}
|
||||||
|
|
||||||
struct TextRendererWrapper(pub IDWriteTextRenderer);
|
struct TextRendererWrapper(pub IDWriteTextRenderer);
|
||||||
|
|
||||||
impl TextRendererWrapper {
|
impl TextRendererWrapper {
|
||||||
|
@ -1470,16 +1869,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 +1893,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 +1932,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 {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +175,8 @@ impl DirectXRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pre_draw(&self) -> Result<()> {
|
fn pre_draw(&self) -> Result<()> {
|
||||||
|
let premultiplied_alpha = 1;
|
||||||
|
|
||||||
update_buffer(
|
update_buffer(
|
||||||
&self.devices.device_context,
|
&self.devices.device_context,
|
||||||
self.globals.global_params_buffer[0].as_ref().unwrap(),
|
self.globals.global_params_buffer[0].as_ref().unwrap(),
|
||||||
|
@ -183,6 +185,7 @@ impl DirectXRenderer {
|
||||||
self.resources.viewport[0].Width,
|
self.resources.viewport[0].Width,
|
||||||
self.resources.viewport[0].Height,
|
self.resources.viewport[0].Height,
|
||||||
],
|
],
|
||||||
|
premultiplied_alpha,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}],
|
}],
|
||||||
)?;
|
)?;
|
||||||
|
@ -819,7 +822,8 @@ impl DirectXGlobalElements {
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
struct GlobalParams {
|
struct GlobalParams {
|
||||||
viewport_size: [f32; 2],
|
viewport_size: [f32; 2],
|
||||||
_pad: u64,
|
premultiplied_alpha: u32,
|
||||||
|
_pad: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PipelineState<T> {
|
struct PipelineState<T> {
|
||||||
|
@ -1073,7 +1077,7 @@ fn create_swap_chain_for_composition(
|
||||||
// Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
|
// Composition SwapChains only support the DXGI_SCALING_STRETCH Scaling.
|
||||||
Scaling: DXGI_SCALING_STRETCH,
|
Scaling: DXGI_SCALING_STRETCH,
|
||||||
SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
|
SwapEffect: DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
|
||||||
AlphaMode: DXGI_ALPHA_MODE_PREMULTIPLIED,
|
AlphaMode: DXGI_ALPHA_MODE_IGNORE,
|
||||||
Flags: 0,
|
Flags: 0,
|
||||||
};
|
};
|
||||||
Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
|
Ok(unsafe { dxgi_factory.CreateSwapChainForComposition(device, &desc, None)? })
|
||||||
|
@ -1277,7 +1281,7 @@ fn create_blend_state(device: &ID3D11Device) -> Result<ID3D11BlendState> {
|
||||||
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
|
||||||
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
|
||||||
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ONE;
|
desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
|
||||||
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
|
desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL.0 as u8;
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut state = None;
|
let mut state = None;
|
||||||
|
@ -1423,7 +1427,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)]
|
||||||
|
@ -1436,7 +1440,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,
|
||||||
|
@ -1444,15 +1448,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)]
|
||||||
|
@ -1460,7 +1465,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))
|
||||||
|
@ -1478,7 +1483,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1513,6 +1518,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 }
|
||||||
}
|
}
|
||||||
|
@ -1521,6 +1530,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(),
|
||||||
|
@ -1537,7 +1552,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());
|
||||||
|
@ -1583,6 +1598,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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
cbuffer GlobalParams: register(b0) {
|
cbuffer GlobalParams: register(b0) {
|
||||||
float2 global_viewport_size;
|
float2 global_viewport_size;
|
||||||
uint2 _global_pad;
|
uint premultiplied_alpha;
|
||||||
|
uint _pad;
|
||||||
};
|
};
|
||||||
|
|
||||||
Texture2D<float4> t_sprite: register(t0);
|
Texture2D<float4> t_sprite: register(t0);
|
||||||
|
@ -1069,6 +1070,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);
|
||||||
|
@ -1090,11 +1092,28 @@ MonochromeSpriteVertexOutput monochrome_sprite_vertex(uint vertex_id: SV_VertexI
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float4 blend_color(float4 color, float alpha_factor) {
|
||||||
|
float alpha = color.a * alpha_factor;
|
||||||
|
float multiplier = premultiplied_alpha != 0 ? alpha : 1.0;
|
||||||
|
return float4(color.rgb * multiplier, alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 linear_to_srgbee(float3 l) {
|
||||||
|
bool cutoff = l < float3(0.0031308, 0.0031308, 0.0031308);
|
||||||
|
float3 higher = float3(1.055, 1.055, 1.055) * pow(l, float3(1.0 / 2.4, 1.0 / 2.4, 1.0 / 2.4)) - float3(0.055, 0.055, 0.055);
|
||||||
|
float3 lower = l * float3(12.92, 12.92, 12.92);
|
||||||
|
return cutoff ? lower : higher;
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
float4 color = input.color;
|
||||||
color.a *= sample.a;
|
// color.a *= sample;
|
||||||
return color;
|
// return float4(color.rgb, color.a);
|
||||||
|
if (any(input.clip_distance < 0.0)) {
|
||||||
|
return float4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
}
|
||||||
|
return blend_color(input.color, sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue