Skip to content

Commit

Permalink
support to compile ftgrays/ftraster/gpac-evg
Browse files Browse the repository at this point in the history
preparing for new renderer implementation
  • Loading branch information
mhfan committed Oct 24, 2023
1 parent 63da749 commit f7c2f44
Show file tree
Hide file tree
Showing 7 changed files with 617 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"C_Cpp.default.defines": [ "STANDALONE_",
"FALL_THROUGH=((void)0)", "FT_BEGIN_HEADER" ],
"C_Cpp.default.includePath": [ "src/evg", "src/raster" ]
}
18 changes: 18 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,30 @@ usvg = "0.36"
kurbo = "0.10" # Bezier curves utils

#build-time = { version = "0.1", git = "https://github.com/AlephAlpha/build-time" }
png = { version = "0.17", optional = true }

[lib]
# Disable doctests as a workaround for https://github.com/rust-lang/rust-bindgen/issues/1313
#doctest = false

[features]
#default = [ "cc" ]
cc = [ "dep:cc", "dep:bindgen", "dep:glob", "dep:png" ] # implied by optional dependency

[dev-dependencies]
#rexpect = "0.5"

[build-dependencies]
cc = { version = "1.0", optional = true }
bindgen = { version = "0.68", optional = true }
glob = { version = "0.3", optional = true }
chrono = "0.4"

[profile.release]
codegen-units = 1
strip = 'debuginfo'
panic = 'abort'
lto = 'fat' # true

[workspace]

71 changes: 67 additions & 4 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,79 @@

fn main() { // https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html
// https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html
fn main() -> Result<(), Box<dyn std::error::Error>> {
//println!("cargo:rerun-if-changed=build.rs"); // XXX: prevent re-run indead
// By default, cargo always re-run the build script if any file within the package
// is changed, and no any rerun-if instruction is emitted.
println!("cargo:rerun-if-changed=src/");

println!("cargo:rerun-if-changed=.git/index");
let output = std::process::Command::new("git")
.args(["rev-parse", "--short", "HEAD"]).output().unwrap();
println!("cargo:rustc-env=BUILD_GIT_HASH={}", String::from_utf8(output.stdout).unwrap());
.args(["rev-parse", "--short", "HEAD"]).output()?;
println!("cargo:rustc-env=BUILD_GIT_HASH={}", String::from_utf8(output.stdout)?);

println!("cargo:rustc-env=BUILD_TIMESTAMP={}",
chrono::Local::now().format("%H:%M:%S%z %Y-%m-%d"));

//println!("cargo:rerun-if-changed=build.rs"); // XXX: prevent re-run indead
#[cfg(feature = "cc")] { use std::path::PathBuf;
#[derive(Debug)] struct DoctestComment;
impl bindgen::callbacks::ParseCallbacks for DoctestComment {
fn process_comment(&self, comment: &str) -> Option<String> {
Some(format!("````c,ignore\n{comment}\n````")) // FIXME:
}
}

let module = "ftgrays"; // "ftgrays_bindings";
cc::Build::new().flag("-std=c17").flag("-pedantic")
.define("FALL_THROUGH", "((void)0)").file("src/raster/ftgrays.c")
.opt_level(3).define("NDEBUG", None).file("src/raster/ftraster.c")
.files(glob::glob("src/raster/stroke/*.c")?.filter_map(Result::ok))
.flag("-Wno-unused-variable")//.flag("-Wno-unused-function")
.define("STANDALONE_", None).compile(module);

// The bindgen::Builder is the main entry point to bindgen,
// and lets you build up options for the resulting bindings.
bindgen::Builder::default() //.header("src/raster/ftimage.h")
.clang_arg("-DSTANDALONE_").header("src/raster/ftgrays.h")
.clang_arg("-DFT_BEGIN_HEADER=").clang_arg("-DFT_END_HEADER=")
.clang_arg("-DFT_STATIC_BYTE_CAST(type,var)=(type)(unsigned char)(var)")
.allowlist_item("FT_OUTLINE_.*|FT_RASTER_FLAG_.*|FT_CURVE_TAG.*")
.layout_tests(false).derive_copy(false).allowlist_var("ft_standard_raster")
.allowlist_var("ft_grays_raster").allowlist_type("FT_Outline|FT_Pixel_Mode")
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
.parse_callbacks(Box::new(DoctestComment)).generate_comments(false) // XXX:
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks)).generate()?
.write_to_file(PathBuf::from(std::env::var("OUT_DIR")?).join(format!("{module}.rs")))?;

