@@ -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(¶ms);
- 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;
+ 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 {
+ DWRITE_RENDERING_MODE1_NATURAL_SYMMETRIC
+ };
+
+ let measuring_mode = DWRITE_MEASURING_MODE_NATURAL;
+
+ 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 {
- 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;
+ // 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],
+ ));
+ }
+
+ let mut rgba_data = vec![0u8; (width * height * 4) as usize];
+
+ unsafe {
+ glyph_analysis.CreateAlphaTexture(texture_type, &texture_bounds, &mut rgba_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 {
- 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;
- }
+ // 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 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 mut alpha_data = vec![0u8; (width * height) as usize];
- // 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,
+ unsafe {
+ glyph_analysis.CreateAlphaTexture(
+ texture_type,
+ &texture_bounds,
+ &mut alpha_data,
)?;
- 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,
- );
}
- 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)?;
+
+ // 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);
}
- 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;
+ 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 {