Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

620
third-party/vendor/fontdue/src/font.rs vendored Normal file
View file

@ -0,0 +1,620 @@
use crate::layout::GlyphRasterConfig;
use crate::math::{Geometry, Line};
use crate::platform::{as_i32, ceil, floor, fract, is_negative};
use crate::raster::Raster;
use crate::table::TableKern;
use crate::unicode;
use crate::FontResult;
use alloc::string::String;
use alloc::vec;
use alloc::vec::*;
use core::hash::{Hash, Hasher};
use core::mem;
use core::num::NonZeroU16;
use core::ops::Deref;
use hashbrown::{HashMap, HashSet};
use ttf_parser::{Face, FaceParsingError, GlyphId, Tag};
#[cfg(feature = "parallel")]
use rayon::prelude::*;
/// Defines the bounds for a glyph's outline in subpixels. A glyph's outline is always contained in
/// its bitmap.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct OutlineBounds {
/// Subpixel offset of the left-most edge of the glyph's outline.
pub xmin: f32,
/// Subpixel offset of the bottom-most edge of the glyph's outline.
pub ymin: f32,
/// The width of the outline in subpixels.
pub width: f32,
/// The height of the outline in subpixels.
pub height: f32,
}
impl Default for OutlineBounds {
fn default() -> Self {
Self {
xmin: 0.0,
ymin: 0.0,
width: 0.0,
height: 0.0,
}
}
}
impl OutlineBounds {
/// Scales the bounding box by the given factor.
#[inline(always)]
pub fn scale(&self, scale: f32) -> OutlineBounds {
OutlineBounds {
xmin: self.xmin * scale,
ymin: self.ymin * scale,
width: self.width * scale,
height: self.height * scale,
}
}
}
/// Encapsulates all layout information associated with a glyph for a fixed scale.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Metrics {
/// Whole pixel offset of the left-most edge of the bitmap. This may be negative to reflect the
/// glyph is positioned to the left of the origin.
pub xmin: i32,
/// Whole pixel offset of the bottom-most edge of the bitmap. This may be negative to reflect
/// the glyph is positioned below the baseline.
pub ymin: i32,
/// The width of the bitmap in whole pixels.
pub width: usize,
/// The height of the bitmap in whole pixels.
pub height: usize,
/// Advance width of the glyph in subpixels. Used in horizontal fonts.
pub advance_width: f32,
/// Advance height of the glyph in subpixels. Used in vertical fonts.
pub advance_height: f32,
/// The bounding box that contains the glyph's outline at the offsets specified by the font.
/// This is always a smaller box than the bitmap bounds.
pub bounds: OutlineBounds,
}
impl Default for Metrics {
fn default() -> Self {
Metrics {
xmin: 0,
ymin: 0,
width: 0,
height: 0,
advance_width: 0.0,
advance_height: 0.0,
bounds: OutlineBounds::default(),
}
}
}
/// Metrics associated with line positioning.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct LineMetrics {
/// The highest point that any glyph in the font extends to above the baseline. Typically
/// positive.
pub ascent: f32,
/// The lowest point that any glyph in the font extends to below the baseline. Typically
/// negative.
pub descent: f32,
/// The gap to leave between the descent of one line and the ascent of the next. This is of
/// course only a guideline given by the font's designers.
pub line_gap: f32,
/// A precalculated value for the height or width of the line depending on if the font is laid
/// out horizontally or vertically. It's calculated by: ascent - descent + line_gap.
pub new_line_size: f32,
}
impl LineMetrics {
/// Creates a new line metrics struct and computes the new line size.
fn new(ascent: i16, descent: i16, line_gap: i16) -> LineMetrics {
// Operations between this values can exceed i16, so we extend to i32 here.
let (ascent, descent, line_gap) = (ascent as i32, descent as i32, line_gap as i32);
LineMetrics {
ascent: ascent as f32,
descent: descent as f32,
line_gap: line_gap as f32,
new_line_size: (ascent - descent + line_gap) as f32,
}
}
/// Scales the line metrics by the given factor.
#[inline(always)]
fn scale(&self, scale: f32) -> LineMetrics {
LineMetrics {
ascent: self.ascent * scale,
descent: self.descent * scale,
line_gap: self.line_gap * scale,
new_line_size: self.new_line_size * scale,
}
}
}
/// Stores compiled geometry and metric information.
#[derive(Clone)]
pub(crate) struct Glyph {
pub v_lines: Vec<Line>,
pub m_lines: Vec<Line>,
advance_width: f32,
advance_height: f32,
pub bounds: OutlineBounds,
}
impl Default for Glyph {
fn default() -> Self {
Glyph {
v_lines: Vec::new(),
m_lines: Vec::new(),
advance_width: 0.0,
advance_height: 0.0,
bounds: OutlineBounds::default(),
}
}
}
/// Settings for controlling specific font and layout behavior.
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct FontSettings {
/// The default is 0. The index of the font to use if parsing a font collection.
pub collection_index: u32,
/// The default is 40. The scale in px the font geometry is optimized for. Fonts rendered at
/// the scale defined here will be the most optimal in terms of looks and performance. Glyphs
/// rendered smaller than this scale will look the same but perform slightly worse, while
/// glyphs rendered larger than this will looks worse but perform slightly better. The units of
/// the scale are pixels per Em unit.
pub scale: f32,
}
impl Default for FontSettings {
fn default() -> FontSettings {
FontSettings {
collection_index: 0,
scale: 40.0,
}
}
}
/// Represents a font. Fonts are immutable after creation and owns its own copy of the font data.
#[derive(Clone)]
pub struct Font {
name: Option<String>,
units_per_em: f32,
glyphs: Vec<Glyph>,
char_to_glyph: HashMap<char, NonZeroU16>,
horizontal_line_metrics: Option<LineMetrics>,
horizontal_kern: Option<HashMap<u32, i16>>,
vertical_line_metrics: Option<LineMetrics>,
settings: FontSettings,
hash: usize,
}
impl Hash for Font {
fn hash<H: Hasher>(&self, state: &mut H) {
self.hash.hash(state);
}
}
impl core::fmt::Debug for Font {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Font")
.field("name", &self.name)
.field("settings", &self.settings)
.field("units_per_em", &self.units_per_em)
.field("hash", &self.hash)
.finish()
}
}
/// Converts a ttf-parser FaceParsingError into a string.
fn convert_error(error: FaceParsingError) -> &'static str {
use FaceParsingError::*;
match error {
MalformedFont => "An attempt to read out of bounds detected.",
UnknownMagic => "Face data must start with 0x00010000, 0x74727565, 0x4F54544F or 0x74746366.",
FaceIndexOutOfBounds => "The face index is larger than the number of faces in the font.",
NoHeadTable => "The head table is missing or malformed.",
NoHheaTable => "The hhea table is missing or malformed.",
NoMaxpTable => "The maxp table is missing or malformed.",
}
}
fn convert_name(face: &Face) -> Option<String> {
for name in face.names() {
if name.name_id == 4 && name.is_unicode() {
return Some(unicode::decode_utf16(name.name));
}
}
None
}
impl Font {
/// Constructs a font from an array of bytes.
pub fn from_bytes<Data: Deref<Target = [u8]>>(data: Data, settings: FontSettings) -> FontResult<Font> {
let hash = crate::hash::hash(&data);
let face = match Face::from_slice(&data, settings.collection_index) {
Ok(f) => f,
Err(e) => return Err(convert_error(e)),
};
let name = convert_name(&face);
// Optionally get kerning values for the font. This should be a try block in the future.
let horizontal_kern: Option<HashMap<u32, i16>> = (|| {
let table: &[u8] = face.table_data(Tag::from_bytes(&b"kern"))?;
let table: TableKern = TableKern::new(table)?;
Some(table.horizontal_mappings)
})();
// Collect all the unique codepoint to glyph mappings.
let glyph_count = face.number_of_glyphs();
let mut seen_mappings = HashSet::with_capacity(glyph_count as usize);
let mut char_to_glyph = HashMap::with_capacity(glyph_count as usize);
seen_mappings.insert(0u16);
if let Some(subtable) = face.tables().cmap {
for subtable in subtable.subtables {
subtable.codepoints(|codepoint| {
if let Some(mapping) = subtable.glyph_index(codepoint) {
if let Some(mapping) = NonZeroU16::new(mapping.0) {
seen_mappings.insert(mapping.get());
char_to_glyph.insert(unsafe { mem::transmute(codepoint) }, mapping);
}
}
})
}
}
let units_per_em = face.units_per_em() as f32;
// Parse and store all unique codepoints.
let mut glyphs: Vec<Glyph> = vec::from_elem(Glyph::default(), glyph_count as usize);
let generate_glyph = |index: u16| -> Result<Glyph, &'static str> {
if index >= glyph_count {
return Err("Attempted to map a codepoint out of bounds.");
}
let mut glyph = Glyph::default();
let glyph_id = GlyphId(index);
if let Some(advance_width) = face.glyph_hor_advance(glyph_id) {
glyph.advance_width = advance_width as f32;
}
if let Some(advance_height) = face.glyph_ver_advance(glyph_id) {
glyph.advance_height = advance_height as f32;
}
let mut geometry = Geometry::new(settings.scale, units_per_em);
face.outline_glyph(glyph_id, &mut geometry);
geometry.finalize(&mut glyph);
Ok(glyph)
};
#[cfg(not(feature = "parallel"))]
for index in seen_mappings {
glyphs[index as usize] = generate_glyph(index)?;
}
#[cfg(feature = "parallel")]
{
let generated: Vec<(u16, Glyph)> = seen_mappings
.into_par_iter()
.map(|index| Ok((index, generate_glyph(index)?)))
.collect::<Result<_, _>>()?;
for (index, glyph) in generated {
glyphs[index as usize] = glyph;
}
}
// New line metrics.
let horizontal_line_metrics =
Some(LineMetrics::new(face.ascender(), face.descender(), face.line_gap()));
let vertical_line_metrics = if let Some(ascender) = face.vertical_ascender() {
Some(LineMetrics::new(
ascender,
face.vertical_descender().unwrap_or(0),
face.vertical_line_gap().unwrap_or(0),
))
} else {
None
};
Ok(Font {
name,
glyphs,
char_to_glyph,
units_per_em,
horizontal_line_metrics,
horizontal_kern,
vertical_line_metrics,
settings,
hash,
})
}
/// Returns all valid unicode codepoints that have mappings to glyph geometry in the font, along
/// with their associated index. This does not include grapheme cluster mappings. The mapped
/// NonZeroU16 index can be used in the _indexed font functions.
pub fn chars(&self) -> &HashMap<char, NonZeroU16> {
&self.char_to_glyph
}
/// Returns a precomputed hash for the font file.
pub fn file_hash(&self) -> usize {
self.hash
}
/// New line metrics for fonts that append characters to lines horizontally, and append new
/// lines vertically (above or below the current line). Only populated for fonts with the
/// appropriate metrics, none if it's missing.
/// # Arguments
///
/// * `px` - The size to scale the line metrics by. The units of the scale are pixels per Em
/// unit.
pub fn horizontal_line_metrics(&self, px: f32) -> Option<LineMetrics> {
let metrics = self.horizontal_line_metrics?;
Some(metrics.scale(self.scale_factor(px)))
}
/// New line metrics for fonts that append characters to lines vertically, and append new
/// lines horizontally (left or right of the current line). Only populated for fonts with the
/// appropriate metrics, none if it's missing.
/// # Arguments
///
/// * `px` - The size to scale the line metrics by. The units of the scale are pixels per Em
/// unit.
pub fn vertical_line_metrics(&self, px: f32) -> Option<LineMetrics> {
let metrics = self.vertical_line_metrics?;
Some(metrics.scale(self.scale_factor(px)))
}
/// Gets the font's units per em.
#[inline(always)]
pub fn units_per_em(&self) -> f32 {
self.units_per_em
}
/// Calculates the glyph's outline scale factor for a given px size. The units of the scale are
/// pixels per Em unit.
#[inline(always)]
pub fn scale_factor(&self, px: f32) -> f32 {
px / self.units_per_em
}
/// Retrieves the horizontal scaled kerning value for two adjacent characters.
/// # Arguments
///
/// * `left` - The character on the left hand side of the pairing.
/// * `right` - The character on the right hand side of the pairing.
/// * `px` - The size to scale the kerning value for. The units of the scale are pixels per Em
/// unit.
/// # Returns
///
/// * `Option<f32>` - The horizontal scaled kerning value if one is present in the font for the
/// given left and right pair, None otherwise.
#[inline(always)]
pub fn horizontal_kern(&self, left: char, right: char, px: f32) -> Option<f32> {
self.horizontal_kern_indexed(self.lookup_glyph_index(left), self.lookup_glyph_index(right), px)
}
/// Retrieves the horizontal scaled kerning value for two adjacent glyph indicies.
/// # Arguments
///
/// * `left` - The glyph index on the left hand side of the pairing.
/// * `right` - The glyph index on the right hand side of the pairing.
/// * `px` - The size to scale the kerning value for. The units of the scale are pixels per Em
/// unit.
/// # Returns
///
/// * `Option<f32>` - The horizontal scaled kerning value if one is present in the font for the
/// given left and right pair, None otherwise.
#[inline(always)]
pub fn horizontal_kern_indexed(&self, left: u16, right: u16, px: f32) -> Option<f32> {
let scale = self.scale_factor(px);
let map = self.horizontal_kern.as_ref()?;
let key = u32::from(left) << 16 | u32::from(right);
let value = map.get(&key)?;
Some((*value as f32) * scale)
}
/// Retrieves the layout metrics for the given character. If the character isn't present in the
/// font, then the layout for the font's default character is returned instead.
/// # Arguments
///
/// * `index` - The character in the font to to generate the layout metrics for.
/// * `px` - The size to generate the layout metrics for the character at. Cannot be negative.
/// The units of the scale are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the glyph.
#[inline]
pub fn metrics(&self, character: char, px: f32) -> Metrics {
self.metrics_indexed(self.lookup_glyph_index(character), px)
}
/// Retrieves the layout metrics at the given index. You normally want to be using
/// metrics(char, f32) instead, unless your glyphs are pre-indexed.
/// # Arguments
///
/// * `index` - The glyph index in the font to to generate the layout metrics for.
/// * `px` - The size to generate the layout metrics for the glyph at. Cannot be negative. The
/// units of the scale are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the glyph.
pub fn metrics_indexed(&self, index: u16, px: f32) -> Metrics {
let glyph = &self.glyphs[index as usize];
let scale = self.scale_factor(px);
let (metrics, _, _) = self.metrics_raw(scale, glyph, 0.0);
metrics
}
/// Internal function to generate the metrics, offset_x, and offset_y of the glyph.
fn metrics_raw(&self, scale: f32, glyph: &Glyph, offset: f32) -> (Metrics, f32, f32) {
let bounds = glyph.bounds.scale(scale);
let mut offset_x = fract(bounds.xmin + offset);
let mut offset_y = fract(1.0 - fract(bounds.height) - fract(bounds.ymin));
if is_negative(offset_x) {
offset_x += 1.0;
}
if is_negative(offset_y) {
offset_y += 1.0;
}
let metrics = Metrics {
xmin: as_i32(floor(bounds.xmin)),
ymin: as_i32(floor(bounds.ymin)),
width: as_i32(ceil(bounds.width + offset_x)) as usize,
height: as_i32(ceil(bounds.height + offset_y)) as usize,
advance_width: scale * glyph.advance_width,
advance_height: scale * glyph.advance_height,
bounds,
};
(metrics, offset_x, offset_y)
}
/// Retrieves the layout rasterized bitmap for the given raster config. If the raster config's
/// character isn't present in the font, then the layout and bitmap for the font's default
/// character's raster is returned instead.
/// # Arguments
///
/// * `config` - The settings to render the character at.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
/// the top left corner of the glyph.
#[inline]
pub fn rasterize_config(&self, config: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
self.rasterize_indexed(config.glyph_index, config.px)
}
/// Retrieves the layout metrics and rasterized bitmap for the given character. If the
/// character isn't present in the font, then the layout and bitmap for the font's default
/// character is returned instead.
/// # Arguments
///
/// * `character` - The character to rasterize.
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
/// are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
/// the top left corner of the glyph.
#[inline]
pub fn rasterize(&self, character: char, px: f32) -> (Metrics, Vec<u8>) {
self.rasterize_indexed(self.lookup_glyph_index(character), px)
}
/// Retrieves the layout rasterized bitmap for the given raster config. If the raster config's
/// character isn't present in the font, then the layout and bitmap for the font's default
/// character's raster is returned instead.
///
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
/// Taking these as RGB values will perform subpixel anti aliasing.
/// # Arguments
///
/// * `config` - The settings to render the character at.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
/// vec starts at the top left corner of the glyph.
#[inline]
pub fn rasterize_config_subpixel(&self, config: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
self.rasterize_indexed_subpixel(config.glyph_index, config.px)
}
/// Retrieves the layout metrics and rasterized bitmap for the given character. If the
/// character isn't present in the font, then the layout and bitmap for the font's default
/// character is returned instead.
///
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
/// Taking these as RGB values will perform subpixel anti aliasing.
/// # Arguments
///
/// * `character` - The character to rasterize.
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
/// are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
/// vec starts at the top left corner of the glyph.
#[inline]
pub fn rasterize_subpixel(&self, character: char, px: f32) -> (Metrics, Vec<u8>) {
self.rasterize_indexed_subpixel(self.lookup_glyph_index(character), px)
}
/// Retrieves the layout metrics and rasterized bitmap at the given index. You normally want to
/// be using rasterize(char, f32) instead, unless your glyphs are pre-indexed.
/// # Arguments
///
/// * `index` - The glyph index in the font to rasterize.
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
/// are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Coverage vector for the glyph. Coverage is a linear scale where 0 represents
/// 0% coverage of that pixel by the glyph and 255 represents 100% coverage. The vec starts at
/// the top left corner of the glyph.
pub fn rasterize_indexed(&self, index: u16, px: f32) -> (Metrics, Vec<u8>) {
if px <= 0.0 {
return (Metrics::default(), Vec::new());
}
let glyph = &self.glyphs[index as usize];
let scale = self.scale_factor(px);
let (metrics, offset_x, offset_y) = self.metrics_raw(scale, glyph, 0.0);
let mut canvas = Raster::new(metrics.width, metrics.height);
canvas.draw(&glyph, scale, scale, offset_x, offset_y);
(metrics, canvas.get_bitmap())
}
/// Retrieves the layout metrics and rasterized bitmap at the given index. You normally want to
/// be using rasterize(char, f32) instead, unless your glyphs are pre-indexed.
///
/// This will perform the operation with the width multiplied by 3, as to simulate subpixels.
/// Taking these as RGB values will perform subpixel anti aliasing.
/// # Arguments
///
/// * `index` - The glyph index in the font to rasterize.
/// * `px` - The size to render the character at. Cannot be negative. The units of the scale
/// are pixels per Em unit.
/// # Returns
///
/// * `Metrics` - Sizing and positioning metadata for the rasterized glyph.
/// * `Vec<u8>` - Swizzled RGB coverage vector for the glyph. Coverage is a linear scale where 0
/// represents 0% coverage of that subpixel by the glyph and 255 represents 100% coverage. The
/// vec starts at the top left corner of the glyph.
pub fn rasterize_indexed_subpixel(&self, index: u16, px: f32) -> (Metrics, Vec<u8>) {
if px <= 0.0 {
return (Metrics::default(), Vec::new());
}
let glyph = &self.glyphs[index as usize];
let scale = self.scale_factor(px);
let (metrics, offset_x, offset_y) = self.metrics_raw(scale, glyph, 0.0);
let mut canvas = Raster::new(metrics.width * 3, metrics.height);
canvas.draw(&glyph, scale * 3.0, scale, offset_x, offset_y);
(metrics, canvas.get_bitmap())
}
/// Finds the internal glyph index for the given character. If the character is not present in
/// the font then 0 is returned.
#[inline]
pub fn lookup_glyph_index(&self, character: char) -> u16 {
// This is safe, Option<NonZeroU16> is documented to have the same layout as u16.
unsafe { mem::transmute::<Option<NonZeroU16>, u16>(self.char_to_glyph.get(&character).copied()) }
}
/// Gets the total glyphs in the font.
pub fn glyph_count(&self) -> u16 {
self.glyphs.len() as u16
}
}

