| #![allow(non_camel_case_types)] |
| #![allow(non_upper_case_globals)] |
| include!(concat!(env!("OUT_DIR"), "/hb.rs")); |
| |
| use std::os::raw::c_void; |
| use std::ptr::null_mut; |
| use std::sync::atomic::{AtomicPtr, Ordering}; |
| |
| use read_fonts::tables::cpal::ColorRecord; |
| use read_fonts::TableProvider; |
| use skrifa::charmap::Charmap; |
| use skrifa::charmap::MapVariant::Variant; |
| use skrifa::color::{ |
| Brush, ColorGlyphCollection, ColorPainter, ColorStop, CompositeMode, Extend, 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; |
| use skrifa::{GlyphId, MetadataProvider}; |
| |
| // A struct for storing your “fontations” data |
| #[repr(C)] |
| struct FontationsData<'a> { |
| face_blob: *mut hb_blob_t, |
| font_ref: FontRef<'a>, |
| char_map: Charmap<'a>, |
| x_size: Size, |
| y_size: Size, |
| location: Location, |
| outline_glyphs: OutlineGlyphCollection<'a>, |
| color_glyphs: ColorGlyphCollection<'a>, |
| } |
| |
| 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 = u32::from(glyph) 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 = u32::from(glyph_id) as hb_codepoint_t }; |
| true 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 { &*(font_data as *const FontationsData) }; |
| let font_ref = &data.font_ref; |
| let size = &data.x_size; |
| let location = &data.location; |
| let glyph_metrics = font_ref.glyph_metrics(*size, location); |
| |
| 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() |
| .round() as i32; |
| *struct_at_offset_mut(first_advance, i, advance_stride) = advance as hb_position_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 { &*(font_data as *const FontationsData) }; |
| let font_ref = &data.font_ref; |
| let x_size = &data.x_size; |
| let y_size = &data.y_size; |
| let location = &data.location; |
| let x_glyph_metrics = font_ref.glyph_metrics(*x_size, location); |
| let y_glyph_metrics = font_ref.glyph_metrics(*y_size, location); |
| |
| let glyph_id = GlyphId::new(glyph); |
| let x_extents = x_glyph_metrics.bounds(glyph_id); |
| let y_extents = y_glyph_metrics.bounds(glyph_id); |
| let (Some(x_extents), Some(y_extents)) = (x_extents, y_extents) else { |
| return false as hb_bool_t; |
| }; |
| |
| unsafe { |
| *extents = hb_glyph_extents_t { |
| x_bearing: x_extents.x_min as hb_position_t, |
| width: (x_extents.x_max - x_extents.x_min) as hb_position_t, |
| y_bearing: y_extents.y_max as hb_position_t, |
| height: (y_extents.y_min - y_extents.y_max) as hb_position_t, |
| }; |
| } |
| |
| 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 { &*(font_data as *const FontationsData) }; |
| let font_ref = &data.font_ref; |
| let size = &data.y_size; |
| let location = &data.location; |
| let metrics = font_ref.metrics(*size, location); |
| |
| unsafe { |
| (*extents).ascender = metrics.ascent as hb_position_t; |
| (*extents).descender = metrics.descent as hb_position_t; |
| (*extents).line_gap = metrics.leading as hb_position_t; |
| } |
| |
| true as hb_bool_t |
| } |
| |
| struct HbPen |
| where |
| Self: OutlinePen, |
| { |
| draw_state: *mut hb_draw_state_t, |
| draw_funcs: *mut hb_draw_funcs_t, |
| draw_data: *mut c_void, |
| } |
| |
| 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, y); |
| } |
| } |
| fn line_to(&mut self, x: f32, y: f32) { |
| unsafe { |
| hb_draw_line_to(self.draw_funcs, self.draw_data, self.draw_state, x, y); |
| } |
| } |
| 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, |
| y1, |
| x, |
| y, |
| ); |
| } |
| } |
| 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, |
| y1, |
| x2, |
| y2, |
| x, |
| y, |
| ); |
| } |
| } |
| fn close(&mut self) { |
| unsafe { |
| hb_draw_close_path(self.draw_funcs, self.draw_data, self.draw_state); |
| } |
| } |
| } |
| |
| extern "C" fn _hb_fontations_draw_glyph( |
| _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, |
| ) { |
| let data = unsafe { &*(font_data as *const FontationsData) }; |
| let x_size = &data.x_size; |
| let location = &data.location; |
| let outline_glyphs = &data.outline_glyphs; |
| |
| // Create an outline-glyph |
| let glyph_id = GlyphId::new(glyph); |
| let Some(outline_glyph) = outline_glyphs.get(glyph_id) else { |
| return; |
| }; |
| let draw_settings = DrawSettings::unhinted(*x_size, location); |
| // Allocate zero bytes for the draw_state_t on the stack. |
| let mut draw_state: hb_draw_state_t = unsafe { std::mem::zeroed::<hb_draw_state_t>() }; |
| |
| let mut pen = HbPen { |
| draw_state: &mut draw_state, |
| draw_funcs, |
| draw_data, |
| }; |
| let _ = outline_glyph.draw(draw_settings, &mut pen); |
| } |
| |
| 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, |
| composite_mode: Vec<CompositeMode>, |
| clip_transform_stack: Vec<bool>, |
| } |
| |
| 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, |
| }); |
| } |
| |
| fn lookup_color(&self, color_index: u16, alpha: f32) -> hb_color_t { |
| let c = self.color_records.get(color_index as usize); |
| if let Some(c) = c.filter(|_| color_index != 0xFFFF) { |
| (((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 { |
| // Apply alpha to foreground color |
| ((self.foreground & 0xFFFFFF00) |
| | (((self.foreground & 0xFF) as f32 * alpha).round() as u32)) |
| 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 |
| } |
| } |
| |
| struct ColorLineData<'a> { |
| painter: &'a HbColorPainter<'a>, |
| color_stops: &'a [ColorStop], |
| extend: Extend, |
| } |
| 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 |
| } |
| 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 |
| } |
| |
| 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) |
| } |
| |
| 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: 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) { |
| 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; // They are the same |
| 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 font_ref = &data.font_ref; |
| let location = &data.location; |
| let color_glyphs = &data.color_glyphs; |
| |
| // Create an color-glyph |
| let glyph_id = GlyphId::new(glyph); |
| let Some(color_glyph) = color_glyphs.get(glyph_id) else { |
| return; |
| }; |
| |
| let cpal = font_ref.cpal(); |
| let color_records = if cpal.is_err() { |
| unsafe { std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0) } |
| } else { |
| let cpal = cpal.unwrap(); |
| let num_entries: usize = cpal.num_palette_entries().into(); |
| let color_records = cpal.color_records_array(); |
| let start_index = cpal.color_record_indices().get(palette_index as usize); |
| |
| 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 { |
| unsafe { std::slice::from_raw_parts(std::ptr::NonNull::dangling().as_ptr(), 0) } |
| } |
| }; |
| |
| let mut painter = HbColorPainter { |
| font, |
| paint_funcs, |
| paint_data, |
| color_records, |
| 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<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_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, |
| ); |
| hb_font_funcs_set_draw_glyph_func( |
| ffuncs, |
| Some(_hb_fontations_draw_glyph), |
| 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)) |
| == Ok(null_mut()) |
| { |
| return ffuncs; |
| } else { |
| unsafe { |
| hb_font_funcs_destroy(ffuncs); |
| } |
| } |
| } |
| } |
| |
| /// # Safety |
| /// |
| /// This function is unsafe because it connects with the HarfBuzz API. |
| #[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 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 = hb_blob_get_data(face_blob, null_mut()); |
| let face_data = std::slice::from_raw_parts(blob_data as *const u8, blob_length as usize); |
| |
| let font_ref = FontRef::from_index(face_data, face_index).unwrap(); |
| |
| let char_map = Charmap::new(&font_ref); |
| |
| let mut x_scale: i32 = 0; |
| let mut y_scale: i32 = 0; |
| hb_font_get_scale(font, &mut x_scale, &mut y_scale); |
| let x_size = Size::new(x_scale as f32); |
| let y_size = Size::new(y_scale as f32); |
| |
| let mut num_coords: u32 = 0; |
| let coords = hb_font_get_var_coords_normalized(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. |
| let 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 outline_glyphs = font_ref.outline_glyphs(); |
| |
| let color_glyphs = font_ref.color_glyphs(); |
| |
| let data = Box::new(FontationsData { |
| face_blob, |
| font_ref, |
| char_map, |
| x_size, |
| y_size, |
| location, |
| outline_glyphs, |
| color_glyphs, |
| }); |
| let data_ptr = Box::into_raw(data) as *mut c_void; |
| |
| hb_font_set_funcs(font, ffuncs, data_ptr, Some(_hb_fontations_data_destroy)); |
| } |