Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New "style" encoding to replace "linewidth" #389

Merged
merged 5 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 15 additions & 18 deletions crates/encoding/src/encoding.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2022 The Vello authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

use super::{DrawColor, DrawTag, PathEncoder, PathTag, Transform};
use super::{DrawColor, DrawTag, PathEncoder, PathTag, Style, Transform};

use peniko::{kurbo::Shape, BlendMode, BrushRef, Color, Fill};

Expand All @@ -25,8 +25,8 @@ pub struct Encoding {
pub draw_data: Vec<u8>,
/// The transform stream.
pub transforms: Vec<Transform>,
/// The line width stream.
pub linewidths: Vec<f32>,
/// The style stream
pub styles: Vec<Style>,
/// Late bound resource data.
#[cfg(feature = "full")]
pub resources: Resources,
Expand Down Expand Up @@ -56,7 +56,7 @@ impl Encoding {
self.transforms.clear();
self.path_tags.clear();
self.path_data.clear();
self.linewidths.clear();
self.styles.clear();
self.draw_data.clear();
self.draw_tags.clear();
self.n_paths = 0;
Expand All @@ -67,7 +67,7 @@ impl Encoding {
self.resources.reset();
if !is_fragment {
self.transforms.push(Transform::IDENTITY);
self.linewidths.push(-1.0);
self.styles.push(Style::from_fill(Fill::NonZero));
}
}

Expand Down Expand Up @@ -96,7 +96,7 @@ impl Encoding {
run.stream_offsets.draw_tags += offsets.draw_tags;
run.stream_offsets.draw_data += offsets.draw_data;
run.stream_offsets.transforms += offsets.transforms;
run.stream_offsets.linewidths += offsets.linewidths;
run.stream_offsets.styles += offsets.styles;
run
}));
self.resources
Expand Down Expand Up @@ -148,7 +148,7 @@ impl Encoding {
} else {
self.transforms.extend_from_slice(&other.transforms);
}
self.linewidths.extend_from_slice(&other.linewidths);
self.styles.extend_from_slice(&other.styles);
}

/// Returns a snapshot of the current stream offsets.
Expand All @@ -159,19 +159,16 @@ impl Encoding {
draw_tags: self.draw_tags.len(),
draw_data: self.draw_data.len(),
transforms: self.transforms.len(),
linewidths: self.linewidths.len(),
styles: self.styles.len(),
}
}

