Compare commits

..

No commits in common. "3602d06a155a0567fe2b2e75a4d5970578d0f14f" and "f364b3c3239261e98da4c28c53463b751addfee7" have entirely different histories.

12 changed files with 141 additions and 194 deletions

View File

@ -69,10 +69,6 @@ fn write_element(w: &mut Writer, element: &HtmlElement) -> SourceResult<()> {
for (attr, value) in &element.attrs.0 { for (attr, value) in &element.attrs.0 {
w.buf.push(' '); w.buf.push(' ');
w.buf.push_str(&attr.resolve()); w.buf.push_str(&attr.resolve());
// If the string is empty, we can use shorthand syntax.
// `<elem attr="">..</div` is equivalent to `<elem attr>..</div>`
if !value.is_empty() {
w.buf.push('='); w.buf.push('=');
w.buf.push('"'); w.buf.push('"');
for c in value.chars() { for c in value.chars() {
@ -84,7 +80,6 @@ fn write_element(w: &mut Writer, element: &HtmlElement) -> SourceResult<()> {
} }
w.buf.push('"'); w.buf.push('"');
} }
}
w.buf.push('>'); w.buf.push('>');

View File

@ -16,21 +16,6 @@ impl Duration {
pub fn is_zero(&self) -> bool { pub fn is_zero(&self) -> bool {
self.0.is_zero() self.0.is_zero()
} }
/// Decomposes the time into whole weeks, days, hours, minutes, and seconds.
pub fn decompose(&self) -> [i64; 5] {
let mut tmp = self.0;
let weeks = tmp.whole_weeks();
tmp -= weeks.weeks();
let days = tmp.whole_days();
tmp -= days.days();
let hours = tmp.whole_hours();
tmp -= hours.hours();
let minutes = tmp.whole_minutes();
tmp -= minutes.minutes();
let seconds = tmp.whole_seconds();
[weeks, days, hours, minutes, seconds]
}
} }
#[scope] #[scope]
@ -133,25 +118,34 @@ impl Debug for Duration {
impl Repr for Duration { impl Repr for Duration {
fn repr(&self) -> EcoString { fn repr(&self) -> EcoString {
let [weeks, days, hours, minutes, seconds] = self.decompose(); let mut tmp = self.0;
let mut vec = Vec::with_capacity(5); let mut vec = Vec::with_capacity(5);
let weeks = tmp.whole_seconds() / 604_800.0 as i64;
if weeks != 0 { if weeks != 0 {
vec.push(eco_format!("weeks: {}", weeks.repr())); vec.push(eco_format!("weeks: {}", weeks.repr()));
} }
tmp -= weeks.weeks();
let days = tmp.whole_days();
if days != 0 { if days != 0 {
vec.push(eco_format!("days: {}", days.repr())); vec.push(eco_format!("days: {}", days.repr()));
} }
tmp -= days.days();
let hours = tmp.whole_hours();
if hours != 0 { if hours != 0 {
vec.push(eco_format!("hours: {}", hours.repr())); vec.push(eco_format!("hours: {}", hours.repr()));
} }
tmp -= hours.hours();
let minutes = tmp.whole_minutes();
if minutes != 0 { if minutes != 0 {
vec.push(eco_format!("minutes: {}", minutes.repr())); vec.push(eco_format!("minutes: {}", minutes.repr()));
} }
tmp -= minutes.minutes();
let seconds = tmp.whole_seconds();
if seconds != 0 { if seconds != 0 {
vec.push(eco_format!("seconds: {}", seconds.repr())); vec.push(eco_format!("seconds: {}", seconds.repr()));
} }

View File

@ -307,7 +307,7 @@ impl Func {
) -> SourceResult<Value> { ) -> SourceResult<Value> {
match &self.repr { match &self.repr {
Repr::Native(native) => { Repr::Native(native) => {
let value = (native.function.0)(engine, context, &mut args)?; let value = (native.function)(engine, context, &mut args)?;
args.finish()?; args.finish()?;
Ok(value) Ok(value)
} }
@ -491,8 +491,8 @@ pub trait NativeFunc {
/// Defines a native function. /// Defines a native function.
#[derive(Debug)] #[derive(Debug)]
pub struct NativeFuncData { pub struct NativeFuncData {
/// The implementation of the function. /// Invokes the function from Typst.
pub function: NativeFuncPtr, pub function: fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value>,
/// The function's normal name (e.g. `align`), as exposed to Typst. /// The function's normal name (e.g. `align`), as exposed to Typst.
pub name: &'static str, pub name: &'static str,
/// The function's title case name (e.g. `Align`). /// The function's title case name (e.g. `Align`).
@ -504,11 +504,11 @@ pub struct NativeFuncData {
/// Whether this function makes use of context. /// Whether this function makes use of context.
pub contextual: bool, pub contextual: bool,
/// Definitions in the scope of the function. /// Definitions in the scope of the function.
pub scope: DynLazyLock<Scope>, pub scope: LazyLock<Scope>,
/// A list of parameter information for each parameter. /// A list of parameter information for each parameter.
pub params: DynLazyLock<Vec<ParamInfo>>, pub params: LazyLock<Vec<ParamInfo>>,
/// Information about the return value of this function. /// Information about the return value of this function.
pub returns: DynLazyLock<CastInfo>, pub returns: LazyLock<CastInfo>,
} }
cast! { cast! {
@ -516,28 +516,6 @@ cast! {
self => Func::from(self).into_value(), self => Func::from(self).into_value(),
} }
/// A pointer to a native function's implementation.
pub struct NativeFuncPtr(pub &'static NativeFuncSignature);
/// The signature of a native function's implementation.
type NativeFuncSignature =
dyn Fn(&mut Engine, Tracked<Context>, &mut Args) -> SourceResult<Value> + Send + Sync;
impl Debug for NativeFuncPtr {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.pad("NativeFuncPtr(..)")
}
}
/// A `LazyLock` that uses a static closure for initialization instead of only
/// working with function pointers.
///
/// Can be created from a normal function or closure by prepending with a `&`,
/// e.g. `LazyLock::new(&|| "hello")`. Can be created from a dynamic closure
/// by allocating and then leaking it. This is equivalent to having it
/// statically allocated, but allows for it to be generated at runtime.
type DynLazyLock<T> = LazyLock<T, &'static (dyn Fn() -> T + Send + Sync)>;
/// Describes a function parameter. /// Describes a function parameter.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ParamInfo { pub struct ParamInfo {

View File

@ -4,12 +4,9 @@ use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Deref, Not};
use typst_utils::Get; use typst_utils::Get;
use crate::diag::{bail, HintedStrResult}; use crate::diag::bail;
use crate::foundations::{ use crate::foundations::{array, cast, Array, Resolve, Smart, StyleChain};
array, cast, Array, CastInfo, FromValue, IntoValue, Reflect, Resolve, Smart, use crate::layout::{Abs, Dir, Length, Ratio, Rel, Size};
StyleChain, Value,
};
use crate::layout::{Abs, Dir, Rel, Size};
/// A container with a horizontal and vertical component. /// A container with a horizontal and vertical component.
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)] #[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
@ -278,39 +275,40 @@ impl BitAndAssign for Axes<bool> {
} }
} }
impl<T: Reflect> Reflect for Axes<T> { cast! {
fn input() -> CastInfo { Axes<Rel<Length>>,
Array::input() self => array![self.x, self.y].into_value(),
} array: Array => {
fn output() -> CastInfo {
Array::output()
}
fn castable(value: &Value) -> bool {
Array::castable(value)
}
}
impl<T: FromValue> FromValue for Axes<T> {
fn from_value(value: Value) -> HintedStrResult<Self> {
let array = value.cast::<Array>()?;
let mut iter = array.into_iter(); let mut iter = array.into_iter();
match (iter.next(), iter.next(), iter.next()) { match (iter.next(), iter.next(), iter.next()) {
(Some(a), Some(b), None) => Ok(Axes::new(a.cast()?, b.cast()?)), (Some(a), Some(b), None) => Axes::new(a.cast()?, b.cast()?),
_ => bail!( _ => bail!("point array must contain exactly two entries"),
"array must contain exactly two items";
hint: "the first item determines the value for the X axis \
and the second item the value for the Y axis"
),
}
} }
},
} }
impl<T: IntoValue> IntoValue for Axes<T> { cast! {
fn into_value(self) -> Value { Axes<Ratio>,
array![self.x.into_value(), self.y.into_value()].into_value() self => array![self.x, self.y].into_value(),
array: Array => {
let mut iter = array.into_iter();
match (iter.next(), iter.next(), iter.next()) {
(Some(a), Some(b), None) => Axes::new(a.cast()?, b.cast()?),
_ => bail!("ratio array must contain exactly two entries"),
} }
},
}
cast! {
Axes<Length>,
self => array![self.x, self.y].into_value(),
array: Array => {
let mut iter = array.into_iter();
match (iter.next(), iter.next(), iter.next()) {
(Some(a), Some(b), None) => Axes::new(a.cast()?, b.cast()?),
_ => bail!("length array must contain exactly two entries"),
}
},
} }
impl<T: Resolve> Resolve for Axes<T> { impl<T: Resolve> Resolve for Axes<T> {

View File

@ -836,7 +836,7 @@ fn to_typst(synt::Color { r, g, b, a }: synt::Color) -> Color {
} }
fn to_syn(color: Color) -> synt::Color { fn to_syn(color: Color) -> synt::Color {
let (r, g, b, a) = color.to_rgb().into_format::<u8, u8>().into_components(); let [r, g, b, a] = color.to_rgb().to_vec4_u8();
synt::Color { r, g, b, a } synt::Color { r, g, b, a }
} }

View File

@ -262,7 +262,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Luma(color.to_luma()) color.to_luma()
} else { } else {
let Component(gray) = let Component(gray) =
args.expect("gray component").unwrap_or(Component(Ratio::one())); args.expect("gray component").unwrap_or(Component(Ratio::one()));
@ -318,7 +318,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Oklab(color.to_oklab()) color.to_oklab()
} else { } else {
let RatioComponent(l) = args.expect("lightness component")?; let RatioComponent(l) = args.expect("lightness component")?;
let ChromaComponent(a) = args.expect("A component")?; let ChromaComponent(a) = args.expect("A component")?;
@ -374,7 +374,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Oklch(color.to_oklch()) color.to_oklch()
} else { } else {
let RatioComponent(l) = args.expect("lightness component")?; let RatioComponent(l) = args.expect("lightness component")?;
let ChromaComponent(c) = args.expect("chroma component")?; let ChromaComponent(c) = args.expect("chroma component")?;
@ -434,7 +434,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::LinearRgb(color.to_linear_rgb()) color.to_linear_rgb()
} else { } else {
let Component(r) = args.expect("red component")?; let Component(r) = args.expect("red component")?;
let Component(g) = args.expect("green component")?; let Component(g) = args.expect("green component")?;
@ -505,7 +505,7 @@ impl Color {
Ok(if let Some(string) = args.find::<Spanned<Str>>()? { Ok(if let Some(string) = args.find::<Spanned<Str>>()? {
Self::from_str(&string.v).at(string.span)? Self::from_str(&string.v).at(string.span)?
} else if let Some(color) = args.find::<Color>()? { } else if let Some(color) = args.find::<Color>()? {
Color::Rgb(color.to_rgb()) color.to_rgb()
} else { } else {
let Component(r) = args.expect("red component")?; let Component(r) = args.expect("red component")?;
let Component(g) = args.expect("green component")?; let Component(g) = args.expect("green component")?;
@ -565,7 +565,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Cmyk(color.to_cmyk()) color.to_cmyk()
} else { } else {
let RatioComponent(c) = args.expect("cyan component")?; let RatioComponent(c) = args.expect("cyan component")?;
let RatioComponent(m) = args.expect("magenta component")?; let RatioComponent(m) = args.expect("magenta component")?;
@ -622,7 +622,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Hsl(color.to_hsl()) color.to_hsl()
} else { } else {
let h: Angle = args.expect("hue component")?; let h: Angle = args.expect("hue component")?;
let Component(s) = args.expect("saturation component")?; let Component(s) = args.expect("saturation component")?;
@ -679,7 +679,7 @@ impl Color {
color: Color, color: Color,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(if let Some(color) = args.find::<Color>()? { Ok(if let Some(color) = args.find::<Color>()? {
Color::Hsv(color.to_hsv()) color.to_hsv()
} else { } else {
let h: Angle = args.expect("hue component")?; let h: Angle = args.expect("hue component")?;
let Component(s) = args.expect("saturation component")?; let Component(s) = args.expect("saturation component")?;
@ -830,7 +830,7 @@ impl Color {
/// omitted if it is equal to `ff` (255 / 100%). /// omitted if it is equal to `ff` (255 / 100%).
#[func] #[func]
pub fn to_hex(self) -> EcoString { pub fn to_hex(self) -> EcoString {
let (r, g, b, a) = self.to_rgb().into_format::<u8, u8>().into_components(); let [r, g, b, a] = self.to_rgb().to_vec4_u8();
if a != 255 { if a != 255 {
eco_format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a) eco_format!("#{:02x}{:02x}{:02x}{:02x}", r, g, b, a)
} else { } else {
@ -886,21 +886,20 @@ impl Color {
/// The factor to saturate the color by. /// The factor to saturate the color by.
factor: Ratio, factor: Ratio,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
let f = factor.get() as f32;
Ok(match self { Ok(match self {
Self::Luma(_) => bail!( Self::Luma(_) => {
bail!(
span, "cannot saturate grayscale color"; span, "cannot saturate grayscale color";
hint: "try converting your color to RGB first" hint: "try converting your color to RGB first"
), );
Self::Hsl(c) => Self::Hsl(c.saturate(f)),
Self::Hsv(c) => Self::Hsv(c.saturate(f)),
Self::Oklab(_)
| Self::Oklch(_)
| Self::LinearRgb(_)
| Self::Rgb(_)
| Self::Cmyk(_) => {
Color::Hsv(self.to_hsv().saturate(f)).to_space(self.space())
} }
Self::Oklab(_) => self.to_hsv().saturate(span, factor)?.to_oklab(),
Self::Oklch(_) => self.to_hsv().saturate(span, factor)?.to_oklch(),
Self::LinearRgb(_) => self.to_hsv().saturate(span, factor)?.to_linear_rgb(),
Self::Rgb(_) => self.to_hsv().saturate(span, factor)?.to_rgb(),
Self::Cmyk(_) => self.to_hsv().saturate(span, factor)?.to_cmyk(),
Self::Hsl(c) => Self::Hsl(c.saturate(factor.get() as f32)),
Self::Hsv(c) => Self::Hsv(c.saturate(factor.get() as f32)),
}) })
} }
@ -912,21 +911,20 @@ impl Color {
/// The factor to desaturate the color by. /// The factor to desaturate the color by.
factor: Ratio, factor: Ratio,
) -> SourceResult<Color> { ) -> SourceResult<Color> {
let f = factor.get() as f32;
Ok(match self { Ok(match self {
Self::Luma(_) => bail!( Self::Luma(_) => {
bail!(
span, "cannot desaturate grayscale color"; span, "cannot desaturate grayscale color";
hint: "try converting your color to RGB first" hint: "try converting your color to RGB first"
), );
Self::Hsl(c) => Self::Hsl(c.desaturate(f)),
Self::Hsv(c) => Self::Hsv(c.desaturate(f)),
Self::Oklab(_)
| Self::Oklch(_)
| Self::LinearRgb(_)
| Self::Rgb(_)
| Self::Cmyk(_) => {
Color::Hsv(self.to_hsv().desaturate(f)).to_space(self.space())
} }
Self::Oklab(_) => self.to_hsv().desaturate(span, factor)?.to_oklab(),
Self::Oklch(_) => self.to_hsv().desaturate(span, factor)?.to_oklch(),
Self::LinearRgb(_) => self.to_hsv().desaturate(span, factor)?.to_linear_rgb(),
Self::Rgb(_) => self.to_hsv().desaturate(span, factor)?.to_rgb(),
Self::Cmyk(_) => self.to_hsv().desaturate(span, factor)?.to_cmyk(),
Self::Hsl(c) => Self::Hsl(c.desaturate(factor.get() as f32)),
Self::Hsv(c) => Self::Hsv(c.desaturate(factor.get() as f32)),
}) })
} }
@ -996,17 +994,23 @@ impl Color {
) -> SourceResult<Color> { ) -> SourceResult<Color> {
Ok(match space { Ok(match space {
ColorSpace::Oklch => { ColorSpace::Oklch => {
let oklch = self.to_oklch(); let Self::Oklch(oklch) = self.to_oklch() else {
unreachable!();
};
let rotated = oklch.shift_hue(angle.to_deg() as f32); let rotated = oklch.shift_hue(angle.to_deg() as f32);
Self::Oklch(rotated).to_space(self.space()) Self::Oklch(rotated).to_space(self.space())
} }
ColorSpace::Hsl => { ColorSpace::Hsl => {
let hsl = self.to_hsl(); let Self::Hsl(hsl) = self.to_hsl() else {
unreachable!();
};
let rotated = hsl.shift_hue(angle.to_deg() as f32); let rotated = hsl.shift_hue(angle.to_deg() as f32);
Self::Hsl(rotated).to_space(self.space()) Self::Hsl(rotated).to_space(self.space())
} }
ColorSpace::Hsv => { ColorSpace::Hsv => {
let hsv = self.to_hsv(); let Self::Hsv(hsv) = self.to_hsv() else {
unreachable!();
};
let rotated = hsv.shift_hue(angle.to_deg() as f32); let rotated = hsv.shift_hue(angle.to_deg() as f32);
Self::Hsv(rotated).to_space(self.space()) Self::Hsv(rotated).to_space(self.space())
} }
@ -1277,19 +1281,19 @@ impl Color {
pub fn to_space(self, space: ColorSpace) -> Self { pub fn to_space(self, space: ColorSpace) -> Self {
match space { match space {
ColorSpace::D65Gray => Self::Luma(self.to_luma()), ColorSpace::Oklab => self.to_oklab(),
ColorSpace::Oklab => Self::Oklab(self.to_oklab()), ColorSpace::Oklch => self.to_oklch(),
ColorSpace::Oklch => Self::Oklch(self.to_oklch()), ColorSpace::Srgb => self.to_rgb(),
ColorSpace::Srgb => Self::Rgb(self.to_rgb()), ColorSpace::LinearRgb => self.to_linear_rgb(),
ColorSpace::LinearRgb => Self::LinearRgb(self.to_linear_rgb()), ColorSpace::Hsl => self.to_hsl(),
ColorSpace::Cmyk => Self::Cmyk(self.to_cmyk()), ColorSpace::Hsv => self.to_hsv(),
ColorSpace::Hsl => Self::Hsl(self.to_hsl()), ColorSpace::Cmyk => self.to_cmyk(),
ColorSpace::Hsv => Self::Hsv(self.to_hsv()), ColorSpace::D65Gray => self.to_luma(),
} }
} }
pub fn to_luma(self) -> Luma { pub fn to_luma(self) -> Self {
match self { Self::Luma(match self {
Self::Luma(c) => c, Self::Luma(c) => c,
Self::Oklab(c) => Luma::from_color(c), Self::Oklab(c) => Luma::from_color(c),
Self::Oklch(c) => Luma::from_color(c), Self::Oklch(c) => Luma::from_color(c),
@ -1298,11 +1302,11 @@ impl Color {
Self::Cmyk(c) => Luma::from_color(c.to_rgba()), Self::Cmyk(c) => Luma::from_color(c.to_rgba()),
Self::Hsl(c) => Luma::from_color(c), Self::Hsl(c) => Luma::from_color(c),
Self::Hsv(c) => Luma::from_color(c), Self::Hsv(c) => Luma::from_color(c),
} })
} }
pub fn to_oklab(self) -> Oklab { pub fn to_oklab(self) -> Self {
match self { Self::Oklab(match self {
Self::Luma(c) => Oklab::from_color(c), Self::Luma(c) => Oklab::from_color(c),
Self::Oklab(c) => c, Self::Oklab(c) => c,
Self::Oklch(c) => Oklab::from_color(c), Self::Oklch(c) => Oklab::from_color(c),
@ -1311,11 +1315,11 @@ impl Color {
Self::Cmyk(c) => Oklab::from_color(c.to_rgba()), Self::Cmyk(c) => Oklab::from_color(c.to_rgba()),
Self::Hsl(c) => Oklab::from_color(c), Self::Hsl(c) => Oklab::from_color(c),
Self::Hsv(c) => Oklab::from_color(c), Self::Hsv(c) => Oklab::from_color(c),
} })
} }
pub fn to_oklch(self) -> Oklch { pub fn to_oklch(self) -> Self {
match self { Self::Oklch(match self {
Self::Luma(c) => Oklch::from_color(c), Self::Luma(c) => Oklch::from_color(c),
Self::Oklab(c) => Oklch::from_color(c), Self::Oklab(c) => Oklch::from_color(c),
Self::Oklch(c) => c, Self::Oklch(c) => c,
@ -1324,11 +1328,11 @@ impl Color {
Self::Cmyk(c) => Oklch::from_color(c.to_rgba()), Self::Cmyk(c) => Oklch::from_color(c.to_rgba()),
Self::Hsl(c) => Oklch::from_color(c), Self::Hsl(c) => Oklch::from_color(c),
Self::Hsv(c) => Oklch::from_color(c), Self::Hsv(c) => Oklch::from_color(c),
} })
} }
pub fn to_rgb(self) -> Rgb { pub fn to_rgb(self) -> Self {
match self { Self::Rgb(match self {
Self::Luma(c) => Rgb::from_color(c), Self::Luma(c) => Rgb::from_color(c),
Self::Oklab(c) => Rgb::from_color(c), Self::Oklab(c) => Rgb::from_color(c),
Self::Oklch(c) => Rgb::from_color(c), Self::Oklch(c) => Rgb::from_color(c),
@ -1337,11 +1341,11 @@ impl Color {
Self::Cmyk(c) => Rgb::from_color(c.to_rgba()), Self::Cmyk(c) => Rgb::from_color(c.to_rgba()),
Self::Hsl(c) => Rgb::from_color(c), Self::Hsl(c) => Rgb::from_color(c),
Self::Hsv(c) => Rgb::from_color(c), Self::Hsv(c) => Rgb::from_color(c),
} })
} }
pub fn to_linear_rgb(self) -> LinearRgb { pub fn to_linear_rgb(self) -> Self {
match self { Self::LinearRgb(match self {
Self::Luma(c) => LinearRgb::from_color(c), Self::Luma(c) => LinearRgb::from_color(c),
Self::Oklab(c) => LinearRgb::from_color(c), Self::Oklab(c) => LinearRgb::from_color(c),
Self::Oklch(c) => LinearRgb::from_color(c), Self::Oklch(c) => LinearRgb::from_color(c),
@ -1350,11 +1354,11 @@ impl Color {
Self::Cmyk(c) => LinearRgb::from_color(c.to_rgba()), Self::Cmyk(c) => LinearRgb::from_color(c.to_rgba()),
Self::Hsl(c) => Rgb::from_color(c).into_linear(), Self::Hsl(c) => Rgb::from_color(c).into_linear(),
Self::Hsv(c) => Rgb::from_color(c).into_linear(), Self::Hsv(c) => Rgb::from_color(c).into_linear(),
} })
} }
pub fn to_cmyk(self) -> Cmyk { pub fn to_cmyk(self) -> Self {
match self { Self::Cmyk(match self {
Self::Luma(c) => Cmyk::from_luma(c), Self::Luma(c) => Cmyk::from_luma(c),
Self::Oklab(c) => Cmyk::from_rgba(Rgb::from_color(c)), Self::Oklab(c) => Cmyk::from_rgba(Rgb::from_color(c)),
Self::Oklch(c) => Cmyk::from_rgba(Rgb::from_color(c)), Self::Oklch(c) => Cmyk::from_rgba(Rgb::from_color(c)),
@ -1363,11 +1367,11 @@ impl Color {
Self::Cmyk(c) => c, Self::Cmyk(c) => c,
Self::Hsl(c) => Cmyk::from_rgba(Rgb::from_color(c)), Self::Hsl(c) => Cmyk::from_rgba(Rgb::from_color(c)),
Self::Hsv(c) => Cmyk::from_rgba(Rgb::from_color(c)), Self::Hsv(c) => Cmyk::from_rgba(Rgb::from_color(c)),
} })
} }
pub fn to_hsl(self) -> Hsl { pub fn to_hsl(self) -> Self {
match self { Self::Hsl(match self {
Self::Luma(c) => Hsl::from_color(c), Self::Luma(c) => Hsl::from_color(c),
Self::Oklab(c) => Hsl::from_color(c), Self::Oklab(c) => Hsl::from_color(c),
Self::Oklch(c) => Hsl::from_color(c), Self::Oklch(c) => Hsl::from_color(c),
@ -1376,11 +1380,11 @@ impl Color {
Self::Cmyk(c) => Hsl::from_color(c.to_rgba()), Self::Cmyk(c) => Hsl::from_color(c.to_rgba()),
Self::Hsl(c) => c, Self::Hsl(c) => c,
Self::Hsv(c) => Hsl::from_color(c), Self::Hsv(c) => Hsl::from_color(c),
} })
} }
pub fn to_hsv(self) -> Hsv { pub fn to_hsv(self) -> Self {
match self { Self::Hsv(match self {
Self::Luma(c) => Hsv::from_color(c), Self::Luma(c) => Hsv::from_color(c),
Self::Oklab(c) => Hsv::from_color(c), Self::Oklab(c) => Hsv::from_color(c),
Self::Oklch(c) => Hsv::from_color(c), Self::Oklch(c) => Hsv::from_color(c),
@ -1389,7 +1393,7 @@ impl Color {
Self::Cmyk(c) => Hsv::from_color(c.to_rgba()), Self::Cmyk(c) => Hsv::from_color(c.to_rgba()),
Self::Hsl(c) => Hsv::from_color(c), Self::Hsl(c) => Hsv::from_color(c),
Self::Hsv(c) => c, Self::Hsv(c) => c,
} })
} }
} }

View File

@ -315,15 +315,15 @@ fn create_func_data(func: &Func) -> TokenStream {
quote! { quote! {
#foundations::NativeFuncData { #foundations::NativeFuncData {
function: #foundations::NativeFuncPtr(&#closure), function: #closure,
name: #name, name: #name,
title: #title, title: #title,
docs: #docs, docs: #docs,
keywords: &[#(#keywords),*], keywords: &[#(#keywords),*],
contextual: #contextual, contextual: #contextual,
scope: ::std::sync::LazyLock::new(&|| #scope), scope: ::std::sync::LazyLock::new(|| #scope),
params: ::std::sync::LazyLock::new(&|| ::std::vec![#(#params),*]), params: ::std::sync::LazyLock::new(|| ::std::vec![#(#params),*]),
returns: ::std::sync::LazyLock::new(&|| <#returns as #foundations::Reflect>::output()), returns: ::std::sync::LazyLock::new(|| <#returns as #foundations::Reflect>::output()),
} }
} }
} }

View File

@ -255,13 +255,13 @@ pub fn to_sk_paint<'a>(
} }
pub fn to_sk_color(color: Color) -> sk::Color { pub fn to_sk_color(color: Color) -> sk::Color {
let (r, g, b, a) = color.to_rgb().into_components(); let [r, g, b, a] = color.to_rgb().to_vec4();
sk::Color::from_rgba(r, g, b, a) sk::Color::from_rgba(r, g, b, a)
.expect("components must always be in the range [0..=1]") .expect("components must always be in the range [0..=1]")
} }
pub fn to_sk_color_u8(color: Color) -> sk::ColorU8 { pub fn to_sk_color_u8(color: Color) -> sk::ColorU8 {
let (r, g, b, a) = color.to_rgb().into_format::<u8, u8>().into_components(); let [r, g, b, a] = color.to_rgb().to_vec4_u8();
sk::ColorU8::from_rgba(r, g, b, a) sk::ColorU8::from_rgba(r, g, b, a)
} }

View File

@ -23,7 +23,7 @@ pub use self::scalar::Scalar;
#[doc(hidden)] #[doc(hidden)]
pub use once_cell; pub use once_cell;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Formatter};
use std::hash::Hash; use std::hash::Hash;
use std::iter::{Chain, Flatten, Rev}; use std::iter::{Chain, Flatten, Rev};
use std::num::{NonZeroU32, NonZeroUsize}; use std::num::{NonZeroU32, NonZeroUsize};
@ -52,25 +52,6 @@ where
Wrapper(f) Wrapper(f)
} }
/// Turn a closure into a struct implementing [`Display`].
pub fn display<F>(f: F) -> impl Display
where
F: Fn(&mut Formatter) -> std::fmt::Result,
{
struct Wrapper<F>(F);
impl<F> Display for Wrapper<F>
where
F: Fn(&mut Formatter) -> std::fmt::Result,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.0(f)
}
}
Wrapper(f)
}
/// Calculate a 128-bit siphash of a value. /// Calculate a 128-bit siphash of a value.
pub fn hash128<T: Hash + ?Sized>(value: &T) -> u128 { pub fn hash128<T: Hash + ?Sized>(value: &T) -> u128 {
let mut state = SipHasher13::new(); let mut state = SipHasher13::new();

View File

@ -84,8 +84,7 @@
--- line-bad-point-array --- --- line-bad-point-array ---
// Test errors. // Test errors.
// Error: 12-19 array must contain exactly two items // Error: 12-19 point array must contain exactly two entries
// Hint: 12-19 the first item determines the value for the X axis and the second item the value for the Y axis
#line(end: (50pt,)) #line(end: (50pt,))
--- line-bad-point-component-type --- --- line-bad-point-component-type ---

View File

@ -76,8 +76,7 @@
#path(((0%, 0%), (0%, 0%), (0%, 0%), (0%, 0%))) #path(((0%, 0%), (0%, 0%), (0%, 0%), (0%, 0%)))
--- path-bad-point-array --- --- path-bad-point-array ---
// Error: 7-31 array must contain exactly two items // Error: 7-31 point array must contain exactly two entries
// Hint: 7-31 the first item determines the value for the X axis and the second item the value for the Y axis
// Warning: 2-6 the `path` function is deprecated, use `curve` instead // Warning: 2-6 the `path` function is deprecated, use `curve` instead
#path(((0%, 0%), (0%, 0%, 0%))) #path(((0%, 0%), (0%, 0%, 0%)))

View File

@ -49,8 +49,7 @@
) )
--- polygon-bad-point-array --- --- polygon-bad-point-array ---
// Error: 10-17 array must contain exactly two items // Error: 10-17 point array must contain exactly two entries
// Hint: 10-17 the first item determines the value for the X axis and the second item the value for the Y axis
#polygon((50pt,)) #polygon((50pt,))
--- polygon-infinite-size --- --- polygon-infinite-size ---