#![warn(missing_docs)]
extern crate alloc;
use crate::properties::InterpolatedPropertyValue;
#[cfg(feature = "rtti")]
use crate::rtti::*;
use crate::SharedString;
use auto_enums::auto_enum;
use const_field_offset::FieldOffsets;
use sixtyfps_corelib_macros::*;
pub type Rect = euclid::default::Rect<f32>;
pub type IntRect = euclid::default::Rect<i32>;
pub type Point = euclid::default::Point2D<f32>;
pub type Size = euclid::default::Size2D<f32>;
#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct RgbaColor<T> {
pub alpha: T,
pub red: T,
pub green: T,
pub blue: T,
}
#[derive(Copy, Clone, PartialEq, Debug, Default)]
#[repr(C)]
pub struct Color {
red: u8,
green: u8,
blue: u8,
alpha: u8,
}
impl From<RgbaColor<u8>> for Color {
fn from(col: RgbaColor<u8>) -> Self {
Self { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha }
}
}
impl From<Color> for RgbaColor<u8> {
fn from(col: Color) -> Self {
RgbaColor { red: col.red, green: col.green, blue: col.blue, alpha: col.alpha }
}
}
impl From<RgbaColor<u8>> for RgbaColor<f32> {
fn from(col: RgbaColor<u8>) -> Self {
Self {
red: (col.red as f32) / 255.0,
green: (col.green as f32) / 255.0,
blue: (col.blue as f32) / 255.0,
alpha: (col.alpha as f32) / 255.0,
}
}
}
impl From<Color> for RgbaColor<f32> {
fn from(col: Color) -> Self {
let u8col: RgbaColor<u8> = col.into();
u8col.into()
}
}
impl From<RgbaColor<f32>> for Color {
fn from(col: RgbaColor<f32>) -> Self {
Self {
red: (col.red * 255.) as u8,
green: (col.green * 255.) as u8,
blue: (col.blue * 255.) as u8,
alpha: (col.alpha * 255.) as u8,
}
}
}
impl Color {
pub const fn from_argb_encoded(encoded: u32) -> Color {
Self {
red: (encoded >> 16) as u8,
green: (encoded >> 8) as u8,
blue: encoded as u8,
alpha: (encoded >> 24) as u8,
}
}
pub fn as_argb_encoded(&self) -> u32 {
((self.red as u32) << 16)
| ((self.green as u32) << 8)
| (self.blue as u32)
| ((self.alpha as u32) << 24)
}
pub fn from_argb_u8(alpha: u8, red: u8, green: u8, blue: u8) -> Self {
Self { red, green, blue, alpha }
}
pub fn from_rgb_u8(red: u8, green: u8, blue: u8) -> Self {
Self::from_argb_u8(255, red, green, blue)
}
pub fn from_argb_f32(alpha: f32, red: f32, green: f32, blue: f32) -> Self {
RgbaColor { alpha, red, green, blue }.into()
}
pub fn from_rgb_f32(red: f32, green: f32, blue: f32) -> Self {
Self::from_argb_f32(1.0, red, green, blue)
}
pub fn to_argb_u8(&self) -> RgbaColor<u8> {
RgbaColor::from(*self)
}
pub fn to_argb_f32(&self) -> RgbaColor<f32> {
RgbaColor::from(*self)
}
pub fn red(self) -> u8 {
self.red
}
pub fn green(self) -> u8 {
self.green
}
pub fn blue(self) -> u8 {
self.blue
}
pub fn alpha(self) -> u8 {
self.alpha
}
}
impl InterpolatedPropertyValue for Color {
fn interpolate(self, target_value: Self, t: f32) -> Self {
Self {
red: self.red.interpolate(target_value.red, t),
green: self.green.interpolate(target_value.green, t),
blue: self.blue.interpolate(target_value.blue, t),
alpha: self.alpha.interpolate(target_value.alpha, t),
}
}
}
impl std::fmt::Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "argb({}, {}, {}, {})", self.alpha, self.red, self.green, self.blue)
}
}
#[cfg(feature = "femtovg_backend")]
impl From<&Color> for femtovg::Color {
fn from(col: &Color) -> Self {
Self::rgba(col.red, col.green, col.blue, col.alpha)
}
}
#[cfg(feature = "femtovg_backend")]
impl From<Color> for femtovg::Color {
fn from(col: Color) -> Self {
Self::rgba(col.red, col.green, col.blue, col.alpha)
}
}
#[derive(Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Resource {
None,
AbsoluteFilePath(crate::SharedString),
EmbeddedData(super::slice::Slice<'static, u8>),
#[allow(missing_docs)]
EmbeddedRgbaImage { width: u32, height: u32, data: super::sharedvector::SharedVector<u32> },
}
impl Default for Resource {
fn default() -> Self {
Resource::None
}
}
pub struct CachedGraphicsData<T> {
pub data: T,
pub dependency_tracker: core::pin::Pin<Box<crate::properties::PropertyTracker>>,
}
impl<T> CachedGraphicsData<T> {
pub fn new(update_fn: impl FnOnce() -> T) -> Self {
let dependency_tracker = Box::pin(crate::properties::PropertyTracker::default());
let data = dependency_tracker.as_ref().evaluate(update_fn);
Self { data, dependency_tracker }
}
}
pub type RenderingCache<T> = vec_arena::Arena<CachedGraphicsData<T>>;
#[derive(Debug, Clone, PartialEq)]
#[repr(C)]
pub struct FontRequest {
pub family: SharedString,
pub weight: Option<i32>,
pub pixel_size: Option<f32>,
}
pub trait FontMetrics {
fn text_size(&self, text: &str) -> Size;
fn text_offset_for_x_position<'a>(&self, text: &'a str, x: f32) -> usize;
fn height(&self) -> f32;
}
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathMoveTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathLineTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathArcTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub radius_x: f32,
#[rtti_field]
pub radius_y: f32,
#[rtti_field]
pub x_rotation: f32,
#[rtti_field]
pub large_arc: bool,
#[rtti_field]
pub sweep: bool,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathCubicTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub control_1_x: f32,
#[rtti_field]
pub control_1_y: f32,
#[rtti_field]
pub control_2_x: f32,
#[rtti_field]
pub control_2_y: f32,
}
#[repr(C)]
#[derive(FieldOffsets, Default, SixtyFPSElement, Clone, Debug, PartialEq)]
#[pin]
pub struct PathQuadraticTo {
#[rtti_field]
pub x: f32,
#[rtti_field]
pub y: f32,
#[rtti_field]
pub control_x: f32,
#[rtti_field]
pub control_y: f32,
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum PathElement {
MoveTo(PathMoveTo),
LineTo(PathLineTo),
ArcTo(PathArcTo),
CubicTo(PathCubicTo),
QuadraticTo(PathQuadraticTo),
Close,
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum PathEvent {
Begin,
Line,
Quadratic,
Cubic,
EndOpen,
EndClosed,
}
struct ToLyonPathEventIterator<'a> {
events_it: std::slice::Iter<'a, PathEvent>,
coordinates_it: std::slice::Iter<'a, Point>,
first: Option<&'a Point>,
last: Option<&'a Point>,
}
impl<'a> Iterator for ToLyonPathEventIterator<'a> {
type Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>;
fn next(&mut self) -> Option<Self::Item> {
use lyon::path::Event;
self.events_it.next().map(|event| match event {
PathEvent::Begin => Event::Begin { at: self.coordinates_it.next().unwrap().clone() },
PathEvent::Line => Event::Line {
from: self.coordinates_it.next().unwrap().clone(),
to: self.coordinates_it.next().unwrap().clone(),
},
PathEvent::Quadratic => Event::Quadratic {
from: self.coordinates_it.next().unwrap().clone(),
ctrl: self.coordinates_it.next().unwrap().clone(),
to: self.coordinates_it.next().unwrap().clone(),
},
PathEvent::Cubic => Event::Cubic {
from: self.coordinates_it.next().unwrap().clone(),
ctrl1: self.coordinates_it.next().unwrap().clone(),
ctrl2: self.coordinates_it.next().unwrap().clone(),
to: self.coordinates_it.next().unwrap().clone(),
},
PathEvent::EndOpen => Event::End {
first: self.first.unwrap().clone(),
last: self.last.unwrap().clone(),
close: false,
},
PathEvent::EndClosed => Event::End {
first: self.first.unwrap().clone(),
last: self.last.unwrap().clone(),
close: true,
},
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.events_it.size_hint()
}
}
impl<'a> ExactSizeIterator for ToLyonPathEventIterator<'a> {}
struct TransformedLyonPathIterator<EventIt> {
it: EventIt,
transform: lyon::math::Transform,
}
impl<EventIt: Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>>> Iterator
for TransformedLyonPathIterator<EventIt>
{
type Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>;
fn next(&mut self) -> Option<Self::Item> {
self.it.next().map(|ev| ev.transformed(&self.transform))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.it.size_hint()
}
}
impl<EventIt: Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>>>
ExactSizeIterator for TransformedLyonPathIterator<EventIt>
{
}
pub struct PathDataIterator<'a> {
it: LyonPathIteratorVariant<'a>,
transform: Option<lyon::math::Transform>,
}
enum LyonPathIteratorVariant<'a> {
FromPath(lyon::path::Path),
FromEvents(&'a crate::SharedVector<PathEvent>, &'a crate::SharedVector<Point>),
}
impl<'a> PathDataIterator<'a> {
#[auto_enum(Iterator)]
pub fn iter(
&'a self,
) -> impl Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a {
match &self.it {
LyonPathIteratorVariant::FromPath(path) => self.apply_transform(path.iter()),
LyonPathIteratorVariant::FromEvents(events, coordinates) => {
self.apply_transform(ToLyonPathEventIterator {
events_it: events.iter(),
coordinates_it: coordinates.iter(),
first: coordinates.first(),
last: coordinates.last(),
})
}
}
}
fn fit(&mut self, width: f32, height: f32) {
if width > 0. || height > 0. {
let br = lyon::algorithms::aabb::bounding_rect(self.iter());
self.transform = Some(lyon::algorithms::fit::fit_rectangle(
&br,
&Rect::from_size(Size::new(width, height)),
lyon::algorithms::fit::FitStyle::Min,
));
}
}
#[auto_enum(Iterator)]
fn apply_transform(
&'a self,
event_it: impl Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a,
) -> impl Iterator<Item = lyon::path::Event<lyon::math::Point, lyon::math::Point>> + 'a {
match self.transform {
Some(transform) => TransformedLyonPathIterator { it: event_it, transform },
None => event_it,
}
}
}
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum PathData {
None,
Elements(crate::SharedVector<PathElement>),
Events(crate::SharedVector<PathEvent>, crate::SharedVector<Point>),
}
impl Default for PathData {
fn default() -> Self {
Self::None
}
}
impl PathData {
pub fn iter(&self) -> PathDataIterator {
PathDataIterator {
it: match self {
PathData::None => LyonPathIteratorVariant::FromPath(lyon::path::Path::new()),
PathData::Elements(elements) => LyonPathIteratorVariant::FromPath(
PathData::build_path(elements.as_slice().iter()),
),
PathData::Events(events, coordinates) => {
LyonPathIteratorVariant::FromEvents(events, coordinates)
}
},
transform: None,
}
}
pub fn iter_fitted(&self, width: f32, height: f32) -> PathDataIterator {
let mut it = self.iter();
it.fit(width, height);
it
}
fn build_path(element_it: std::slice::Iter<PathElement>) -> lyon::path::Path {
use lyon::geom::SvgArc;
use lyon::math::{Angle, Point, Vector};
use lyon::path::traits::SvgPathBuilder;
use lyon::path::ArcFlags;
let mut path_builder = lyon::path::Path::builder().with_svg();
for element in element_it {
match element {
PathElement::MoveTo(PathMoveTo { x, y }) => {
path_builder.move_to(Point::new(*x, *y));
}
PathElement::LineTo(PathLineTo { x, y }) => {
path_builder.line_to(Point::new(*x, *y));
}
PathElement::ArcTo(PathArcTo {
x,
y,
radius_x,
radius_y,
x_rotation,
large_arc,
sweep,
}) => {
let radii = Vector::new(*radius_x, *radius_y);
let x_rotation = Angle::degrees(*x_rotation);
let flags = ArcFlags { large_arc: *large_arc, sweep: *sweep };
let to = Point::new(*x, *y);
let svg_arc = SvgArc {
from: path_builder.current_position(),
radii,
x_rotation,
flags,
to,
};
if svg_arc.is_straight_line() {
path_builder.line_to(to);
} else {
path_builder.arc_to(radii, x_rotation, flags, to)
}
}
PathElement::CubicTo(PathCubicTo {
x,
y,
control_1_x,
control_1_y,
control_2_x,
control_2_y,
}) => {
path_builder.cubic_bezier_to(
Point::new(*control_1_x, *control_1_y),
Point::new(*control_2_x, *control_2_y),
Point::new(*x, *y),
);
}
PathElement::QuadraticTo(PathQuadraticTo { x, y, control_x, control_y }) => {
path_builder.quadratic_bezier_to(
Point::new(*control_x, *control_y),
Point::new(*x, *y),
);
}
PathElement::Close => path_builder.close(),
}
}
path_builder.build()
}
}
pub(crate) mod ffi {
#![allow(unsafe_code)]
use super::*;
#[allow(non_camel_case_types)]
type c_void = ();
#[cfg(cbindgen)]
#[repr(C)]
struct Rect {
x: f32,
y: f32,
width: f32,
height: f32,
}
#[cfg(cbindgen)]
#[repr(C)]
struct IntRect {
x: i32,
y: i32,
width: i32,
height: i32,
}
#[cfg(cbindgen)]
#[repr(C)]
struct Point {
x: f32,
y: f32,
}
#[cfg(cbindgen)]
#[repr(C)]
struct Size {
width: f32,
height: f32,
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_new_path_elements(
out: *mut c_void,
first_element: *const PathElement,
count: usize,
) {
let arr = crate::SharedVector::from(std::slice::from_raw_parts(first_element, count));
core::ptr::write(out as *mut crate::SharedVector<PathElement>, arr.clone());
}
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_new_path_events(
out_events: *mut c_void,
out_coordinates: *mut c_void,
first_event: *const PathEvent,
event_count: usize,
first_coordinate: *const Point,
coordinate_count: usize,
) {
let events =
crate::SharedVector::from(std::slice::from_raw_parts(first_event, event_count));
core::ptr::write(out_events as *mut crate::SharedVector<PathEvent>, events.clone());
let coordinates = crate::SharedVector::from(std::slice::from_raw_parts(
first_coordinate,
coordinate_count,
));
core::ptr::write(out_coordinates as *mut crate::SharedVector<Point>, coordinates.clone());
}
}