initial removal attempt

This commit is contained in:
Kate 2025-07-14 20:55:16 +02:00 committed by Junkui Zhang
parent 8514850ad4
commit 6928488aad

View file

@ -1,4 +1,4 @@
use std::{borrow::Cow, sync::Arc};
use std::{borrow::Cow, mem::ManuallyDrop, sync::Arc};
use ::util::ResultExt;
use anyhow::Result;
@ -9,13 +9,7 @@ use windows::{
Win32::{
Foundation::*,
Globalization::GetUserDefaultLocaleName,
Graphics::{
Direct2D::{Common::*, *},
DirectWrite::*,
Dxgi::Common::*,
Gdi::LOGFONTW,
Imaging::*,
},
Graphics::{DirectWrite::*, Dxgi::Common::*, Gdi::LOGFONTW, Imaging::*},
System::SystemServices::LOCALE_NAME_MAX_LENGTH,
UI::WindowsAndMessaging::*,
},
@ -40,7 +34,6 @@ struct DirectWriteComponent {
locale: String,
factory: IDWriteFactory5,
bitmap_factory: AgileReference<IWICImagingFactory>,
d2d1_factory: ID2D1Factory,
in_memory_loader: IDWriteInMemoryFontFileLoader,
builder: IDWriteFontSetBuilder1,
text_renderer: Arc<TextRendererWrapper>,
@ -49,7 +42,6 @@ struct DirectWriteComponent {
struct GlyphRenderContext {
params: IDWriteRenderingParams3,
dc_target: ID2D1DeviceContext4,
}
struct DirectWriteState {
@ -74,8 +66,6 @@ impl DirectWriteComponent {
unsafe {
let factory: IDWriteFactory5 = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED)?;
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
// Windows 10 Creators Update, which consequently requires the entire
// `DirectWriteTextSystem` to run on `win10 1703`+.
@ -86,13 +76,12 @@ impl DirectWriteComponent {
GetUserDefaultLocaleName(&mut locale_vec);
let locale = String::from_utf16_lossy(&locale_vec);
let text_renderer = Arc::new(TextRendererWrapper::new(&locale));
let render_context = GlyphRenderContext::new(&factory, &d2d1_factory)?;
let render_context = GlyphRenderContext::new(&factory)?;
Ok(DirectWriteComponent {
locale,
factory,
bitmap_factory,
d2d1_factory,
in_memory_loader,
builder,
text_renderer,
@ -103,7 +92,7 @@ impl DirectWriteComponent {
}
impl GlyphRenderContext {
pub fn new(factory: &IDWriteFactory5, d2d1_factory: &ID2D1Factory) -> Result<Self> {
pub fn new(factory: &IDWriteFactory5) -> Result<Self> {
unsafe {
let default_params: IDWriteRenderingParams3 =
factory.CreateRenderingParams()?.cast()?;
@ -122,17 +111,8 @@ impl GlyphRenderContext {
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC,
grid_fit_mode,
)?;
let dc_target = {
let target = d2d1_factory.CreateDCRenderTarget(&get_render_target_property(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED,
))?;
let target = target.cast::<ID2D1DeviceContext4>()?;
target.SetTextRenderingParams(&params);
target
};
Ok(Self { params, dc_target })
Ok(Self { params })
}
}
}
@ -649,17 +629,12 @@ impl DirectWriteState {
}
fn raster_bounds(&self, params: &RenderGlyphParams) -> Result<Bounds<DevicePixels>> {
let render_target = &self.components.render_context.dc_target;
unsafe {
render_target.SetUnitMode(D2D1_UNIT_MODE_DIPS);
render_target.SetDpi(96.0 * params.scale_factor, 96.0 * params.scale_factor);
}
let font = &self.fonts[params.font_id.0];
let glyph_id = [params.glyph_id.0 as u16];
let advance = [0.0f32];
let offset = [DWRITE_GLYPH_OFFSET::default()];
let glyph_run = DWRITE_GLYPH_RUN {
fontFace: unsafe { std::mem::transmute_copy(&font.font_face) },
fontFace: ManuallyDrop::new(Some(font.font_face.cast()?)),
fontEmSize: params.font_size.0,
glyphCount: 1,
glyphIndices: glyph_id.as_ptr(),
@ -668,13 +643,29 @@ impl DirectWriteState {
isSideways: BOOL(0),
bidiLevel: 0,
};
let bounds = unsafe {
render_target.GetGlyphRunWorldBounds(
Vector2 { X: 0.0, Y: 0.0 },
let transform = DWRITE_MATRIX::default();
let rendering_mode = DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC;
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
let baseline_origin_x = 0.0;
let baseline_origin_y = 0.0;
let glyph_analysis = unsafe {
self.components.factory.CreateGlyphRunAnalysis(
&glyph_run,
DWRITE_MEASURING_MODE_NATURAL,
Some(&transform as *const _),
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 bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
// todo(windows)
// This is a walkaround, deleted when figured out.
let y_offset;
@ -696,12 +687,13 @@ impl DirectWriteState {
} else {
Ok(Bounds {
origin: point(
((bounds.left * params.scale_factor).ceil() as i32).into(),
((bounds.top * params.scale_factor).ceil() as i32 + y_offset).into(),
((bounds.left as f32 * params.scale_factor).ceil() as i32).into(),
((bounds.top as f32 * params.scale_factor).ceil() as i32 + y_offset).into(),
),
size: size(
(((bounds.right - bounds.left) * params.scale_factor).ceil() as i32).into(),
(((bounds.bottom - bounds.top) * params.scale_factor).ceil() as i32
(((bounds.right - bounds.left) as f32 * params.scale_factor).ceil() as i32)
.into(),
(((bounds.bottom - bounds.top) as f32 * params.scale_factor).ceil() as i32
+ extra_height)
.into(),
),
@ -739,7 +731,7 @@ impl DirectWriteState {
ascenderOffset: glyph_bounds.origin.y.0 as f32 / params.scale_factor,
}];
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,
glyphCount: 1,
glyphIndices: glyph_id.as_ptr(),
@ -759,149 +751,108 @@ impl DirectWriteState {
}
let bitmap_size = bitmap_size;
let total_bytes;
let bitmap_format;
let render_target_property;
let bitmap_width;
let bitmap_height;
let bitmap_stride;
let bitmap_dpi;
if params.is_emoji {
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize * 4;
bitmap_format = &GUID_WICPixelFormat32bppPBGRA;
render_target_property = get_render_target_property(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_PREMULTIPLIED,
);
bitmap_width = bitmap_size.width.0 as u32;
bitmap_height = bitmap_size.height.0 as u32;
bitmap_stride = bitmap_size.width.0 as u32 * 4;
bitmap_dpi = 96.0;
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 rendering_mode = if params.is_emoji {
DWRITE_RENDERING_MODE1_NATURAL
} else {
total_bytes = bitmap_size.height.0 as usize * bitmap_size.width.0 as usize;
bitmap_format = &GUID_WICPixelFormat8bppAlpha;
render_target_property =
get_render_target_property(DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT);
bitmap_width = bitmap_size.width.0 as u32 * 2;
bitmap_height = bitmap_size.height.0 as u32 * 2;
bitmap_stride = bitmap_size.width.0 as u32;
bitmap_dpi = 192.0;
}
DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC
};
let bitmap_factory = self.components.bitmap_factory.resolve()?;
unsafe {
let bitmap = bitmap_factory.CreateBitmap(
bitmap_width,
bitmap_height,
bitmap_format,
WICBitmapCacheOnLoad,
)?;
let render_target = self
.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,
};
let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
// 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();
let glyph_analysis = unsafe {
self.components.factory.CreateGlyphRunAnalysis(
&glyph_run,
Some(&transform),
rendering_mode,
measuring_mode,
DWRITE_GRID_FIT_MODE_DEFAULT,
DWRITE_TEXT_ANTIALIAS_MODE_CLEARTYPE,
baseline_origin_x,
baseline_origin_y,
)?
};
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() {
let Ok(color_glyph) = enumerator.GetCurrentRun() else {
break;
};
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,
);
if params.is_emoji {
// For emoji, we need to handle color glyphs differently
// This is a simplified approach - in a full implementation you'd want to
// properly handle color glyph runs using TranslateColorGlyphRun
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
let width = (texture_bounds.right - texture_bounds.left) as u32;
let height = (texture_bounds.bottom - texture_bounds.top) as u32;
if width == 0 || height == 0 {
return Ok((
bitmap_size,
vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4],
));
}
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)?;
let mut rgba_data = vec![0u8; (width * height * 4) as usize];
unsafe {
glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_data)?;
}
Ok((bitmap_size, raw_data))
// Resize to match expected bitmap_size if needed
let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize * 4;
rgba_data.resize(expected_size, 0);
Ok((bitmap_size, rgba_data))
} else {
// For regular text, use grayscale or cleartype
let texture_type = DWRITE_TEXTURE_CLEARTYPE_3x1;
let texture_bounds = unsafe { glyph_analysis.GetAlphaTextureBounds(texture_type)? };
let width = (texture_bounds.right - texture_bounds.left) as u32;
let height = (texture_bounds.bottom - texture_bounds.top) as u32;
if width == 0 || height == 0 {
return Ok((
bitmap_size,
vec![0u8; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize],
));
}
let mut alpha_data = vec![0u8; (width * height) as usize];
unsafe {
glyph_analysis.CreateAlphaTexture(
texture_type,
&texture_bounds,
&mut alpha_data,
)?;
}
// For cleartype, we need to convert the 3x1 subpixel data to grayscale
// This is a simplified conversion - you might want to do proper subpixel rendering
let mut grayscale_data = Vec::new();
for chunk in alpha_data.chunks_exact(3) {
let avg = (chunk[0] as u32 + chunk[1] as u32 + chunk[2] as u32) / 3;
grayscale_data.push(avg as u8);
}
// Resize to match expected bitmap_size if needed
let expected_size = bitmap_size.width.0 as usize * bitmap_size.height.0 as usize;
grayscale_data.resize(expected_size, 0);
Ok((bitmap_size, grayscale_data))
}
}
@ -1471,13 +1422,8 @@ fn get_name(string: IDWriteLocalizedStrings, locale: &str) -> Result<String> {
}
#[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 translate_color(color: &DWRITE_COLOR_F) -> [f32; 4] {
[color.r, color.g, color.b, color.a]
}
fn get_system_ui_font_name() -> SharedString {
@ -1504,24 +1450,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
// but that doesn't seem to work for some glyphs, say ❤
fn is_color_glyph(
@ -1561,12 +1489,6 @@ fn is_color_glyph(
}
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)]
mod tests {