123
third-party/vendor/fontdue/src/hash.rs vendored Normal file
View file

@ -0,0 +1,123 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! # Fx Hash
//!
//! This hashing algorithm was extracted from the Rustc compiler. This is the same hashing
//! algorithm used for some internal operations in Firefox. The strength of this algorithm
//! is in hashing 8 bytes at a time on 64-bit platforms, where the FNV algorithm works on one
//! byte at a time.
//!
//! ## Disclaimer
//!
//! It is **not a cryptographically secure** hash, so it is strongly recommended that you do
//! not use this hash for cryptographic purproses. Furthermore, this hashing algorithm was
//! not designed to prevent any attacks for determining collisions which could be used to
//! potentially cause quadratic behavior in `HashMap`s. So it is not recommended to expose
//! this hash in places where collissions or DDOS attacks may be a concern.
use core::convert::TryInto;
use core::ops::BitXor;
const ROTATE: u32 = 5;
const SEED64: u64 = 0x517cc1b727220a95;
const SEED32: u32 = (SEED64 & 0xFFFF_FFFF) as u32;
#[cfg(target_pointer_width = "32")]
const SEED: usize = SEED32 as usize;
#[cfg(target_pointer_width = "64")]
const SEED: usize = SEED64 as usize;
trait HashWord {
fn hash_word(&mut self, word: Self);
}
impl HashWord for usize {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED);
}
}
impl HashWord for u32 {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED32);
}
}
impl HashWord for u64 {
#[inline]
fn hash_word(&mut self, word: Self) {
*self = self.rotate_left(ROTATE).bitxor(word).wrapping_mul(SEED64);
}
}
#[cfg(target_endian = "little")]
fn read_u32(buf: &[u8]) -> u32 {
u32::from_le_bytes(buf[..4].try_into().unwrap())
}
#[cfg(target_endian = "little")]
fn read_u64(buf: &[u8]) -> u64 {
u64::from_le_bytes(buf[..8].try_into().unwrap())
}
#[cfg(target_endian = "big")]
fn read_u32(buf: &[u8]) -> u32 {
u32::from_be_bytes(buf[..4].try_into().unwrap())
}
#[cfg(target_endian = "big")]
fn read_u64(buf: &[u8]) -> u64 {
u64::from_be_bytes(buf[..8].try_into().unwrap())
}
#[inline]
#[cfg(target_pointer_width = "32")]
fn write(initial_state: usize, mut bytes: &[u8]) -> usize {
let mut hash = initial_state as u32;
while bytes.len() >= 4 {
let n = read_u32(bytes);
hash.hash_word(n);
bytes = bytes.split_at(4).1;
}
for byte in bytes {
hash.hash_word(*byte as u32);
}
hash as usize
}
#[inline]
#[cfg(target_pointer_width = "64")]
fn write(initial_state: usize, mut bytes: &[u8]) -> usize {
let mut hash = initial_state as u64;
while bytes.len() >= 8 {
let n = read_u64(bytes);
hash.hash_word(n);
bytes = bytes.split_at(8).1;
}
if bytes.len() >= 4 {
let n = read_u32(bytes);
hash.hash_word(n as u64);
bytes = bytes.split_at(4).1;
}
for byte in bytes {
hash.hash_word(*byte as u64);
}
hash as usize
}
pub fn hash(bytes: &[u8]) -> usize {
write(0usize, bytes)
}

567
third-party/vendor/fontdue/src/layout.rs vendored Normal file
View file