/* ln -s /path/to/freetype2/{include/freetype/ftimage.h,src/smooth/ftgrays.[ch],\
src/raster/ft{misc.h,raster.c}} src/raster/
* ln -s /path/to/freetype2/src/{raster/ftmisc.h,base/ft{stroke,trigon}.c} src/raster/stroke/
* ln -s /path/to/freetype2/include/freetype/ft{stroke,trigon,image}.h src/raster/stroke/
* ln -s /path/to/gpac/src/evg/{ftgrays.c,rast_soft.h,stencil.c,surface.c,\
raster_{argb,rgb,565,yuv}.c},raster3d.c src/evg/
* ln -s /path/to/gpac/src/utils/{path2d{,_stroke},math,alloc,color,error}.c src/evg/
* ln -s /path/gpac/include/gpac/{evg,setup,constants,maths,color,path2d,\
tools,thread}.h src/evg/gpac/
* touch src/evg/gpac/{Remotery,config_file,configuration,main,module,version}.h
*/
let module = "gpac_evg"; // "evg_bindings";
cc::Build::new().flag("-std=c17").flag("-Isrc/evg").define("GPAC_FIXED_POINT", None)
.flag("-Wno-unused-parameter").define("GPAC_DISABLE_THREADS", None)
.flag("-Wno-pointer-sign").define("GPAC_DISABLE_LOG", None)
.files(glob::glob("src/evg/*.c")?.filter_map(Result::ok))
.opt_level(3).define("NDEBUG", None).compile(module);

bindgen::Builder::default().header("src/evg/gpac/evg.h").clang_arg("-Isrc/evg")
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: true })
.clang_arg("-DGPAC_DISABLE_THREADS").allowlist_function("gf_evg_s.*")
//.allowlist_item("GF_LINE_.*")
.clang_arg("-DGPAC_FIXED_POINT").allowlist_function("gf_path_.*")
.layout_tests(false).derive_copy(false).new_type_alias("Fixed")
.parse_callbacks(Box::new(bindgen::CargoCallbacks)).generate()?
.write_to_file(PathBuf::from(std::env::var("OUT_DIR")?).join(format!("{module}.rs")))?;
}

Ok(())
}
253 changes: 253 additions & 0 deletions src/gpac_evg.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
/****************************************************************
* $ID: gpac_evg.rs Tue 24 Oct 2023 15:58:07+0800 *
* *
* Maintainer: 范美辉 (MeiHui FAN) <[email protected]> *
* Copyright (c) 2023 M.H.Fan, All rights reserved. *
****************************************************************/

#![allow(non_snake_case)] #![allow(non_camel_case_types)] #![allow(non_upper_case_globals)]
#![allow(clippy::approx_constant)] #![allow(clippy::useless_transmute)]

//#[macro_export] macro_rules! dbg_ne { ($($v:expr),*) => () }
#[macro_export] macro_rules! dbg_ne { ($v:expr, $g:expr) => { if $v != $g { dbg!($v); } } }

/* TODO: https://github.com/sammycage/plutovg
https://github.com/kiba/SDL_svg/blob/master/ftgrays.c
https://github.com/Samsung/rlottie/tree/master/src/vector/freetype
https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/smooth/ftgrays.c
https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/src/raster/ftraster.c */

pub mod ftgrays { include!(concat!(env!("OUT_DIR"), "/ftgrays.rs"));
impl From<(FT_Pos, FT_Pos)> for FT_Vector {
fn from(v: (FT_Pos, FT_Pos)) -> Self { Self { x: v.0, y: v.1 } }
}
}

//pub mod gpac_evg { // https://github.com/gpac/gpac/tree/master/src/evg/
include!(concat!(env!("OUT_DIR"), "/gpac_evg.rs")); // evg_bindings.rs

