#![allow(clippy::many_single_char_names)]
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
#![cfg(not(target_arch = "wasm32"))]
use crate::{
BaseLine, CanvasContext, Color, Direction, Gradient, GradientType, LineCap, LineJoin,
LinearGradient, PatternExtend, Point, RadialGradient, Rect, RgbaColor, Size, TextAlign,
TextMetrics, TextStyle, TextWeight,
};
use cairo::{self, FontFace, FontSlant, FontWeight, ImageSurface, Surface, SurfacePattern};
use std::{any::Any, cell::RefCell};
#[derive(Debug, Clone)]
pub struct Pattern {
pub extend: PatternExtend,
pub inner: cairo::SurfacePattern,
}
impl Pattern {
pub fn new(extend: PatternExtend, surface: &cairo::Surface) -> Self {
Self {
extend,
inner: SurfacePattern::create(surface),
}
}
}
#[derive(Clone)]
enum Paint {
None,
Solid(Color),
Gradient(Gradient),
Pattern(Pattern),
}
impl Default for Paint {
fn default() -> Self {
Paint::None
}
}
#[derive(Default, Clone)]
struct CanvasState {
stroke: Paint,
fill: Paint,
}
pub struct Canvas<'a> {
ctx: &'a cairo::Context,
state: RefCell<CanvasState>,
}
impl<'a> Canvas<'a> {
#[allow(dead_code)]
pub fn new(ctx: &'a cairo::Context) -> Self {
Self {
ctx,
state: Default::default(),
}
}
fn handle_paint(&self, paint: &Paint) -> bool {
match paint {
Paint::Solid(value) => {
let value = *value;
let color: RgbaColor = value.into();
self.ctx.set_source_rgba(
color.red as f64 / 255.,
color.green as f64 / 255.,
color.blue as f64 / 255.,
color.alpha as f64 / 255.,
);
true
}
Paint::Gradient(value) => {
match value.kind {
GradientType::Linear(params) => {
let LinearGradient { x0, y0, x1, y1 } = params;
let gradient = cairo::LinearGradient::new(x0, y0, x1, y1);
let stops = value.stops.borrow();
for stop in stops.iter() {
let RgbaColor {
red,
green,
blue,
alpha,
} = stop.color.into();
gradient.add_color_stop_rgba(
stop.offset,
red as f64 / 255.,
green as f64 / 255.,
blue as f64 / 255.,
alpha as f64 / 255.,
);
}
self.ctx.set_source(&gradient);
}
GradientType::Radial(params) => {
let RadialGradient {
x0,
y0,
r0,
x1,
y1,
r1,
} = params;
let gradient = cairo::RadialGradient::new(x0, y0, r0, x1, y1, r1);
let stops = value.stops.borrow();
for stop in stops.iter() {
let RgbaColor {
red,
green,
blue,
alpha,
} = stop.color.into();
gradient.add_color_stop_rgba(
stop.offset,
red as f64 / 255.,
green as f64 / 255.,
blue as f64 / 255.,
alpha as f64 / 255.,
);
}
self.ctx.set_source(&gradient);
}
}
true
}
Paint::Pattern(value) => {
let extend = match value.extend {
PatternExtend::None => cairo::Extend::None,
PatternExtend::Repeat => cairo::Extend::Repeat,
PatternExtend::Reflect => cairo::Extend::Reflect,
PatternExtend::Pad => cairo::Extend::Pad,
};
value.inner.set_extend(extend);
self.ctx.set_source(&value.inner);
true
}
Paint::None => false,
}
}
}
impl<'a> CanvasContext for Canvas<'a> {
type Pattern = Pattern;
fn get_direction(&self) -> Direction {
unimplemented!()
}
fn set_direction(&self, value: Direction) -> String {
info!("NOT IMPLEMENTED");
unimplemented!()
}
fn set_fill_color(&self, value: Color) {
let mut state = self.state.borrow_mut();
state.fill = Paint::Solid(value);
let RgbaColor {
red,
green,
blue,
alpha,
} = value.into();
self.ctx.set_source_rgba(
red as f64 / 255.,
green as f64 / 255.,
blue as f64 / 255.,
alpha as f64 / 255.,
);
}
fn set_fill_gradient(&self, value: &Gradient) {
let mut state = self.state.borrow_mut();
state.fill = Paint::Gradient(value.clone());
match value.kind {
GradientType::Linear(params) => {
let LinearGradient { x0, y0, x1, y1 } = params;
let gradient = cairo::LinearGradient::new(x0, y0, x1, y1);
let stops = value.stops.borrow();
for stop in stops.iter() {
let RgbaColor {
red,
green,
blue,
alpha,
} = stop.color.into();
gradient.add_color_stop_rgba(
stop.offset,
red as f64 / 255.,
green as f64 / 255.,
blue as f64 / 255.,
alpha as f64 / 255.,
);
}
self.ctx.set_source(&gradient);
}
GradientType::Radial(params) => {
let RadialGradient {
x0,
y0,
r0,
x1,
y1,
r1,
} = params;
let gradient = cairo::RadialGradient::new(x0, y0, r0, x1, y1, r1);
let stops = value.stops.borrow();
for stop in stops.iter() {
let RgbaColor {
red,
green,
blue,
alpha,
} = stop.color.into();
gradient.add_color_stop_rgba(
stop.offset,
red as f64 / 255.,
green as f64 / 255.,
blue as f64 / 255.,
alpha as f64 / 255.,
);
}
self.ctx.set_source(&gradient);
}
}
}
fn set_fill_pattern(&self, value: &Self::Pattern) {
let mut state = self.state.borrow_mut();
state.fill = Paint::Pattern(value.clone());
let extend = match value.extend {
PatternExtend::None => cairo::Extend::None,
PatternExtend::Repeat => cairo::Extend::Repeat,
PatternExtend::Reflect => cairo::Extend::Reflect,
PatternExtend::Pad => cairo::Extend::Pad,
};
value.inner.set_extend(extend);
self.ctx.set_source(&value.inner);
}
fn get_filter(&self) -> String {
unimplemented!()
}
fn set_filter(&self, value: &str) {
unimplemented!()
}
fn get_font(&self) -> String {
unimplemented!()
}
fn set_font(&self, family: &str, style: TextStyle, weight: TextWeight, size: f64) {
let slant = match style {
TextStyle::Italic => FontSlant::Italic,
TextStyle::Normal => FontSlant::Normal,
TextStyle::Oblique => FontSlant::Oblique,
};
let weight = match weight {
TextWeight::Bold => FontWeight::Bold,
_ => FontWeight::Normal,
};
self.ctx.select_font_face(family, slant, weight);
self.ctx.set_font_size(size);
}
fn get_global_alpha(&self) -> f64 {
unimplemented!()
}
fn set_global_alpha(&self, value: f64) {
unimplemented!()
}
fn get_global_composite_operation(&self) -> String {
unimplemented!()
}
fn set_global_composite_operation(&self, value: &str) {
unimplemented!()
}
fn is_image_smoothing_enabled(&self) -> bool {
unimplemented!()
}
fn set_image_smoothing(&self, value: bool) {
unimplemented!()
}
fn get_line_cap(&self) -> LineCap {
match self.ctx.get_line_cap() {
cairo::LineCap::Round => LineCap::Round,
cairo::LineCap::Square => LineCap::Square,
_ => LineCap::Butt, }
}
fn set_line_cap(&self, value: LineCap) {
let value = match value {
LineCap::Round => cairo::LineCap::Round,
LineCap::Square => cairo::LineCap::Square,
LineCap::Butt => cairo::LineCap::Butt,
};
self.ctx.set_line_cap(value)
}
fn get_line_dash_offset(&self) -> f64 {
let (_, result) = self.ctx.get_dash();
result
}
fn set_line_dash_offset(&self, value: f64) {
let (dash, _) = self.ctx.get_dash();
self.ctx.set_dash(&dash, value);
}
fn get_line_join(&self) -> LineJoin {
match self.ctx.get_line_join() {
cairo::LineJoin::Bevel => LineJoin::Bevel,
cairo::LineJoin::Round => LineJoin::Round,
_ => LineJoin::Miter,
}
}
fn set_line_join(&self, value: LineJoin) {
let value = match value {
LineJoin::Bevel => cairo::LineJoin::Bevel,
LineJoin::Round => cairo::LineJoin::Round,
LineJoin::Miter => cairo::LineJoin::Miter,
};
self.ctx.set_line_join(value)
}
fn get_line_width(&self) -> f64 {
self.ctx.get_line_width()
}
fn set_line_width(&self, value: f64) {
self.ctx.set_line_width(value);
}
fn get_miter_limit(&self) -> f64 {
self.ctx.get_miter_limit()
}
fn set_miter_limit(&self, value: f64) {
self.ctx.set_miter_limit(value);
}
fn get_shadow_blur(&self) -> f64 {
unimplemented!()
}
fn set_shadow_blur(&self, value: f64) {
unimplemented!()
}
fn get_shadow_color(&self) -> Color {
unimplemented!()
}
fn set_shadow_color(&self, value: Color) {
unimplemented!()
}
fn get_shadow_offset_x(&self) -> f64 {
unimplemented!()
}
fn set_shadow_offset_x(&self, value: f64) {
unimplemented!()
}
fn get_shadow_offset_y(&self) -> f64 {
unimplemented!()
}
fn set_shadow_offset_y(&self, value: f64) {
unimplemented!()
}
fn set_stroke_color(&self, value: Color) {
let mut state = self.state.borrow_mut();
state.stroke = Paint::Solid(value);
}
fn set_stroke_gradient(&self, value: &Gradient) {
let mut state = self.state.borrow_mut();
state.stroke = Paint::Gradient(value.clone());
}
fn set_stroke_pattern(&self, value: &Self::Pattern) {
let mut state = self.state.borrow_mut();
state.stroke = Paint::Pattern(value.clone());
}
fn get_text_align(&self) -> TextAlign {
unimplemented!()
}
fn set_text_align(&self, value: TextAlign) {
}
fn get_text_baseline(&self) -> BaseLine {
unimplemented!()
}
fn set_text_baseline(&self, value: BaseLine) {
}
fn arc(
&self,
x: f64,
y: f64,
radius: f64,
start_angle: f64,
end_angle: f64,
anticlockwise: bool,
) {
if anticlockwise {
self.ctx.arc_negative(x, y, radius, start_angle, end_angle);
} else {
self.ctx.arc(x, y, radius, start_angle, end_angle);
}
}
fn arc_to(&self, x1: f64, y1: f64, x2: f64, y2: f64, radius: f64) {
unimplemented!()
}
fn begin_path(&self) {
self.ctx.new_path();
}
fn bezier_curve_to(&self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
self.ctx.curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
}
fn clear_rect(&self, x: f64, y: f64, width: f64, height: f64) {
self.ctx.new_path();
self.ctx.save();
self.ctx.set_source_rgba(0., 0., 0., 0.);
self.ctx.set_operator(cairo::Operator::Clear);
self.ctx.rectangle(x, y, width, height);
self.ctx.fill();
self.ctx.restore();
}
fn close_path(&self) {
self.ctx.close_path();
}
fn ellipse(
&self,
x: f64,
y: f64,
radius_x: f64,
radius_y: f64,
rotation: f64,
start_angle: f64,
end_angle: f64,
anticlockwise: bool,
) {
self.ctx.save();
self.ctx.translate(x, y);
self.ctx.scale(1., radius_y / radius_x);
self.ctx.rotate(rotation);
self.ctx.translate(-x, -y);
self.ctx.arc(x, y, radius_x, start_angle, end_angle);
self.ctx.restore();
}
fn fill(&self) {
let state = self.state.borrow();
if self.handle_paint(&state.fill) {
self.ctx.fill_preserve();
}
}
fn fill_rect(&self, x: f64, y: f64, width: f64, height: f64) {
let state = self.state.borrow();
if self.handle_paint(&state.fill) {
self.ctx.new_path();
self.ctx.rectangle(x, y, width, height);
self.ctx.fill();
}
}
fn fill_text(&self, text: &str, x: f64, y: f64) {
let state = self.state.borrow();
if self.handle_paint(&state.fill) {
self.ctx.new_path();
self.ctx.save();
self.ctx.move_to(x, y);
self.ctx.text_path(text);
self.ctx.fill();
self.ctx.restore();
}
}
fn get_line_dash(&self) -> Vec<f64> {
let (result, _) = self.ctx.get_dash();
result
}
fn line_to(&self, x: f64, y: f64) {
self.ctx.line_to(x, y);
}
fn measure_text(&self, text: &str) -> TextMetrics {
let ext = self.ctx.text_extents(text);
TextMetrics {
width: ext.width,
height: ext.height,
}
}
fn move_to(&self, x: f64, y: f64) {
self.ctx.move_to(x, y);
}
fn quadratic_curve_to(&self, cpx: f64, cpy: f64, x: f64, y: f64) {
let (x0, y0) = self.ctx.get_current_point();
self.ctx.curve_to(
2.0 / 3.0 * cpx + 1.0 / 3.0 * x0,
2.0 / 3.0 * cpy + 1.0 / 3.0 * y0,
2.0 / 3.0 * cpx + 1.0 / 3.0 * x,
2.0 / 3.0 * cpy + 1.0 / 3.0 * y,
x,
y,
);
}
fn rect(&self, x: f64, y: f64, width: f64, height: f64) {
self.ctx.rectangle(x, y, width, height);
}
fn reset_transform(&self) {
unimplemented!()
}
fn restore(&self) {
self.ctx.restore();
}
fn rotate(&self, angle: f64) {
self.ctx.rotate(angle);
}
fn save(&self) {
self.ctx.save();
}
fn scale(&self, x: f64, y: f64) {
self.ctx.scale(x, y);
}
fn set_line_dash(&self, dash: &Vec<f64>) {
let (_, offset) = self.ctx.get_dash();
self.ctx.set_dash(dash, offset);
}
fn set_transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
let m = cairo::Matrix::new(a, b, c, d, e, f);
self.ctx.transform(m);
}
fn stroke(&self) {
let state = self.state.borrow();
if self.handle_paint(&state.stroke) {
self.ctx.stroke_preserve();
}
}
fn stroke_rect(&self, x: f64, y: f64, width: f64, height: f64) {
let state = self.state.borrow();
if self.handle_paint(&state.stroke) {
self.ctx.new_path();
self.ctx.rectangle(x, y, width, height);
self.ctx.stroke();
}
}
fn stroke_text(&self, text: &str, x: f64, y: f64) {
let state = self.state.borrow();
if self.handle_paint(&state.stroke) {
self.ctx.new_path();
self.ctx.save();
self.ctx.move_to(x, y);
self.ctx.text_path(text);
self.ctx.stroke();
self.ctx.restore();
}
}
fn transform(&self, a: f64, b: f64, c: f64, d: f64, e: f64, f: f64) {
let m = cairo::Matrix::new(a, b, c, d, e, f);
self.ctx.transform(m);
}
fn translate(&self, x: f64, y: f64) {
self.ctx.translate(x, y);
}
}