@ -0,0 +1,567 @@
pub use crate::unicode::CharacterData;
use crate::unicode::{read_utf8, LinebreakData, Linebreaker, LINEBREAK_NONE};
use crate::Font;
use crate::{
platform::{ceil, floor},
Metrics,
};
use alloc::vec::*;
use core::borrow::Borrow;
use core::hash::{Hash, Hasher};
/// Horizontal alignment options for text when a max_width is provided.
#[derive(Copy, Clone, PartialEq)]
pub enum HorizontalAlign {
/// Aligns text to the left of the region defined by the max_width.
Left,
/// Aligns text to the center of the region defined by the max_width.
Center,
/// Aligns text to the right of the region defined by the max_width.
Right,
}
/// Vertical alignment options for text when a max_height is provided.
#[derive(Copy, Clone, PartialEq)]
pub enum VerticalAlign {
/// Aligns text to the top of the region defined by the max_height.
Top,
/// Aligns text to the middle of the region defined by the max_height.
Middle,
/// Aligns text to the bottom of the region defined by the max_height.
Bottom,
}
/// Wrap style is a hint for how strings of text should be wrapped to the next line. Line wrapping
/// can happen when the max width/height is reached.
#[derive(Copy, Clone, PartialEq)]
pub enum WrapStyle {
/// Word will break lines by the Unicode line breaking algorithm (Standard Annex #14) This will
/// generally break lines where you expect them to be broken at and will preserve words.
Word,
/// Letter will not preserve words, breaking into a new line after the nearest letter.
Letter,
}
/// The direction that the Y coordinate increases in. Layout needs to be aware of your coordinate
/// system to place the glyphs correctly.
#[derive(Copy, Clone, PartialEq)]
pub enum CoordinateSystem {
/// The Y coordinate increases up relative to the window or image. The higher up on the window,
/// the more positive Y becomes.
PositiveYUp,
/// The Y coordinate increases down relative to the window or image. The lower down on the
/// window, the more positive Y becomes.
PositiveYDown,
}
/// Settings to configure how text layout is constrained. Text layout is considered best effort and
/// layout may violate the constraints defined here if they prevent text from being laid out.
#[derive(Copy, Clone, PartialEq)]
pub struct LayoutSettings {
/// The leftmost boundary of the text region.
pub x: f32,
/// The topmost boundary of the text region.
pub y: f32,
/// An optional rightmost boundary on the text region. A line of text that exceeds the
/// max_width is wrapped to the line below. If the width of a glyph is larger than the
/// max_width, the glyph will overflow past the max_width. The application is responsible for
/// handling the overflow.
pub max_width: Option<f32>,
/// An optional bottom boundary on the text region. This is used for positioning the
/// vertical_align option. Text that exceeds the defined max_height will overflow past it. The
/// application is responsible for handling the overflow.
pub max_height: Option<f32>,
/// The default is Left. This option does nothing if the max_width isn't set.
pub horizontal_align: HorizontalAlign,
/// The default is Top. This option does nothing if the max_height isn't set.
pub vertical_align: VerticalAlign,
/// The height of each line as a multiplier of the default.
pub line_height: f32,
/// The default is Word. Wrap style is a hint for how strings of text should be wrapped to the
/// next line. Line wrapping can happen when the max width/height is reached.
pub wrap_style: WrapStyle,
/// The default is true. This option enables hard breaks, like new line characters, to
/// prematurely wrap lines. If false, hard breaks will not prematurely create a new line.
pub wrap_hard_breaks: bool,
}
impl Default for LayoutSettings {
fn default() -> LayoutSettings {
LayoutSettings {
x: 0.0,
y: 0.0,
max_width: None,
max_height: None,
horizontal_align: HorizontalAlign::Left,
vertical_align: VerticalAlign::Top,
line_height: 1.0,
wrap_style: WrapStyle::Word,
wrap_hard_breaks: true,
}
}
}
/// Configuration for rasterizing a glyph. This struct is also a hashable key that can be used to
/// uniquely identify a rasterized glyph for applications that want to cache glyphs.
#[derive(Debug, Copy, Clone)]
pub struct GlyphRasterConfig {
/// The glyph index represented by the glyph being positioned.
pub glyph_index: u16,
/// The scale of the glyph being positioned in px.
pub px: f32,
/// The hash of the font used in layout to raster the glyph.
pub font_hash: usize,
}
impl Hash for GlyphRasterConfig {
fn hash<H: Hasher>(&self, state: &mut H) {
self.glyph_index.hash(state);
self.px.to_bits().hash(state);
self.font_hash.hash(state);
}
}
impl PartialEq for GlyphRasterConfig {
fn eq(&self, other: &Self) -> bool {
self.glyph_index == other.glyph_index && self.px == other.px && self.font_hash == other.font_hash
}
}
impl Eq for GlyphRasterConfig {}
/// A positioned scaled glyph.
#[derive(Debug, Copy, Clone)]
pub struct GlyphPosition<U: Copy + Clone = ()> {
/// Hashable key that can be used to uniquely identify a rasterized glyph.
pub key: GlyphRasterConfig,
/// The index of the font used to generate this glyph position.
pub font_index: usize,
/// The associated character that generated this glyph. A character may generate multiple
/// glyphs.
pub parent: char,
/// The xmin of the glyph bounding box. This represents the left side of the glyph. Dimensions
/// are in pixels, and are always whole numbers.
pub x: f32,
/// The ymin of the glyph bounding box. If your coordinate system is PositiveYUp, this
/// represents the bottom side of the glyph. If your coordinate system is PositiveYDown, this
/// represents the top side of the glyph. This is like this so that (y + height) always produces
/// the other bound for the glyph.
pub y: f32,
/// The width of the glyph. Dimensions are in pixels.
pub width: usize,
/// The height of the glyph. Dimensions are in pixels.
pub height: usize,
/// The byte offset into the original string used in the append call which created
/// this glyph.
pub byte_offset: usize,
/// Additional metadata associated with the character used to generate this glyph.
pub char_data: CharacterData,
/// Custom user data associated with the text styled used to generate this glyph.
pub user_data: U,
}
/// A style description for a segment of text.
pub struct TextStyle<'a, U: Copy + Clone = ()> {
/// The text to layout.
pub text: &'a str,
/// The scale of the text in pixel units. The units of the scale are pixels per Em unit.
pub px: f32,
/// The font to layout the text in.
pub font_index: usize,
/// Additional user data to associate with glyphs produced by this text style.
pub user_data: U,
}
impl<'a> TextStyle<'a> {
pub fn new(text: &'a str, px: f32, font_index: usize) -> TextStyle<'a> {
TextStyle {
text,
px,
font_index,
user_data: (),
}
}
}
impl<'a, U: Copy + Clone> TextStyle<'a, U> {
pub fn with_user_data(text: &'a str, px: f32, font_index: usize, user_data: U) -> TextStyle<'a, U> {
TextStyle {
text,
px,
font_index,
user_data,
}
}
}
/// Metrics about a positioned line.
#[derive(Debug, Copy, Clone)]
pub struct LinePosition {
/// The y coordinate of the baseline of this line, in pixels.
pub baseline_y: f32,
/// How much empty space is left at the end of the line before any alignment. If no max width is
/// specified, f32::MAX is used.
pub padding: f32,
/// The highest point that any glyph in the font extends to above the baseline. Typically
/// positive. If there are multiple styles on this line, this is their max value.
pub max_ascent: f32,
/// The lowest point that any glyph in the font extends to below the baseline. Typically
/// negative. If there are multiple styles on this line, this is their min value.
pub min_descent: f32,
/// The gap to leave between the descent of one line and the ascent of the next. This is of
/// course only a guideline given by the font's designers. If there are multiple styles on this
/// line, this is their max value.
pub max_line_gap: f32,
/// A precalculated value for the of the line depending. It's calculated by: ascent - descent +
/// line_gap. If there are multiple styles on this line, this is their max value.
pub max_new_line_size: f32,
/// The GlyphPosition index of the first glyph in the line.
pub glyph_start: usize,
/// The GlyphPosition index of the last glyph in the line.
pub glyph_end: usize,
/// The x offset into the first layout pass.
tracking_x: f32,
}
impl Default for LinePosition {
fn default() -> Self {
LinePosition {
baseline_y: 0.0,
padding: 0.0,
max_ascent: 0.0,
min_descent: 0.0,
max_line_gap: 0.0,
max_new_line_size: 0.0,
glyph_start: 0,
glyph_end: 0,
tracking_x: 0.0,
}
}
}
/// Text layout requires a small amount of heap usage which is contained in the Layout struct. This
/// context is reused between layout calls. Reusing the Layout struct will greatly reduce memory
/// allocations and is advisable for performance.
pub struct Layout<U: Copy + Clone = ()> {
/// Marks if layout should be performed as if the Y axis is flipped (Positive Y incrementing
/// down instead of up).
flip: bool,
/// Origin position. Left side of the region text is being laid out in.
x: f32,
/// Origin position. Top side of the region text is being laid out in.
y: f32,
/// A mask to filter only linebreak types being requested.
wrap_mask: LinebreakData,
/// The max width of the region text is being laid out in.
max_width: f32,
/// The max height of the region text is being laid out in.
max_height: f32,
/// A multiplier for how text fills unused vertical space.
vertical_align: f32,
/// A multiplier for how text fills unused horizontal space.
horizontal_align: f32,
/// A multiplier for the amount of space between lines.
line_height: f32,
/// The current height of all laid out text.
height: f32,
/// Finalized glyph state.
output: Vec<GlyphPosition<U>>,
/// Intermediate glyph state.
glyphs: Vec<GlyphPosition<U>>,
/// Linebreak state. Used to derive linebreaks from past glyphs.
linebreaker: Linebreaker,
/// The current highest priority linebreak (Hard > Soft > None).
linebreak_prev: LinebreakData,
/// The x position that the glyph that has the current highest priority linebreak status starts
/// at.
linebreak_pos: f32,
/// The index of the glyph that has the current highest priority linebreak status. This glyph is
/// the last glyph on a line if a linebreak is required.
linebreak_idx: usize,
/// Layout state of each line currently laid out. This always has at least 1 element.
line_metrics: Vec<LinePosition>,
/// The x position the next glyph starts at.
current_pos: f32,
/// The ceil(ascent) of the current style.
current_ascent: f32,
/// The ceil(descent) of the current style.
current_descent: f32,
/// The ceil(line_gap) of the current style.
current_line_gap: f32,
/// The ceil(new_line_size) of the current style.
current_new_line: f32,
/// The x position the current line starts at.
start_pos: f32,
/// The settings currently being used for layout.
settings: LayoutSettings,
}
impl<'a, U: Copy + Clone> Layout<U> {
/// Creates a layout instance. This requires the direction that the Y coordinate increases in.
/// Layout needs to be aware of your coordinate system to place the glyphs correctly.
pub fn new(coordinate_system: CoordinateSystem) -> Layout<U> {
let settings = LayoutSettings::default();
let mut layout = Layout {
flip: coordinate_system == CoordinateSystem::PositiveYDown,
x: 0.0,
y: 0.0,
wrap_mask: LINEBREAK_NONE,
max_width: 0.0,
max_height: 0.0,
vertical_align: 0.0,
horizontal_align: 0.0,
line_height: 1.0,
output: Vec::new(),
glyphs: Vec::new(),
line_metrics: Vec::new(),
linebreaker: Linebreaker::new(),
linebreak_prev: LINEBREAK_NONE,
linebreak_pos: 0.0,
linebreak_idx: 0,
current_pos: 0.0,
current_ascent: 0.0,
current_descent: 0.0,
current_line_gap: 0.0,
current_new_line: 0.0,
start_pos: 0.0,
height: 0.0,
settings,
};
layout.reset(&settings);
layout
}
/// Resets the current layout settings and clears all appended text.
pub fn reset(&mut self, settings: &LayoutSettings) {
self.settings = *settings;
self.x = settings.x;
self.y = settings.y;
self.wrap_mask = LinebreakData::from_mask(
settings.wrap_style == WrapStyle::Word,
settings.wrap_hard_breaks,
settings.max_width.is_some(),
);
self.max_width = settings.max_width.unwrap_or(core::f32::MAX);
self.max_height = settings.max_height.unwrap_or(core::f32::MAX);
self.vertical_align = if settings.max_height.is_none() {
0.0
} else {
match settings.vertical_align {
VerticalAlign::Top => 0.0,
VerticalAlign::Middle => 0.5,
VerticalAlign::Bottom => 1.0,
}
};
self.horizontal_align = if settings.max_width.is_none() {
0.0
} else {
match settings.horizontal_align {
HorizontalAlign::Left => 0.0,
HorizontalAlign::Center => 0.5,
HorizontalAlign::Right => 1.0,
}
};
self.line_height = settings.line_height;
self.clear();
}
/// Keeps current layout settings but clears all appended text.
pub fn clear(&mut self) {
self.glyphs.clear();
self.output.clear();
self.line_metrics.clear();
self.line_metrics.push(LinePosition::default());
self.linebreaker.reset();
self.linebreak_prev = LINEBREAK_NONE;
self.linebreak_pos = 0.0;
self.linebreak_idx = 0;
self.current_pos = 0.0;
self.current_ascent = 0.0;
self.current_descent = 0.0;
self.current_line_gap = 0.0;
self.current_new_line = 0.0;
self.start_pos = 0.0;
self.height = 0.0;
}
/// Gets the current height of the appended text.
pub fn height(&self) -> f32 {
if let Some(line) = self.line_metrics.last() {
self.height + line.max_new_line_size
} else {
0.0
}
}
/// Gets the currently positioned lines. If there are no lines positioned, this returns none.
pub fn lines(&'a self) -> Option<&'a Vec<LinePosition>> {
if self.glyphs.is_empty() {
None
} else {
Some(&self.line_metrics)
}
}
/// Performs layout for text horizontally, and wrapping vertically. This makes a best effort
/// attempt at laying out the text defined in the given styles with the provided layout
/// settings. Text may overflow out of the bounds defined in the layout settings and it's up
/// to the application to decide how to deal with this.
///
/// Characters from the input string can only be omitted from the output, they are never
/// reordered. The output buffer will always contain characters in the order they were defined
/// in the styles.
pub fn append<T: Borrow<Font>>(&mut self, fonts: &[T], style: &TextStyle<U>) {
// The first layout pass requires some text.
if style.text.is_empty() {
return;
}
let font: &Font = &fonts[style.font_index].borrow();
if let Some(metrics) = font.horizontal_line_metrics(style.px) {
self.current_ascent = ceil(metrics.ascent);
self.current_new_line = ceil(metrics.new_line_size);
self.current_descent = ceil(metrics.descent);
self.current_line_gap = ceil(metrics.line_gap);
if let Some(line) = self.line_metrics.last_mut() {
if self.current_ascent > line.max_ascent {
line.max_ascent = self.current_ascent;
}
if self.current_descent < line.min_descent {
line.min_descent = self.current_descent;
}
if self.current_line_gap > line.max_line_gap {
line.max_line_gap = self.current_line_gap;
}
if self.current_new_line > line.max_new_line_size {
line.max_new_line_size = self.current_new_line;
}
}
}
let mut byte_offset = 0;
while byte_offset < style.text.len() {
let prev_byte_offset = byte_offset;
let character = read_utf8(style.text.as_bytes(), &mut byte_offset);
let linebreak = self.linebreaker.next(character).mask(self.wrap_mask);
let glyph_index = font.lookup_glyph_index(character);
let char_data = CharacterData::classify(character, glyph_index);
let metrics = if !char_data.is_control() {
font.metrics_indexed(glyph_index, style.px)
} else {
Metrics::default()
};
let advance = ceil(metrics.advance_width);
if linebreak >= self.linebreak_prev {
self.linebreak_prev = linebreak;
self.linebreak_pos = self.current_pos;
self.linebreak_idx = self.glyphs.len().saturating_sub(1); // Mark the previous glyph
}
// Perform a linebreak
if linebreak.is_hard() || (self.current_pos - self.start_pos + advance > self.max_width) {
self.linebreak_prev = LINEBREAK_NONE;
let mut next_glyph_start = self.glyphs().len();
if let Some(line) = self.line_metrics.last_mut() {
line.glyph_end = self.linebreak_idx;
line.padding = self.max_width - (self.linebreak_pos - self.start_pos);
self.height += line.max_new_line_size * self.line_height;
next_glyph_start = self.linebreak_idx + 1;
}
self.line_metrics.push(LinePosition {
baseline_y: 0.0,
padding: 0.0,
max_ascent: self.current_ascent,
min_descent: self.current_descent,
max_line_gap: self.current_line_gap,
max_new_line_size: self.current_new_line,
glyph_start: next_glyph_start,
glyph_end: 0,
tracking_x: self.linebreak_pos,
});
self.start_pos = self.linebreak_pos;
}
let y = if self.flip {
floor(-metrics.bounds.height - metrics.bounds.ymin) // PositiveYDown
} else {
floor(metrics.bounds.ymin) // PositiveYUp
};
self.glyphs.push(GlyphPosition {
key: GlyphRasterConfig {
glyph_index: glyph_index as u16,
px: style.px,
font_hash: font.file_hash(),
},
font_index: style.font_index,
parent: character,
byte_offset: prev_byte_offset,
x: floor(self.current_pos + metrics.bounds.xmin),
y,
width: metrics.width,
height: metrics.height,
char_data,
user_data: style.user_data,
});
self.current_pos += advance;
}
if let Some(line) = self.line_metrics.last_mut() {
line.padding = self.max_width - (self.current_pos - self.start_pos);
line.glyph_end = self.glyphs.len().saturating_sub(1);
}
self.finalize();
}
fn finalize(&mut self) {
// The second layout pass requires at least 1 glyph to layout.
if self.glyphs.is_empty() {
return;
}
unsafe { self.output.set_len(0) };
self.output.reserve(self.glyphs.len());
let dir = if self.flip {
-1.0 // PositiveYDown
} else {
1.0 // PositiveYUp
};
let mut baseline_y = self.y - dir * floor((self.max_height - self.height()) * self.vertical_align);
let mut idx = 0;
for line in &mut self.line_metrics {
let x_padding = self.x - line.tracking_x + floor(line.padding * self.horizontal_align);
baseline_y -= dir * line.max_ascent;
line.baseline_y = baseline_y;
while idx <= line.glyph_end {
let mut glyph = self.glyphs[idx];
glyph.x += x_padding;
glyph.y += baseline_y;
self.output.push(glyph);
idx += 1;
}
baseline_y -= dir * (line.max_new_line_size * self.line_height - line.max_ascent);
}
}
/// Gets the currently laid out glyphs.
pub fn glyphs(&'a self) -> &'a Vec<GlyphPosition<U>> {
&self.output
}
/// Gets the settings currently being used for layout.
pub fn settings(&self) -> &LayoutSettings {
&self.settings
}
}

26
third-party/vendor/fontdue/src/lib.rs vendored Normal file
View file

@ -0,0 +1,26 @@
//! Fontdue is a font parser, rasterizer, and layout tool.
//!
//! This is a no_std crate, but still requires the alloc crate.
#![no_std]
#![allow(dead_code)]
#![allow(clippy::style)]
#![allow(clippy::complexity)]
#![allow(clippy::misnamed_getters)]
extern crate alloc;
mod font;
mod hash;
/// Tools for laying out strings of text.
pub mod layout;
mod math;
mod platform;
mod raster;
mod table;
mod unicode;
pub use crate::font::*;
/// Alias for Result<T, &'static str>.
pub type FontResult<T> = Result<T, &'static str>;

480
third-party/vendor/fontdue/src/math.rs vendored Normal file
View file

@ -0,0 +1,480 @@
use crate::platform::{self, abs, atan2, f32x4, sqrt};
use crate::{Glyph, OutlineBounds};
use alloc::vec;
use alloc::vec::*;
#[derive(Copy, Clone, PartialEq, Debug)]
struct AABB {
/// Coordinate of the left-most edge.
xmin: f32,
/// Coordinate of the right-most edge.
xmax: f32,
/// Coordinate of the bottom-most edge.
ymin: f32,
/// Coordinate of the top-most edge.
ymax: f32,
}
impl Default for AABB {
fn default() -> Self {
AABB {
xmin: 0.0,
xmax: 0.0,
ymin: 0.0,
ymax: 0.0,
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct CubeCurve {
a: Point,
b: Point,
c: Point,
d: Point,
}
impl CubeCurve {
const fn new(a: Point, b: Point, c: Point, d: Point) -> CubeCurve {
CubeCurve {
a,
b,
c,
d,
}
}
fn scale(&self, scale: f32) -> CubeCurve {
CubeCurve {
a: self.a.scale(scale),
b: self.b.scale(scale),
c: self.c.scale(scale),
d: self.d.scale(scale),
}
}
fn is_flat(&self, threshold: f32) -> bool {
let (d1, d2, d3, d4) = f32x4::new(
self.a.distance_squared(self.b),
self.b.distance_squared(self.c),
self.c.distance_squared(self.d),
self.a.distance_squared(self.d),
)
.sqrt()
.copied();
(d1 + d2 + d3) < threshold * d4
}
fn split(&self) -> (CubeCurve, CubeCurve) {
let q0 = self.a.midpoint(self.b);
let q1 = self.b.midpoint(self.c);
let q2 = self.c.midpoint(self.d);
let r0 = q0.midpoint(q1);
let r1 = q1.midpoint(q2);
let s0 = r0.midpoint(r1);
(CubeCurve::new(self.a, q0, r0, s0), CubeCurve::new(s0, r1, q2, self.d))
}
/// The point at time t in the curve.
fn point(&self, t: f32) -> Point {
let tm = 1.0 - t;
let a = tm * tm * tm;
let b = 3.0 * (tm * tm) * t;
let c = 3.0 * tm * (t * t);
let d = t * t * t;
let x = a * self.a.x + b * self.b.x + c * self.c.x + d * self.d.x;
let y = a * self.a.y + b * self.b.y + c * self.c.y + d * self.d.y;
Point::new(x, y)
}
/// The slope of the tangent line at time t.
fn slope(&self, t: f32) -> (f32, f32) {
let tm = 1.0 - t;
let a = 3.0 * (tm * tm);
let b = 6.0 * tm * t;
let c = 3.0 * (t * t);
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x) + c * (self.d.x - self.c.x);
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y) + c * (self.d.y - self.c.y);
(x, y)
}
/// The angle of the tangent line at time t in rads.
fn angle(&self, t: f32) -> f32 {
let (x, y) = self.slope(t);
abs(atan2(x, y))
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
struct QuadCurve {
a: Point,
b: Point,
c: Point,
}
impl QuadCurve {
fn new(a: Point, b: Point, c: Point) -> QuadCurve {
QuadCurve {
a,
b,
c,
}
}
fn scale(&self, scale: f32) -> QuadCurve {
QuadCurve {
a: self.a.scale(scale),
b: self.b.scale(scale),
c: self.c.scale(scale),
}
}
fn is_flat(&self, threshold: f32) -> bool {
let (d1, d2, d3, _) = f32x4::new(
self.a.distance_squared(self.b),
self.b.distance_squared(self.c),
self.a.distance_squared(self.c),
1.0,
)
.sqrt()
.copied();
(d1 + d2) < threshold * d3
}
fn split(&self) -> (QuadCurve, QuadCurve) {
let q0 = self.a.midpoint(self.b);
let q1 = self.b.midpoint(self.c);
let r0 = q0.midpoint(q1);
(QuadCurve::new(self.a, q0, r0), QuadCurve::new(r0, q1, self.c))
}
/// The point at time t in the curve.
fn point(&self, t: f32) -> Point {
let tm = 1.0 - t;
let a = tm * tm;
let b = 2.0 * tm * t;
let c = t * t;
let x = a * self.a.x + b * self.b.x + c * self.c.x;
let y = a * self.a.y + b * self.b.y + c * self.c.y;
Point::new(x, y)
}
/// The slope of the tangent line at time t.
fn slope(&self, t: f32) -> (f32, f32) {
let tm = 1.0 - t;
let a = 2.0 * tm;
let b = 2.0 * t;
let x = a * (self.b.x - self.a.x) + b * (self.c.x - self.b.x);
let y = a * (self.b.y - self.a.y) + b * (self.c.y - self.b.y);
(x, y)
}
/// The angle of the tangent line at time t in rads.
fn angle(&self, t: f32) -> f32 {
let (x, y) = self.slope(t);
abs(atan2(x, y))
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Point {
/// Absolute X coordinate.
pub x: f32,
/// Absolute Y coordinate.
pub y: f32,
}
impl Default for Point {
fn default() -> Self {
Point {
x: 0.0,
y: 0.0,
}
}
}
impl Point {
pub const fn new(x: f32, y: f32) -> Point {
Point {
x,
y,
}
}
pub fn scale(&self, scale: f32) -> Point {
Point {
x: self.x * scale,
y: self.y * scale,
}
}
pub fn distance_squared(&self, other: Point) -> f32 {
let x = self.x - other.x;
let y = self.y - other.y;
x * x + y * y
}
pub fn distance(&self, other: Point) -> f32 {
let x = self.x - other.x;
let y = self.y - other.y;
sqrt(x * x + y * y)
}
pub fn midpoint(&self, other: Point) -> Point {
Point {
x: (self.x + other.x) / 2.0,
y: (self.y + other.y) / 2.0,
}
}
}
#[derive(Copy, Clone)]
pub struct Line {
/// X0, Y0, X1, Y1.
pub coords: f32x4,
/// start_x_nudge, start_y_nudge, end_x_nudge, end_y_nudge.
pub nudge: f32x4,
/// x_first_adj, y_first_adj, none, none.
pub adjustment: f32x4,
/// tdx, tdy, dx, dy.
pub params: f32x4,
}
impl Line {
pub fn new(start: Point, end: Point) -> Line {
// Floor adjustment and nudge: 0.0, 0
// Ceil adjustment and nudge: 1.0, 1
const FLOOR_NUDGE: u32 = 0;
const CEIL_NUDGE: u32 = 1;
let (x_start_nudge, x_first_adj) = if end.x >= start.x {
(FLOOR_NUDGE, 1.0)
} else {
(CEIL_NUDGE, 0.0)
};
let (y_start_nudge, y_first_adj) = if end.y >= start.y {
(FLOOR_NUDGE, 1.0)
} else {
(CEIL_NUDGE, 0.0)
};
let x_end_nudge = if end.x > start.x {
CEIL_NUDGE
} else {
FLOOR_NUDGE
};
let y_end_nudge = if end.y > start.y {
CEIL_NUDGE
} else {
FLOOR_NUDGE
};
let dx = end.x - start.x;
let dy = end.y - start.y;
let tdx = if dx == 0.0 {
core::f32::MAX
} else {
1.0 / dx
};
let tdy = 1.0 / dy;
Line {
coords: f32x4::new(start.x, start.y, end.x, end.y),
nudge: f32x4::new_u32(x_start_nudge, y_start_nudge, x_end_nudge, y_end_nudge),
adjustment: f32x4::new(x_first_adj, y_first_adj, 0.0, 0.0),
params: f32x4::new(tdx, tdy, dx, dy),
}
}
fn reposition(&mut self, bounds: AABB, reverse: bool) {
let (mut x0, mut y0, mut x1, mut y1) = if !reverse {
self.coords.copied()
} else {
let (x0, y0, x1, y1) = self.coords.copied();
(x1, y1, x0, y0)
};
x0 -= bounds.xmin;
y0 -= bounds.ymax;
y0 = abs(y0);
x1 -= bounds.xmin;
y1 -= bounds.ymax;
y1 = abs(y1);
*self = Self::new(Point::new(x0, y0), Point::new(x1, y1));
}
}
#[derive(Clone)]
pub struct Geometry {
v_lines: Vec<Line>,
m_lines: Vec<Line>,
effective_bounds: AABB,
start_point: Point,
previous_point: Point,
area: f32,
reverse_points: bool,
max_area: f32,
}
struct Segment {
a: Point,
at: f32,
c: Point,
ct: f32,
}
impl Segment {
const fn new(a: Point, at: f32, c: Point, ct: f32) -> Segment {
Segment {
a,
at,
c,
ct,
}
}
}
impl ttf_parser::OutlineBuilder for Geometry {
fn move_to(&mut self, x0: f32, y0: f32) {
let next_point = Point::new(x0, y0);
self.start_point = next_point;
self.previous_point = next_point;
}
fn line_to(&mut self, x0: f32, y0: f32) {
let next_point = Point::new(x0, y0);
self.push(self.previous_point, next_point);
self.previous_point = next_point;
}
fn quad_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32) {
let control_point = Point::new(x0, y0);
let next_point = Point::new(x1, y1);
let curve = QuadCurve::new(self.previous_point, control_point, next_point);
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
while let Some(seg) = stack.pop() {
let bt = (seg.at + seg.ct) * 0.5;
let b = curve.point(bt);
// This is twice the triangle area
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
if platform::abs(area) > self.max_area {
stack.push(Segment::new(seg.a, seg.at, b, bt));
stack.push(Segment::new(b, bt, seg.c, seg.ct));
} else {
self.push(seg.a, seg.c);
}
}
self.previous_point = next_point;
}
fn curve_to(&mut self, x0: f32, y0: f32, x1: f32, y1: f32, x2: f32, y2: f32) {
let first_control = Point::new(x0, y0);
let second_control = Point::new(x1, y1);
let next_point = Point::new(x2, y2);
let curve = CubeCurve::new(self.previous_point, first_control, second_control, next_point);
let mut stack = vec![Segment::new(self.previous_point, 0.0, next_point, 1.0)];
while let Some(seg) = stack.pop() {
let bt = (seg.at + seg.ct) * 0.5;
let b = curve.point(bt);
// This is twice the triangle area
let area = (b.x - seg.a.x) * (seg.c.y - seg.a.y) - (seg.c.x - seg.a.x) * (b.y - seg.a.y);
if platform::abs(area) > self.max_area {
stack.push(Segment::new(seg.a, seg.at, b, bt));
stack.push(Segment::new(b, bt, seg.c, seg.ct));
} else {
self.push(seg.a, seg.c);
}
}
self.previous_point = next_point;
}
fn close(&mut self) {
if self.start_point != self.previous_point {
self.push(self.previous_point, self.start_point);
}
self.previous_point = self.start_point;
}
}
impl Geometry {
// Artisanal bespoke hand carved curves
pub fn new(scale: f32, units_per_em: f32) -> Geometry {
const ERROR_THRESHOLD: f32 = 3.0; // In pixels.
let max_area = ERROR_THRESHOLD * 2.0 * (units_per_em / scale);
Geometry {
v_lines: Vec::new(),
m_lines: Vec::new(),
effective_bounds: AABB {
xmin: core::f32::MAX,
xmax: core::f32::MIN,
ymin: core::f32::MAX,
ymax: core::f32::MIN,
},
start_point: Point::default(),
previous_point: Point::default(),
area: 0.0,
reverse_points: false,
max_area,
}
}
fn push(&mut self, start: Point, end: Point) {
// We're using to_bits here because we only care if they're _exactly_ the same.
if start.y.to_bits() != end.y.to_bits() {
self.area += (end.y - start.y) * (end.x + start.x);
if start.x.to_bits() == end.x.to_bits() {
self.v_lines.push(Line::new(start, end));
} else {
self.m_lines.push(Line::new(start, end));
}
Self::recalculate_bounds(&mut self.effective_bounds, start.x, start.y);
Self::recalculate_bounds(&mut self.effective_bounds, end.x, end.y);
}
}
pub(crate) fn finalize(mut self, glyph: &mut Glyph) {
if self.v_lines.is_empty() && self.m_lines.is_empty() {
self.effective_bounds = AABB::default();
} else {
self.reverse_points = self.area > 0.0;
for line in self.v_lines.iter_mut().chain(self.m_lines.iter_mut()) {
line.reposition(self.effective_bounds, self.reverse_points);
}
self.v_lines.shrink_to_fit();
self.m_lines.shrink_to_fit();
}
glyph.v_lines = self.v_lines;
glyph.m_lines = self.m_lines;
glyph.bounds = OutlineBounds {
xmin: self.effective_bounds.xmin,
ymin: self.effective_bounds.ymin,
width: self.effective_bounds.xmax - self.effective_bounds.xmin,
height: self.effective_bounds.ymax - self.effective_bounds.ymin,
};
}
fn recalculate_bounds(bounds: &mut AABB, x: f32, y: f32) {
if x < bounds.xmin {
bounds.xmin = x;
}
if x > bounds.xmax {
bounds.xmax = x;
}
if y < bounds.ymin {
bounds.ymin = y;
}
if y > bounds.ymax {
bounds.ymax = y;
}
}
}

View file

@ -0,0 +1,16 @@
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
#[inline(always)]
pub fn as_i32(value: f32) -> i32 {
value as i32
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
#[inline(always)]
pub fn as_i32(value: f32) -> i32 {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
unsafe { _mm_cvtss_si32(_mm_set_ss(value)) }
}

View file

@ -0,0 +1,98 @@
/*
* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c
*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/// The arctangent function.
pub fn atan(mut x: f32) -> f32 {
const ATAN_HI: [f32; 4] = [
4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */
7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */
9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */
1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */
];
const ATAN_LO: [f32; 4] = [
5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */
3.7748947079e-08, /* atan(1.0)lo 0x33222168 */
3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */
7.5497894159e-08, /* atan(inf)lo 0x33a22168 */
];
const A_T: [f32; 5] =
[3.3333328366e-01, -1.9999158382e-01, 1.4253635705e-01, -1.0648017377e-01, 6.1687607318e-02];
let x1p_120 = f32::from_bits(0x03800000); // 0x1p-120 === 2 ^ (-120)
let z: f32;
let mut ix = x.to_bits();
let sign = (ix >> 31) != 0;
ix &= 0x7fffffff;
if ix >= 0x4c800000 {
/* if |x| >= 2**26 */
if x.is_nan() {
return x;
}
z = ATAN_HI[3] + x1p_120;
return if sign {
-z
} else {
z
};
}
let id = if ix < 0x3ee00000 {
/* |x| < 0.4375 */
if ix < 0x39800000 {
return x;
}
-1
} else {
x = super::abs(x);
if ix < 0x3f980000 {
/* |x| < 1.1875 */
if ix < 0x3f300000 {
/* 7/16 <= |x| < 11/16 */
x = (2. * x - 1.) / (2. + x);
0
} else {
/* 11/16 <= |x| < 19/16 */
x = (x - 1.) / (x + 1.);
1
}
} else if ix < 0x401c0000 {
/* |x| < 2.4375 */
x = (x - 1.5) / (1. + 1.5 * x);
2
} else {
/* 2.4375 <= |x| < 2**26 */
x = -1. / x;
3
}
};
/* end of argument reduction */
z = x * x;
let w = z * z;
/* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */
let s1 = z * (A_T[0] + w * (A_T[2] + w * A_T[4]));
let s2 = w * (A_T[1] + w * A_T[3]);
if id < 0 {
return x - x * (s1 + s2);
}
let id = id as usize;
let z = ATAN_HI[id] - ((x * (s1 + s2) - ATAN_LO[id]) - x);
if sign {
-z
} else {
z
}
}

View file

@ -0,0 +1,118 @@
/*
* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c
*
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*/
/// The arctangent function.
pub fn atan2f(y: f32, x: f32) -> f32 {
const PI: f32 = 3.1415927410e+00; /* 0x40490fdb */
const PI_LO: f32 = -8.7422776573e-08; /* 0xb3bbbd2e */
if x.is_nan() || y.is_nan() {
return x + y;
}
let mut ix = x.to_bits();
let mut iy = y.to_bits();
if ix == 0x3f800000 {
/* x=1.0 */
return super::atan(y);
}
let m = ((iy >> 31) & 1) | ((ix >> 30) & 2); /* 2*sign(x)+sign(y) */
ix &= 0x7fffffff;
iy &= 0x7fffffff;
/* when y = 0 */
if iy == 0 {
return match m {
0 | 1 => y, /* atan(+-0,+anything)=+-0 */
2 => PI, /* atan(+0,-anything) = pi */
3 | _ => -PI, /* atan(-0,-anything) =-pi */
};
}
/* when x = 0 */
if ix == 0 {
return if m & 1 != 0 {
-PI / 2.
} else {
PI / 2.
};
}
/* when x is INF */
if ix == 0x7f800000 {
return if iy == 0x7f800000 {
match m {
0 => PI / 4., /* atan(+INF,+INF) */
1 => -PI / 4., /* atan(-INF,+INF) */
2 => 3. * PI / 4., /* atan(+INF,-INF)*/
3 | _ => -3. * PI / 4., /* atan(-INF,-INF)*/
}
} else {
match m {
0 => 0., /* atan(+...,+INF) */
1 => -0., /* atan(-...,+INF) */
2 => PI, /* atan(+...,-INF) */
3 | _ => -PI, /* atan(-...,-INF) */
}
};
}
/* |y/x| > 0x1p26 */
if (ix + (26 << 23) < iy) || (iy == 0x7f800000) {
return if m & 1 != 0 {
-PI / 2.
} else {
PI / 2.
};
}
/* z = atan(|y/x|) with correct underflow */
let z = if (m & 2 != 0) && (iy + (26 << 23) < ix) {
/*|y/x| < 0x1p-26, x < 0 */
0.
} else {
super::atan(super::abs(y / x))
};
match m {
0 => z, /* atan(+,+) */
1 => -z, /* atan(-,+) */
2 => PI - (z - PI_LO), /* atan(+,-) */
_ => (z - PI_LO) - PI, /* case 3 */ /* atan(-,-) */
}
}
pub fn atan2(x: f32, y: f32) -> f32 {
use core::f32::consts::PI;
const PI_2: f32 = PI / 2.0;
const M_PI_2: f32 = -PI / 2.0;
const PI_4: f32 = PI / 4.0;
const PI_3_4: f32 = PI_4 * 3.0;
let mut r;
let c;
let abs_y = super::abs(y);
if x == 0.0 {
if y > 0.0 {
return PI_2;
}
if y == 0.0 {
return 0.0;
}
return M_PI_2;
} else if x > 0.0 {
r = (x - abs_y) / (x + abs_y);
c = PI_4;
} else {
r = (x + abs_y) / (abs_y - x);
c = PI_3_4;
}
r = 0.1963 * r * r * r - 0.9817 * r + c;
return super::copysign(r, y);
}

View file

@ -0,0 +1,25 @@
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
pub fn ceil(x: f32) -> f32 {
let mut ui = x.to_bits();
let e = (((ui >> 23) & 0xff).wrapping_sub(0x7f)) as i32;
if e >= 23 {
return x;
}
if e >= 0 {
let m = 0x007fffff >> e;
if (ui & m) == 0 {
return x;
}
if ui >> 31 == 0 {
ui += m;
}
ui &= !m;
} else {
if ui >> 31 != 0 {
return -0.0;
} else if ui << 1 != 0 {
return 1.0;
}
}
f32::from_bits(ui)
}

View file

@ -0,0 +1,26 @@
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
pub fn floor(x: f32) -> f32 {
let mut ui = x.to_bits();
let e = (((ui >> 23) as i32) & 0xff) - 0x7f;
if e >= 23 {
return x;
}
if e >= 0 {
let m: u32 = 0x007fffff >> e;
if (ui & m) == 0 {
return x;
}
if ui >> 31 != 0 {
ui += m;
}
ui &= !m;
} else {
if ui >> 31 == 0 {
ui = 0;
} else if ui << 1 != 0 {
return -1.0;
}
}
f32::from_bits(ui)
}

View file

@ -0,0 +1,19 @@
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
#[inline(always)]
pub fn fract(value: f32) -> f32 {
value - super::trunc(value)
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
#[inline(always)]
pub fn fract(value: f32) -> f32 {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
unsafe {
let packed = _mm_set_ss(value);
_mm_cvtss_f32(_mm_sub_ps(packed, _mm_cvtepi32_ps(_mm_cvttps_epi32(packed))))
}
}

View file

@ -0,0 +1,67 @@
use alloc::vec::*;
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
pub fn get_bitmap(a: &Vec<f32>, length: usize) -> Vec<u8> {
use crate::platform::{abs, clamp};
use alloc::vec;
let mut height = 0.0;
assert!(length <= a.len());
let mut output = vec![0; length];
for i in 0..length {
unsafe {
height += a.get_unchecked(i);
// Clamping because as u8 is undefined outside of its range in rustc.
*(output.get_unchecked_mut(i)) = clamp(abs(height) * 255.9, 0.0, 255.0) as u8;
}
}
output
}
#[allow(clippy::uninit_vec)]
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
pub fn get_bitmap(a: &Vec<f32>, length: usize) -> Vec<u8> {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
let aligned_length = (length + 3) & !3;
assert!(aligned_length <= a.len());
// Turns out zeroing takes a while on very large sizes.
let mut output = Vec::with_capacity(aligned_length);
unsafe {
output.set_len(aligned_length);
// offset = Zeroed out lanes
let mut offset = _mm_setzero_ps();
// Negative zero is important here.
let nzero = _mm_castps_si128(_mm_set1_ps(-0.0));
for i in (0..aligned_length).step_by(4) {
// x = Read 4 floats from self.a
let mut x = _mm_loadu_ps(a.get_unchecked(i));
// x += (0.0, x[0], x[1], x[2])
x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 4)));
// x += (0.0, 0.0, x[0], x[1])
x = _mm_add_ps(x, _mm_castsi128_ps(_mm_slli_si128(_mm_castps_si128(x), 8)));
// x += offset
x = _mm_add_ps(x, offset);
// y = x * 255.9
let y = _mm_mul_ps(x, _mm_set1_ps(255.9));
// y = abs(y)
let y = _mm_andnot_ps(_mm_castsi128_ps(nzero), y);
// y = Convert y to i32s and truncate
let mut y = _mm_cvttps_epi32(y);
// y = Take the first byte of each of the 4 values in y and pack them into
// the first 4 bytes of y.
y = _mm_packus_epi16(_mm_packs_epi32(y, nzero), nzero);
// Store the first 4 u8s from y in output.
let pointer: &mut i32 = core::mem::transmute(output.get_unchecked_mut(i));
*pointer = core::mem::transmute::<__m128i, [i32; 4]>(y)[0];
// offset = (x[3], x[3], x[3], x[3])
offset = _mm_set1_ps(core::mem::transmute::<__m128, [f32; 4]>(x)[3]);
}
}
output.truncate(length);
output
}

View file

@ -0,0 +1,61 @@
mod as_i32;
mod atan;
mod atan2;
mod ceil;
mod floor;
mod fract;
mod get_bitmap;
mod sqrt;
mod trunc;
pub use as_i32::*;
pub use atan::*;
pub use atan2::*;
pub use ceil::*;
pub use floor::*;
pub use fract::*;
pub use get_bitmap::*;
pub use sqrt::*;
pub use trunc::*;
/// Sets the high bit 0x80000000 on a float.
#[inline(always)]
pub fn abs(value: f32) -> f32 {
f32::from_bits(value.to_bits() & 0x7fffffff)
}
/// Checks if the high bit 0x80000000 is set on a float.
#[inline(always)]
pub fn is_negative(value: f32) -> bool {
value.to_bits() >= 0x80000000
}
/// Checks if the high bit 0x80000000 is not set on a float.
#[inline(always)]
pub fn is_positive(value: f32) -> bool {
value.to_bits() < 0x80000000
}
/// Inverts the high bit 0x80000000 on a float.
#[inline(always)]
pub fn flipsign(value: f32) -> f32 {
f32::from_bits(value.to_bits() ^ 0x80000000)
}
/// Assigns the high bit 0x80000000 on the sign to the value.
#[inline(always)]
pub fn copysign(value: f32, sign: f32) -> f32 {
f32::from_bits((value.to_bits() & 0x7fffffff) | (sign.to_bits() & 0x80000000))
}
#[inline(always)]
pub fn clamp(value: f32, min: f32, max: f32) -> f32 {
let mut x = value;
if x < min {
x = min;
}
if x > max {
x = max;
}
x
}

View file

@ -0,0 +1,97 @@
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
pub fn sqrt(x: f32) -> f32 {
const TINY: f32 = 1.0e-30;
let mut z: f32;
let sign: i32 = 0x80000000u32 as i32;
let mut ix: i32;
let mut s: i32;
let mut q: i32;
let mut m: i32;
let mut t: i32;
let mut i: i32;
let mut r: u32;
ix = x.to_bits() as i32;
/* take care of Inf and NaN */
if (ix as u32 & 0x7f800000) == 0x7f800000 {
return x * x + x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf, sqrt(-inf)=sNaN */
}
/* take care of zero */
if ix <= 0 {
if (ix & !sign) == 0 {
return x; /* sqrt(+-0) = +-0 */
}
if ix < 0 {
#[allow(clippy::eq_op)] // This has special semantics and is not wrong.
return (x - x) / (x - x); /* sqrt(-ve) = sNaN */
}
}
/* normalize x */
m = ix >> 23;
if m == 0 {
/* subnormal x */
i = 0;
while ix & 0x00800000 == 0 {
ix <<= 1;
i = i + 1;
}
m -= i - 1;
}
m -= 127; /* unbias exponent */
ix = (ix & 0x007fffff) | 0x00800000;
if m & 1 == 1 {
/* odd m, double x to make it even */
ix += ix;
}
m >>= 1; /* m = [m/2] */
/* generate sqrt(x) bit by bit */
ix += ix;
q = 0;
s = 0;
r = 0x01000000; /* r = moving bit from right to left */
while r != 0 {
t = s + r as i32;
if t <= ix {
s = t + r as i32;
ix -= t;
q += r as i32;
}
ix += ix;
r >>= 1;
}
/* use floating add to find out rounding direction */
if ix != 0 {
z = 1.0 - TINY; /* raise inexact flag */
if z >= 1.0 {
z = 1.0 + TINY;
if z > 1.0 {
q += 2;
} else {
q += q & 1;
}
}
}
ix = (q >> 1) + 0x3f000000;
ix += m << 23;
f32::from_bits(ix as u32)
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
#[inline(always)]
pub fn sqrt(value: f32) -> f32 {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
unsafe { _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(value))) }
}

View file

@ -0,0 +1,30 @@
// [See license/rust-lang/libm] Copyright (c) 2018 Jorge Aparicio
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
pub fn trunc(x: f32) -> f32 {
let mut i: u32 = x.to_bits();
let mut e: i32 = (i >> 23 & 0xff) as i32 - 0x7f + 9;
let m: u32;
if e >= 23 + 9 {
return x;
}
if e < 9 {
e = 1;
}
m = -1i32 as u32 >> e;
if (i & m) == 0 {
return x;
}
i &= !m;
f32::from_bits(i)
}
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
#[inline(always)]
pub fn trunc(value: f32) -> f32 {
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
unsafe { _mm_cvtss_f32(_mm_cvtepi32_ps(_mm_cvttps_epi32(_mm_set_ss(value)))) }
}

View file

@ -0,0 +1,75 @@
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
mod simd_core;
#[cfg(not(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd")))]
pub use simd_core::*;
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
mod simd_x86;
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "simd"))]
pub use simd_x86::*;
mod float;
pub use float::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn platform_ceil_test() {
use core::mem::transmute;
let mut y = 3.0;
while y < 9.0 {
assert_eq!(ceil(y), f32::ceil(y));
y = unsafe { transmute::<u32, f32>(transmute::<f32, u32>(y) + 1) };
}
assert_eq!(ceil(-1.5), -1.0);
assert_eq!(ceil(-1.0), -1.0);
assert_eq!(ceil(-0.5), 0.0);
assert_eq!(ceil(0.0), 0.0);
assert_eq!(ceil(0.5), 1.0);
assert_eq!(ceil(1.0), 1.0);
assert_eq!(ceil(1.5), 2.0);
}
#[test]
fn platform_floor_test() {
use core::mem::transmute;
let mut y = -3.0;
while y > -9.0 {
assert_eq!(ceil(y), f32::ceil(y));
y = unsafe { transmute::<u32, f32>(transmute::<f32, u32>(y) + 1) };
}
assert_eq!(floor(-1.5), -2.0);
assert_eq!(floor(-1.0), -1.0);
assert_eq!(floor(-0.5), -1.0);
assert_eq!(floor(0.0), 0.0);
assert_eq!(floor(0.5), 0.0);
assert_eq!(floor(1.0), 1.0);
assert_eq!(floor(1.5), 1.0);
}
#[test]
fn platform_fract_test() {
assert_eq!(fract(-1.5), -0.5);
assert_eq!(fract(-1.0), 0.0);
assert_eq!(fract(-0.5), -0.5);
assert_eq!(fract(0.0), 0.0);
assert_eq!(fract(0.5), 0.5);
assert_eq!(fract(1.0), 0.0);
assert_eq!(fract(1.5), 0.5);
}
#[test]
fn platform_trunc_test() {
assert_eq!(trunc(-1.5), -1.0);
assert_eq!(trunc(-1.0), -1.0);
assert_eq!(trunc(-0.5), 0.0);
assert_eq!(trunc(0.0), 0.0);
assert_eq!(trunc(0.5), 0.0);
assert_eq!(trunc(1.0), 1.0);
assert_eq!(trunc(1.5), 1.0);
}
}

