From 5b27f3287617b555124816fd90ed87a331d8271c Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Tue, 4 Mar 2025 21:37:00 -0700 Subject: [PATCH] [fontations] Implement most of paint API --- src/fontations/lib.rs | 193 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) diff --git a/src/fontations/lib.rs b/src/fontations/lib.rs index 548f47aad..bc4a50957 100644 --- a/src/fontations/lib.rs +++ b/src/fontations/lib.rs @@ -8,8 +8,10 @@ use std::sync::atomic::{AtomicPtr, Ordering}; use skrifa::charmap::Charmap; use skrifa::charmap::MapVariant::Variant; +use skrifa::color::{Brush, ColorGlyphCollection, ColorPainter, CompositeMode, Transform}; use skrifa::font::FontRef; use skrifa::instance::{Location, NormalizedCoord, Size}; +use skrifa::metrics::BoundingBox; use skrifa::outline::pen::OutlinePen; use skrifa::outline::DrawSettings; use skrifa::OutlineGlyphCollection; @@ -25,6 +27,7 @@ struct FontationsData { y_size: Size, location: Location, outline_glyphs: OutlineGlyphCollection<'static>, + color_glyphs: ColorGlyphCollection<'static>, } extern "C" fn _hb_fontations_data_destroy(font_data: *mut c_void) { @@ -270,6 +273,187 @@ extern "C" fn _hb_fontations_draw_glyph( let _ = outline_glyph.draw(draw_settings, &mut pen); } +struct HbColorPainter { + font: *mut hb_font_t, + paint_funcs: *mut hb_paint_funcs_t, + paint_data: *mut c_void, + palette_index: u32, + foreground: hb_color_t, + composite_mode: Vec, + clip_transform_stack: Vec, +} + +impl HbColorPainter { + fn push_root_transform(&mut self) { + let font = self.font; + let face = unsafe { hb_font_get_face(font) }; + let upem = unsafe { hb_face_get_upem(face) }; + let mut x_scale: i32 = 0; + let mut y_scale: i32 = 0; + unsafe { + hb_font_get_scale(font, &mut x_scale, &mut y_scale); + } + let slant = unsafe { hb_font_get_synthetic_slant(font) }; + let slant = if y_scale != 0 { + slant as f32 * x_scale as f32 / y_scale as f32 + } else { + 0. + }; + + self.push_transform(Transform { + xx: x_scale as f32 / upem as f32, + yx: 0.0, + xy: slant * y_scale as f32 / upem as f32, + yy: y_scale as f32 / upem as f32, + dx: 0.0, + dy: 0.0, + }); + } + + fn push_inverse_root_transform(&mut self) { + let font = self.font; + let face = unsafe { hb_font_get_face(font) }; + let upem = unsafe { hb_face_get_upem(face) }; + let mut x_scale: i32 = 0; + let mut y_scale: i32 = 0; + unsafe { + hb_font_get_scale(font, &mut x_scale, &mut y_scale); + } + let slant = unsafe { hb_font_get_synthetic_slant(font) }; + + self.push_transform(Transform { + xx: upem as f32 / x_scale as f32, + yx: 0.0, + xy: -slant * upem as f32 / x_scale as f32, + yy: upem as f32 / y_scale as f32, + dx: 0.0, + dy: 0.0, + }); + } +} + +impl ColorPainter for HbColorPainter { + fn push_transform(&mut self, transform: Transform) { + unsafe { + hb_paint_push_transform( + self.paint_funcs, + self.paint_data, + transform.xx, + transform.yx, + transform.xy, + transform.yy, + transform.dx, + transform.dy, + ); + } + } + fn pop_transform(&mut self) { + unsafe { + hb_paint_pop_transform(self.paint_funcs, self.paint_data); + } + } + fn push_clip_glyph(&mut self, glyph: GlyphId) { + let gid = u32::from(glyph); + self.clip_transform_stack.push(true); + self.push_inverse_root_transform(); + unsafe { + hb_paint_push_clip_glyph( + self.paint_funcs, + self.paint_data, + gid as hb_codepoint_t, + self.font, + ); + } + self.push_root_transform(); + } + fn push_clip_box(&mut self, bbox: BoundingBox) { + self.clip_transform_stack.push(false); + unsafe { + hb_paint_push_clip_rectangle( + self.paint_funcs, + self.paint_data, + bbox.x_min, + bbox.y_min, + bbox.x_max, + bbox.y_max, + ); + } + } + fn pop_clip(&mut self) { + let pop_transforms = self.clip_transform_stack.pop().unwrap_or(false); + if pop_transforms { + self.pop_transform(); + } + unsafe { + hb_paint_pop_clip(self.paint_funcs, self.paint_data); + } + if pop_transforms { + self.pop_transform(); + } + } + fn fill(&mut self, _: Brush<'_>) { + unsafe { + hb_paint_color( + self.paint_funcs, + self.paint_data, + self.foreground as i32, + 0x000000FF, + ); + } + } + fn push_layer(&mut self, mode: CompositeMode) { + self.composite_mode.push(mode); + unsafe { + hb_paint_push_group(self.paint_funcs, self.paint_data); + } + } + fn pop_layer(&mut self) { + let mode = self.composite_mode.pop(); + if mode.is_none() { + return; + } + let mode = mode.unwrap() as hb_paint_composite_mode_t; + unsafe { + hb_paint_pop_group(self.paint_funcs, self.paint_data, mode); + } + } +} + +extern "C" fn _hb_fontations_paint_glyph( + font: *mut hb_font_t, + font_data: *mut ::std::os::raw::c_void, + glyph: hb_codepoint_t, + paint_funcs: *mut hb_paint_funcs_t, + paint_data: *mut ::std::os::raw::c_void, + palette_index: ::std::os::raw::c_uint, + foreground: hb_color_t, + _user_data: *mut ::std::os::raw::c_void, +) { + let data = unsafe { &*(font_data as *const FontationsData) }; + let location = &data.location; + let color_glyphs = &data.color_glyphs; + + // Create an color-glyph + let glyph_id = GlyphId::new(glyph as u32); + let color_glyph = color_glyphs.get(glyph_id); + if color_glyph.is_none() { + return; + } + let color_glyph = color_glyph.unwrap(); + + let mut painter = HbColorPainter { + font: font, + paint_funcs, + paint_data, + palette_index, + foreground, + composite_mode: Vec::new(), + clip_transform_stack: Vec::new(), + }; + painter.push_root_transform(); + let _ = color_glyph.paint(location, &mut painter); +} + fn _hb_fontations_font_funcs_get() -> *mut hb_font_funcs_t { static static_ffuncs: AtomicPtr = AtomicPtr::new(null_mut()); @@ -319,6 +503,12 @@ fn _hb_fontations_font_funcs_get() -> *mut hb_font_funcs_t { null_mut(), None, ); + hb_font_funcs_set_paint_glyph_func( + ffuncs, + Some(_hb_fontations_paint_glyph), + null_mut(), + None, + ); } if (static_ffuncs.compare_exchange(null_mut(), ffuncs, Ordering::SeqCst, Ordering::Relaxed)) @@ -378,6 +568,8 @@ pub extern "C" fn hb_fontations_font_set_funcs(font: *mut hb_font_t) { let outline_glyphs = font_ref.outline_glyphs(); + let color_glyphs = font_ref.color_glyphs(); + let data = Box::new(FontationsData { face_blob, font_ref, @@ -386,6 +578,7 @@ pub extern "C" fn hb_fontations_font_set_funcs(font: *mut hb_font_t) { y_size, location, outline_glyphs, + color_glyphs, }); let data_ptr = Box::into_raw(data) as *mut c_void;