Skip to content

Commit

Permalink
Tiling pattern (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
yutannihilation authored Oct 28, 2024
1 parent 8dfde7a commit 5e6d04f
Show file tree
Hide file tree
Showing 12 changed files with 543 additions and 250 deletions.
16 changes: 8 additions & 8 deletions src/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

93 changes: 72 additions & 21 deletions src/rust/src/debug_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ use vellogd_shared::text_layouter::TextMetric;

#[cfg(debug_assertions)]
fn fill_related_params(gc: R_GE_gcontext) -> String {
format!("fill: {:08x}", gc.fill)
let pattern_fill = unsafe {
if gc.patternFill != R_NilValue {
(*INTEGER(gc.patternFill)).to_string()
} else {
"NULL".to_string()
}
};
format!("fill: {:08x}, patternFill: {pattern_fill}", gc.fill)
}

#[cfg(debug_assertions)]
Expand All @@ -32,23 +39,27 @@ fn text_related_params(gc: R_GE_gcontext) -> String {

pub struct DebugGraphicsDevice {
n_clip: i32,
n_pattern: i32,
}

impl DebugGraphicsDevice {
pub fn new() -> Self {
Self { n_clip: 0 }
Self {
n_clip: 0,
n_pattern: 0,
}
}
}

fn take3<T: std::fmt::Debug>(x: &[T]) -> String {
fn take3(x: &[f64]) -> String {
if x.len() < 3 {
return format!("{x:?}");
}

let x = x
.iter()
.take(3)
.map(|x| format!("{x:?}"))
.map(|x| format!("{x:.1}"))
.collect::<Vec<String>>()
.join(", ");

Expand Down Expand Up @@ -121,27 +132,37 @@ impl DeviceDriver for DebugGraphicsDevice {

fn new_page(&mut self, gc: R_GE_gcontext, _: DevDesc) {
add_tracing_point!();
let fill = gc.fill;
savvy::r_eprintln!("[new_page] fill: {fill:#08x}");
savvy::r_eprintln!("[new_page] fill params: {{ {} }}", fill_related_params(gc));
}

fn polygon(&mut self, x: &[f64], y: &[f64], _: R_GE_gcontext, _: DevDesc) {
fn polygon(&mut self, x: &[f64], y: &[f64], gc: R_GE_gcontext, _: DevDesc) {
add_tracing_point!();
savvy::r_eprintln!("[polygon] x: {} y: {}", take3(x), take3(y));
savvy::r_eprintln!(
"[polygon] x: {} y: {} fill params: {{ {} }}, line params: {{ {} }}",
take3(x),
take3(y),
fill_related_params(gc),
line_related_params(gc),
);
}

fn polyline(&mut self, x: &[f64], y: &[f64], _: R_GE_gcontext, _: DevDesc) {
fn polyline(&mut self, x: &[f64], y: &[f64], gc: R_GE_gcontext, _: DevDesc) {
add_tracing_point!();
savvy::r_eprintln!("[polyline] x: {} y: {}", take3(x), take3(y));
savvy::r_eprintln!(
"[polyline] x: {} y: {}, line params: {{ {} }}",
take3(x),
take3(y),
line_related_params(gc),
);
}

fn rect(&mut self, from: (f64, f64), to: (f64, f64), gc: R_GE_gcontext, _: DevDesc) {
add_tracing_point!();
savvy::r_eprintln!("[rect] from: {from:?} to: {to:?}");
if unsafe { gc.patternFill != R_NilValue } {
let fill = unsafe { *INTEGER(gc.patternFill) };
savvy::r_eprintln!(" fill: {fill}")
}
savvy::r_eprintln!(
"[rect] from: {from:?} to: {to:?} fill params: {{ {} }}, line params: {{ {} }}",
fill_related_params(gc),
line_related_params(gc),
);
}

fn path(
Expand All @@ -150,11 +171,15 @@ impl DeviceDriver for DebugGraphicsDevice {
_y: &[f64],
nper: &[i32],
_winding: bool,
_gc: R_GE_gcontext,
gc: R_GE_gcontext,
_: DevDesc,
) {
add_tracing_point!();
savvy::r_eprintln!("[path] nper: {nper:?}");
savvy::r_eprintln!(
"[path] nper: {nper:?} fill params: {{ {} }}, line params: {{ {} }}",
fill_related_params(gc),
line_related_params(gc),
);
}

fn raster(
Expand Down Expand Up @@ -264,7 +289,7 @@ impl DeviceDriver for DebugGraphicsDevice {
let y2 = R_GE_linearGradientY2(pattern);
let extend = R_GE_linearGradientExtend(pattern);
savvy::r_eprintln!(
"[setPattern]
"[setPattern] linearGradient
from: ({x1}, {y1})
to: ({x2}, {y2})
extend: {extend}"
Expand All @@ -288,7 +313,7 @@ impl DeviceDriver for DebugGraphicsDevice {
let r2 = R_GE_radialGradientR2(pattern);
let extend = R_GE_radialGradientExtend(pattern);
savvy::r_eprintln!(
"[setPattern]
"[setPattern] radialGradient
from: ({cx1}, {cy1}), r: {r1}
to: ({cx2}, {cy2}), r: {r2}
extend: {extend}"
Expand All @@ -303,11 +328,37 @@ impl DeviceDriver for DebugGraphicsDevice {
savvy::r_eprintln!(" {i}: {stop},{color:08x}");
}
},
3 => {} // tiling
3 => unsafe {
let x = R_GE_tilingPatternX(pattern);
let y = R_GE_tilingPatternY(pattern);
let w = R_GE_tilingPatternWidth(pattern);
let h = R_GE_tilingPatternHeight(pattern);
let extend = R_GE_tilingPatternExtend(pattern);
savvy::r_eprintln!(
"[setPattern] tilingPattern
pos: ({x}, {y})
size: ({w}, {h})
extend: {extend}
=== pattern function ==============
"
);

let fun = R_GE_tilingPatternFunction(pattern);
let call = Rf_protect(Rf_lang1(fun));
Rf_eval(call, R_GlobalEnv);
Rf_unprotect(1);

savvy::r_eprintln!("=== pattern function end ==========")
},
_ => {}
}

unsafe { R_NilValue }
unsafe {
let pattern_id = self.n_pattern;
self.n_pattern += 1;
Rf_ScalarInteger(pattern_id)
}
}

fn release_pattern(&mut self, ref_: SEXP, _: DevDesc) {
Expand Down
29 changes: 22 additions & 7 deletions src/rust/src/graphics/device_driver.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use savvy::ffi::SEXP;
use std::slice;
use std::{ffi::CString, os::raw::c_uint};
use vellogd_shared::ffi::{
R_GE_linearGradientPattern, R_GE_radialGradientPattern, R_GE_tilingPattern, R_NaInt,
};
use vellogd_shared::{
ffi::{
pDevDesc, pGEcontext, DevDesc, GEaddDevice2, GEcreateDevDesc, GEinitDisplayList,
Expand Down Expand Up @@ -254,34 +257,46 @@ pub trait DeviceDriver: std::marker::Sized {
unsafe {
let len = 3;
let patterns = Rf_protect(Rf_allocVector(INTSXP, len));
std::ptr::write_bytes(INTEGER(patterns), 0, len);
// *INTEGER(patterns) = 1;
*INTEGER(patterns).offset(0) = R_GE_linearGradientPattern as i32;
*INTEGER(patterns).offset(1) = R_GE_radialGradientPattern as i32;
*INTEGER(patterns).offset(2) = R_GE_tilingPattern as i32;
SET_VECTOR_ELT(cap, R_GE_capability_patterns, patterns);
Rf_unprotect(1);
}

// clipping_paths
unsafe {
let clipping_paths = Rf_protect(Rf_allocVector(INTSXP, 1));
*INTEGER(clipping_paths) = 0;
*INTEGER(clipping_paths) = R_NaInt;
SET_VECTOR_ELT(cap, R_GE_capability_clippingPaths, clipping_paths);
Rf_unprotect(1);
}

// masks
unsafe {
let len = 2;
let len = 1; // TODO: 2
let masks = Rf_protect(Rf_allocVector(INTSXP, len));
std::ptr::write_bytes(INTEGER(masks), 0, len);
*INTEGER(masks).offset(0) = R_NaInt;
// *INTEGER(masks).offset(1) = R_NaInt;
SET_VECTOR_ELT(cap, R_GE_capability_masks, masks);
Rf_unprotect(1);
}

// compositing
unsafe {
let len = 11;
let len = 1; // TODO: 11
let compositing = Rf_protect(Rf_allocVector(INTSXP, len));
std::ptr::write_bytes(INTEGER(compositing), 0, len);
*INTEGER(compositing).offset(0) = R_NaInt;
// *INTEGER(compositing).offset(1) = R_NaInt;
// *INTEGER(compositing).offset(2) = R_NaInt;
// *INTEGER(compositing).offset(3) = R_NaInt;
// *INTEGER(compositing).offset(4) = R_NaInt;
// *INTEGER(compositing).offset(5) = R_NaInt;
// *INTEGER(compositing).offset(6) = R_NaInt;
// *INTEGER(compositing).offset(7) = R_NaInt;
// *INTEGER(compositing).offset(8) = R_NaInt;
// *INTEGER(compositing).offset(9) = R_NaInt;
// *INTEGER(compositing).offset(10) = R_NaInt;
SET_VECTOR_ELT(cap, R_GE_capability_compositing, compositing);
Rf_unprotect(1);
}
Expand Down
97 changes: 97 additions & 0 deletions src/rust/src/graphics/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,100 @@ mod device_driver;
pub use device_descriptor::DeviceDescriptor;

pub use device_driver::DeviceDriver;
use vellogd_shared::{
ffi::{R_GE_gcontext, R_NilValue, INTEGER},
protocol::{FillBrush, FillParams, StrokeParams},
};

pub fn gc_to_fill_params(gc: R_GE_gcontext) -> Option<FillParams> {
gc_to_fill_params_with_flag(gc, true)
}

pub fn gc_to_fill_params_with_flag(
gc: R_GE_gcontext,
use_nonzero_rule: bool,
) -> Option<FillParams> {
if gc.fill == 0 {
return None;
}

let brush = if unsafe { gc.patternFill != R_NilValue } {
let index = unsafe { *INTEGER(gc.patternFill) };
FillBrush::PatternRef(index as u32)
} else {
let [r, g, b, a] = gc.fill.to_ne_bytes();
let color = peniko::Color::rgba8(r, g, b, a);
FillBrush::Color(color)
};

Some(FillParams {
brush,
use_nonzero_rule,
})
}

pub fn gc_to_stroke_params(gc: R_GE_gcontext) -> Option<StrokeParams> {
if gc.col == 0 || gc.lty == -1 {
return None;
}

let [r, g, b, a] = gc.col.to_ne_bytes();
let color = peniko::Color::rgba8(r, g, b, a);

let width = gc.lwd;

// cf. https://github.com/r-devel/r-svn/blob/6ad1e0f2702fd0308e4f3caac2e22541d014ab6a/src/include/R_ext/GraphicsEngine.h#L183-L187
let join = match gc.ljoin {
1 => kurbo::Join::Round,
2 => kurbo::Join::Miter,
3 => kurbo::Join::Bevel,
v => panic!("invalid join value: {v}"),
};
// cf. https://github.com/r-devel/r-svn/blob/6ad1e0f2702fd0308e4f3caac2e22541d014ab6a/src/include/R_ext/GraphicsEngine.h#L183-L187
let cap = match gc.lend {
1 => kurbo::Cap::Round,
2 => kurbo::Cap::Butt,
3 => kurbo::Cap::Square,
v => panic!("invalid cap value: {v}"),
};

// cf. https://github.com/r-devel/r-svn/blob/6ad1e0f2702fd0308e4f3caac2e22541d014ab6a/src/include/R_ext/GraphicsEngine.h#L413C1-L419C50
//
// Based on these implementations
//
// https://github.com/r-devel/r-svn/blob/6ad1e0f2702fd0308e4f3caac2e22541d014ab6a/src/modules/X11/devX11.c#L1224
// https://github.com/r-lib/ragg/blob/6e8bfd1264dfaa36aa6f92592e13a1169986e7b9/src/AggDevice.h#L195C8-L205
let dash_pattern: Vec<f64> = match gc.lty {
-1 => vec![], // LTY_BLANK;
0 => vec![], // LTY_SOLID;
lty => {
let ptn_bytes = lty.to_ne_bytes();
let mut ptn = Vec::new();
for b in ptn_bytes {
let dash = b & 0b00001111;
let gap = (b & 0b11110000) >> 4;

if dash == 0 {
break;
}

ptn.push(dash as f64 * width);
ptn.push(gap as f64 * width);
}
ptn
}
};

Some(StrokeParams {
color,
stroke: kurbo::Stroke {
width,
join,
miter_limit: gc.lmitre,
start_cap: cap,
end_cap: cap,
dash_pattern: dash_pattern.into(),
dash_offset: 0.0,
},
})
}
Loading

0 comments on commit 5e6d04f

Please sign in to comment.