View file

@ -0,0 +1,141 @@
#![allow(non_camel_case_types)]
use core::mem::transmute;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
#[repr(C)]
#[derive(Copy, Clone)]
pub struct f32x4 {
x0: f32,
x1: f32,
x2: f32,
x3: f32,
}
impl f32x4 {
#[inline(always)]
pub const fn new(x0: f32, x1: f32, x2: f32, x3: f32) -> Self {
f32x4 {
x0,
x1,
x2,
x3,
}
}
#[inline(always)]
pub fn new_u32(x0: u32, x1: u32, x2: u32, x3: u32) -> Self {
unsafe { Self::new(transmute(x0), transmute(x1), transmute(x2), transmute(x3)) }
}
#[inline(always)]
pub fn sub_integer(&self, other: f32x4) -> f32x4 {
unsafe {
Self::new(
transmute(transmute::<f32, u32>(self.x0) - transmute::<f32, u32>(other.x0)),
transmute(transmute::<f32, u32>(self.x1) - transmute::<f32, u32>(other.x1)),
transmute(transmute::<f32, u32>(self.x2) - transmute::<f32, u32>(other.x2)),
transmute(transmute::<f32, u32>(self.x3) - transmute::<f32, u32>(other.x3)),
)
}
}
#[inline(always)]
pub const fn splat(value: f32) -> Self {
Self::new(value, value, value, value)
}
#[inline(always)]
pub const fn zero() -> Self {
Self::splat(0.0)
}
#[inline(always)]
pub const fn copied(self) -> (f32, f32, f32, f32) {
(self.x0, self.x1, self.x2, self.x3)
}
#[inline(always)]
pub fn trunc(self) -> Self {
use super::trunc;
Self::new(trunc(self.x0), trunc(self.x1), trunc(self.x2), trunc(self.x3))
}
#[inline(always)]
pub fn sqrt(self) -> Self {
use super::sqrt;
Self::new(sqrt(self.x0), sqrt(self.x1), sqrt(self.x2), sqrt(self.x3))
}
}
impl Add for f32x4 {
type Output = f32x4;
#[inline(always)]
fn add(self, other: f32x4) -> f32x4 {
Self::new(self.x0 + other.x0, self.x1 + other.x1, self.x2 + other.x2, self.x3 + other.x3)
}
}
impl AddAssign for f32x4 {
#[inline(always)]
fn add_assign(&mut self, other: f32x4) {
self.x0 += other.x0;
self.x1 += other.x1;
self.x2 += other.x2;
self.x3 += other.x3;
}
}
impl Sub for f32x4 {
type Output = f32x4;
#[inline(always)]
fn sub(self, other: f32x4) -> f32x4 {
Self::new(self.x0 - other.x0, self.x1 - other.x1, self.x2 - other.x2, self.x3 - other.x3)
}
}
impl SubAssign for f32x4 {
#[inline(always)]
fn sub_assign(&mut self, other: f32x4) {
self.x0 -= other.x0;
self.x1 -= other.x1;
self.x2 -= other.x2;
self.x3 -= other.x3;
}
}
impl Mul for f32x4 {
type Output = f32x4;
#[inline(always)]
fn mul(self, other: f32x4) -> f32x4 {
Self::new(self.x0 * other.x0, self.x1 * other.x1, self.x2 * other.x2, self.x3 * other.x3)
}
}
impl MulAssign for f32x4 {
#[inline(always)]
fn mul_assign(&mut self, other: f32x4) {
self.x0 *= other.x0;
self.x1 *= other.x1;
self.x2 *= other.x2;
self.x3 *= other.x3;
}
}
impl Div for f32x4 {
type Output = f32x4;
#[inline(always)]
fn div(self, other: f32x4) -> f32x4 {
Self::new(self.x0 / other.x0, self.x1 / other.x1, self.x2 / other.x2, self.x3 / other.x3)
}
}
impl DivAssign for f32x4 {
#[inline(always)]
fn div_assign(&mut self, other: f32x4) {
self.x0 /= other.x0;
self.x1 /= other.x1;
self.x2 /= other.x2;
self.x3 /= other.x3;
}
}

