use super::{
Color, Image, MeasurementPoint, MeasurementPoints, Parameter, Point, RendererConfig, Size,
};
use crate::{animation, svg, STATE};
use cairo::Context;
use elapsed::measure_time;
use log::{debug, info};
use resvg::{prelude::NodeExt, usvg, ScreenSize};
use serde_derive::{Deserialize, Serialize};
use std::{
collections::HashMap,
sync::{
atomic::{AtomicBool, Ordering},
Arc, Mutex, RwLock,
},
time::Instant,
};
#[derive(Deserialize, Serialize)]
#[serde(default)]
pub struct Renderer {
pub config: Arc<RwLock<RendererConfig>>,
pub hide_sidepanels: bool,
#[serde(skip)]
pub running: Arc<AtomicBool>,
#[serde(skip)]
pub frozen: Arc<AtomicBool>,
#[serde(skip)]
pub image_svg: Arc<Mutex<Option<Image>>>,
#[serde(skip)]
pub image_live: Arc<Mutex<Option<Image>>>,
#[serde(skip)]
pub image_preview: Arc<Mutex<Option<Image>>>,
#[serde(skip)]
pub live: Arc<Mutex<Option<Image>>>,
#[serde(skip)]
pub preview: Arc<Mutex<Option<Image>>>,
#[serde(skip)]
pub measurement_points: MeasurementPoints,
#[serde(skip)]
pub palettes_cycle_last: Arc<Mutex<Instant>>,
#[serde(skip)]
pub presets_cycle_last: Arc<Mutex<Instant>>,
#[serde(skip)]
pub render_group_cycle_last: Arc<Mutex<Instant>>,
}
impl Default for Renderer {
fn default() -> Self {
Self {
config: Arc::new(RwLock::new(RendererConfig {
..Default::default()
})),
running: Arc::new(AtomicBool::new(false)),
frozen: Arc::new(AtomicBool::new(false)),
image_svg: Arc::new(Mutex::new(None)),
image_live: Arc::new(Mutex::new(None)),
image_preview: Arc::new(Mutex::new(None)),
live: Arc::new(Mutex::new(None)),
preview: Arc::new(Mutex::new(None)),
measurement_points: Arc::new(RwLock::new(None)),
hide_sidepanels: false,
palettes_cycle_last: Arc::new(Mutex::new(Instant::now())),
presets_cycle_last: Arc::new(Mutex::new(Instant::now())),
render_group_cycle_last: Arc::new(Mutex::new(Instant::now())),
}
}
}
impl Renderer {
pub fn start(&self) {
self.running.store(true, Ordering::Relaxed);
animation::spawn(&self);
}
pub fn stop(&self) {
self.running.store(false, Ordering::Relaxed);
}
pub fn render_on_context(&self, context: &Context, size: &Size, live: bool) {
let scaling_factor = {
let config = self.config.read().unwrap();
let sx = size.width as f64 / config.size.width as f64;
let sy = size.height as f64 / config.size.height as f64;
sx.min(sy)
};
context.scale(scaling_factor, scaling_factor);
let (elapsed, _) = measure_time(|| {
let mut result = if live {
self.live.lock().unwrap()
} else {
self.preview.lock().unwrap()
};
if let Some(result) = result.as_mut() {
result.with_surface(|result| {
context.set_source_surface(&result, 0., 0.);
context.paint();
context.set_source_rgb(0.0, 0.0, 0.0);
});
}
});
debug!("Plit: {:?}", elapsed);
}
pub fn configure(&self) {
let config = self.config.read().unwrap();
create_surface(&config.size, &self.image_svg);
create_surface(&config.size, &self.image_live);
create_surface(&config.size, &self.image_preview);
create_surface(&config.size, &self.live);
create_surface(&config.size, &self.preview);
if let Some((parameters, tree)) = config
.file_path
.as_ref()
.map(|file_path| {
if file_path.is_relative() {
crate::path().unwrap().parent().unwrap().join(file_path)
} else {
file_path.clone()
}
})
.map(|file_path| svg::tree(&file_path))
{
render_svg(
¶meters,
&tree,
&config.size,
&self.image_svg,
&self.measurement_points,
);
let state = STATE.read().unwrap();
state.update_uis.iter().for_each(|update_ui| {
let _ = update_ui.send(());
});
}
}
}
fn create_surface(size: &Size, image: &Arc<Mutex<Option<Image>>>) {
info!("Create surfaces");
let (elapsed, _) = measure_time(|| {
*image.lock().unwrap() = Some(Image::new(size.width, size.height));
});
debug!("Create surfaces: {:?}", elapsed);
info!("Done creating surfaces");
}
fn render_svg(
parameters: &HashMap<String, Parameter>,
tree: &usvg::Tree,
size: &Size,
image_svg: &Arc<Mutex<Option<Image>>>,
measurement_points: &MeasurementPoints,
) {
info!("Render svg");
let (elapsed, _) = measure_time(|| {
if let Some(image_svg) = image_svg.lock().unwrap().as_mut() {
image_svg.with_surface(|image_svg| {
let opt = resvg::Options::default();
let context = Context::new(&image_svg);
resvg::backend_cairo::render_to_canvas(
&tree,
&opt,
ScreenSize::new(size.width as u32, size.height as u32)
.expect("Wrong screen size"),
&context,
);
image_svg.flush();
let mut measurement_points = measurement_points.write().unwrap();
*measurement_points = {
let mut measurement_points = HashMap::new();
tree.root().descendants().for_each(|node| {
if let (Some(rect), Some(parameter)) = (
&node.calculate_bbox(),
parameters.get(&node.id().to_owned()),
) {
parameter.render_groups.iter().for_each(|render_group| {
let x = rect.x() + rect.width() / 2.0;
let y = rect.y() + rect.height() / 2.0;
let point = context.user_to_device(x, y);
measurement_points
.entry(render_group.to_owned())
.or_insert_with(Vec::new)
.push(MeasurementPoint {
leds: parameter.leds.clone(),
point: Point::new(point.0, point.1),
color: Color {
red: 0,
green: 0,
blue: 0,
},
});
});
}
});
Some(measurement_points)
};
});
}
});
debug!("Render SVG: {:?}", elapsed);
info!("Done rendering svg");
crate::set_has_ui_changes();
}