Rebrand to Galaxy, major improvements to Bedrock support, still needs some TLC though

This commit is contained in:
Ryan Ward
2026-05-07 11:29:34 -05:00
parent f4e2475c60
commit a41cbd8cc7
2433 changed files with 14208 additions and 9409 deletions
+168
View File
@@ -0,0 +1,168 @@
//! A text rasterizer backed by [`font_kit`] that supports rasterizing at subpixel offsets
use std::sync::Arc;
use anyhow::Result;
use dashmap::DashMap;
use font_kit::canvas::{AntialiasingStrategy, Canvas, RasterizationOptions};
use font_kit::font::Font;
use font_kit::hinting::HintingOptions;
use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{vec2i, Vector2F, Vector2I};
use galaxyui_core::fonts::canvas::RasterFormat;
use galaxyui_core::fonts::{
FontId, GlyphId, Properties, RasterizedGlyph, Style, SubpixelAlignment, Weight,
};
use galaxyui_core::rendering;
#[cfg(target_os = "macos")]
use crate::platform::mac::AutoreleasePoolGuard;
/// A simpler rasterizer backed by font-kit.
pub(crate) struct Rasterizer {
fonts: DashMap<FontId, Arc<Font>>,
}
impl Rasterizer {
pub fn new() -> Self {
Self {
fonts: Default::default(),
}
}
pub fn insert(&self, font_id: FontId, font: Arc<Font>) {
self.fonts.insert(font_id, font);
}
pub fn font_for_id(&self, font_id: FontId) -> Arc<Font> {
self.fonts.get(&font_id).expect("Font must exist").clone()
}
pub fn glyph_raster_bounds(
&self,
font_id: FontId,
point_size: f32,
glyph_id: GlyphId,
scale: Vector2F,
glyph_config: &rendering::GlyphConfig,
) -> Result<RectI> {
let raw_raster_bounds = self.font_for_id(font_id).raster_bounds(
glyph_id,
point_size,
Transform2F::from_scale(scale),
HintingOptions::None,
RasterizationOptions {
antialiasing_strategy: AntialiasingStrategy::GrayscaleAa,
use_thin_strokes: glyph_config
.use_thin_strokes
.enabled_for_scale_factor(scale.x()),
},
)?;
if raw_raster_bounds.size() == Vector2I::zero() {
// Don't adjust the size of a glyph with a default size of zero.
return Ok(raw_raster_bounds);
}
// The default raster bounds provided by font-kit sometimes clip pixels
// off of anti-aliased glyphs; add one pixel to the glyph bounds to
// compensate. We only adjust the origin vertically because the extra
// pixel of height changes the baseline; the extra pixel on the right
// side doesn't change positioning (as the origin is on the left edge of
// the glyph).
let fudge_factor = vec2i(1, 1);
let offset = vec2i(0, 1);
Ok(RectI::new(
raw_raster_bounds.origin() - offset,
raw_raster_bounds.size() + fudge_factor,
))
}
#[allow(clippy::too_many_arguments)]
pub fn rasterize_glyph(
&self,
font_id: FontId,
point_size: f32,
glyph_id: GlyphId,
scale: Vector2F,
subpixel_alignment: SubpixelAlignment,
glyph_config: &rendering::GlyphConfig,
format: RasterFormat,
) -> Result<RasterizedGlyph> {
// On macOS, this function calls into Core Graphics and Core Text
// (`CGBitmapContextCreate` per glyph plus `raster_bounds` reading font
// metadata), each of which leaves transient bookkeeping objects in the
// thread's autorelease pool. Because this is invoked during Metal
// frame rendering on the main thread, hundreds of those objects can
// accumulate between run-loop turns before AppKit's outer pool
// drains. A local pool bounds that peak without relying on the outer
// pool. The guard drains on `Drop`, covering the error paths from `?`
// below and any panics from `font_kit`.
#[cfg(target_os = "macos")]
let _pool = AutoreleasePoolGuard::new();
let bounds =
self.glyph_raster_bounds(font_id, point_size, glyph_id, scale, glyph_config)?;
let mut canvas = Canvas::new(bounds.size(), raster_format_to_font_kit(format));
let base_transform = Transform2F::from_scale(scale).translate(-bounds.origin().to_f32());
let aligned_transform = base_transform.translate(subpixel_alignment.to_offset());
self.font_for_id(font_id).rasterize_glyph(
&mut canvas,
glyph_id,
point_size,
aligned_transform,
HintingOptions::None,
RasterizationOptions {
antialiasing_strategy: AntialiasingStrategy::GrayscaleAa,
use_thin_strokes: glyph_config
.use_thin_strokes
.enabled_for_scale_factor(scale.x()),
},
)?;
Ok(RasterizedGlyph {
canvas: canvas.into(),
// TODO(alokedesai): Properly support colored glyphs on Windows.
is_emoji: self.font_for_id(font_id).is_colored() && !cfg!(windows),
})
}
}
pub fn properties_to_font_kit(properties: Properties) -> font_kit::properties::Properties {
font_kit::properties::Properties {
style: style_to_font_kit(properties.style),
weight: weight_to_font_kit(properties.weight),
stretch: Default::default(),
}
}
fn raster_format_to_font_kit(format: RasterFormat) -> font_kit::canvas::Format {
use font_kit::canvas::Format as FKFormat;
match format {
RasterFormat::Rgba32 => FKFormat::Rgba32,
RasterFormat::Rgb24 => FKFormat::Rgb24,
RasterFormat::A8 => FKFormat::Rgb24,
}
}
fn weight_to_font_kit(weight: Weight) -> font_kit::properties::Weight {
match weight {
Weight::Thin => font_kit::properties::Weight::THIN,
Weight::ExtraLight => font_kit::properties::Weight::EXTRA_LIGHT,
Weight::Light => font_kit::properties::Weight::LIGHT,
Weight::Normal => font_kit::properties::Weight::NORMAL,
Weight::Medium => font_kit::properties::Weight::MEDIUM,
Weight::Semibold => font_kit::properties::Weight::SEMIBOLD,
Weight::Bold => font_kit::properties::Weight::BOLD,
Weight::ExtraBold => font_kit::properties::Weight::EXTRA_BOLD,
Weight::Black => font_kit::properties::Weight::BLACK,
}
}
fn style_to_font_kit(value: Style) -> font_kit::properties::Style {
match value {
Style::Normal => font_kit::properties::Style::Normal,
Style::Italic => font_kit::properties::Style::Italic,
}
}
+15
View File
@@ -0,0 +1,15 @@
#[cfg(native)]
#[cfg_attr(not(macos), allow(dead_code))]
pub mod font_kit;
#[cfg(test)]
#[path = "text_layout_test.rs"]
mod text_layout_tests;
pub use galaxyui_core::fonts::*;
#[cfg(test)]
pub(crate) use text_layout_tests::{collect_glyph_indices, init_fonts};
#[cfg(all(test, target_os = "macos"))]
pub(crate) use text_layout_tests::collect_line_caret_position_starts;
File diff suppressed because it is too large Load Diff