View file

@ -0,0 +1,120 @@
#![allow(non_camel_case_types)]
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
#[cfg(target_arch = "x86")]
use core::arch::x86::*;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::*;
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct f32x4(__m128);
impl f32x4 {
#[inline(always)]
pub fn new(x0: f32, x1: f32, x2: f32, x3: f32) -> Self {
f32x4(unsafe { _mm_set_ps(x3, x2, x1, x0) })
}
#[inline(always)]
pub fn new_u32(x0: u32, x1: u32, x2: u32, x3: u32) -> Self {
f32x4(unsafe {
_mm_set_ps(
core::mem::transmute(x3),
core::mem::transmute(x2),
core::mem::transmute(x1),
core::mem::transmute(x0),
)
})
}
#[inline(always)]
pub fn splat(value: f32) -> Self {
f32x4(unsafe { _mm_set1_ps(value) })
}
pub fn sub_integer(&self, other: f32x4) -> f32x4 {
f32x4(unsafe { _mm_castsi128_ps(_mm_sub_epi32(_mm_castps_si128(self.0), _mm_castps_si128(other.0))) })
}
#[inline(always)]
pub fn zero() -> Self {
f32x4(unsafe { _mm_setzero_ps() })
}
#[inline(always)]
pub fn copied(self) -> (f32, f32, f32, f32) {
unsafe { core::mem::transmute(self.0) }
}
#[inline(always)]
pub fn trunc(self) -> Self {
unsafe { f32x4(_mm_cvtepi32_ps(_mm_cvttps_epi32(self.0))) }
}
#[inline(always)]
pub fn sqrt(self) -> Self {
unsafe { f32x4(_mm_sqrt_ps(self.0)) }
}
}
impl Add for f32x4 {
type Output = f32x4;
#[inline(always)]
fn add(self, other: f32x4) -> f32x4 {
unsafe { f32x4(_mm_add_ps(self.0, other.0)) }
}
}
impl AddAssign for f32x4 {
#[inline(always)]
fn add_assign(&mut self, other: f32x4) {
self.0 = unsafe { _mm_add_ps(self.0, other.0) };
}
}
impl Sub for f32x4 {
type Output = f32x4;
#[inline(always)]
fn sub(self, other: f32x4) -> f32x4 {
unsafe { f32x4(_mm_sub_ps(self.0, other.0)) }
}
}
impl SubAssign for f32x4 {
#[inline(always)]
fn sub_assign(&mut self, other: f32x4) {
self.0 = unsafe { _mm_sub_ps(self.0, other.0) };
}
}
impl Mul for f32x4 {
type Output = f32x4;
#[inline(always)]
fn mul(self, other: f32x4) -> f32x4 {
unsafe { f32x4(_mm_mul_ps(self.0, other.0)) }
}
}
impl MulAssign for f32x4 {
#[inline(always)]
fn mul_assign(&mut self, other: f32x4) {
self.0 = unsafe { _mm_mul_ps(self.0, other.0) };
}
}
impl Div for f32x4 {
type Output = f32x4;
#[inline(always)]
fn div(self, other: f32x4) -> f32x4 {
unsafe { f32x4(_mm_div_ps(self.0, other.0)) }
}
}
impl DivAssign for f32x4 {
#[inline(always)]
fn div_assign(&mut self, other: f32x4) {
self.0 = unsafe { _mm_div_ps(self.0, other.0) };
}
}

