#[allow(non_snake_case)] #[allow(non_camel_case_types)] #[allow(unused)] mod evg_ffi { include!(concat!(env!("OUT_DIR"), "/gpac_evg.rs")); } use evg_ffi::*;
pub use evg_ffi::{GF_Point2D, GF_Rect, GF_Color, GF_Matrix2D, GF_PenSettings, GF_StencilType};
macro_rules! safe_dbg { ($v:expr,$g:expr) => { match unsafe { $v as i32 } {
res => { if res != $g as i32 { dbg!(res); } res } } };
($v:expr) => { safe_dbg!($v, 0) };
}
impl From<i32> for Fixed { #[cfg(feature = "evg_fixed")] #[inline] fn from(v: i32) -> Self { Self(v << 16) }
#[cfg(not(feature = "evg_fixed"))] #[inline] fn from(v: i32) -> Self { Self(v as _) }
}
#[cfg(feature = "evg_fixed")] impl From<Fixed> for i32 {
#[inline] fn from(v: Fixed) -> Self { (v.0 + (1 << 15)) >> 16 }
}
impl From<f32> for Fixed { #[cfg(feature = "evg_fixed")]
#[inline] fn from(v: f32) -> Self { Self((v * (1 << 16) as f32) as _) }
#[cfg(not(feature = "evg_fixed"))] #[inline] fn from(v: f32) -> Self { Self(v) }
}
impl From<Fixed> for f32 { #[cfg(feature = "evg_fixed")]
#[inline] fn from(v: Fixed) -> Self { v.0 as f32 / (1 << 16) as f32 }
#[cfg(not(feature = "evg_fixed"))] #[inline] fn from(v: Fixed) -> Self { v.0 }
}
impl From<bool> for Bool {
fn from(value: bool) -> Self { if value { Bool::GF_TRUE } else { Bool::GF_FALSE } }
}
impl From<(Fixed, Fixed)> for GF_Point2D {
fn from(v: (Fixed, Fixed)) -> Self { Self { x: v.0, y: v.1 } }
}
impl Copy for Fixed {}
impl Copy for GF_Point2D {}
impl Copy for GF_PenSettings {}
impl Clone for Fixed { #[inline] fn clone(&self) -> Self { *self } }
impl Clone for GF_Point2D { #[inline] fn clone(&self) -> Self { *self } }
impl Clone for GF_PenSettings { #[inline] fn clone(&self) -> Self { *self } }
impl Default for GF_PenSettings {
fn default() -> Self { Self {
width: 0.into(), cap: 1, join: 1, align: 0, dash: 0,
dash_offset: 0.into(), dash_set: core::ptr::null_mut(),
path_length: 0.into(), miterLimit: 4.into(),
} }
}
pub struct VGPath(*mut GF_Path);
impl Drop for VGPath { #[inline] fn drop(&mut self) { unsafe { gf_path_del(self.0) } } }
impl VGPath { #[allow(clippy::new_without_default)] #[inline] pub fn new() -> Self { Self(unsafe { gf_path_new() }) }
#[inline] pub fn move_to(&self, pt: GF_Point2D) {
safe_dbg!(gf_path_add_move_to_vec(self.0, &pt as *const _ as _));
}
#[inline] pub fn line_to(&self, pt: GF_Point2D) {
safe_dbg!(gf_path_add_line_to_vec(self.0, &pt as *const _ as _));
}
#[inline] pub fn cubic_to(&self, c1: GF_Point2D, c2: GF_Point2D, pt: GF_Point2D) {
safe_dbg!(gf_path_add_cubic_to_vec(self.0,
&c1 as *const _ as _, &c2 as *const _ as _, &pt as *const _ as _));
}
#[inline] pub fn quad_to(&self, cp: GF_Point2D, pt: GF_Point2D) {
safe_dbg!(gf_path_add_quadratic_to_vec(self.0,
&cp as *const _ as _, &pt as *const _ as _));
}
#[inline] pub fn svg_arc_to(&self, radius: GF_Point2D,
x_rot: Fixed, large: bool, sweep: bool, pt: GF_Point2D) {
safe_dbg!(gf_path_add_svg_arc_to(self.0, pt.x, pt.y,
radius.x, radius.y, x_rot, large.into(), sweep.into()));
}
#[inline] pub fn add_rect(&self, rect: GF_Rect) {
safe_dbg!(gf_path_add_rect(self.0, rect.x, rect.y, rect.width, rect.height));
}
#[inline] pub fn reset(&self) { unsafe { gf_path_reset(self.0) }; }
#[allow(clippy::len_without_is_empty)] #[inline] pub fn len(&self) -> u32 {
if let Some(path) = unsafe { self.0.as_ref() } { path.n_points } else { 0 }
}
pub fn last_point(&self) -> Option<GF_Point2D> {
let cnt = self.len(); if cnt < 1 { None } else {
Some(unsafe { *(*self.0).points.offset(cnt as isize - 1) })
}
}
pub fn print_out(&self) {
unsafe { let path = &*self.0;
for n in 0..path.n_points { let n = n as _;
let pt = &*path.points.offset(n);
eprintln!("{}-({:?}, {:?})", *path.tags.offset(n), <f32>::from(pt.x), <f32>::from(pt.y));
}
}
}
#[inline] pub fn close(&self) { safe_dbg!(gf_path_close(self.0)); }
}
pub struct Stencil(*mut GF_EVGStencil);
impl Drop for Stencil { #[inline] fn drop(&mut self) { unsafe { gf_evg_stencil_delete(self.0) } } }
impl Stencil {
#[inline] pub fn new(t: GF_StencilType) -> Self { unsafe { Self(gf_evg_stencil_new(t)) } }
#[inline] pub fn set_color(&self, color: GF_Color) {
safe_dbg!(gf_evg_stencil_set_brush_color(self.0, color));
}
#[inline] pub fn set_linear(&self, start: GF_Point2D, end: GF_Point2D) {
safe_dbg!(gf_evg_stencil_set_linear_gradient(self.0, start.x, start.y, end.x, end.y));
}
#[inline] pub fn set_radial(&self,
center: GF_Point2D, focal: GF_Point2D, radius: GF_Point2D) {
safe_dbg!(gf_evg_stencil_set_radial_gradient(self.0,
center.x, center.y, focal.x, focal.y, radius.x, radius.y));
}
#[inline] pub fn push_interpolation(&self, pos: Fixed, col: GF_Color) {
safe_dbg!(gf_evg_stencil_push_gradient_interpolation(self.0, pos, col));
}
#[inline] pub fn set_matrix(&self, mat: &GF_Matrix2D) {
safe_dbg!(gf_evg_stencil_set_matrix(self.0, mat as *const _ as _));
}
}
pub struct Surface(*mut GF_EVGSurface);
impl Drop for Surface { #[inline] fn drop(&mut self) { unsafe { gf_evg_surface_delete(self.0) } } }
impl Surface {
#[inline] pub fn new(pixm: &mut Pixmap) -> Self {
let surf = Self(unsafe { gf_evg_surface_new(Bool::GF_FALSE) });
safe_dbg!(gf_evg_surface_attach_to_buffer(surf.0,
pixm.data.as_mut_ptr(), pixm.width, pixm.height, 4, (pixm.width << 2) as _,
GF_PixelFormat::GF_PIXEL_RGBA));
surf
}
#[inline] pub fn fill_path(&self, path: &VGPath, sten: &Stencil) {
safe_dbg!(gf_evg_surface_set_path(self.0, path.0));
safe_dbg!(gf_evg_surface_fill(self.0, sten.0));
}
#[inline] pub fn stroke_path(&self, path: &VGPath, sten: &Stencil, pens: &GF_PenSettings) {
let path = VGPath(unsafe { gf_path_get_outline(path.0, *pens) });
self.fill_path(&path, sten);
}
#[inline] pub fn set_matrix(&self, mat: &GF_Matrix2D) {
safe_dbg!(gf_evg_surface_set_matrix(self.0, mat as *const _ as _));
}
}
pub struct Pixmap { pub data: Vec<u8>, pub width: u32, pub height: u32, }
impl Pixmap {
#[inline] pub fn new(width: u32, height: u32) -> Self {
Self { width, height, data: vec![0; (width * height * 4) as _] }
}
pub fn save_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), std::io::Error> {
let mut encoder = png::Encoder::new(std::io::BufWriter::new(
std::fs::File::create(path)?), self.width, self.height);
encoder.set_color(png::ColorType::Rgba);
encoder.set_depth(png::BitDepth::Eight);
encoder.set_source_gamma(png::ScaledFloat::new(1.0 / 2.2));
encoder.write_header()?.write_image_data(&self.data)?; Ok(())
}
}
#[cfg(test)] mod tests { use super::*;
#[test] fn fill_stroke() {
let mut pixm = Pixmap::new(1024, 512);
let mut pens = GF_PenSettings::default();
let sten = Stencil::new(GF_StencilType::GF_STENCIL_SOLID);
let (surf, path) = (Surface::new(&mut pixm), VGPath::new());
path.add_rect(GF_Rect { x: (pixm.width as i32 >> 2) .into(),
y: (pixm.height as i32 - (pixm.height as i32 >> 2)).into(),
width: (pixm.width as i32 >> 1).into(), height: (pixm.height as i32 >> 1).into() });
sten.set_color(0xFF0000FF); surf.fill_path(&path, &sten);
sten.set_color(0xAA00FF00); pens.width = 10.into();
surf.stroke_path(&path, &sten, &pens);
pixm.save_png(concat!("target", "/demo_evg.png")).unwrap(); }
}