use alloc::vec::Vec;
use crate::{DrawTarget, DrawnGlyph, Font, GpuBatch, Policy, Prefer};
use crate::ffi::handle::{Status, borrow, deliver, release};
use crate::ffi::list::{Axis, axes_of};
use crate::ffi::options::RasterOptionsC;
use crate::ffi::raster::Bitmap;
pub struct Batch(GpuBatch);
impl Batch {
pub fn inner(&self) -> &GpuBatch {
&self.0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_batch_new(out: *mut *mut Batch) -> Status {
unsafe { deliver(out, Batch(GpuBatch::new())) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_batch_clear(batch: *mut Batch) -> Status {
if batch.is_null() {
return Status::Null;
}
unsafe { (*batch).0.clear() };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_batch_append(
batch: *mut Batch,
quads: *const f32,
count: usize,
out: *mut crate::GlyphSlot,
) -> Status {
if batch.is_null() || quads.is_null() || out.is_null() {
return Status::Null;
}
let Some(floats) = count.checked_mul(6) else { return Status::Range };
let src = unsafe { core::slice::from_raw_parts(quads, floats) };
let mut curves: Vec<[[f32; 2]; 3]> = src
.chunks_exact(6)
.map(|q| [[q[0], q[1]], [q[2], q[3]], [q[4], q[5]]])
.collect();
match unsafe { (*batch).0.append(&mut curves) } {
Some(slot) => {
unsafe { *out = slot };
Status::Ok
}
None => Status::Range,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_batch_revision(batch: *const Batch, out: *mut u64) -> Status {
let Some(b) = (unsafe { borrow(batch) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
unsafe { *out = b.0.revision() };
Status::Ok
}
macro_rules! batch_view {
($fn_name:ident, $method:ident, $elem:ty, $doc:literal) => {
#[doc = $doc]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn $fn_name(
batch: *const Batch,
out_count: *mut usize,
) -> *const $elem {
let Some(b) = (unsafe { borrow(batch) }) else { return core::ptr::null() };
if out_count.is_null() {
return core::ptr::null();
}
let slice = b.0.$method();
unsafe { *out_count = slice.len() };
slice.as_ptr()
}
};
}
batch_view!(
daegun_batch_curves,
curves,
crate::CurvePoint,
"The curve points every glyph in the batch contributed."
);
batch_view!(
daegun_batch_band_curves,
band_curves,
u32,
"Which curves each band holds, as indices into the curve points."
);
batch_view!(daegun_batch_bands, bands, crate::Band, "The bands, horizontal then vertical.");
batch_view!(
daegun_batch_hulls,
hulls,
crate::HullVertex,
"The drawn polygon of every glyph, as vertices."
);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_batch_free(batch: *mut Batch) {
unsafe { release(batch) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_font_gpu_glyph(
font: *const Font,
batch: *mut Batch,
gid: u16,
axes: *const Axis,
axes_len: usize,
out: *mut crate::GlyphSlot,
) -> Status {
let Some(font) = (unsafe { borrow(font) }) else { return Status::Null };
if batch.is_null() || out.is_null() {
return Status::Null;
}
let location = unsafe { axes_of(axes, axes_len) };
match unsafe { font.gpu_glyph(&mut (*batch).0, gid, &location) } {
Ok(slot) => {
unsafe { *out = slot };
Status::Ok
}
Err(e) => {
crate::ffi::set_error(&alloc::format!("{e:?}"));
Status::Absent
}
}
}
pub struct ColorSlots(Vec<crate::ColorSlot>);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_font_gpu_color_glyph(
font: *const Font,
batch: *mut Batch,
gid: u16,
axes: *const Axis,
axes_len: usize,
palette_index: u16,
out: *mut *mut ColorSlots,
) -> Status {
unsafe {
daegun_font_gpu_color_glyph_with(
font, batch, gid, axes, axes_len, palette_index, core::ptr::null(), out,
)
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_font_gpu_color_glyph_with(
font: *const Font,
batch: *mut Batch,
gid: u16,
axes: *const Axis,
axes_len: usize,
palette_index: u16,
foreground: *const u8,
out: *mut *mut ColorSlots,
) -> Status {
let Some(font) = (unsafe { borrow(font) }) else { return Status::Null };
if batch.is_null() {
return Status::Null;
}
let location = unsafe { axes_of(axes, axes_len) };
let built = unsafe {
match crate::ffi::rgba_of(foreground) {
Some(fg) => {
font.gpu_color_glyph_with(&mut (*batch).0, gid, &location, palette_index, fg)
}
None => font.gpu_color_glyph(&mut (*batch).0, gid, &location, palette_index),
}
};
match built {
Ok(slots) => unsafe { deliver(out, ColorSlots(slots)) },
Err(e) => {
crate::ffi::set_error(&alloc::format!("{e:?}"));
Status::Absent
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_color_slots_data(
slots: *const ColorSlots,
out_count: *mut usize,
) -> *const crate::ColorSlot {
let Some(s) = (unsafe { borrow(slots) }) else { return core::ptr::null() };
if out_count.is_null() {
return core::ptr::null();
}
unsafe { *out_count = s.0.len() };
s.0.as_ptr()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_color_slots_free(slots: *mut ColorSlots) {
unsafe { release(slots) }
}
pub const PREFER_AUTO: i32 = 0;
pub const PREFER_CPU: i32 = 1;
pub const PREFER_GPU: i32 = 2;
pub const PREFER_REFERENCE: i32 = 3;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct PolicyC {
pub prefer: i32,
pub strict: bool,
pub has_cpu_below_ppem: bool,
pub cpu_below_ppem: f32,
pub avoid_software_gpu: bool,
}
const _: () = assert!(size_of::<PolicyC>() == 16);
impl PolicyC {
pub fn to_rust(self) -> Policy {
Policy {
prefer: match self.prefer {
PREFER_CPU => Prefer::Cpu,
PREFER_GPU => Prefer::Gpu,
PREFER_REFERENCE => Prefer::Reference,
_ => Prefer::Auto,
},
strict: self.strict,
cpu_below_ppem: self.has_cpu_below_ppem.then_some(self.cpu_below_ppem),
avoid_software_gpu: self.avoid_software_gpu,
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_policy_default(out: *mut PolicyC) -> Status {
if out.is_null() {
return Status::Null;
}
unsafe {
*out = PolicyC {
prefer: PREFER_AUTO,
strict: false,
has_cpu_below_ppem: false,
cpu_below_ppem: 0.0,
avoid_software_gpu: false,
}
};
Status::Ok
}
pub const DRAWN_NOTHING: i32 = 0;
pub const DRAWN_CPU: i32 = 1;
pub const DRAWN_GPU: i32 = 2;
pub const DRAWN_GPU_COLOR: i32 = 3;
pub const DRAWN_SCENE: i32 = 4;
pub const DRAWN_REFERENCE: i32 = 5;
pub const DRAWN_BATCH_FULL: i32 = 6;
pub const DRAWN_REFUSED: i32 = 7;
pub struct Drawn {
kind: i32,
bitmap: Option<Bitmap>,
slot: Option<crate::GlyphSlot>,
color_slots: Vec<crate::ColorSlot>,
scene: Option<crate::ffi::color::Scene>,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_font_draw_glyph(
font: *const Font,
batch: *mut Batch,
device: *const crate::DeviceProfile,
policy: *const PolicyC,
gid: u16,
px: f32,
axes: *const Axis,
axes_len: usize,
opts: *const RasterOptionsC,
palette: i32,
out: *mut *mut Drawn,
) -> Status {
unsafe {
daegun_font_draw_glyph_with(
font, batch, device, policy, gid, px, axes, axes_len, opts, palette,
core::ptr::null(), out,
)
}
}
#[unsafe(no_mangle)]
#[allow(clippy::too_many_arguments, reason = "the router needs every input the two paths take")]
pub unsafe extern "C" fn daegun_font_draw_glyph_with(
font: *const Font,
batch: *mut Batch,
device: *const crate::DeviceProfile,
policy: *const PolicyC,
gid: u16,
px: f32,
axes: *const Axis,
axes_len: usize,
opts: *const RasterOptionsC,
palette: i32,
foreground: *const u8,
out: *mut *mut Drawn,
) -> Status {
let Some(font) = (unsafe { borrow(font) }) else { return Status::Null };
if batch.is_null() {
return Status::Null;
}
let location = unsafe { axes_of(axes, axes_len) };
let options = match unsafe { borrow(opts) } {
Some(o) => o.to_rust(),
None => RasterOptionsC::DEFAULT.to_rust(),
};
let pal = if palette < 0 { None } else { u16::try_from(palette).ok() };
let built_policy =
unsafe { borrow(policy) }.map_or_else(Policy::default, |p| p.to_rust());
let drawn = unsafe {
let batch = &mut (*batch).0;
let mut target = match borrow(device) {
Some(d) => DrawTarget::new(batch, d),
None => DrawTarget::cpu_only(batch),
}
.with_policy(built_policy);
match crate::ffi::rgba_of(foreground) {
Some(fg) => {
font.draw_glyph_with(&mut target, gid, px, &location, &options, pal, fg)
}
None => font.draw_glyph(&mut target, gid, px, &location, &options, pal),
}
};
let built = match drawn {
DrawnGlyph::Nothing => Drawn::empty(DRAWN_NOTHING),
DrawnGlyph::Cpu(g) => Drawn { bitmap: Some(crate::ffi::raster::wrap(g)), ..Drawn::empty(DRAWN_CPU) },
DrawnGlyph::Reference(g) => {
Drawn { bitmap: Some(crate::ffi::raster::wrap(g)), ..Drawn::empty(DRAWN_REFERENCE) }
}
DrawnGlyph::Gpu(slot) => Drawn { slot: Some(slot), ..Drawn::empty(DRAWN_GPU) },
DrawnGlyph::GpuColor(slots) => {
Drawn { color_slots: slots, ..Drawn::empty(DRAWN_GPU_COLOR) }
}
DrawnGlyph::Scene(s) => {
Drawn { scene: Some(crate::ffi::color::wrap_scene(s)), ..Drawn::empty(DRAWN_SCENE) }
}
DrawnGlyph::BatchFull => Drawn::empty(DRAWN_BATCH_FULL),
DrawnGlyph::Refused(r) => {
crate::ffi::set_error(&alloc::format!("{r:?}"));
Drawn::empty(DRAWN_REFUSED)
}
};
unsafe { deliver(out, built) }
}
impl Drawn {
fn empty(kind: i32) -> Drawn {
Drawn { kind, bitmap: None, slot: None, color_slots: Vec::new(), scene: None }
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_kind(d: *const Drawn, out: *mut i32) -> Status {
let Some(d) = (unsafe { borrow(d) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
unsafe { *out = d.kind };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_is_ok(d: *const Drawn, out: *mut bool) -> Status {
let Some(d) = (unsafe { borrow(d) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
unsafe { *out = d.kind != DRAWN_REFUSED && d.kind != DRAWN_BATCH_FULL };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_bitmap(
d: *const Drawn,
out: *mut *const Bitmap,
) -> Status {
let Some(d) = (unsafe { borrow(d) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
let Some(b) = &d.bitmap else { return Status::Absent };
unsafe { *out = &raw const *b };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_slot(
d: *const Drawn,
out: *mut crate::GlyphSlot,
) -> Status {
let Some(d) = (unsafe { borrow(d) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
let Some(s) = d.slot else { return Status::Absent };
unsafe { *out = s };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_color_slots(
d: *const Drawn,
out_count: *mut usize,
) -> *const crate::ColorSlot {
let Some(d) = (unsafe { borrow(d) }) else { return core::ptr::null() };
if out_count.is_null() {
return core::ptr::null();
}
unsafe { *out_count = d.color_slots.len() };
d.color_slots.as_ptr()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_scene(
d: *const Drawn,
out: *mut *const crate::ffi::color::Scene,
) -> Status {
let Some(d) = (unsafe { borrow(d) }) else { return Status::Null };
if out.is_null() {
return Status::Null;
}
let Some(s) = &d.scene else { return Status::Absent };
unsafe { *out = &raw const *s };
Status::Ok
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn daegun_drawn_free(d: *mut Drawn) {
unsafe { release(d) }
}