125
third-party/vendor/fontdue/src/raster.rs vendored Normal file
View file

@ -0,0 +1,125 @@
/* Notice to anyone that wants to repurpose the raster for your library:
* Please don't reuse this raster. Fontdue's raster is very unsafe, with nuanced invariants that
* need to be accounted for. Fontdue sanitizes the input that the raster will consume to ensure it
* is safe. Please be aware of this.
*/
use crate::math::Line;
use crate::platform::{abs, as_i32, copysign, f32x4, fract};
use crate::Glyph;
use alloc::vec;
use alloc::vec::*;
pub struct Raster {
w: usize,
h: usize,
a: Vec<f32>,
}
impl Raster {
pub fn new(w: usize, h: usize) -> Raster {
Raster {
w,
h,
a: vec![0.0; w * h + 3],
}
}
pub(crate) fn draw(&mut self, glyph: &Glyph, scale_x: f32, scale_y: f32, offset_x: f32, offset_y: f32) {
let params = f32x4::new(1.0 / scale_x, 1.0 / scale_y, scale_x, scale_y);
let scale = f32x4::new(scale_x, scale_y, scale_x, scale_y);
let offset = f32x4::new(offset_x, offset_y, offset_x, offset_y);
for line in &glyph.v_lines {
self.v_line(line, line.coords * scale + offset);
}
for line in &glyph.m_lines {
self.m_line(line, line.coords * scale + offset, line.params * params);
}
}
#[inline(always)]
fn add(&mut self, index: usize, height: f32, mid_x: f32) {
// This is fast and hip.
unsafe {
let m = height * mid_x;
*self.a.get_unchecked_mut(index) += height - m;
*self.a.get_unchecked_mut(index + 1) += m;
}
// This is safe but slow.
// let m = height * mid_x;
// self.a[index] += height - m;
// self.a[index + 1] += m;
}
#[inline(always)]
fn v_line(&mut self, line: &Line, coords: f32x4) {
let (x0, y0, _, y1) = coords.copied();
let temp = coords.sub_integer(line.nudge).trunc();
let (start_x, start_y, end_x, end_y) = temp.copied();
let (_, mut target_y, _, _) = (temp + line.adjustment).copied();
let sy = copysign(1f32, y1 - y0);
let mut y_prev = y0;
let mut index = as_i32(start_x + start_y * self.w as f32);
let index_y_inc = as_i32(copysign(self.w as f32, sy));
let mut dist = as_i32(abs(start_y - end_y));
let mid_x = fract(x0);
while dist > 0 {
dist -= 1;
self.add(index as usize, y_prev - target_y, mid_x);
index += index_y_inc;
y_prev = target_y;
target_y += sy;
}
self.add(as_i32(end_x + end_y * self.w as f32) as usize, y_prev - y1, mid_x);
}
#[inline(always)]
fn m_line(&mut self, line: &Line, coords: f32x4, params: f32x4) {
let (x0, y0, x1, y1) = coords.copied();
let temp = coords.sub_integer(line.nudge).trunc();
let (start_x, start_y, end_x, end_y) = temp.copied();
let (tdx, tdy, dx, dy) = params.copied();
let (mut target_x, mut target_y, _, _) = (temp + line.adjustment).copied();
let sx = copysign(1f32, tdx);
let sy = copysign(1f32, tdy);
let mut tmx = tdx * (target_x - x0);
let mut tmy = tdy * (target_y - y0);
let tdx = abs(tdx);
let tdy = abs(tdy);
let mut x_prev = x0;
let mut y_prev = y0;
let mut index = as_i32(start_x + start_y * self.w as f32);
let index_x_inc = as_i32(sx);
let index_y_inc = as_i32(copysign(self.w as f32, sy));
let mut dist = as_i32(abs(start_x - end_x) + abs(start_y - end_y));
while dist > 0 {
dist -= 1;
let prev_index = index;
let y_next: f32;
let x_next: f32;
if tmx < tmy {
y_next = tmx * dy + y0; // FMA is not faster.
x_next = target_x;
tmx += tdx;
target_x += sx;
index += index_x_inc;
} else {
y_next = target_y;
x_next = tmy * dx + x0;
tmy += tdy;
target_y += sy;
index += index_y_inc;
}
self.add(prev_index as usize, y_prev - y_next, fract((x_prev + x_next) / 2.0));
x_prev = x_next;
y_prev = y_next;
}
self.add(as_i32(end_x + end_y * self.w as f32) as usize, y_prev - y1, fract((x_prev + x1) / 2.0));
}
#[inline(always)]
pub fn get_bitmap(&self) -> Vec<u8> {
crate::platform::get_bitmap(&self.a, self.w * self.h)
}
}

