use super::*;
use crate::abi;
use crate::resolve;
use crate::mangle::{self, swift_type};
pub(crate) unsafe fn shape_view(bytes: &[u8], mangled: &[u8]) -> ViewHandle {
let meta = unsafe { abi::resolve_type_by_mangled_name(mangled) };
assert!(!meta.is_null(), "shape metadata: {}", crate::diag::demangle_type(mangled));
let wt = unsafe { abi::get_view_wt(meta) };
assert!(!wt.is_null(), "{} !: View", crate::diag::demangle_type(mangled));
unsafe { wrap_buf(bytes, meta, wt) }
}
pub fn rectangle() -> ViewHandle {
unsafe { shape_view(&[], swift_type!(SwiftUI.Rectangle).as_bytes()) }
}
pub fn circle() -> ViewHandle {
unsafe { shape_view(&[], swift_type!(SwiftUI.Circle).as_bytes()) }
}
pub fn ellipse() -> ViewHandle {
unsafe { shape_view(&[], swift_type!(SwiftUI.Ellipse).as_bytes()) }
}
pub fn capsule() -> ViewHandle {
unsafe { shape_view(&[0u8], swift_type!(SwiftUI.Capsule).as_bytes()) }
}
pub fn rounded_rectangle_styled(radius: f64, continuous: bool) -> ViewHandle {
let mut buf = [0u8; 17];
buf[..8].copy_from_slice(&radius.to_le_bytes());
buf[8..16].copy_from_slice(&radius.to_le_bytes());
buf[16] = u8::from(continuous);
unsafe { shape_view(&buf, swift_type!(SwiftUI.RoundedRectangle).as_bytes()) }
}
pub fn rounded_rectangle(radius: f64) -> ViewHandle {
rounded_rectangle_styled(radius, true)
}
mangle::modifier_types! {
pub(crate) LINEAR_GRADIENT_BG_T = ty("_BackgroundStyleModifier").of(vec![ty("LinearGradient")]);
pub(crate) RADIAL_GRADIENT_BG_T = ty("_BackgroundStyleModifier").of(vec![ty("RadialGradient")]);
}
pub(crate) unsafe fn color_array(colors: &[u64]) -> u64 {
let (array, buf): (u64, u64);
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::array_alloc_uninit(),
in("x0") colors.len() as u64,
in("x1") resolve::color_meta(),
lateout("x0") array, lateout("x1") buf,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
let dst = buf as *mut u64;
for (i, c) in colors.iter().enumerate() {
*dst.add(i) = *c;
}
}
array
}
pub(crate) unsafe fn gradient(colors: &[crate::Color]) -> u64 {
let raw: Vec<u64> =
colors.iter().map(|c| unsafe { color_value(c.r, c.g, c.b, c.a) }).collect();
unsafe { gradient_of(&raw) }
}
pub(crate) unsafe fn gradient_of(raw: &[u64]) -> u64 {
let array = unsafe { color_array(raw) };
let g: u64;
unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::gradient_init(),
in("x0") array,
in("x20") resolve::gradient_meta(),
lateout("x0") g,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
}
g
}
pub fn color_meta_for_style() -> *const c_void {
resolve::color_meta()
}
pub fn color_shapestyle_wt_for_style() -> *const c_void {
resolve::color_shapestyle_wt()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Hierarchical {
Primary,
Secondary,
Tertiary,
Quaternary,
Quinary,
}
pub fn hierarchical_style(level: Hierarchical) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let getter = match level {
Hierarchical::Primary => resolve::hierarchical_primary(),
Hierarchical::Secondary => resolve::hierarchical_secondary(),
Hierarchical::Tertiary => resolve::hierarchical_tertiary(),
Hierarchical::Quaternary => resolve::hierarchical_quaternary(),
Hierarchical::Quinary => resolve::hierarchical_quinary(),
};
let meta = abi::metadata0(resolve::hierarchical_ma());
assert!(!meta.is_null(), "HierarchicalShapeStyle metadata");
let r = abi::static_regs(getter, meta);
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let n = bytes.len().min(8);
bytes[..n].copy_from_slice(&r[0].to_le_bytes()[..n]);
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "HierarchicalShapeStyle !: ShapeStyle");
(bytes, meta, wt)
}
}
pub fn opacity_of_style(
base: &[u8],
base_ty: mangle::Type,
opacity: f64,
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_OpacityShapeStyle").of(vec![base_ty]).mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "_OpacityShapeStyle metadata: {ty}");
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_OpacityShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[..base.len()].copy_from_slice(base);
let at = base.len().next_multiple_of(4);
bytes[at..at + 4].copy_from_slice(&(opacity as f32).to_le_bytes());
(bytes, meta, wt)
}
}
pub fn implicit_opacity_style(opacity: f64) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_OpacityShapeStyle")
.of(vec![mangle::ty("_ImplicitShapeStyle")])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "_OpacityShapeStyle<_ImplicitShapeStyle> metadata");
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_OpacityShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[..4].copy_from_slice(&(opacity as f32).to_le_bytes());
(bytes, meta, wt)
}
}
pub fn shadow_style(
drop: bool,
color: Option<u64>,
radius: f64,
x: f64,
y: f64,
) -> Vec<u8> {
unsafe {
let meta = abi::metadata0(resolve::shadow_style_ma());
assert!(!meta.is_null(), "ShadowStyle metadata");
let colour = color.unwrap_or_else(|| default_shadow_colour(if drop { 0.33 } else { 0.55 }));
let mut out = vec![0u8; abi::type_bytes(meta)];
let f = if drop { resolve::shadow_style_drop() } else { resolve::shadow_style_inner() };
core::arch::asm!("blr {f}", f = in(reg) f,
in("x0") colour,
in("d0") radius, in("d1") x, in("d2") y,
in("x8") out.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
fn default_shadow_colour(opacity: f64) -> u64 {
unsafe {
let mut space: u8 = 1;
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::color_white_init(),
in("x0") &mut space as *mut u8,
in("d0") 0.0f64, in("d1") opacity,
in("x20") resolve::color_meta(),
lateout("x0") out,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
out
}
}
pub fn implicit_shadow_style(shadow: &[u8]) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_ShadowShapeStyle")
.of(vec![mangle::ty("_ImplicitShapeStyle")])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "_ShadowShapeStyle<_ImplicitShapeStyle> metadata");
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_ShadowShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let n = shadow.len().min(bytes.len());
bytes[..n].copy_from_slice(&shadow[..n]);
(bytes, meta, wt)
}
}
pub fn color_shadow_style(color: u64, shadow: &[u8]) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let subst = [resolve::color_meta(), resolve::color_shapestyle_wt()];
let (meta, _, size) =
super::opaque_result_type(resolve::shape_style_shadow_oret(), &subst);
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "shadowed style !: ShapeStyle");
let mut out = vec![0u8; size];
let mut arg = shadow.to_vec();
let mut receiver = color;
core::arch::asm!("blr {f}", f = in(reg) resolve::shape_style_shadow_fn(),
in("x0") arg.as_mut_ptr(),
in("x1") subst[0], in("x2") subst[1],
in("x8") out.as_mut_ptr(),
in("x20") &mut receiver as *mut u64,
lateout("x3") _, lateout("x4") _, lateout("x5") _, lateout("x6") _,
lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
clobber_abi("C"),
);
(out, meta, wt)
}
}
pub fn implicit_blend_mode_style(mode: u8) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_BlendModeShapeStyle")
.of(vec![mangle::ty("_ImplicitShapeStyle")])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "_BlendModeShapeStyle<_ImplicitShapeStyle> metadata");
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_BlendModeShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[0] = mode;
(bytes, meta, wt)
}
}
swift_statics! {
pub enum GradientColorSpace {
Device = c"$s7SwiftUI8GradientV10ColorSpaceV6deviceAEvgZ",
Perceptual = c"$s7SwiftUI8GradientV10ColorSpaceV10perceptualAEvgZ",
}
}
pub fn gradient_color_space_perceptual(
colors: &[u64],
) -> (Vec<u8>, *const c_void, *const c_void) {
gradient_color_space(colors, GradientColorSpace::Perceptual)
}
pub fn gradient_color_space(
colors: &[u64],
space: GradientColorSpace,
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let g = gradient_of(colors);
let space_meta = abi::metadata0(resolve::gradient_color_space_ma());
assert!(!space_meta.is_null(), "Gradient.ColorSpace metadata");
let mut space = super::static_getter_indirect(
space.resolved(),
space_meta,
abi::type_bytes(space_meta),
);
let out: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::gradient_color_space_fn(),
in("x0") space.as_mut_ptr(),
in("x1") g,
lateout("x0") out,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
(out.to_le_bytes().to_vec(), resolve::any_gradient_meta(), resolve::any_gradient_wt())
}
}
pub fn linear_gradient_style(
colors: &[crate::Color],
start: (f64, f64),
end: (f64, f64),
) -> (Vec<u8>, *const c_void, *const c_void) {
let raw: Vec<u64> = colors.iter().map(|c| super::color_value_of(*c)).collect();
linear_gradient_style_of(&raw, start, end)
}
pub fn angular_gradient_style(
colors: &[crate::Color],
center: (f64, f64),
start_degrees: f64,
) -> (Vec<u8>, *const c_void, *const c_void) {
let raw: Vec<u64> = colors.iter().map(|c| super::color_value_of(*c)).collect();
unsafe {
let g = gradient_of(&raw);
let meta = crate::type_by_name!(swift_type!(SwiftUI.AngularGradient).as_bytes());
assert!(!meta.is_null(), "AngularGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::angular_gradient_init(),
in("x0") g,
in("d0") center.0, in("d1") center.1,
in("d2") start_degrees * core::f64::consts::PI / 180.0,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
(value, meta, resolve::angular_gradient_wt())
}
}
pub fn elliptical_gradient_style(
colors: &[crate::Color],
center: (f64, f64),
start_fraction: f64,
end_fraction: f64,
) -> (Vec<u8>, *const c_void, *const c_void) {
let raw: Vec<u64> = colors.iter().map(|c| super::color_value_of(*c)).collect();
unsafe {
let array = color_array(&raw);
let meta = crate::type_by_name!(swift_type!(SwiftUI.EllipticalGradient).as_bytes());
assert!(!meta.is_null(), "EllipticalGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::elliptical_gradient_init(),
in("x0") array,
in("d0") center.0, in("d1") center.1,
in("d2") start_fraction, in("d3") end_fraction,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
(value, meta, resolve::elliptical_gradient_wt())
}
}
pub fn linear_gradient_style_of(
colors: &[u64],
start: (f64, f64),
end: (f64, f64),
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let g = gradient_of(colors);
let meta = crate::type_by_name!(swift_type!(SwiftUI.LinearGradient).as_bytes());
assert!(!meta.is_null(), "LinearGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::linear_gradient_init(),
in("x0") g,
in("d0") start.0, in("d1") start.1, in("d2") end.0, in("d3") end.1,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
(value, meta, resolve::linear_gradient_wt())
}
}
pub fn background_linear_gradient(
view: &ViewHandle,
colors: &[crate::Color],
start: (f64, f64),
end: (f64, f64),
) -> ViewHandle {
unsafe {
let g = gradient(colors);
let meta = crate::type_by_name!(swift_type!(SwiftUI.LinearGradient).as_bytes());
assert!(!meta.is_null(), "LinearGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::linear_gradient_init(),
in("x0") g,
in("d0") start.0, in("d1") start.1, in("d2") end.0, in("d3") end.1,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
background_style(
view,
value.as_mut_ptr(),
meta,
resolve::linear_gradient_wt(),
&LINEAR_GRADIENT_BG_T,
)
}
}
pub fn background_radial_gradient(
view: &ViewHandle,
colors: &[crate::Color],
center: (f64, f64),
start_radius: f64,
end_radius: f64,
) -> ViewHandle {
unsafe {
let g = gradient(colors);
let meta = crate::type_by_name!(swift_type!(SwiftUI.RadialGradient).as_bytes());
assert!(!meta.is_null(), "RadialGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::radial_gradient_init(),
in("x0") g,
in("d0") center.0, in("d1") center.1, in("d2") start_radius, in("d3") end_radius,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
background_style(
view,
value.as_mut_ptr(),
meta,
resolve::radial_gradient_wt(),
&RADIAL_GRADIENT_BG_T,
)
}
}
pub struct Shape {
bytes: Vec<u8>,
meta: *const c_void,
wt: *const c_void,
ty: mangle::Type,
}
impl Shape {
pub(crate) unsafe fn of_metadata(
bytes: Vec<u8>,
meta: *const c_void,
wt: *const c_void,
ty: mangle::Type,
) -> Self {
Self { bytes, meta, wt, ty }
}
unsafe fn of(bytes: Vec<u8>, ty: mangle::Type) -> Self {
unsafe {
let mangled = ty.mangle();
let meta = abi::resolve_type_by_mangled_name(mangled.as_bytes());
assert!(!meta.is_null(), "shape metadata: {}", mangle::demangle(&mangled));
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_proto());
assert!(!wt.is_null(), "{} !: Shape", mangle::demangle(&mangled));
Self { bytes, meta, wt, ty }
}
}
pub fn circle() -> Self {
unsafe { Self::empty(mangle::ty("Circle"), resolve::circle_init()) }
}
pub fn rectangle() -> Self {
unsafe { Self::empty(mangle::ty("Rectangle"), resolve::rectangle_init()) }
}
pub fn ellipse() -> Self {
unsafe { Self::empty(mangle::ty("Ellipse"), resolve::ellipse_init()) }
}
unsafe fn empty(ty: mangle::Type, init: *const c_void) -> Self {
unsafe {
let mut me = Self::of(Vec::new(), ty);
let size = abi::type_bytes(me.meta);
let mut bytes = vec![0u8; size.max(1)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x8") bytes.as_mut_ptr(),
in("x20") me.meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
bytes.truncate(size);
me.bytes = bytes;
me
}
}
pub fn capsule() -> Self {
unsafe { Self::of(vec![1u8], mangle::ty("Capsule")) }
}
pub fn rounded_rectangle(radius: f64, continuous: bool) -> Self {
let mut bytes = Vec::with_capacity(24);
bytes.extend_from_slice(&radius.to_le_bytes());
bytes.extend_from_slice(&radius.to_le_bytes());
bytes.push(u8::from(continuous));
unsafe { Self::of(bytes, mangle::ty("RoundedRectangle")) }
}
pub(crate) fn ty(&self) -> &mangle::Type {
&self.ty
}
pub fn view(&self) -> ViewHandle {
unsafe {
let wt = abi::get_view_wt(self.meta);
ViewHandle::wrap(self.bytes.as_ptr() as _, self.meta, wt)
}
}
pub fn path() -> PathBuilder {
let mut bytes = vec![0u8; 40]; unsafe {
core::arch::asm!("blr {f}", f = in(reg) resolve::path_init(),
in("x8") bytes.as_mut_ptr(),
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
}
PathBuilder { bytes }
}
fn base_name(&self) -> Option<&'static str> {
match &self.ty {
mangle::Type::Nominal { path, .. } => path.last().copied(),
_ => None,
}
}
pub fn inset(&self, amount: f64) -> Self {
let name = self.base_name().expect("inset needs a plain shape");
let symbol: &core::ffi::CStr = match name {
"Circle" => c"$s7SwiftUI6CircleV5inset2byQr12CoreGraphics7CGFloatV_tF",
"Capsule" => c"$s7SwiftUI7CapsuleV5inset2byQr12CoreGraphics7CGFloatV_tF",
"Ellipse" => c"$s7SwiftUI7EllipseV5inset2byQr12CoreGraphics7CGFloatV_tF",
"Rectangle" => c"$s7SwiftUI9RectangleV5inset2byQr12CoreGraphics7CGFloatV_tF",
"RoundedRectangle" => c"$s7SwiftUI16RoundedRectangleV5inset2byQr12CoreGraphics7CGFloatV_tF",
other => panic!("no exported inset(by:) for {other}"),
};
let oret: &core::ffi::CStr = match name {
"Circle" => c"$s7SwiftUI6CircleV5inset2byQr12CoreGraphics7CGFloatV_tFQOMQ",
"Capsule" => c"$s7SwiftUI7CapsuleV5inset2byQr12CoreGraphics7CGFloatV_tFQOMQ",
"Ellipse" => c"$s7SwiftUI7EllipseV5inset2byQr12CoreGraphics7CGFloatV_tFQOMQ",
"RoundedRectangle" => c"$s7SwiftUI16RoundedRectangleV5inset2byQr12CoreGraphics7CGFloatV_tFQOMQ",
_ => c"$s7SwiftUI9RectangleV5inset2byQr12CoreGraphics7CGFloatV_tFQOMQ",
};
unsafe {
let (meta, _, size) = super::opaque_result_type(resolve::sym(oret), &[]);
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_proto());
assert!(!wt.is_null(), "inset shape !: Shape");
let mut bytes = vec![0u8; size.max(1)];
let mut receiver = self.receiver();
core::arch::asm!("blr {f}", f = in(reg) resolve::sym(symbol),
in("d0") amount,
in("x8") bytes.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
let ty = mangle::Type::Nominal {
module: "SwiftUI",
path: vec![name, "_Inset"],
kind: mangle::Kind::Struct,
};
debug_assert_eq!(
abi::mangled_type_name(meta).as_deref(),
Some(ty.mangle().as_bytes()),
"{name}.inset(by:) does not return {name}._Inset",
);
Self { bytes, meta, wt, ty }
}
}
pub fn offset(&self, x: f64, y: f64) -> Self {
let ty = mangle::ty("OffsetShape").of(vec![self.ty.clone()]);
unsafe { self.wrapped(resolve::shape_offset_fn(), ty, [x, y, 0.0, 0.0]) }
}
pub fn rotation(&self, degrees: f64) -> Self {
let ty = mangle::ty("RotatedShape").of(vec![self.ty.clone()]);
let radians = degrees * std::f64::consts::PI / 180.0;
unsafe { self.wrapped(resolve::shape_rotation_fn(), ty, [radians, 0.5, 0.5, 0.0]) }
}
unsafe fn wrapped(&self, func: *const c_void, ty: mangle::Type, floats: [f64; 4]) -> Self {
unsafe {
let mangled = ty.mangle();
let meta = abi::resolve_type_by_mangled_name(mangled.as_bytes());
assert!(!meta.is_null(), "shape metadata: {}", mangle::demangle(&mangled));
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_proto());
assert!(!wt.is_null(), "{} !: Shape", mangle::demangle(&mangled));
let mut bytes = vec![0u8; abi::type_bytes(meta).max(1)];
let mut receiver = self.receiver();
core::arch::asm!("blr {f}", f = in(reg) func,
in("x0") self.meta,
in("x1") self.wt,
in("d0") floats[0], in("d1") floats[1],
in("d2") floats[2], in("d3") floats[3],
in("x8") bytes.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
Self { bytes, meta, wt, ty }
}
}
pub unsafe fn fill_style(&self, style: &[u8], meta: *const c_void, wt: *const c_void) -> ViewHandle {
unsafe { self.filled(style, meta, wt) }
}
unsafe fn filled(
&self,
style: &[u8],
style_meta: *const c_void,
style_wt: *const c_void,
) -> ViewHandle {
unsafe {
let meta = abi::generic_metadata_from_descriptor(
resolve::shape_view_desc(),
&[self.meta, style_meta, self.wt, style_wt],
);
assert!(!meta.is_null(), "_ShapeView metadata");
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "_ShapeView !: View");
let mut arg = style.to_vec();
arg.resize(abi::type_bytes(style_meta).max(style.len()).max(1), 0);
let mut receiver = self.receiver();
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::shape_view_init(),
in("x0") receiver.as_mut_ptr(),
in("x1") arg.as_mut_ptr(),
in("x2") FILL_STYLE_DEFAULT,
in("x3") self.meta,
in("x4") style_meta,
in("x5") self.wt,
in("x6") style_wt,
in("x8") result.as_mut_ptr(),
lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x20") _, lateout("x21") _, clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub(crate) fn fill_parts(
&self,
style: &[u8],
meta: *const c_void,
wt: *const c_void,
) -> (Vec<u8>, *const c_void) {
unsafe {
let subst = [self.meta, meta, self.wt, wt];
let (ty, _, size) = super::opaque_result_type(resolve::shape_fill_oret(), &subst);
let mut result = vec![0u8; size];
let mut arg = style.to_vec();
arg.resize(abi::type_bytes(meta).max(style.len()).max(1), 0);
let mut receiver = self.receiver();
core::arch::asm!("blr {f}", f = in(reg) resolve::shape_fill_fn(),
in("x0") arg.as_mut_ptr(),
in("x1") FILL_STYLE_DEFAULT as *const c_void,
in("x2") subst[0], in("x3") subst[1], in("x4") subst[2], in("x5") subst[3],
in("d0") 0.0f64,
in("x8") result.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
(result, ty)
}
}
pub unsafe fn stroke_style(
&self,
style: &[u8],
meta: *const c_void,
wt: *const c_void,
line_width: f64,
) -> ViewHandle {
unsafe {
let inner = self.styled(
resolve::shape_stroke_fn(),
resolve::shape_stroke_oret(),
style,
meta,
wt,
false,
line_width,
);
self.as_stroke_shape_view(inner, meta, wt)
}
}
pub fn fill(&self, color: u64) -> ViewHandle {
let bytes = color.to_le_bytes();
unsafe { self.fill_style(&bytes, resolve::color_meta(), resolve::color_shapestyle_wt()) }
}
pub fn stroked(&self, line_width: f64) -> Self {
unsafe {
let subst = [self.meta, self.wt];
let (meta, _, size) =
super::opaque_result_type(resolve::shape_stroke_plain_oret(), &subst);
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_proto());
assert!(!wt.is_null(), "stroked shape !: Shape");
let mut bytes = vec![0u8; size.max(1)];
let mut receiver = self.receiver();
core::arch::asm!("blr {f}", f = in(reg) resolve::shape_stroke_plain_fn(),
in("x0") subst[0], in("x1") subst[1],
in("d0") line_width,
in("x8") bytes.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
Self { bytes, meta, wt, ty: self.ty.clone() }
}
}
pub fn parts(&self) -> (Vec<u8>, *const c_void, *const c_void) {
(self.bytes.clone(), self.meta, self.wt)
}
pub fn stroke_default(&self, line_width: f64) -> ViewHandle {
unsafe {
let subst = [self.meta, self.wt];
let (meta, _, size) =
super::opaque_result_type(resolve::shape_stroke_plain_oret(), &subst);
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "stroked shape !: View");
let mut result = vec![0u8; size];
let mut receiver = self.receiver();
core::arch::asm!("blr {f}", f = in(reg) resolve::shape_stroke_plain_fn(),
in("x0") subst[0], in("x1") subst[1],
in("d0") line_width,
in("x8") result.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
pub fn stroke(&self, color: u64, line_width: f64) -> ViewHandle {
let bytes = color.to_le_bytes();
unsafe {
self.stroke_style(&bytes, resolve::color_meta(), resolve::color_shapestyle_wt(), line_width)
}
}
unsafe fn as_stroke_shape_view(
&self,
inner: ViewHandle,
style_meta: *const c_void,
style_wt: *const c_void,
) -> ViewHandle {
unsafe {
let empty = super::empty_view();
let bg = super::background_content(&inner, &empty, super::Alignment::Center);
let desc = resolve::stroke_shape_view_desc();
if desc.is_null() {
return inner;
}
let args = [
self.meta,
style_meta,
resolve::emptyview_meta(),
self.wt,
style_wt,
resolve::emptyview_view_wt(),
];
let meta = abi::generic_metadata_from_descriptor(desc, &args);
if meta.is_null() {
return inner;
}
if abi::struct_field_offsets(meta).as_deref() != Some(&[0][..])
|| abi::type_bytes(meta) != abi::type_bytes(bg.meta)
{
return inner;
}
if !crate::diag::type_name(meta).starts_with("SwiftUI.StrokeShapeView<") {
return inner;
}
let wt = abi::get_view_wt(meta);
if wt.is_null() {
return inner;
}
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(bg.meta);
let mut out = vec![0u8; abi::type_bytes(meta)];
if vwt.size > 0 {
(vwt.initialize_with_copy)(
out.as_mut_ptr() as *mut c_void,
bg.value_ptr() as *mut c_void,
bg.meta,
);
}
super::wrap_owned(out, meta, wt)
}
}
#[allow(clippy::too_many_arguments)]
unsafe fn styled(
&self,
func: *const c_void,
oret: *const c_void,
style: &[u8],
style_meta: *const c_void,
style_wt: *const c_void,
filling: bool,
line_width: f64,
) -> ViewHandle {
unsafe {
let subst = [self.meta, style_meta, self.wt, style_wt];
let (meta, wt, size) = super::opaque_result_type(oret, &subst);
let mut result = vec![0u8; size];
let mut arg = style.to_vec();
arg.resize(abi::type_bytes(style_meta).max(style.len()).max(1), 0);
let mut receiver = self.receiver();
let (x1, x2, x3, x4, x5) = if filling {
(FILL_STYLE_DEFAULT as *const c_void, subst[0], subst[1], subst[2], subst[3])
} else {
(subst[0], subst[1], subst[2], subst[3], core::ptr::null())
};
core::arch::asm!("blr {f}", f = in(reg) func,
in("x0") arg.as_mut_ptr(),
in("x1") x1, in("x2") x2, in("x3") x3, in("x4") x4, in("x5") x5,
in("d0") line_width,
in("x8") result.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
fn receiver(&self) -> Vec<u8> {
let mut v = self.bytes.clone();
v.push(0);
v
}
}
pub struct PathBuilder {
bytes: Vec<u8>,
}
impl PathBuilder {
pub(crate) unsafe fn move_into(self, out: *mut u8) {
let me = core::mem::ManuallyDrop::new(self);
unsafe { core::ptr::copy_nonoverlapping(me.bytes.as_ptr(), out, me.bytes.len()) };
}
pub fn move_to(&mut self, x: f64, y: f64) -> &mut Self {
unsafe { self.point(resolve::path_move_to(), x, y, 0.0, 0.0) }
}
pub fn line_to(&mut self, x: f64, y: f64) -> &mut Self {
unsafe { self.point(resolve::path_add_line(), x, y, 0.0, 0.0) }
}
pub fn quad_curve_to(&mut self, x: f64, y: f64, cx: f64, cy: f64) -> &mut Self {
unsafe { self.point(resolve::path_add_quad_curve(), x, y, cx, cy) }
}
pub fn close(&mut self) -> &mut Self {
unsafe { self.point(resolve::path_close_subpath(), 0.0, 0.0, 0.0, 0.0) }
}
unsafe fn point(&mut self, f: *const c_void, a: f64, b: f64, c: f64, d: f64) -> &mut Self {
unsafe {
core::arch::asm!("blr {f}", f = in(reg) f,
in("d0") a, in("d1") b, in("d2") c, in("d3") d,
in("x20") self.bytes.as_mut_ptr(),
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x8") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
clobber_abi("C"),
);
}
self
}
pub fn shape(&self) -> Shape {
unsafe { Shape::of(self.bytes.clone(), mangle::ty("Path")) }
}
}
const FILL_STYLE_DEFAULT: u64 = 0x0100;
pub fn stroke_border(
shape: super::InsettableShape,
color: u64,
line_width: f64,
) -> ViewHandle {
unsafe {
let (ma, _insettable_wt, init) = shape.parts();
let shape_meta = abi::metadata0(ma);
let mut receiver = vec![0u8; abi::type_bytes(shape_meta).max(1)];
core::arch::asm!("blr {f}", f = in(reg) init,
in("x8") receiver.as_mut_ptr(),
in("x20") shape_meta,
lateout("x0") _, lateout("x1") _, lateout("x2") _, lateout("x3") _,
lateout("x4") _, lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
let subst = [shape_meta, resolve::color_meta(), _insettable_wt, resolve::color_shapestyle_wt()];
let (meta, wt, size) =
super::opaque_result_type(resolve::stroke_border_oret(), &subst);
let style = color.to_le_bytes();
let mut result = vec![0u8; size.max(1)];
core::arch::asm!("blr {f}", f = in(reg) resolve::stroke_border_fn(),
in("x0") style.as_ptr(),
in("d0") line_width,
in("x1") 1u64,
in("x2") subst[0], in("x3") subst[1], in("x4") subst[2], in("x5") subst[3],
in("x8") result.as_mut_ptr(),
in("x20") receiver.as_mut_ptr(),
lateout("x6") _, lateout("x7") _, lateout("x9") _, lateout("x10") _,
lateout("x11") _, lateout("x12") _, lateout("x13") _, lateout("x14") _,
lateout("x15") _, lateout("x16") _, lateout("x17") _, lateout("lr") _,
lateout("x21") _, clobber_abi("C"),
);
let outer_mangled = format!(
"7SwiftUI21StrokeBorderShapeViewVy{}7SwiftUI5ColorV7SwiftUI9EmptyViewVG",
shape.type_mangling()
);
let outer = abi::resolve_type_by_mangled_name(outer_mangled.as_bytes());
assert!(!outer.is_null(), "{}", crate::diag::demangle_type(outer_mangled.as_bytes()));
let outer_wt = abi::get_view_wt(outer);
assert!(!outer_wt.is_null(), "StrokeBorderShapeView !: View");
let (ah, av) = super::static_getter(resolve::alignment_center(), resolve::alignment_meta());
let mut bytes = vec![0u8; abi::type_bytes(outer)];
let inner = abi::type_size(meta);
bytes[..inner].copy_from_slice(&result[..inner]);
let align_at = border_view_offsets(outer)
.map(|(_, _, _, align)| align)
.unwrap_or_else(|| inner.next_multiple_of(8));
bytes[align_at..align_at + 8].copy_from_slice(&ah.to_le_bytes());
bytes[align_at + 8..align_at + 16].copy_from_slice(&av.to_le_bytes());
let _ = wt;
wrap_buf(&bytes, outer, outer_wt)
}
}
pub fn fill_then_stroke(
shape: &Shape,
fill: u64,
stroke: u64,
line_width: f64,
result_mangled: &[u8],
) -> ViewHandle {
unsafe {
let color_meta = resolve::color_meta();
let color_wt = resolve::color_shapestyle_wt();
let (background, bg_ty) = shape.fill_parts(&fill.to_le_bytes(), color_meta, color_wt);
let (content, content_ty) =
shape.stroked(line_width).fill_parts(&stroke.to_le_bytes(), color_meta, color_wt);
let meta = abi::resolve_type_by_mangled_name(result_mangled);
assert!(!meta.is_null(), "{}", crate::diag::demangle_type(result_mangled));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "StrokeShapeView !: View");
let (ah, av) = super::static_getter(resolve::alignment_center(), resolve::alignment_meta());
let content_size = abi::type_size(content_ty);
let bg_size = abi::type_size(bg_ty);
let (content_at, bg_at, align_at) =
stroke_view_offsets(meta)
.unwrap_or_else(|| {
let bg_at = content_size.next_multiple_of(8);
(0, bg_at, (bg_at + bg_size).next_multiple_of(8))
});
let mut bytes = vec![0u8; abi::type_bytes(meta)];
assert!(
align_at + 16 <= bytes.len() && bg_at + bg_size <= bytes.len(),
"{} lays out its fields differently than expected: content at {content_at} \
({content_size} bytes), background at {bg_at} ({bg_size}), alignment at \
{align_at} — but the type is only {} bytes.",
crate::diag::type_name(meta),
bytes.len(),
);
bytes[content_at..content_at + content_size].copy_from_slice(&content[..content_size]);
bytes[bg_at..bg_at + bg_size].copy_from_slice(&background[..bg_size]);
bytes[align_at..align_at + 8].copy_from_slice(&ah.to_le_bytes());
bytes[align_at + 8..align_at + 16].copy_from_slice(&av.to_le_bytes());
wrap_buf(&bytes, meta, wt)
}
}
unsafe fn stroke_view_offsets(meta: *const c_void) -> Option<(usize, usize, usize)> {
unsafe {
let &[view_at] = abi::struct_field_offsets(meta)?.as_slice() else { return None };
let modified = abi::field_type_metadata(meta, 0);
if modified.is_null() {
return None;
}
let &[content_at, modifier_at] = abi::struct_field_offsets(modified)?.as_slice() else {
return None;
};
let background = abi::field_type_metadata(modified, 1);
if background.is_null() {
return None;
}
let &[bg_at, align_at] = abi::struct_field_offsets(background)?.as_slice() else {
return None;
};
Some((
view_at + content_at,
view_at + modifier_at + bg_at,
view_at + modifier_at + align_at,
))
}
}
unsafe fn border_view_offsets(meta: *const c_void) -> Option<(usize, usize, usize, usize)> {
unsafe {
let &[shape_at, view_at] = abi::struct_field_offsets(meta)?.as_slice() else {
return None;
};
let modified = abi::field_type_metadata(meta, 1);
if modified.is_null() {
return None;
}
let &[content_at, modifier_at] = abi::struct_field_offsets(modified)?.as_slice() else {
return None;
};
let background = abi::field_type_metadata(modified, 1);
if background.is_null() {
return None;
}
let &[bg_at, align_at] = abi::struct_field_offsets(background)?.as_slice() else {
return None;
};
Some((
shape_at,
view_at + content_at,
view_at + modifier_at + bg_at,
view_at + modifier_at + align_at,
))
}
}
pub fn fill_then_stroke_border(
shape: &Shape,
fill: u64,
border: u64,
line_width: f64,
result_mangled: &[u8],
) -> ViewHandle {
unsafe {
let color_meta = resolve::color_meta();
let color_wt = resolve::color_shapestyle_wt();
let (background, bg_ty) = shape.fill_parts(&fill.to_le_bytes(), color_meta, color_wt);
let (content, content_ty) = shape
.inset(line_width / 2.0)
.stroked(line_width)
.fill_parts(&border.to_le_bytes(), color_meta, color_wt);
let meta = abi::resolve_type_by_mangled_name(result_mangled);
assert!(!meta.is_null(), "{}", crate::diag::demangle_type(result_mangled));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "StrokeBorderShapeView !: View");
let (ah, av) = super::static_getter(resolve::alignment_center(), resolve::alignment_meta());
let shape_size = abi::type_size(shape.meta);
let content_size = abi::type_size(content_ty);
let bg_size = abi::type_size(bg_ty);
let (shape_at, content_at, bg_at, align_at) = border_view_offsets(meta)
.unwrap_or_else(|| {
let content_at = shape_size.next_multiple_of(8);
let bg_at = (content_at + content_size).next_multiple_of(8);
(0, content_at, bg_at, (bg_at + bg_size).next_multiple_of(8))
});
let mut bytes = vec![0u8; abi::type_bytes(meta)];
assert!(
align_at + 16 <= bytes.len(),
"{} lays out its fields differently than expected",
crate::diag::type_name(meta),
);
bytes[shape_at..shape_at + shape_size].copy_from_slice(&shape.bytes[..shape_size]);
bytes[content_at..content_at + content_size].copy_from_slice(&content[..content_size]);
bytes[bg_at..bg_at + bg_size].copy_from_slice(&background[..bg_size]);
bytes[align_at..align_at + 8].copy_from_slice(&ah.to_le_bytes());
bytes[align_at + 8..align_at + 16].copy_from_slice(&av.to_le_bytes());
wrap_buf(&bytes, meta, wt)
}
}
pub fn linear_gradient_stops(
stops: &[(u64, f64)],
start: (f64, f64),
end: (f64, f64),
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let stop_meta = abi::metadata0(resolve::gradient_stop_ma());
let stride = abi::type_stride(stop_meta);
let mut bytes = vec![0u8; stride * stops.len()];
for (i, (color, location)) in stops.iter().enumerate() {
let (word, loc): (u64, f64);
core::arch::asm!("blr {f}", f = in(reg) resolve::gradient_stop_init(),
inout("x0") *color => word,
inout("d0") *location => loc,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
bytes[i * stride..i * stride + 8].copy_from_slice(&word.to_le_bytes());
bytes[i * stride + 8..i * stride + 16].copy_from_slice(&loc.to_le_bytes());
}
let array = abi::swift_array(stop_meta, stops.len(), &bytes);
let g: u64;
core::arch::asm!("blr {f}", f = in(reg) resolve::gradient_stops_init(),
in("x0") array,
in("x20") resolve::gradient_meta(),
lateout("x0") g,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x8") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
let meta = crate::type_by_name!(swift_type!(SwiftUI.LinearGradient).as_bytes());
assert!(!meta.is_null(), "LinearGradient metadata");
let mut value = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::linear_gradient_init(),
in("x0") g,
in("d0") start.0, in("d1") start.1, in("d2") end.0, in("d3") end.1,
in("x8") value.as_mut_ptr(),
in("x20") meta,
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _, lateout("x9") _,
lateout("x10") _, lateout("x11") _, lateout("x12") _, lateout("x13") _,
lateout("x14") _, lateout("x15") _, lateout("x16") _, lateout("x17") _,
lateout("lr") _, lateout("x21") _, clobber_abi("C"),
);
(value, meta, resolve::linear_gradient_wt())
}
}
pub fn linear_gradient_stops_view(
stops: &[(u64, f64)],
start: (f64, f64),
end: (f64, f64),
) -> ViewHandle {
let (bytes, meta, _) = linear_gradient_stops(stops, start, end);
unsafe {
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "LinearGradient !: View");
wrap_buf(&bytes, meta, wt)
}
}
pub fn shadow_over_style(
base: &[u8],
base_ty: &str,
shadow: &[u8],
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_ShadowShapeStyle")
.of(vec![mangle::Type::Verbatim(base_ty.into())])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&ty));
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_ShadowShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let offsets = abi::struct_field_offsets(meta).expect("two stored fields");
let &[base_at, shadow_at] = offsets.as_slice() else {
panic!("_ShadowShapeStyle is {} fields, not 2", offsets.len());
};
bytes[base_at..base_at + base.len()].copy_from_slice(base);
let n = shadow.len().min(bytes.len() - shadow_at);
bytes[shadow_at..shadow_at + n].copy_from_slice(&shadow[..n]);
(bytes, meta, wt)
}
}
pub fn opacity_over_style(
base: &[u8],
base_ty: &str,
opacity: f64,
) -> (Vec<u8>, *const c_void, *const c_void) {
unsafe {
let ty = mangle::ty("_OpacityShapeStyle")
.of(vec![mangle::Type::Verbatim(base_ty.into())])
.mangle();
let meta = abi::resolve_type_by_mangled_name(ty.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&ty));
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_style_proto());
assert!(!wt.is_null(), "_OpacityShapeStyle !: ShapeStyle");
let mut bytes = vec![0u8; abi::type_bytes(meta)];
bytes[..base.len()].copy_from_slice(base);
let at = base.len().next_multiple_of(4);
bytes[at..at + 4].copy_from_slice(&(opacity as f32).to_le_bytes());
(bytes, meta, wt)
}
}
impl Shape {
pub fn trim(&self, from: f64, to: f64) -> Self {
unsafe {
let desc = resolve::trimmed_shape_desc();
let meta = if desc.is_null() {
core::ptr::null()
} else {
abi::generic_metadata_from_descriptor(desc, &[self.meta, self.wt])
};
let offsets = if meta.is_null() {
None
} else {
abi::struct_field_offsets(meta)
};
let same = || Self {
bytes: self.bytes.clone(),
meta: self.meta,
wt: self.wt,
ty: self.ty.clone(),
};
let Some(offs) = offsets.filter(|o| o.len() == 3) else {
return same();
};
let wt = abi::get_view_wt_for_proto(meta, resolve::shape_proto());
if wt.is_null() {
return same();
}
let mut bytes = vec![0u8; abi::type_bytes(meta)];
let vwt = &*swift_runtime_sys::SwiftABI::get_value_witness_table(self.meta);
if vwt.size > 0 {
(vwt.initialize_with_copy)(
bytes.as_mut_ptr().add(offs[0]) as *mut c_void,
self.bytes.as_ptr() as *mut c_void,
self.meta,
);
}
bytes[offs[1]..offs[1] + 8].copy_from_slice(&from.to_le_bytes());
bytes[offs[2]..offs[2] + 8].copy_from_slice(&to.to_le_bytes());
Self {
bytes,
meta,
wt,
ty: mangle::ty("_TrimmedShape").of(vec![self.ty.clone()]),
}
}
}
}