| use super::hb::*; | |
| use std::collections::HashMap; | |
| use std::ffi::c_void; | |
| use std::mem::transmute; | |
| use std::ptr::null_mut; | |
| use std::sync::atomic::{AtomicPtr, AtomicU32, Ordering}; | |
| use std::sync::Mutex; | |
| use skrifa::charmap::Charmap; | |
| use skrifa::charmap::MapVariant::{UseDefault, Variant}; | |
| use skrifa::color::ColorGlyphCollection; | |
| use skrifa::font::FontRef; | |
| use skrifa::instance::{Location, NormalizedCoord, Size}; | |
| use skrifa::metrics::GlyphMetrics; | |
| use skrifa::raw::tables::vmtx::Vmtx; | |
| use skrifa::raw::tables::vorg::Vorg; | |
| use skrifa::raw::tables::vvar::Vvar; | |
| use skrifa::raw::TableProvider; | |
| use skrifa::OutlineGlyphCollection; | |
| use skrifa::{GlyphId, GlyphNames, MetadataProvider}; | |
| #[cfg(feature = "draw")] | |
| use skrifa::outline::{pen::OutlinePen, DrawSettings}; | |
| #[cfg(feature = "paint")] | |
| use skrifa::{ | |
| color::{Brush, ColorPainter, ColorStop, CompositeMode, Extend, Transform}, | |
| metrics::BoundingBox, | |
| raw::tables::cpal::ColorRecord, | |
| }; | |
| // A struct for storing your “fontations” data | |
| #[repr(C)] | |
| struct FontationsData<'a> { | |
| face_blob: *mut hb_blob_t, | |
| font: *mut hb_font_t, | |
| font_ref: FontRef<'a>, | |
| char_map: Charmap<'a>, | |
| outline_glyphs: OutlineGlyphCollection<'a>, | |
| color_glyphs: ColorGlyphCollection<'a>, | |
| glyph_names: GlyphNames<'a>, | |
| size: Size, | |
| vert_metrics: Option<Vmtx<'a>>, | |
| vert_origin: Option<Vorg<'a>>, | |
| vert_vars: Option<Vvar<'a>>, | |
| // Mutex for the below | |
| mutex: Mutex<()>, | |
| serial: AtomicU32, | |
| x_mult: f32, | |
| y_mult: f32, | |
| location: Location, | |
| glyph_metrics: Option<GlyphMetrics<'a>>, | |
| } | |
| impl FontationsData<'_> { | |
| unsafe fn from_hb_font(font: *mut hb_font_t) -> Option<Self> { | |
| let face_index = hb_face_get_index(hb_font_get_face(font)); | |
| let face_blob = hb_face_reference_blob(hb_font_get_face(font)); | |
| let blob_length = hb_blob_get_length(face_blob); | |
| let blob_data: *const u8 = hb_blob_get_data(face_blob, null_mut()).cast(); | |
| if blob_data.is_null() { | |
| return None; | |
| } | |
| let face_data = std::slice::from_raw_parts(blob_data, blob_length as usize); | |
| let font_ref = FontRef::from_index(face_data, face_index); | |
| let font_ref = match font_ref { | |
| Ok(f) => f, | |
| Err(_) => return None, | |
| }; | |
| let char_map = Charmap::new(&font_ref); | |
| let outline_glyphs = font_ref.outline_glyphs(); | |
| let color_glyphs = font_ref.color_glyphs(); | |
| let glyph_names = font_ref.glyph_names(); | |
| let upem = hb_face_get_upem(hb_font_get_face(font)); | |
| let vert_metrics = font_ref.vmtx().ok(); | |
| let vert_origin = font_ref.vorg().ok(); | |
| let vert_vars = font_ref.vvar().ok(); | |
| let mut data = FontationsData { | |
| face_blob, | |
| font, | |
| font_ref, | |
| char_map, | |
| outline_glyphs, | |
| color_glyphs, | |
| glyph_names, | |
| size: Size::new(upem as f32), | |
| vert_metrics, | |
| vert_origin, | |
| vert_vars, | |
| mutex: Mutex::new(()), | |
| x_mult: 1.0, | |
| y_mult: 1.0, | |
| serial: AtomicU32::new(u32::MAX), | |
| location: Location::default(), | |
| glyph_metrics: None, | |
| }; | |
| data.check_for_updates(); | |
| Some(data) | |
| } | |
| unsafe fn _check_for_updates(&mut self) { | |
| let font_serial = hb_font_get_serial(self.font); | |
| let serial = self.serial.load(Ordering::Relaxed); | |
| if serial == font_serial { | |
| return; | |
| } | |
| let _lock = self.mutex.lock().unwrap(); | |
| let mut x_scale: i32 = 0; | |
| let mut y_scale: i32 = 0; | |
| hb_font_get_scale(self.font, &mut x_scale, &mut y_scale); | |
| let upem = hb_face_get_upem(hb_font_get_face(self.font)); | |
| self.x_mult = x_scale as f32 / upem as f32; | |
| self.y_mult = y_scale as f32 / upem as f32; | |
| let mut num_coords: u32 = 0; | |
| let coords = hb_font_get_var_coords_normalized(self.font, &mut num_coords); | |
| let coords = if coords.is_null() { | |
| &[] | |
| } else { | |
| std::slice::from_raw_parts(coords, num_coords as usize) | |
| }; | |
| let all_zeros = coords.iter().all(|&x| x == 0); | |
| // if all zeros, use Location::default() | |
| // otherwise, use the provided coords. | |
| // This currently doesn't seem to have a perf effect on fontations, but it's a good idea to | |
| // check if the coords are all zeros before creating a Location. | |
| self.location = if all_zeros { | |
| Location::default() | |
| } else { | |
| let mut location = Location::new(num_coords as usize); | |
| let coords_mut = location.coords_mut(); | |
| coords_mut | |
| .iter_mut() | |
| .zip(coords.iter().map(|v| NormalizedCoord::from_bits(*v as i16))) | |
| .for_each(|(dest, source)| *dest = source); | |
| location | |
| }; | |
| let location = transmute::<&Location, &Location>(&self.location); | |
| self.glyph_metrics = Some(self.font_ref.glyph_metrics(self.size, location)); | |
| self.serial.store(font_serial, Ordering::Release); | |
| } | |
| fn check_for_updates(&mut self) { | |
| unsafe { self._check_for_updates() } | |
| } | |
| } | |
| extern "C" fn _hb_fontations_data_destroy(font_data: *mut c_void) { | |
| let data = unsafe { Box::from_raw(font_data as *mut FontationsData) }; | |
| unsafe { | |
| hb_blob_destroy(data.face_blob); | |
| } | |
| } | |
| fn struct_at_offset<T: Copy>(first: *const T, index: u32, stride: u32) -> T { | |
| unsafe { *((first as *const u8).offset((index * stride) as isize) as *const T) } | |
| } | |
| fn struct_at_offset_mut<T: Copy>(first: *mut T, index: u32, stride: u32) -> &'static mut T { | |
| unsafe { &mut *((first as *mut u8).offset((index * stride) as isize) as *mut T) } | |
| } | |
| extern "C" fn _hb_fontations_get_nominal_glyphs( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| count: ::std::os::raw::c_uint, | |
| first_unicode: *const hb_codepoint_t, | |
| unicode_stride: ::std::os::raw::c_uint, | |
| first_glyph: *mut hb_codepoint_t, | |
| glyph_stride: ::std::os::raw::c_uint, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> ::std::os::raw::c_uint { | |
| let data = unsafe { &*(font_data as *const FontationsData) }; | |
| let char_map = &data.char_map; | |
| for i in 0..count { | |
| let unicode = struct_at_offset(first_unicode, i, unicode_stride); | |
| let Some(glyph) = char_map.map(unicode) else { | |
| return i; | |
| }; | |
| let glyph_id = glyph.to_u32() as hb_codepoint_t; | |
| *struct_at_offset_mut(first_glyph, i, glyph_stride) = glyph_id; | |
| } | |
| count | |
| } | |
| extern "C" fn _hb_fontations_get_variation_glyph( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| unicode: hb_codepoint_t, | |
| variation_selector: hb_codepoint_t, | |
| glyph: *mut hb_codepoint_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &*(font_data as *const FontationsData) }; | |
| let char_map = &data.char_map; | |
| match char_map.map_variant(unicode, variation_selector) { | |
| Some(Variant(glyph_id)) => { | |
| unsafe { *glyph = glyph_id.to_u32() as hb_codepoint_t }; | |
| true as hb_bool_t | |
| } | |
| Some(UseDefault) => match char_map.map(unicode) { | |
| Some(glyph_id) => { | |
| unsafe { *glyph = glyph_id.to_u32() as hb_codepoint_t }; | |
| true as hb_bool_t | |
| } | |
| None => false as hb_bool_t, | |
| }, | |
| _ => false as hb_bool_t, | |
| } | |
| } | |
| extern "C" fn _hb_fontations_get_glyph_h_advances( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| count: ::std::os::raw::c_uint, | |
| first_glyph: *const hb_codepoint_t, | |
| glyph_stride: ::std::os::raw::c_uint, | |
| first_advance: *mut hb_position_t, | |
| advance_stride: ::std::os::raw::c_uint, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| let glyph_metrics = &data.glyph_metrics.as_ref().unwrap(); | |
| for i in 0..count { | |
| let glyph = struct_at_offset(first_glyph, i, glyph_stride); | |
| let glyph_id = GlyphId::new(glyph); | |
| let advance = glyph_metrics.advance_width(glyph_id).unwrap_or_default(); | |
| let scaled = (advance * data.x_mult).round() as hb_position_t; | |
| *struct_at_offset_mut(first_advance, i, advance_stride) = scaled; | |
| } | |
| } | |
| extern "C" fn _hb_fontations_get_glyph_v_advances( | |
| font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| count: ::std::os::raw::c_uint, | |
| first_glyph: *const hb_codepoint_t, | |
| glyph_stride: ::std::os::raw::c_uint, | |
| first_advance: *mut hb_position_t, | |
| advance_stride: ::std::os::raw::c_uint, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| if let Some(vert_metrics) = &data.vert_metrics { | |
| let vert_vars = &data.vert_vars; | |
| for i in 0..count { | |
| let glyph = struct_at_offset(first_glyph, i, glyph_stride); | |
| let glyph_id = GlyphId::new(glyph); | |
| let mut advance = vert_metrics.advance(glyph_id).unwrap_or_default() as f32; | |
| if let Some(vert_vars) = vert_vars { | |
| let coords = data.location.coords(); | |
| if !coords.is_empty() { | |
| advance += vert_vars | |
| .advance_height_delta(glyph_id, coords) | |
| .unwrap_or_default() | |
| .to_f32(); | |
| } | |
| } | |
| let scaled = -(advance * data.y_mult).round() as hb_position_t; | |
| *struct_at_offset_mut(first_advance, i, advance_stride) = scaled; | |
| } | |
| } else { | |
| let mut font_extents = unsafe { std::mem::zeroed() }; | |
| unsafe { | |
| hb_font_get_extents_for_direction( | |
| font, | |
| hb_direction_t_HB_DIRECTION_LTR, | |
| &mut font_extents, | |
| ); | |
| } | |
| let advance: hb_position_t = -(font_extents.ascender - font_extents.descender); | |
| for i in 0..count { | |
| *struct_at_offset_mut(first_advance, i, advance_stride) = advance; | |
| } | |
| } | |
| } | |
| extern "C" fn _hb_fontations_get_glyph_v_origin( | |
| font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| glyph: hb_codepoint_t, | |
| x: *mut hb_position_t, | |
| y: *mut hb_position_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| unsafe { | |
| *x = hb_font_get_glyph_h_advance(font, glyph) / 2; | |
| } | |
| let vert_origin = &data.vert_origin; | |
| if let Some(vert_origin) = vert_origin { | |
| let glyph_id = GlyphId::new(glyph); | |
| let mut y_origin = vert_origin.vertical_origin_y(glyph_id) as f32; | |
| let vert_vars = &data.vert_vars; | |
| if let Some(vert_vars) = vert_vars { | |
| let coords = data.location.coords(); | |
| if !coords.is_empty() { | |
| y_origin += vert_vars | |
| .v_org_delta(glyph_id, coords) | |
| .unwrap_or_default() | |
| .to_f32(); | |
| } | |
| } | |
| unsafe { | |
| *y = (y_origin * data.y_mult).round() as hb_position_t; | |
| } | |
| return true as hb_bool_t; | |
| } | |
| let mut extents: hb_glyph_extents_t = unsafe { std::mem::zeroed() }; | |
| if unsafe { hb_font_get_glyph_extents(font, glyph, &mut extents) != 0 } { | |
| if let Some(vert_metrics) = &data.vert_metrics { | |
| let glyph = GlyphId::new(glyph); | |
| let mut tsb: f32 = vert_metrics.side_bearing(glyph).unwrap_or_default() as f32; | |
| if let Some(vert_vars) = &data.vert_vars { | |
| let coords = data.location.coords(); | |
| if !coords.is_empty() { | |
| tsb += vert_vars | |
| .tsb_delta(glyph, coords) | |
| .unwrap_or_default() | |
| .to_f32(); | |
| } | |
| } | |
| unsafe { | |
| *y = extents.y_bearing + (tsb * data.y_mult).round() as hb_position_t; | |
| } | |
| return true as hb_bool_t; | |
| } | |
| let mut font_extents: hb_font_extents_t = unsafe { std::mem::zeroed() }; | |
| unsafe { | |
| hb_font_get_extents_for_direction( | |
| font, | |
| hb_direction_t_HB_DIRECTION_LTR, | |
| &mut font_extents, | |
| ); | |
| } | |
| let advance: hb_position_t = font_extents.ascender - font_extents.descender; | |
| let diff: hb_position_t = advance - -extents.height; | |
| unsafe { | |
| *y = extents.y_bearing + (diff >> 1); | |
| } | |
| return true as hb_bool_t; | |
| } | |
| let mut font_extents: hb_font_extents_t = unsafe { std::mem::zeroed() }; | |
| unsafe { | |
| hb_font_get_extents_for_direction(font, hb_direction_t_HB_DIRECTION_LTR, &mut font_extents); | |
| } | |
| unsafe { | |
| *y = font_extents.ascender; | |
| } | |
| true as hb_bool_t | |
| } | |
| extern "C" fn _hb_fontations_get_glyph_extents( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| glyph: hb_codepoint_t, | |
| extents: *mut hb_glyph_extents_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| let glyph_id = GlyphId::new(glyph); | |
| // COLRv1 color glyphs are not supported by glyph_metrics. | |
| let color_glyphs = &data.color_glyphs; | |
| if color_glyphs.get(glyph_id).is_some() { | |
| return false as hb_bool_t; | |
| }; | |
| let glyph_metrics = &data.glyph_metrics.as_ref().unwrap(); | |
| let Some(glyph_extents) = glyph_metrics.bounds(glyph_id) else { | |
| return false as hb_bool_t; | |
| }; | |
| let x_bearing = (glyph_extents.x_min * data.x_mult).round() as hb_position_t; | |
| let width = (glyph_extents.x_max * data.x_mult).round() as hb_position_t - x_bearing; | |
| let y_bearing = (glyph_extents.y_max * data.y_mult).round() as hb_position_t; | |
| let height = (glyph_extents.y_min * data.y_mult).round() as hb_position_t - y_bearing; | |
| unsafe { | |
| *extents = hb_glyph_extents_t { | |
| x_bearing, | |
| y_bearing, | |
| width, | |
| height, | |
| }; | |
| } | |
| true as hb_bool_t | |
| } | |
| extern "C" fn _hb_fontations_get_font_h_extents( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| extents: *mut hb_font_extents_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| let font_ref = &data.font_ref; | |
| let size = &data.size; | |
| let location = &data.location; | |
| let metrics = font_ref.metrics(*size, location); | |
| unsafe { | |
| (*extents).ascender = (metrics.ascent * data.y_mult).round() as hb_position_t; | |
| (*extents).descender = (metrics.descent * data.y_mult).round() as hb_position_t; | |
| (*extents).line_gap = (metrics.leading * data.y_mult).round() as hb_position_t; | |
| } | |
| true as hb_bool_t | |
| } | |
| #[cfg(feature = "draw")] | |
| struct HbPen { | |
| x_mult: f32, | |
| y_mult: f32, | |
| draw_state: *mut hb_draw_state_t, | |
| draw_funcs: *mut hb_draw_funcs_t, | |
| draw_data: *mut c_void, | |
| } | |
| #[cfg(feature = "draw")] | |
| impl OutlinePen for HbPen { | |
| fn move_to(&mut self, x: f32, y: f32) { | |
| unsafe { | |
| hb_draw_move_to( | |
| self.draw_funcs, | |
| self.draw_data, | |
| self.draw_state, | |
| x * self.x_mult, | |
| y * self.y_mult, | |
| ); | |
| } | |
| } | |
| fn line_to(&mut self, x: f32, y: f32) { | |
| unsafe { | |
| hb_draw_line_to( | |
| self.draw_funcs, | |
| self.draw_data, | |
| self.draw_state, | |
| x * self.x_mult, | |
| y * self.y_mult, | |
| ); | |
| } | |
| } | |
| fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32) { | |
| unsafe { | |
| hb_draw_quadratic_to( | |
| self.draw_funcs, | |
| self.draw_data, | |
| self.draw_state, | |
| x1 * self.x_mult, | |
| y1 * self.y_mult, | |
| x * self.x_mult, | |
| y * self.y_mult, | |
| ); | |
| } | |
| } | |
| fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32) { | |
| unsafe { | |
| hb_draw_cubic_to( | |
| self.draw_funcs, | |
| self.draw_data, | |
| self.draw_state, | |
| x1 * self.x_mult, | |
| y1 * self.y_mult, | |
| x2 * self.x_mult, | |
| y2 * self.y_mult, | |
| x * self.x_mult, | |
| y * self.y_mult, | |
| ); | |
| } | |
| } | |
| fn close(&mut self) { | |
| unsafe { | |
| hb_draw_close_path(self.draw_funcs, self.draw_data, self.draw_state); | |
| } | |
| } | |
| } | |
| #[cfg(feature = "draw")] | |
| extern "C" fn _hb_fontations_draw_glyph_or_fail( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| glyph: hb_codepoint_t, | |
| draw_funcs: *mut hb_draw_funcs_t, | |
| draw_data: *mut ::std::os::raw::c_void, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| let size = &data.size; | |
| let location = &data.location; | |
| let outline_glyphs = &data.outline_glyphs; | |
| let glyph_id = GlyphId::new(glyph); | |
| let Some(outline_glyph) = outline_glyphs.get(glyph_id) else { | |
| return false as hb_bool_t; | |
| }; | |
| let draw_settings = DrawSettings::unhinted(*size, location); | |
| let mut draw_state = unsafe { std::mem::zeroed::<hb_draw_state_t>() }; | |
| let mut pen = HbPen { | |
| x_mult: data.x_mult, | |
| y_mult: data.y_mult, | |
| draw_state: &mut draw_state, | |
| draw_funcs, | |
| draw_data, | |
| }; | |
| let _ = outline_glyph.draw(draw_settings, &mut pen); | |
| true as hb_bool_t | |
| } | |
| #[cfg(feature = "paint")] | |
| struct HbColorPainter<'a> { | |
| font: *mut hb_font_t, | |
| paint_funcs: *mut hb_paint_funcs_t, | |
| paint_data: *mut c_void, | |
| color_records: &'a [ColorRecord], | |
| foreground: hb_color_t, | |
| is_glyph_clip: u64, | |
| clip_depth: u32, | |
| } | |
| #[cfg(feature = "paint")] | |
| impl HbColorPainter<'_> { | |
| fn lookup_color(&self, color_index: u16, alpha: f32) -> hb_color_t { | |
| if color_index == 0xFFFF { | |
| // Apply alpha to foreground color | |
| return ((self.foreground & 0xFFFFFF00) | |
| | (((self.foreground & 0xFF) as f32 * alpha).round() as u32)) | |
| as hb_color_t; | |
| } | |
| let c = self.color_records.get(color_index as usize); | |
| if let Some(c) = c { | |
| (((c.blue as u32) << 24) | |
| | ((c.green as u32) << 16) | |
| | ((c.red as u32) << 8) | |
| | ((c.alpha as f32 * alpha).round() as u32)) as hb_color_t | |
| } else { | |
| 0 as hb_color_t | |
| } | |
| } | |
| fn make_color_line(&self, color_line: &ColorLineData) -> hb_color_line_t { | |
| let mut cl = unsafe { std::mem::zeroed::<hb_color_line_t>() }; | |
| cl.data = color_line as *const ColorLineData as *mut ::std::os::raw::c_void; | |
| cl.get_color_stops = Some(_hb_fontations_get_color_stops); | |
| cl.get_extend = Some(_hb_fontations_get_extend); | |
| cl | |
| } | |
| } | |
| #[cfg(feature = "paint")] | |
| struct ColorLineData<'a> { | |
| painter: &'a HbColorPainter<'a>, | |
| color_stops: &'a [ColorStop], | |
| extend: Extend, | |
| } | |
| #[cfg(feature = "paint")] | |
| extern "C" fn _hb_fontations_get_color_stops( | |
| _color_line: *mut hb_color_line_t, | |
| color_line_data: *mut ::std::os::raw::c_void, | |
| start: ::std::os::raw::c_uint, | |
| count_out: *mut ::std::os::raw::c_uint, | |
| color_stops_out: *mut hb_color_stop_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> ::std::os::raw::c_uint { | |
| let color_line_data = unsafe { &*(color_line_data as *const ColorLineData) }; | |
| let color_stops = &color_line_data.color_stops; | |
| if count_out.is_null() { | |
| return color_stops.len() as u32; | |
| } | |
| let count = unsafe { *count_out }; | |
| for i in 0..count { | |
| let Some(stop) = color_stops.get(start as usize + i as usize) else { | |
| unsafe { | |
| *count_out = i; | |
| }; | |
| break; | |
| }; | |
| unsafe { | |
| *(color_stops_out.offset(i as isize)) = hb_color_stop_t { | |
| offset: stop.offset, | |
| color: color_line_data | |
| .painter | |
| .lookup_color(stop.palette_index, stop.alpha), | |
| is_foreground: (stop.palette_index == 0xFFFF) as hb_bool_t, | |
| }; | |
| } | |
| } | |
| color_stops.len() as u32 | |
| } | |
| #[cfg(feature = "paint")] | |
| extern "C" fn _hb_fontations_get_extend( | |
| _color_line: *mut hb_color_line_t, | |
| color_line_data: *mut ::std::os::raw::c_void, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_paint_extend_t { | |
| let color_line_data = unsafe { &*(color_line_data as *const ColorLineData) }; | |
| color_line_data.extend as hb_paint_extend_t // They are the same | |
| } | |
| #[cfg(feature = "paint")] | |
| pub fn _hb_fontations_unreduce_anchors( | |
| x0: f32, | |
| y0: f32, | |
| x1: f32, | |
| y1: f32, | |
| ) -> (f32, f32, f32, f32, f32, f32) { | |
| /* Returns (x0, y0, x1, y1, x2, y2) such that the original | |
| * `_hb_cairo_reduce_anchors` would produce (xx0, yy0, xx1, yy1) | |
| * as outputs. | |
| * The OT spec has the following wording; we just need to | |
| * invert that operation here: | |
| * | |
| * Note: An implementation can derive a single vector, from p₀ to a point p₃, by computing the | |
| * orthogonal projection of the vector from p₀ to p₁ onto a line perpendicular to line p₀p₂ and | |
| * passing through p₀ to obtain point p₃. The linear gradient defined using p₀, p₁ and p₂ as | |
| * described above is functionally equivalent to a linear gradient defined by aligning stop | |
| * offset 0 to p₀ and aligning stop offset 1.0 to p₃, with each color projecting on either side | |
| * of that line in a perpendicular direction. This specification uses three points, p₀, p₁ and | |
| * p₂, as that provides greater flexibility in controlling the placement and rotation of the | |
| * gradient, as well as variations thereof. | |
| */ | |
| let dx = x1 - x0; | |
| let dy = y1 - y0; | |
| (x0, y0, x1, y1, x0 + dy, y0 - dx) | |
| } | |
| #[cfg(feature = "paint")] | |
| 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 fill_glyph( | |
| &mut self, | |
| glyph_id: GlyphId, | |
| brush_transform: Option<Transform>, | |
| brush: Brush<'_>, | |
| ) { | |
| unsafe { | |
| hb_paint_push_inverse_font_transform(self.paint_funcs, self.paint_data, self.font); | |
| } | |
| if brush_transform.is_none() { | |
| if let Brush::Solid { | |
| palette_index: color_index, | |
| alpha, | |
| } = brush | |
| { | |
| let is_foreground = color_index == 0xFFFF; | |
| let color = self.lookup_color(color_index, alpha); | |
| unsafe { | |
| hb_paint_fill_glyph( | |
| self.paint_funcs, | |
| self.paint_data, | |
| glyph_id.to_u32() as hb_codepoint_t, | |
| self.font, | |
| is_foreground as hb_bool_t, | |
| color, | |
| ); | |
| } | |
| self.pop_transform(); | |
| return; | |
| } | |
| } | |
| unsafe { | |
| hb_paint_push_clip_glyph( | |
| self.paint_funcs, | |
| self.paint_data, | |
| glyph_id.to_u32() as hb_codepoint_t, | |
| self.font, | |
| ); | |
| hb_paint_push_font_transform(self.paint_funcs, self.paint_data, self.font); | |
| } | |
| if let Some(wrap_in_transform) = brush_transform { | |
| self.push_transform(wrap_in_transform); | |
| self.fill(brush); | |
| self.pop_transform(); | |
| } else { | |
| self.fill(brush); | |
| } | |
| self.pop_transform(); | |
| unsafe { | |
| hb_paint_pop_clip(self.paint_funcs, self.paint_data); | |
| } | |
| self.pop_transform(); | |
| } | |
| fn push_clip_glyph(&mut self, glyph_id: GlyphId) { | |
| if self.clip_depth < 64 { | |
| self.is_glyph_clip |= 1 << self.clip_depth; | |
| self.clip_depth += 1; | |
| } else { | |
| return; | |
| } | |
| unsafe { | |
| hb_paint_push_inverse_font_transform(self.paint_funcs, self.paint_data, self.font); | |
| hb_paint_push_clip_glyph( | |
| self.paint_funcs, | |
| self.paint_data, | |
| glyph_id.to_u32() as hb_codepoint_t, | |
| self.font, | |
| ); | |
| hb_paint_push_font_transform(self.paint_funcs, self.paint_data, self.font); | |
| } | |
| } | |
| fn push_clip_box(&mut self, bbox: BoundingBox) { | |
| if self.clip_depth < 64 { | |
| self.is_glyph_clip &= !(1 << self.clip_depth); | |
| self.clip_depth += 1; | |
| } else { | |
| return; | |
| } | |
| 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) { | |
| if self.clip_depth > 0 { | |
| self.clip_depth -= 1; | |
| } else { | |
| return; | |
| } | |
| unsafe { | |
| if (self.is_glyph_clip & (1 << self.clip_depth)) != 0 { | |
| hb_paint_pop_transform(self.paint_funcs, self.paint_data); | |
| } | |
| hb_paint_pop_clip(self.paint_funcs, self.paint_data); | |
| if (self.is_glyph_clip & (1 << self.clip_depth)) != 0 { | |
| hb_paint_pop_transform(self.paint_funcs, self.paint_data); | |
| } | |
| } | |
| } | |
| fn fill(&mut self, brush: Brush) { | |
| match brush { | |
| Brush::Solid { | |
| palette_index: color_index, | |
| alpha, | |
| } => { | |
| let is_foreground = color_index == 0xFFFF; | |
| unsafe { | |
| hb_paint_color( | |
| self.paint_funcs, | |
| self.paint_data, | |
| is_foreground as hb_bool_t, | |
| self.lookup_color(color_index, alpha), | |
| ); | |
| } | |
| } | |
| Brush::LinearGradient { | |
| color_stops, | |
| extend, | |
| p0, | |
| p1, | |
| } => { | |
| let color_stops = ColorLineData { | |
| painter: self, | |
| color_stops, | |
| extend, | |
| }; | |
| let mut color_line = self.make_color_line(&color_stops); | |
| let (x0, y0, x1, y1, x2, y2) = | |
| _hb_fontations_unreduce_anchors(p0.x, p0.y, p1.x, p1.y); | |
| unsafe { | |
| hb_paint_linear_gradient( | |
| self.paint_funcs, | |
| self.paint_data, | |
| &mut color_line, | |
| x0, | |
| y0, | |
| x1, | |
| y1, | |
| x2, | |
| y2, | |
| ); | |
| } | |
| } | |
| Brush::RadialGradient { | |
| color_stops, | |
| extend, | |
| c0, | |
| r0, | |
| c1, | |
| r1, | |
| } => { | |
| let color_stops = ColorLineData { | |
| painter: self, | |
| color_stops, | |
| extend, | |
| }; | |
| let mut color_line = self.make_color_line(&color_stops); | |
| unsafe { | |
| hb_paint_radial_gradient( | |
| self.paint_funcs, | |
| self.paint_data, | |
| &mut color_line, | |
| c0.x, | |
| c0.y, | |
| r0, | |
| c1.x, | |
| c1.y, | |
| r1, | |
| ); | |
| } | |
| } | |
| Brush::SweepGradient { | |
| color_stops, | |
| extend, | |
| c0, | |
| start_angle, | |
| end_angle, | |
| } => { | |
| let color_stops = ColorLineData { | |
| painter: self, | |
| color_stops, | |
| extend, | |
| }; | |
| let mut color_line = self.make_color_line(&color_stops); | |
| // Skrifa has this gem, so we swap end_angle and start_angle | |
| // when passing to our API: | |
| // | |
| // * Convert angles and stops from counter-clockwise to clockwise | |
| // * for the shader if the gradient is not already reversed due to | |
| // * start angle being larger than end angle. | |
| // | |
| // Undo that. | |
| let (start_angle, end_angle) = (360. - start_angle, 360. - end_angle); | |
| let start_angle = start_angle.to_radians(); | |
| let end_angle = end_angle.to_radians(); | |
| unsafe { | |
| hb_paint_sweep_gradient( | |
| self.paint_funcs, | |
| self.paint_data, | |
| &mut color_line, | |
| c0.x, | |
| c0.y, | |
| start_angle, | |
| end_angle, | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| fn push_layer(&mut self, mode: CompositeMode) { | |
| let mode = mode as hb_paint_composite_mode_t; | |
| unsafe { | |
| hb_paint_push_group_for(self.paint_funcs, self.paint_data, mode); | |
| } | |
| } | |
| fn pop_layer_with_mode(&mut self, mode: CompositeMode) { | |
| let mode = mode as hb_paint_composite_mode_t; // They are the same | |
| unsafe { | |
| hb_paint_pop_group(self.paint_funcs, self.paint_data, mode); | |
| } | |
| } | |
| } | |
| #[cfg(feature = "paint")] | |
| extern "C" fn _hb_fontations_paint_glyph_or_fail( | |
| 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, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| data.check_for_updates(); | |
| let font_ref = &data.font_ref; | |
| let location = &data.location; | |
| let color_glyphs = &data.color_glyphs; | |
| let glyph_id = GlyphId::new(glyph); | |
| let Some(color_glyph) = color_glyphs.get(glyph_id) else { | |
| return false as hb_bool_t; | |
| }; | |
| let cpal = font_ref.cpal(); | |
| let color_records = if let Ok(cpal) = cpal { | |
| let num_entries = cpal.num_palette_entries().into(); | |
| let color_records = cpal.color_records_array(); | |
| let start_index = cpal | |
| .color_record_indices() | |
| .get(palette_index as usize) | |
| .or_else(|| { | |
| // https://github.com/harfbuzz/harfbuzz/issues/5116 | |
| cpal.color_record_indices().first() | |
| }); | |
| if let (Some(Ok(color_records)), Some(start_index)) = (color_records, start_index) { | |
| let start_index: usize = start_index.get().into(); | |
| let color_records = &color_records[start_index..start_index + num_entries]; | |
| unsafe { std::slice::from_raw_parts(color_records.as_ptr(), num_entries) } | |
| } else { | |
| &[] | |
| } | |
| } else { | |
| &[] | |
| }; | |
| let font = if (unsafe { hb_font_is_synthetic(font) } != false as hb_bool_t) { | |
| unsafe { | |
| let sub_font = hb_font_create_sub_font(font); | |
| hb_font_set_synthetic_bold(sub_font, 0.0, 0.0, true as hb_bool_t); | |
| hb_font_set_synthetic_slant(sub_font, 0.0); | |
| sub_font | |
| } | |
| } else { | |
| unsafe { hb_font_reference(font) } | |
| }; | |
| let mut painter = HbColorPainter { | |
| font, | |
| paint_funcs, | |
| paint_data, | |
| color_records, | |
| foreground, | |
| is_glyph_clip: 0, | |
| clip_depth: 0, | |
| }; | |
| unsafe { | |
| hb_paint_push_font_transform(paint_funcs, paint_data, font); | |
| } | |
| let _ = color_glyph.paint(location, &mut painter); | |
| unsafe { | |
| hb_paint_pop_transform(paint_funcs, paint_data); | |
| hb_font_destroy(font); | |
| } | |
| true as hb_bool_t | |
| } | |
| extern "C" fn _hb_fontations_glyph_name( | |
| _font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| glyph: hb_codepoint_t, | |
| name: *mut ::std::os::raw::c_char, | |
| size: ::std::os::raw::c_uint, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &mut *(font_data as *mut FontationsData) }; | |
| if let Some(glyph_name) = data.glyph_names.get(GlyphId::new(glyph)) { | |
| if size == 0 { | |
| return true as hb_bool_t; | |
| } | |
| let glyph_name = glyph_name.as_str(); | |
| // Copy the glyph name into the buffer, up to size-1 bytes | |
| let len = glyph_name.len().min((size as usize) - 1); | |
| let name: *mut u8 = name.cast(); | |
| unsafe { | |
| std::slice::from_raw_parts_mut(name, len) | |
| .copy_from_slice(&glyph_name.as_bytes()[..len]); | |
| *name.add(len) = 0; | |
| } | |
| true as hb_bool_t | |
| } else { | |
| false as hb_bool_t | |
| } | |
| } | |
| static mut GLYPH_FROM_NAMES_KEY: hb_user_data_key_t = hb_user_data_key_t { unused: 0 }; | |
| extern "C" fn _hb_glyph_from_names_destroy(data: *mut c_void) { | |
| let _ = unsafe { Box::from_raw(data as *mut HashMap<String, u32>) }; | |
| } | |
| extern "C" fn _hb_fontations_glyph_from_name( | |
| font: *mut hb_font_t, | |
| font_data: *mut ::std::os::raw::c_void, | |
| name: *const ::std::os::raw::c_char, | |
| len: ::std::os::raw::c_int, | |
| glyph: *mut hb_codepoint_t, | |
| _user_data: *mut ::std::os::raw::c_void, | |
| ) -> hb_bool_t { | |
| let data = unsafe { &*(font_data as *const FontationsData) }; | |
| let name: *const u8 = name.cast(); | |
| // SAFETY: HarfBuzz guarantees the string is valid memory for `len` bytes. | |
| let name_bytes = unsafe { std::slice::from_raw_parts(name, len as usize) }; | |
| let name_str = match std::str::from_utf8(name_bytes) { | |
| Ok(s) => s, | |
| Err(_) => return false as hb_bool_t, | |
| }; | |
| let face = unsafe { hb_font_get_face(font) }; | |
| let mut user_data_ptr: *mut c_void; | |
| loop { | |
| user_data_ptr = | |
| unsafe { hb_face_get_user_data(face, std::ptr::addr_of_mut!(GLYPH_FROM_NAMES_KEY)) }; | |
| if !user_data_ptr.is_null() { | |
| break; | |
| } | |
| // Build the HashMap from glyph names to IDs | |
| let mut map = HashMap::new(); | |
| for (glyph_id, glyph_name) in data.glyph_names.iter() { | |
| map.insert(glyph_name.to_string(), glyph_id.to_u32()); | |
| } | |
| let boxed_map = Box::new(map); | |
| let ptr = Box::into_raw(boxed_map) as *mut c_void; | |
| let success = unsafe { | |
| hb_face_set_user_data( | |
| face, | |
| std::ptr::addr_of_mut!(GLYPH_FROM_NAMES_KEY), | |
| ptr, | |
| Some(_hb_glyph_from_names_destroy), | |
| true as hb_bool_t, | |
| ) | |
| }; | |
| if success != false as hb_bool_t { | |
| user_data_ptr = ptr; | |
| break; | |
| } | |
| // We failed to set user data — reclaim the pointer to avoid leaking | |
| _hb_glyph_from_names_destroy(ptr); | |
| // Try again in next loop iteration | |
| } | |
| let glyph_from_names = unsafe { &*(user_data_ptr as *const HashMap<String, u32>) }; | |
| match glyph_from_names.get(name_str) { | |
| Some(gid) => { | |
| unsafe { *glyph = *gid }; | |
| true as hb_bool_t | |
| } | |
| None => false as hb_bool_t, | |
| } | |
| } | |
| fn _hb_fontations_font_funcs_get() -> *mut hb_font_funcs_t { | |
| static STATIC_FFUNCS: AtomicPtr<hb_font_funcs_t> = AtomicPtr::new(null_mut()); | |
| loop { | |
| let mut ffuncs = STATIC_FFUNCS.load(Ordering::Acquire); | |
| if !ffuncs.is_null() { | |
| return ffuncs; | |
| } | |
| ffuncs = unsafe { hb_font_funcs_create() }; | |
| unsafe { | |
| hb_font_funcs_set_nominal_glyphs_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_nominal_glyphs), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_variation_glyph_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_variation_glyph), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_h_advances_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_glyph_h_advances), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_v_advances_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_glyph_v_advances), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_v_origin_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_glyph_v_origin), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_extents_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_glyph_extents), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_font_h_extents_func( | |
| ffuncs, | |
| Some(_hb_fontations_get_font_h_extents), | |
| null_mut(), | |
| None, | |
| ); | |
| #[cfg(feature = "draw")] | |
| hb_font_funcs_set_draw_glyph_or_fail_func( | |
| ffuncs, | |
| Some(_hb_fontations_draw_glyph_or_fail), | |
| null_mut(), | |
| None, | |
| ); | |
| #[cfg(feature = "paint")] | |
| hb_font_funcs_set_paint_glyph_or_fail_func( | |
| ffuncs, | |
| Some(_hb_fontations_paint_glyph_or_fail), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_name_func( | |
| ffuncs, | |
| Some(_hb_fontations_glyph_name), | |
| null_mut(), | |
| None, | |
| ); | |
| hb_font_funcs_set_glyph_from_name_func( | |
| ffuncs, | |
| Some(_hb_fontations_glyph_from_name), | |
| null_mut(), | |
| None, | |
| ); | |
| } | |
| if (STATIC_FFUNCS.compare_exchange(null_mut(), ffuncs, Ordering::SeqCst, Ordering::Relaxed)) | |
| == Ok(null_mut()) | |
| { | |
| return ffuncs; | |
| } else { | |
| unsafe { | |
| hb_font_funcs_destroy(ffuncs); | |
| } | |
| } | |
| } | |
| } | |
| /// # Safety | |
| /// | |
| /// This function is unsafe because it connects with the HarfBuzz API. | |
| #[unsafe(no_mangle)] | |
| pub unsafe extern "C" fn hb_fontations_font_set_funcs(font: *mut hb_font_t) { | |
| let ffuncs = _hb_fontations_font_funcs_get(); | |
| let data = FontationsData::from_hb_font(font); | |
| let data = match data { | |
| Some(d) => d, | |
| None => return, | |
| }; | |
| let data_ptr = Box::into_raw(Box::new(data)) as *mut c_void; | |
| hb_font_set_funcs(font, ffuncs, data_ptr, Some(_hb_fontations_data_destroy)); | |
| } |