impl From<i32> for Fixed { #[inline] fn from(v: i32) -> Self { Self(v << 16) } }
impl From<Fixed> for i32 { #[inline] fn from(v: Fixed) -> Self { (v.0 + (1 << 15)) >> 16 } }
impl From<f32> for Fixed { // 16.16 fixed-point, or 26.6?
#[inline] fn from(v: f32) -> Self { Self((v * (1 << 16) as f32) as i32) }
}
impl From<Fixed> for f32 { #[inline] fn from(v: Fixed) -> Self { v.0 as f32 / (1 << 16) as f32 } }

impl From<bool> for Bool {
fn from(value: bool) -> Self { if value { Bool::GF_TRUE } else { Bool::GF_FALSE } }
}

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,
// GF_LINE_(CAP/JOIN)_ROUND, GF_PATH_LINE_CENTER, GF_DASH_STYLE_PLAIN
dash_offset: 0.into(), dash_set: core::ptr::null_mut(),
path_length: 0.into(), miterLimit: 0.into(),
} }
}

pub struct VGPath(*mut GF_Path);
impl Drop for VGPath { fn drop(&mut self) { unsafe { gf_path_del(self.0) } } }
impl VGPath { #[allow(clippy::new_without_default)] // to build path and stencil
#[inline] pub fn new() -> Self { Self(unsafe { gf_path_new() }) }

#[inline] pub fn move_to(&self, pt: GF_Point2D) {
let _err = unsafe { gf_path_add_move_to_vec(self.0, &pt as *const _ as *mut _) };
dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn line_to(&self, pt: GF_Point2D) {
let _err = unsafe { gf_path_add_line_to_vec(self.0, &pt as *const _ as *mut _) };
dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn cubic_to(&self, c1: GF_Point2D, c2: GF_Point2D, pt: GF_Point2D) {
let _err = unsafe { gf_path_add_cubic_to_vec(self.0,
&c1 as *const _ as *mut _, &c2 as *const _ as *mut _, &pt as *const _ as *mut _)
}; dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn quad_to(&self, cp: GF_Point2D, pt: GF_Point2D) {
let _err = unsafe { gf_path_add_quadratic_to_vec(self.0,
&cp as *const _ as *mut _, &pt as *const _ as *mut _)
}; dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn svg_arc_to(&self, radius: GF_Point2D,
rotation: Fixed, large: bool, sweep: bool, pt: GF_Point2D) {
let _err = unsafe { gf_path_add_svg_arc_to(self.0, pt.x, pt.y,
radius.x, radius.y, rotation, large.into(), sweep.into())
}; dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn add_rect(&self, rect: GF_Rect) {
let _err = unsafe {
gf_path_add_rect(self.0, rect.x, rect.y, rect.width, rect.height)
}; dbg_ne!(_err, GF_Err::GF_OK);
}

//gf_path_add_arc_to(path, end_x, end_y, fa_x, fa_y, fb_x, fb_y, cw);
//gf_path_add_arc(path, radius, start_angle, end_angle, close_type);
//gf_path_add_ellipse(path, cx, cy, a_axis, b_axis);
//gf_path_add_bezier(path, pts, nb_pts);

#[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 isize;
let pt = &*path.points.offset(n);
eprintln!("{}-({:?}, {:?})", *path.tags.offset(n), //pt.x, pt.y);
<f32>::from(pt.x), <f32>::from(pt.y));
}
}
}

#[inline] pub fn close(&self) { // XXX: fix and simplify difference judgement in path2d.c
let _err = unsafe { gf_path_close(self.0) }; dbg_ne!(_err, GF_Err::GF_OK);
}
}

pub struct Stencil(*mut GF_EVGStencil);
impl Drop for Stencil { 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) {
let _err = unsafe { gf_evg_stencil_set_brush_color(self.0, color) };
dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn set_linear(&self, start: GF_Point2D, end: GF_Point2D) {
let _err = unsafe {
gf_evg_stencil_set_linear_gradient(self.0, start.x, start.y, end.x, end.y)
}; dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn set_radial(&self,
center: GF_Point2D, focal: GF_Point2D, radius: GF_Point2D) {
let _err = unsafe { gf_evg_stencil_set_radial_gradient(self.0,
center.x, center.y, focal.x, focal.y, radius.x, radius.y) };
dbg_ne!(_err, GF_Err::GF_OK);
}

#[inline] pub fn push_interpolation(&self, pos: Fixed, col: GF_Color) {
let _err = unsafe {
gf_evg_stencil_push_gradient_interpolation(self.0, pos, col)
}; dbg_ne!(_err, GF_Err::GF_OK);
}

/* #[inline] pub fn set_interpolation(&self, pos: &[Fixed], col: &[GF_Color], cnt: u32) {
let _err = unsafe { gf_evg_stencil_set_gradient_interpolation(self.0,
pos.as_mut_ptr(), col.as_mut_ptr(), cnt) }; dbg_ne!(_err, GF_Err::GF_OK);
} */

//let _err = gf_evg_stencil_set_gradient_mode(sten, GF_GradientMode::GF_GRADIENT_MODE_PAD);
//let _err = gf_evg_stencil_set_alpha(sten, alpha);

#[inline] pub fn set_matrix(&self, mat: &GF_Matrix2D) {
let _err = unsafe {
gf_evg_stencil_set_matrix(self.0, mat as *const _ as *mut _)
}; dbg_ne!(_err, GF_Err::GF_OK);
}
}

