use core::marker::PhantomData;
use core::ptr::NonNull;
use std::ffi::{CStr, c_void};
use crate::brushes::Brush;
use crate::ffi::{
noesis_base_component_release, noesis_drawing_draw_ellipse, noesis_drawing_draw_geometry,
noesis_drawing_draw_image, noesis_drawing_draw_line, noesis_drawing_draw_mesh,
noesis_drawing_draw_rectangle, noesis_drawing_draw_rounded_rectangle, noesis_drawing_draw_text,
noesis_drawing_pop, noesis_drawing_push_blending_mode, noesis_drawing_push_clip,
noesis_drawing_push_transform, noesis_pen_create, noesis_pen_get_brush,
noesis_pen_get_dash_offset, noesis_pen_get_dashes, noesis_pen_get_line_caps,
noesis_pen_get_line_join, noesis_pen_get_thickness, noesis_pen_set_brush,
noesis_pen_set_dash_style, noesis_pen_set_line_caps, noesis_pen_set_line_join,
noesis_pen_set_thickness,
};
pub use crate::geometry::{Geometry, RectangleGeometry};
pub use crate::shapes::{PenLineCap, PenLineJoin};
use crate::transforms::Transform;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum BlendingMode {
Normal = 0,
Multiply = 1,
Screen = 2,
Additive = 3,
}
pub struct Pen {
ptr: NonNull<c_void>,
}
unsafe impl Send for Pen {}
impl Pen {
#[must_use]
pub fn new(brush: &dyn Brush, thickness: f32) -> Self {
let ptr = unsafe { noesis_pen_create(brush.brush_raw(), thickness) };
Self {
ptr: NonNull::new(ptr).expect("noesis_pen_create returned null"),
}
}
#[must_use]
pub fn with_thickness(thickness: f32) -> Self {
let ptr = unsafe { noesis_pen_create(core::ptr::null_mut(), thickness) };
Self {
ptr: NonNull::new(ptr).expect("noesis_pen_create returned null"),
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_brush(&mut self, brush: &dyn Brush) -> bool {
unsafe { noesis_pen_set_brush(self.ptr.as_ptr(), brush.brush_raw()) }
}
#[must_use]
pub fn brush(&self) -> Option<NonNull<c_void>> {
let p = unsafe { noesis_pen_get_brush(self.ptr.as_ptr()) };
NonNull::new(p)
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_thickness(&mut self, thickness: f32) -> bool {
unsafe { noesis_pen_set_thickness(self.ptr.as_ptr(), thickness) }
}
#[must_use]
pub fn thickness(&self) -> f32 {
let mut out = 0.0f32;
unsafe { noesis_pen_get_thickness(self.ptr.as_ptr(), &mut out) };
out
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_line_caps(&mut self, start: PenLineCap, end: PenLineCap, dash: PenLineCap) -> bool {
unsafe {
noesis_pen_set_line_caps(self.ptr.as_ptr(), start as i32, end as i32, dash as i32)
}
}
#[must_use]
pub fn line_caps(&self) -> Option<(PenLineCap, PenLineCap, PenLineCap)> {
let mut out = [0i32; 3];
let ok = unsafe { noesis_pen_get_line_caps(self.ptr.as_ptr(), out.as_mut_ptr()) };
if !ok {
return None;
}
Some((
cap_from_i32(out[0]),
cap_from_i32(out[1]),
cap_from_i32(out[2]),
))
}
#[must_use = "a false return means the property was not set (unknown name / type mismatch / read-only)"]
pub fn set_line_join(&mut self, join: PenLineJoin, miter_limit: f32) -> bool {
unsafe { noesis_pen_set_line_join(self.ptr.as_ptr(), join as i32, miter_limit) }
}
#[must_use]
pub fn line_join(&self) -> Option<(PenLineJoin, f32)> {
let mut join = 0i32;
let mut miter = 0.0f32;
let ok = unsafe { noesis_pen_get_line_join(self.ptr.as_ptr(), &mut join, &mut miter) };
ok.then(|| (join_from_i32(join), miter))
}
pub fn set_dash_style(&mut self, dashes: &[f32], offset: f32) -> bool {
let count = u32::try_from(dashes.len()).unwrap_or(u32::MAX);
unsafe { noesis_pen_set_dash_style(self.ptr.as_ptr(), dashes.as_ptr(), count, offset) }
}
#[must_use]
pub fn dash_offset(&self) -> Option<f32> {
let mut out = 0.0f32;
let ok = unsafe { noesis_pen_get_dash_offset(self.ptr.as_ptr(), &mut out) };
ok.then_some(out)
}
#[must_use]
pub fn dashes(&self) -> Option<Vec<f32>> {
let p = unsafe { noesis_pen_get_dashes(self.ptr.as_ptr()) };
if p.is_null() {
return None;
}
let s = unsafe { CStr::from_ptr(p) }.to_string_lossy();
Some(
s.split([' ', ','])
.filter(|t| !t.is_empty())
.filter_map(|t| t.parse::<f32>().ok())
.collect(),
)
}
}
impl Drop for Pen {
fn drop(&mut self) {
unsafe { noesis_base_component_release(self.ptr.as_ptr()) }
}
}
fn cap_from_i32(v: i32) -> PenLineCap {
match v {
1 => PenLineCap::Square,
2 => PenLineCap::Round,
3 => PenLineCap::Triangle,
_ => PenLineCap::Flat,
}
}
fn join_from_i32(v: i32) -> PenLineJoin {
match v {
1 => PenLineJoin::Bevel,
2 => PenLineJoin::Round,
_ => PenLineJoin::Miter,
}
}
pub struct DrawingContext<'a> {
ptr: NonNull<c_void>,
_marker: PhantomData<&'a ()>,
}
impl DrawingContext<'_> {
#[must_use]
pub unsafe fn from_raw(ptr: NonNull<c_void>) -> Self {
Self {
ptr,
_marker: PhantomData,
}
}
#[must_use]
pub fn raw(&self) -> *mut c_void {
self.ptr.as_ptr()
}
pub fn draw_line(&self, pen: &Pen, p0: (f32, f32), p1: (f32, f32)) -> bool {
unsafe { noesis_drawing_draw_line(self.ptr.as_ptr(), pen.raw(), p0.0, p0.1, p1.0, p1.1) }
}
pub fn draw_rectangle(
&self,
brush: Option<&dyn Brush>,
pen: Option<&Pen>,
rect: [f32; 4],
) -> bool {
unsafe {
noesis_drawing_draw_rectangle(
self.ptr.as_ptr(),
brush_ptr(brush),
pen_ptr(pen),
rect[0],
rect[1],
rect[2],
rect[3],
)
}
}
pub fn draw_rounded_rectangle(
&self,
brush: Option<&dyn Brush>,
pen: Option<&Pen>,
rect: [f32; 4],
r_x: f32,
r_y: f32,
) -> bool {
unsafe {
noesis_drawing_draw_rounded_rectangle(
self.ptr.as_ptr(),
brush_ptr(brush),
pen_ptr(pen),
rect[0],
rect[1],
rect[2],
rect[3],
r_x,
r_y,
)
}
}
pub fn draw_ellipse(
&self,
brush: Option<&dyn Brush>,
pen: Option<&Pen>,
center: (f32, f32),
r_x: f32,
r_y: f32,
) -> bool {
unsafe {
noesis_drawing_draw_ellipse(
self.ptr.as_ptr(),
brush_ptr(brush),
pen_ptr(pen),
center.0,
center.1,
r_x,
r_y,
)
}
}
pub fn draw_geometry(
&self,
brush: Option<&dyn Brush>,
pen: Option<&Pen>,
geometry: &dyn Geometry,
) -> bool {
unsafe {
noesis_drawing_draw_geometry(
self.ptr.as_ptr(),
brush_ptr(brush),
pen_ptr(pen),
geometry.geometry_raw(),
)
}
}
pub fn draw_text(
&self,
formatted_text: &crate::formatted_text::FormattedText,
bounds: [f32; 4],
) -> bool {
unsafe {
noesis_drawing_draw_text(
self.ptr.as_ptr(),
formatted_text.raw(),
bounds[0],
bounds[1],
bounds[2],
bounds[3],
)
}
}
pub fn draw_mesh(&self, brush: Option<&dyn Brush>, mesh: &crate::mesh::MeshData) -> bool {
unsafe { noesis_drawing_draw_mesh(self.ptr.as_ptr(), brush_ptr(brush), mesh.raw()) }
}
pub unsafe fn draw_image(&self, image_source: *mut c_void, rect: [f32; 4]) -> bool {
unsafe {
noesis_drawing_draw_image(
self.ptr.as_ptr(),
image_source,
rect[0],
rect[1],
rect[2],
rect[3],
)
}
}
pub fn pop(&self) -> bool {
unsafe { noesis_drawing_pop(self.ptr.as_ptr()) }
}
pub fn push_clip(&self, geometry: &dyn Geometry) -> bool {
unsafe { noesis_drawing_push_clip(self.ptr.as_ptr(), geometry.geometry_raw()) }
}
pub fn push_transform(&self, transform: &dyn Transform) -> bool {
unsafe { noesis_drawing_push_transform(self.ptr.as_ptr(), transform.transform_raw()) }
}
pub fn push_blending_mode(&self, mode: BlendingMode) -> bool {
unsafe { noesis_drawing_push_blending_mode(self.ptr.as_ptr(), mode as i32) }
}
}
fn brush_ptr(brush: Option<&dyn Brush>) -> *mut c_void {
brush.map_or(core::ptr::null_mut(), Brush::brush_raw)
}
fn pen_ptr(pen: Option<&Pen>) -> *mut c_void {
pen.map_or(core::ptr::null_mut(), Pen::raw)
}