polygon-art/src/context/renderer.rs

160 lines
4.3 KiB
Rust

use crate::context::palette::Palette;
use cairo::Context;
use cairo::{ImageSurface, Surface, SvgSurface};
use std::fs::File;
/// container that holds everything to render surface
pub(crate) trait RenderContainer {
/// render command
fn render(&self);
/// get render context just in case
fn context(&self) -> &Context;
/// surface to create cairo context
fn surface(&self) -> &Surface;
/// initialize RenderContainer
fn init(&self, width: f64, height: f64, line_size: f64);
/// plot a path
fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool);
fn put_path_on_stack(&self, path: &Vec<[f64; 2]>) {
let context = self.context();
let mut init = true;
for &[x, y] in path.iter() {
if init {
context.move_to(x, y);
init = false;
} else {
context.line_to(x, y);
}
}
context.close_path();
}
}
/// container to render svg images
pub struct SvgRenderContainer {
surface: SvgSurface,
context: Context,
}
impl SvgRenderContainer {
pub fn new(surface: SvgSurface, _: Palette) -> Self {
let context = Context::new(&surface);
SvgRenderContainer { surface, context }
}
}
impl RenderContainer for SvgRenderContainer {
fn render(&self) {
// nothing to do
}
fn context(&self) -> &Context {
&self.context
}
fn surface(&self) -> &Surface {
&self.surface as &Surface
}
fn init(&self, _width: f64, _height: f64, line_size: f64) {
self.context.set_line_width(line_size);
}
/// plot a path
fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool) {
self.put_path_on_stack(path);
let context = self.context();
if fill {
context.fill()
} else {
context.stroke();
}
}
}
/// container to render png images
pub struct PngRenderContainer {
/// output path of the png
path: String,
surface: ImageSurface,
context: Context,
palette: Palette,
}
impl PngRenderContainer {
pub fn new(path: String, surface: ImageSurface, palette: Palette) -> Self {
let context = Context::new(&surface);
PngRenderContainer {
path,
surface,
context,
palette,
}
}
}
impl RenderContainer for PngRenderContainer {
fn render(&self) {
let mut file = File::create(&self.path).expect("Couldn't create 'file.png'");
match self.surface.write_to_png(&mut file) {
Ok(_) => println!("{}, created", self.path),
Err(_) => println!("Error create file.png"),
}
}
fn context(&self) -> &Context {
&self.context
}
fn surface(&self) -> &Surface {
&self.surface as &Surface
}
fn init(&self, width: f64, height: f64, line_size: f64) {
// set background color
self.context.set_source_rgb(
self.palette.background_color.red as f64,
self.palette.background_color.green as f64,
self.palette.background_color.blue as f64,
);
self.context.rectangle(0., 0., width, height);
self.context.fill();
// configure line
self.context.set_source_rgb(
self.palette.fill_color.red as f64,
self.palette.fill_color.green as f64,
self.palette.fill_color.blue as f64,
);
self.context.set_line_width(line_size);
}
/// plot a path
fn plot_path(&self, path: &Vec<[f64; 2]>, fill: bool) {
if fill {
self.put_path_on_stack(path);
self.context.fill()
} else {
// fill with background
self.context.set_source_rgb(
self.palette.background_color.red as f64,
self.palette.background_color.green as f64,
self.palette.background_color.blue as f64,
);
self.put_path_on_stack(path);
self.context.fill();
// set drawing color back to normal
self.context.set_source_rgb(
self.palette.fill_color.red as f64,
self.palette.fill_color.green as f64,
self.palette.fill_color.blue as f64,
);
self.put_path_on_stack(path);
self.context.stroke();
}
}
}