View file

@ -0,0 +1,200 @@
use crate::table::parse::*;
use hashbrown::HashMap;
// Apple: https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6kern.html
// Microsoft: https://docs.microsoft.com/en-us/typography/opentype/spec/kern
#[derive(Debug)]
pub struct TableKern {
pub horizontal_mappings: HashMap<u32, i16>,
}
#[derive(Copy, Clone, Debug)]
struct Header {
pub version_major: u16,
pub version_minor: u16,
pub number_sub_tables: u32,
}
#[derive(Copy, Clone, Debug)]
struct SubTableHeader {
pub version: u16,
pub length: usize,
pub coverage: Coverage,
pub format: u8,
pub tuple_index: u16,
}
#[derive(Copy, Clone, Debug)]
struct Coverage {
is_horizontal: bool,
}
impl Coverage {
pub const fn aat(cov: u8) -> Coverage {
Coverage {
is_horizontal: cov & 0x80 != 0x80,
}
}
pub const fn ot(cov: u8) -> Coverage {
Coverage {
is_horizontal: cov & 0x01 == 0x01,
}
}
}
impl TableKern {
pub fn new(kern: &[u8]) -> Option<TableKern> {
let mut stream = Stream::new(kern);
let version_major = stream.read_u16()?;
let header;
match version_major {
0x0000 => header = Self::read_ot_header(&mut stream)?,
0x0001 => header = Self::read_aat_header(&mut stream)?,
_ => return None, // Font.kern: Unsupported kern table version.
}
for _ in 0..header.number_sub_tables {
let sub_table_start = stream.offset();
let sub_header = if version_major == 0x0000 {
Self::read_ot_subtable(&mut stream)?
} else {
Self::read_aat_subtable(&mut stream)?
};
match sub_header.format {
// Ordered List of Kerning Pairs
0 => {
if sub_header.coverage.is_horizontal {
let mappings = Self::read_format0(&mut stream)?;
return Some(TableKern {
horizontal_mappings: mappings,
});
}
}
// State Table for Contextual Kerning
// 1 => { /* TODO: State Table for Contextual Kerning */ }
// Simple n x m Array of Kerning Values
// 2 => { /* TODO: Simple n x m Array of Kerning Values */ }
// Simple n x m Array of Kerning Indices
3 => {
if sub_header.coverage.is_horizontal {
let mappings = Self::read_format3(&mut stream)?;
return Some(TableKern {
horizontal_mappings: mappings,
});
}
}
_ => {
stream.seek(sub_table_start + sub_header.length);
}
}
}
None // Font.kern: No supported sub-table format available.
}
fn read_format0(stream: &mut Stream) -> Option<HashMap<u32, i16>> {
let pairs = stream.read_u16()?;
stream.skip(6); // searchRange: u16, entrySelector: u16, rangeShift: u16
let mut mappings = HashMap::new();
for _ in 0..pairs {
let left = stream.read_u16()?;
let right = stream.read_u16()?;
let id = u32::from(left) << 16 | u32::from(right);
let value = stream.read_i16()?;
mappings.insert(id, value);
}
Some(mappings)
}
fn read_format3(stream: &mut Stream) -> Option<HashMap<u32, i16>> {
let glyph_count = stream.read_u16()?;
let kerning_values_count = stream.read_u8()?;
let left_hand_classes_count = stream.read_u8()?;
let right_hand_classes_count = stream.read_u8()?;
stream.skip(1); // flags - reserved
let indices_count = u16::from(left_hand_classes_count) * u16::from(right_hand_classes_count);
let kerning_values = stream.read_i16_slice(usize::from(kerning_values_count))?;
let left_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?;
let right_hand_classes = stream.read_u8_slice(usize::from(glyph_count))?;
let indices = stream.read_u8_slice(usize::from(indices_count))?;
let mut mappings = HashMap::new();
for left in 0..glyph_count {
for right in 0..glyph_count {
if let Some((id, value)) = {
let left_class = left_hand_classes.get(usize::from(left))?;
let right_class = right_hand_classes.get(usize::from(right))?;
if left_class > left_hand_classes_count || right_class > right_hand_classes_count {
continue;
}
let index =
u16::from(left_class) * u16::from(right_hand_classes_count) + u16::from(right_class);
let index = indices.get(usize::from(index))?;
let id = u32::from(left) << 16 | u32::from(right);
let value = kerning_values.get(usize::from(index))?;
Some((id, value))
} {
mappings.insert(id, value);
};
}
}
Some(mappings)
}
fn read_ot_header(stream: &mut Stream) -> Option<Header> {
let version_major = 0x0000;
let version_minor = 0x0000;
let number_sub_tables = stream.read_u16()? as u32;
Some(Header {
version_major,
version_minor,
number_sub_tables,
})
}
fn read_aat_header(stream: &mut Stream) -> Option<Header> {
let version_major = 0x0001;
let version_minor = stream.read_u16()?;
let number_sub_tables = stream.read_u32()?;
Some(Header {
version_major,
version_minor,
number_sub_tables,
})
}
fn read_ot_subtable(stream: &mut Stream) -> Option<SubTableHeader> {
let version = stream.read_u16()?;
let length = stream.read_u16()? as usize;
let format = stream.read_u8()?;
let coverage = Coverage::ot(stream.read_u8()?);
Some(SubTableHeader {
version,
length,
coverage,
format,
tuple_index: 0,
})
}
fn read_aat_subtable(stream: &mut Stream) -> Option<SubTableHeader> {
let length = stream.read_u32()? as usize;
let coverage = Coverage::aat(stream.read_u8()?);
let format = stream.read_u8()?;
let tuple_index = stream.read_u16()?;
Some(SubTableHeader {
version: 0x0001,
length,
coverage,
format,
tuple_index,
})
}
}

