2020-05-21 13:49:59 +00:00
|
|
|
use crate::backend::render::ShapeHandle;
|
|
|
|
use crate::bounding_box::BoundingBox;
|
2020-05-21 16:36:25 +00:00
|
|
|
use crate::context::RenderContext;
|
2020-05-21 13:49:59 +00:00
|
|
|
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
2020-05-21 16:36:25 +00:00
|
|
|
use std::cell::Cell;
|
2020-05-21 14:45:22 +00:00
|
|
|
use swf::{FillStyle, LineStyle, Twips};
|
2020-05-21 13:49:59 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Drawing {
|
2020-05-21 16:36:25 +00:00
|
|
|
render_handle: Cell<Option<ShapeHandle>>,
|
2020-05-21 13:49:59 +00:00
|
|
|
shape_bounds: BoundingBox,
|
|
|
|
edge_bounds: BoundingBox,
|
2020-05-21 16:36:25 +00:00
|
|
|
dirty: Cell<bool>,
|
2020-05-21 13:49:59 +00:00
|
|
|
fills: Vec<(FillStyle, Vec<DrawCommand>)>,
|
|
|
|
lines: Vec<(LineStyle, Vec<DrawCommand>)>,
|
|
|
|
current_fill: Option<(FillStyle, Vec<DrawCommand>)>,
|
|
|
|
current_line: Option<(LineStyle, Vec<DrawCommand>)>,
|
2020-05-21 14:45:22 +00:00
|
|
|
cursor: (Twips, Twips),
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Drawing {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Self {
|
2020-05-21 16:36:25 +00:00
|
|
|
render_handle: Cell::new(None),
|
2020-05-21 13:49:59 +00:00
|
|
|
shape_bounds: BoundingBox::default(),
|
|
|
|
edge_bounds: BoundingBox::default(),
|
2020-05-21 16:36:25 +00:00
|
|
|
dirty: Cell::new(false),
|
2020-05-21 13:49:59 +00:00
|
|
|
fills: Vec::new(),
|
|
|
|
lines: Vec::new(),
|
|
|
|
current_fill: None,
|
|
|
|
current_line: None,
|
2020-05-21 14:45:22 +00:00
|
|
|
cursor: (Twips::zero(), Twips::zero()),
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_fill_style(&mut self, style: Option<FillStyle>) {
|
|
|
|
// TODO: If current_fill is not closed, we should close it and also close current_line
|
|
|
|
|
|
|
|
if let Some(existing) = self.current_fill.take() {
|
|
|
|
self.fills.push(existing);
|
|
|
|
}
|
|
|
|
if let Some(style) = style {
|
2020-05-21 14:45:22 +00:00
|
|
|
self.current_fill = Some((
|
|
|
|
style,
|
|
|
|
vec![DrawCommand::MoveTo {
|
|
|
|
x: self.cursor.0,
|
|
|
|
y: self.cursor.1,
|
|
|
|
}],
|
|
|
|
));
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
self.dirty.set(true);
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.current_fill = None;
|
|
|
|
self.current_line = None;
|
|
|
|
self.fills.clear();
|
|
|
|
self.lines.clear();
|
|
|
|
self.edge_bounds = BoundingBox::default();
|
|
|
|
self.shape_bounds = BoundingBox::default();
|
2020-05-21 16:36:25 +00:00
|
|
|
self.dirty.set(true);
|
2020-05-21 14:45:22 +00:00
|
|
|
self.cursor = (Twips::zero(), Twips::zero());
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_line_style(&mut self, style: Option<LineStyle>) {
|
|
|
|
if let Some(existing) = self.current_line.take() {
|
|
|
|
self.lines.push(existing);
|
|
|
|
}
|
|
|
|
if let Some(style) = style {
|
2020-05-21 14:45:22 +00:00
|
|
|
self.current_line = Some((
|
|
|
|
style,
|
|
|
|
vec![DrawCommand::MoveTo {
|
|
|
|
x: self.cursor.0,
|
|
|
|
y: self.cursor.1,
|
|
|
|
}],
|
|
|
|
));
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
self.dirty.set(true);
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn draw_command(&mut self, command: DrawCommand) {
|
|
|
|
let mut include_last = false;
|
2020-05-21 18:34:48 +00:00
|
|
|
let stroke_width = if let Some((style, _)) = &self.current_line {
|
|
|
|
style.width
|
|
|
|
} else {
|
|
|
|
Twips::zero()
|
|
|
|
};
|
2020-05-21 13:49:59 +00:00
|
|
|
|
|
|
|
match command {
|
|
|
|
DrawCommand::MoveTo { .. } => {}
|
2020-05-21 18:34:48 +00:00
|
|
|
DrawCommand::LineTo { .. } => {
|
|
|
|
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
|
|
|
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::zero());
|
2020-05-21 13:49:59 +00:00
|
|
|
include_last = true;
|
|
|
|
}
|
2020-05-21 18:34:48 +00:00
|
|
|
DrawCommand::CurveTo { .. } => {
|
|
|
|
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
|
|
|
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::zero());
|
2020-05-21 13:49:59 +00:00
|
|
|
include_last = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 14:45:22 +00:00
|
|
|
self.cursor = command.end_point();
|
|
|
|
|
2020-05-21 13:49:59 +00:00
|
|
|
if let Some((_, commands)) = &mut self.current_line {
|
|
|
|
commands.push(command.clone());
|
|
|
|
}
|
|
|
|
if let Some((_, commands)) = &mut self.current_fill {
|
|
|
|
commands.push(command);
|
|
|
|
}
|
|
|
|
|
|
|
|
if include_last {
|
|
|
|
if let Some(command) = self
|
|
|
|
.current_fill
|
|
|
|
.as_ref()
|
2020-05-21 16:20:27 +00:00
|
|
|
.and_then(|(_, commands)| commands.last())
|
2020-05-21 13:49:59 +00:00
|
|
|
{
|
2020-05-21 18:34:48 +00:00
|
|
|
stretch_bounding_box(&mut self.shape_bounds, command, stroke_width);
|
|
|
|
stretch_bounding_box(&mut self.edge_bounds, command, Twips::zero());
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(command) = self
|
|
|
|
.current_line
|
|
|
|
.as_ref()
|
2020-05-21 16:20:27 +00:00
|
|
|
.and_then(|(_, commands)| commands.last())
|
2020-05-21 13:49:59 +00:00
|
|
|
{
|
2020-05-21 18:34:48 +00:00
|
|
|
stretch_bounding_box(&mut self.shape_bounds, command, stroke_width);
|
|
|
|
stretch_bounding_box(&mut self.edge_bounds, command, Twips::zero());
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
self.dirty.set(true);
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
pub fn render(&self, context: &mut RenderContext) {
|
|
|
|
if self.dirty.get() {
|
|
|
|
self.dirty.set(false);
|
2020-05-21 13:49:59 +00:00
|
|
|
let mut paths = Vec::new();
|
|
|
|
|
|
|
|
for (style, commands) in &self.fills {
|
|
|
|
paths.push(DrawPath::Fill {
|
|
|
|
style,
|
|
|
|
commands: commands.to_owned(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: If the current_fill is not closed, we should automatically close current_line
|
|
|
|
|
|
|
|
if let Some((style, commands)) = &self.current_fill {
|
|
|
|
paths.push(DrawPath::Fill {
|
|
|
|
style,
|
|
|
|
commands: commands.to_owned(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for (style, commands) in &self.lines {
|
|
|
|
paths.push(DrawPath::Stroke {
|
|
|
|
style,
|
|
|
|
commands: commands.to_owned(),
|
|
|
|
is_closed: false, // TODO: Determine this
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some((style, commands)) = &self.current_line {
|
|
|
|
paths.push(DrawPath::Stroke {
|
|
|
|
style,
|
|
|
|
commands: commands.to_owned(),
|
|
|
|
is_closed: false, // TODO: Determine this
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
let shape = DistilledShape {
|
|
|
|
paths,
|
|
|
|
shape_bounds: self.shape_bounds.clone(),
|
2020-05-21 18:34:48 +00:00
|
|
|
edge_bounds: self.edge_bounds.clone(),
|
2020-05-21 13:49:59 +00:00
|
|
|
id: 0,
|
|
|
|
};
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
if let Some(handle) = self.render_handle.get() {
|
2020-05-21 13:49:59 +00:00
|
|
|
context.renderer.replace_shape(shape, handle);
|
|
|
|
} else {
|
2020-05-21 16:36:25 +00:00
|
|
|
self.render_handle
|
|
|
|
.set(Some(context.renderer.register_shape(shape)));
|
2020-05-21 13:49:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-21 16:36:25 +00:00
|
|
|
if let Some(handle) = self.render_handle.get() {
|
2020-05-21 13:49:59 +00:00
|
|
|
context
|
|
|
|
.renderer
|
|
|
|
.render_shape(handle, context.transform_stack.transform());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn self_bounds(&self) -> BoundingBox {
|
|
|
|
self.shape_bounds.clone()
|
|
|
|
}
|
|
|
|
}
|
2020-05-21 16:20:27 +00:00
|
|
|
|
2020-05-21 18:34:48 +00:00
|
|
|
fn stretch_bounding_box(
|
|
|
|
bounding_box: &mut BoundingBox,
|
|
|
|
command: &DrawCommand,
|
|
|
|
stroke_width: Twips,
|
|
|
|
) {
|
|
|
|
let radius = stroke_width / 2;
|
2020-05-21 16:20:27 +00:00
|
|
|
match *command {
|
|
|
|
DrawCommand::MoveTo { x, y } => {
|
2020-05-21 18:34:48 +00:00
|
|
|
bounding_box.encompass(x - radius, y - radius);
|
|
|
|
bounding_box.encompass(x + radius, y + radius);
|
2020-05-21 16:20:27 +00:00
|
|
|
}
|
|
|
|
DrawCommand::LineTo { x, y } => {
|
2020-05-21 18:34:48 +00:00
|
|
|
bounding_box.encompass(x - radius, y - radius);
|
|
|
|
bounding_box.encompass(x + radius, y + radius);
|
2020-05-21 16:20:27 +00:00
|
|
|
}
|
|
|
|
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
2020-05-21 18:34:48 +00:00
|
|
|
bounding_box.encompass(x1 - radius, y1 - radius);
|
|
|
|
bounding_box.encompass(x1 + radius, y1 + radius);
|
|
|
|
bounding_box.encompass(x2 - radius, y2 - radius);
|
|
|
|
bounding_box.encompass(x2 + radius, y2 + radius);
|
2020-05-21 16:20:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|