/// Encodes a fill style.
pub fn encode_fill_style(&mut self, fill: Fill) {
let linewidth = match fill {
Fill::NonZero => -1.0,
Fill::EvenOdd => -2.0,
};
if self.linewidths.last() != Some(&linewidth) {
self.path_tags.push(PathTag::LINEWIDTH);
self.linewidths.push(linewidth);
let style = Style::from_fill(fill);
if self.styles.last() != Some(&style) {
self.path_tags.push(PathTag::STYLE);
self.styles.push(style);
}
}

Expand Down Expand Up @@ -449,8 +446,8 @@ pub struct StreamOffsets {
pub draw_data: usize,
/// Current length of transform stream.
pub transforms: usize,
/// Current length of linewidth stream.
pub linewidths: usize,
/// Current length of style stream.
pub styles: usize,
}

impl StreamOffsets {
Expand All @@ -461,6 +458,6 @@ impl StreamOffsets {
self.draw_tags += other.draw_tags;
self.draw_data += other.draw_data;
self.transforms += other.transforms;
self.linewidths += other.linewidths;
self.styles += other.styles;
}
}
2 changes: 1 addition & 1 deletion crates/encoding/src/glyph_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ impl CachedRange {
draw_tags: self.end.draw_tags - self.start.draw_tags,
draw_data: self.end.draw_data - self.start.draw_data,
transforms: self.end.transforms - self.start.transforms,
linewidths: self.end.linewidths - self.start.linewidths,
styles: self.end.styles - self.start.styles,
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/encoding/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ pub use math::Transform;
pub use monoid::Monoid;
pub use path::{
Cubic, LineSoup, Path, PathBbox, PathEncoder, PathMonoid, PathSegment, PathSegmentType,
PathTag, SegmentCount, Tile,
PathTag, SegmentCount, Style, Tile,
};
pub use resolve::{resolve_solid_paths_only, Layout};

Expand Down
97 changes: 97 additions & 0 deletions crates/encoding/src/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,100 @@ impl Mul for Transform {
pub fn point_to_f32(point: kurbo::Point) -> [f32; 2] {
[point.x as f32, point.y as f32]
}

/// Converts an f32 to IEEE-754 binary16 format represented as the bits of a u16.
/// This implementation was adapted from Fabian Giesen's float_to_half_fast3()
/// function which can be found at https://gist.github.com/rygorous/2156668#file-gistfile1-cpp-L285
///
/// TODO: We should consider adopting https://crates.io/crates/half as a dependency since it nicely
/// wraps native ARM and x86 instructions for floating-point conversion.
#[allow(unused)] // for now
pub(crate) fn f32_to_f16(val: f32) -> u16 {
const INF_32: u32 = 255 << 23;
const INF_16: u32 = 31 << 23;
const MAGIC: u32 = 15 << 23;
const SIGN_MASK: u32 = 0x8000_0000u32;
const ROUND_MASK: u32 = !0xFFFu32;

let u = val.to_bits();
let sign = u & SIGN_MASK;
let u = u ^ sign;

// NOTE all the integer compares in this function can be safely
// compiled into signed compares since all operands are below
// 0x80000000. Important if you want fast straight SSE2 code
// (since there's no unsigned PCMPGTD).

// Inf or NaN (all exponent bits set)
let output: u16 = if u >= INF_32 {
// NaN -> qNaN and Inf->Inf
if u > INF_32 {
0x7E00
} else {
0x7C00
}
} else {
// (De)normalized number or zero
let mut u = u & ROUND_MASK;
u = (f32::from_bits(u) * f32::from_bits(MAGIC)).to_bits();
u = u.overflowing_sub(ROUND_MASK).0;

// Clamp to signed infinity if exponent overflowed
if u > INF_16 {
u = INF_16;
}
(u >> 13) as u16 // Take the bits!
};
output | (sign >> 16) as u16
}

#[cfg(test)]
mod tests {
use super::f32_to_f16;

#[test]
fn test_f32_to_f16_simple() {
let input: f32 = std::f32::consts::PI;
let output: u16 = f32_to_f16(input);
assert_eq!(0x4248u16, output) // 3.141
}

#[test]
fn test_f32_to_f16_nan_overflow() {
// A signaling NaN with unset high bits but a low bit that could get accidentally masked
// should get converted to a quiet NaN and not infinity.
let input: f32 = f32::from_bits(0x7F800001u32);
assert!(input.is_nan());
let output: u16 = f32_to_f16(input);
assert_eq!(0x7E00, output);
}

#[test]
fn test_f32_to_16_inf() {
let input: f32 = f32::from_bits(0x7F800000u32);
assert!(input.is_infinite());
let output: u16 = f32_to_f16(input);
assert_eq!(0x7C00, output);
}

#[test]
fn test_f32_to_16_exponent_rebias() {
let input: f32 = 0.00003051758;
let output: u16 = f32_to_f16(input);
assert_eq!(0x0200, output); // 0.00003052
}

#[test]
fn test_f32_to_16_exponent_overflow() {
let input: f32 = 1.701412e38;
let output: u16 = f32_to_f16(input);
assert_eq!(0x7C00, output); // +inf
}

#[test]
fn test_f32_to_16_exponent_overflow_neg_inf() {
let input: f32 = -1.701412e38;
let output: u16 = f32_to_f16(input);
assert_eq!(0xFC00, output); // -inf
}
}
Loading