View file

@ -0,0 +1,4 @@
mod kern;
pub mod parse;
pub use self::kern::*;

View file

@ -0,0 +1,196 @@
use core::convert::TryInto;
pub struct StreamSliceU8<'a>(&'a [u8]);
pub struct StreamSliceU16<'a>(&'a [u8]);
pub struct StreamSliceU32<'a>(&'a [u8]);
impl<'a> StreamSliceU8<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<u8> {
const SIZE: usize = 1;
let offset = index * SIZE;
let slice = self.0.get(offset..offset + SIZE)?;
Some(slice[0])
}
}
impl<'a> StreamSliceU16<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<u16> {
const SIZE: usize = 2;
let offset = index * SIZE;
let slice = self.0.get(offset..offset + SIZE)?;
Some(u16::from_be_bytes(slice.try_into().unwrap()))
}
}
impl<'a> StreamSliceU32<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<u32> {
const SIZE: usize = 4;
let offset = index * SIZE;
let slice = self.0.get(offset..offset + SIZE)?;
Some(u32::from_be_bytes(slice.try_into().unwrap()))
}
}
pub struct StreamSliceI8<'a>(StreamSliceU8<'a>);
pub struct StreamSliceI16<'a>(StreamSliceU16<'a>);
pub struct StreamSliceI32<'a>(StreamSliceU32<'a>);
impl<'a> StreamSliceI8<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<i8> {
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
}
}
impl<'a> StreamSliceI16<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<i16> {
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
}
}
impl<'a> StreamSliceI32<'a> {
#[inline]
pub fn get(&self, index: usize) -> Option<i32> {
Some(unsafe { core::mem::transmute(self.0.get(index)?) })
}
}
pub struct Stream<'a> {
bytes: &'a [u8],
offset: usize,
}
impl<'a> Stream<'a> {
pub const fn new(bytes: &'a [u8]) -> Stream<'a> {
Stream {
bytes,
offset: 0,
}
}
// UTILITY
#[inline]
pub fn reset(&mut self) {
self.offset = 0;
}
#[inline]
pub const fn offset(&self) -> usize {
self.offset
}
#[inline]
pub fn seek(&mut self, offset: usize) {
self.offset = offset;
}
#[inline]
pub fn skip(&mut self, offset: usize) {
self.offset += offset;
}
// UNSIGNED SLICE
#[inline]
pub fn read_u8_slice(&mut self, len: usize) -> Option<StreamSliceU8<'a>> {
let end = self.offset + len;
self.bytes.get(self.offset..end).map(|slice| {
self.offset = end;
StreamSliceU8(slice)
})
}
#[inline]
pub fn read_u16_slice(&mut self, len: usize) -> Option<StreamSliceU16<'a>> {
let end = self.offset + len * 2;
self.bytes.get(self.offset..end).map(|slice| {
self.offset = end;
StreamSliceU16(slice)
})
}
#[inline]
pub fn read_u32_slice(&mut self, len: usize) -> Option<StreamSliceU32<'a>> {
let end = self.offset + len * 4;
self.bytes.get(self.offset..end).map(|slice| {
self.offset = end;
StreamSliceU32(slice)
})
}
// SIGNED SLICE
#[inline]
pub fn read_i8_slice(&mut self, len: usize) -> Option<StreamSliceI8<'a>> {
Some(StreamSliceI8(self.read_u8_slice(len)?))
}
#[inline]
pub fn read_i16_slice(&mut self, len: usize) -> Option<StreamSliceI16<'a>> {
Some(StreamSliceI16(self.read_u16_slice(len)?))
}
#[inline]
pub fn read_i32_slice(&mut self, len: usize) -> Option<StreamSliceI32<'a>> {
Some(StreamSliceI32(self.read_u32_slice(len)?))
}
// UNSIGNED
#[inline]
pub fn read_u8(&mut self) -> Option<u8> {
const SIZE: usize = 1;
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
self.offset += SIZE;
Some(slice[0])
}
#[inline]
pub fn read_u16(&mut self) -> Option<u16> {
const SIZE: usize = 2;
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
self.offset += SIZE;
Some(u16::from_be_bytes(slice.try_into().unwrap()))
}
#[inline]
pub fn read_u32(&mut self) -> Option<u32> {
const SIZE: usize = 4;
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
self.offset += SIZE;
Some(u32::from_be_bytes(slice.try_into().unwrap()))
}
// SIGNED
#[inline]
pub fn read_i8(&mut self) -> Option<i8> {
Some(unsafe { core::mem::transmute(self.read_u8()?) })
}
#[inline]
pub fn read_i16(&mut self) -> Option<i16> {
Some(unsafe { core::mem::transmute(self.read_u16()?) })
}
#[inline]
pub fn read_i32(&mut self) -> Option<i32> {
Some(unsafe { core::mem::transmute(self.read_u32()?) })
}
// FONT
#[inline]
pub fn read_f2dot14(&mut self) -> Option<f32> {
let val = self.read_i16()?;
let result = val as f32 * (1.0 / (1 << 14) as f32);
Some(result)
}
#[inline]
pub fn read_tag(&mut self) -> Option<[u8; 4]> {
const SIZE: usize = 4;
let slice = self.bytes.get(self.offset..self.offset + SIZE)?;
self.offset += SIZE;
Some(slice.try_into().unwrap())
}
}

View file

@ -0,0 +1,202 @@
mod tables;
use crate::unicode::tables::*;
use alloc::string::String;
const CONT_MASK: u8 = 0b0011_1111;
#[inline(always)]
fn utf8_acc_cont_byte(ch: u32, byte: u8) -> u32 {
(ch << 6) | (byte & CONT_MASK) as u32
}
pub fn decode_utf16(bytes: &[u8]) -> String {
let mut output = String::new();
let mut offset = 0;
while offset < bytes.len() {
output.push(read_utf16(bytes, &mut offset));
}
output
}
pub fn read_utf16(bytes: &[u8], offset: &mut usize) -> char {
let a = ((bytes[*offset] as u16) << 8) | bytes[*offset + 1] as u16;
*offset += 2;
if a < 0xD800 || 0xDFFF < a {
unsafe { core::char::from_u32_unchecked(a as u32) }
} else {
let b = ((bytes[*offset] as u16) << 8) | bytes[*offset + 1] as u16;
*offset += 2;
let c = (((a - 0xD800) as u32) << 10 | (b - 0xDC00) as u32) + 0x1_0000;
unsafe { core::char::from_u32_unchecked(c as u32) }
}
}
/// Returns (length, character). Cannot be run at the end of the string.
pub fn read_utf8(bytes: &[u8], byte_offset: &mut usize) -> char {
let x = bytes[*byte_offset];
*byte_offset += 1;
if x < 128 {
return unsafe { core::char::from_u32_unchecked(x as u32) };
}
let init = (x & (0x7F >> 2)) as u32;
let y = bytes[*byte_offset];
*byte_offset += 1;
let mut ch = utf8_acc_cont_byte(init, y);
if x >= 0xE0 {
let z = bytes[*byte_offset];
*byte_offset += 1;
let y_z = utf8_acc_cont_byte((y & CONT_MASK) as u32, z);
ch = init << 12 | y_z;
if x >= 0xF0 {
let w = bytes[*byte_offset];
*byte_offset += 1;
ch = (init & 7) << 18 | utf8_acc_cont_byte(y_z, w);
}
}
unsafe { core::char::from_u32_unchecked(ch) }
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
/// Ordering is based on linebreak priority. Ordering is Hard > Soft > None.
pub struct LinebreakData {
bits: u8,
}
pub const LINEBREAK_NONE: LinebreakData = LinebreakData::new(0b0000_0000);
pub const LINEBREAK_SOFT: LinebreakData = LinebreakData::new(0b0000_0001);
pub const LINEBREAK_HARD: LinebreakData = LinebreakData::new(0b0000_0010);
impl LinebreakData {
const NONE: u8 = 0b0000_0000;
const SOFT: u8 = 0b0000_0001;
const HARD: u8 = 0b0000_0010;
const fn new(bits: u8) -> LinebreakData {
LinebreakData {
bits,
}
}
pub fn from_mask(wrap_soft_breaks: bool, wrap_hard_breaks: bool, has_width: bool) -> LinebreakData {
let mut mask = 0;
if wrap_hard_breaks {
mask |= LinebreakData::HARD;
}
if wrap_soft_breaks && has_width {
mask |= LinebreakData::SOFT;
}
LinebreakData {
bits: mask,
}
}
pub fn is_hard(&self) -> bool {
self.bits == LinebreakData::HARD
}
pub fn is_soft(&self) -> bool {
self.bits == LinebreakData::SOFT
}
pub fn mask(&self, other: LinebreakData) -> LinebreakData {
Self::new(self.bits & other.bits)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Linebreaker {
state: u8,
}
impl Linebreaker {
pub fn new() -> Linebreaker {
Linebreaker {
state: 0,
}
}
pub fn reset(&mut self) {
self.state = 0;
}
// [See license/xi-editor/xi-unicode] Copyright 2016 The xi-editor Authors
pub fn next(&mut self, codepoint: char) -> LinebreakData {
let cp = codepoint as usize;
let lb = if cp < 0x800 {
LINEBREAK_1_2[cp]
} else if cp < 0x10000 {
let child = LINEBREAK_3_ROOT[cp >> 6];
LINEBREAK_3_CHILD[(child as usize) * 0x40 + (cp & 0x3f)]
} else {
let mid = LINEBREAK_4_ROOT[cp >> 12];
let leaf = LINEBREAK_4_MID[(mid as usize) * 0x40 + ((cp >> 6) & 0x3f)];
LINEBREAK_4_LEAVES[(leaf as usize) * 0x40 + (cp & 0x3f)]
};
let i = (self.state as usize) * N_LINEBREAK_CATEGORIES + (lb as usize);
let new = LINEBREAK_STATE_MACHINE[i];
if (new as i8) < 0 {
self.state = new & 0x3f;
if new >= 0xc0 {
LINEBREAK_HARD
} else {
LINEBREAK_SOFT
}
} else {
self.state = new;
LINEBREAK_NONE
}
}
}
/// Miscellaneous metadata associated with a character to assist in layout.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct CharacterData {
bits: u8,
}
impl CharacterData {
const WHITESPACE: u8 = 0b0000_0001;
const CONTROL: u8 = 0b0000_0010;
const MISSING: u8 = 0b0000_0100;
/// Classifies a character given its index in the font.
pub fn classify(c: char, index: u16) -> CharacterData {
let mut class = 0;
if index == 0 {
class |= CharacterData::MISSING;
}
match c {
'\t' | '\n' | '\x0C' | '\r' | ' ' => class |= CharacterData::WHITESPACE,
_ => {}
}
match c {
'\0'..='\x1F' | '\x7F' => class |= CharacterData::CONTROL,
_ => {}
}
CharacterData {
bits: class,
}
}
/// A heuristic for if the glpyh this was classified from should be rasterized. Missing glyphs,
/// whitespace, and control characters will return false.
pub fn rasterize(&self) -> bool {
self.bits == 0
}
/// Marks if the character is an ASCII whitespace character.
pub fn is_whitespace(&self) -> bool {
self.bits & CharacterData::WHITESPACE != 0
}
/// Marks if the character is an ASCII control character.
pub fn is_control(&self) -> bool {
self.bits & CharacterData::CONTROL != 0
}
/// Marks if the character is missing from its associated font.
pub fn is_missing(&self) -> bool {
self.bits & CharacterData::MISSING != 0
}
}

File diff suppressed because one or more lines are too long