pub struct Surface(*mut GF_EVGSurface);
impl Drop for Surface { fn drop(&mut self) { unsafe { gf_evg_surface_delete(self.0) } } }
impl Surface {
#[inline] pub fn new(pixm: &mut Pixmap) -> Self {
let surf = unsafe { Self(gf_evg_surface_new(Bool::GF_FALSE)) };
let _err = unsafe { gf_evg_surface_attach_to_buffer(surf.0,
pixm.data.as_mut_ptr(), pixm.width, pixm.height, 4, (pixm.width << 2) as i32,
GF_PixelFormat::GF_PIXEL_RGBA)
}; dbg_ne!(_err, GF_Err::GF_OK); surf
//let _err = unsafe { gf_evg_surface_clear(surf, &mut bbox, 0xFF000000) };
}

#[inline] pub fn fill_path(&self, path: &VGPath, sten: &Stencil) {
let _err = unsafe { gf_evg_surface_set_path(self.0, path.0) };
dbg_ne!(_err, GF_Err::GF_OK);
let _err = unsafe { gf_evg_surface_fill(self.0, sten.0) };
dbg_ne!(_err, GF_Err::GF_OK);
}

/** ```
use intvg::gpac_evg::*;
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() });
// RUSTDOCFLAGS="-Z unstable-options --nocapture" cargo +nightly test #--doc
/* path.move_to(GF_Point2D { x: rect.x, y: rect.y });
path.line_to(GF_Point2D { x: Fixed(rect.x.0 + rect.width.0), y: rect.y });
path.line_to(GF_Point2D { x: Fixed(rect.x.0 + rect.width.0),
y: Fixed(rect.y.0 - rect.height.0) });
path.line_to(GF_Point2D { x: rect.x, y: Fixed(rect.y.0 - rect.height.0) });
path.line_to(GF_Point2D { x: rect.x, y: rect.y }); path.print_out();
path.close(); */
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", "/stroke.png")).unwrap(); //env!("OUT_DIR")
``` */

#[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) {
let _err = unsafe {
gf_evg_surface_set_matrix(self.0, mat as *const _ as *mut _)
}; dbg_ne!(_err, GF_Err::GF_OK);
}
}

pub struct Pixmap { pub data: Vec<u8>, pub width: u32, pub height: u32, }

impl Pixmap {
pub fn new(width: u32, height: u32) -> Self {
Self { width, height, data: vec![0; (width * height * 4) as usize] }
}

pub fn save_png(&self, path: &str) -> Result<(), png::EncodingError> {
let bufw = std::io::BufWriter::new(
std::fs::File::create(std::path::Path::new(path))?);
let mut encoder = png::Encoder::new(bufw, 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));
// png::ScaledFloat::from_scaled(45455) // 1.0 / 2.2 scaled by 100000
//let source_chromaticities = png::SourceChromaticities::new( // unscaled instant
// (0.3127, 0.3290), (0.6400, 0.3300), (0.3000, 0.6000), (0.1500, 0.0600));
//encoder.set_source_chromaticities(source_chromaticities);
encoder.write_header()?.write_image_data(&self.data)?; Ok(())
}
}
//}

3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ pub mod tinyvg;
pub mod render;
pub mod convert;

#[cfg(feature = "cc")] pub mod gpac_evg;
#[cfg(feature = "cc")] pub mod render_evg;

Loading

0 comments on commit f7c2f44

Please sign in to comment.