core: Move drawing api out from `movie_clip` into `drawing`
This commit is contained in:
parent
4f69566f77
commit
99574cfa72
|
@ -2,16 +2,16 @@
|
|||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
|
||||
use crate::backend::render::ShapeHandle;
|
||||
use crate::character::Character;
|
||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||
use crate::display_object::{
|
||||
Bitmap, Button, DisplayObjectBase, EditText, Graphic, MorphShapeStatic, TDisplayObject, Text,
|
||||
};
|
||||
use crate::drawing::Drawing;
|
||||
use crate::events::{ButtonKeyCode, ClipEvent};
|
||||
use crate::font::Font;
|
||||
use crate::prelude::*;
|
||||
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
use crate::shape_utils::DrawCommand;
|
||||
use crate::tag_utils::{self, DecodeResult, SwfMovie, SwfSlice, SwfStream};
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
|
@ -46,14 +46,7 @@ pub struct MovieClipData<'gc> {
|
|||
clip_actions: SmallVec<[ClipAction; 2]>,
|
||||
flags: EnumSet<MovieClipFlags>,
|
||||
avm1_constructor: Option<Object<'gc>>,
|
||||
custom_shape: Option<ShapeHandle>,
|
||||
custom_shape_bounds: BoundingBox,
|
||||
custom_edge_bounds: BoundingBox,
|
||||
dirty_shape: bool,
|
||||
custom_fills: Vec<(FillStyle, Vec<DrawCommand>)>,
|
||||
custom_lines: Vec<(LineStyle, Vec<DrawCommand>)>,
|
||||
current_fill: Option<(FillStyle, Vec<DrawCommand>)>,
|
||||
current_line: Option<(LineStyle, Vec<DrawCommand>)>,
|
||||
drawing: Drawing,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -72,14 +65,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: EnumSet::empty(),
|
||||
avm1_constructor: None,
|
||||
custom_shape: None,
|
||||
custom_shape_bounds: BoundingBox::default(),
|
||||
custom_edge_bounds: BoundingBox::default(),
|
||||
dirty_shape: false,
|
||||
custom_fills: Vec::new(),
|
||||
custom_lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
drawing: Drawing::new(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -112,14 +98,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: MovieClipFlags::Playing.into(),
|
||||
avm1_constructor: None,
|
||||
custom_shape: None,
|
||||
custom_shape_bounds: BoundingBox::default(),
|
||||
custom_edge_bounds: BoundingBox::default(),
|
||||
dirty_shape: false,
|
||||
custom_fills: Vec::new(),
|
||||
custom_lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
drawing: Drawing::new(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -585,28 +564,12 @@ impl<'gc> MovieClip<'gc> {
|
|||
style: Option<FillStyle>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
|
||||
// TODO: If current_fill is not closed, we should close it and also close current_line
|
||||
|
||||
if let Some(existing) = mc.current_fill.take() {
|
||||
mc.custom_fills.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
mc.current_fill = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
mc.drawing.set_fill_style(style);
|
||||
}
|
||||
|
||||
pub fn clear(self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.current_fill = None;
|
||||
mc.current_line = None;
|
||||
mc.custom_fills.clear();
|
||||
mc.custom_lines.clear();
|
||||
mc.custom_edge_bounds = BoundingBox::default();
|
||||
mc.custom_shape_bounds = BoundingBox::default();
|
||||
mc.dirty_shape = true;
|
||||
mc.drawing.clear();
|
||||
}
|
||||
|
||||
pub fn set_line_style(
|
||||
|
@ -615,94 +578,12 @@ impl<'gc> MovieClip<'gc> {
|
|||
style: Option<LineStyle>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
|
||||
if let Some(existing) = mc.current_line.take() {
|
||||
mc.custom_lines.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
mc.current_line = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
mc.drawing.set_line_style(style);
|
||||
}
|
||||
|
||||
pub fn draw_command(self, context: &mut UpdateContext<'_, 'gc, '_>, command: DrawCommand) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
|
||||
let mut include_last = false;
|
||||
|
||||
match command {
|
||||
DrawCommand::MoveTo { .. } => {}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
include_last = true;
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
include_last = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, commands)) = &mut mc.current_line {
|
||||
commands.push(command.clone());
|
||||
}
|
||||
if let Some((_, commands)) = &mut mc.current_fill {
|
||||
commands.push(command);
|
||||
}
|
||||
|
||||
if include_last {
|
||||
if let Some(command) = mc
|
||||
.current_fill
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(command) = mc
|
||||
.current_line
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
mc.drawing.draw_command(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -742,70 +623,18 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
mc.run_clip_postaction((*self).into(), context, ClipEvent::Load);
|
||||
}
|
||||
|
||||
if mc.dirty_shape {
|
||||
mc.dirty_shape = false;
|
||||
let mut paths = Vec::new();
|
||||
|
||||
for (style, commands) in &mc.custom_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)) = &mc.current_fill {
|
||||
paths.push(DrawPath::Fill {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
for (style, commands) in &mc.custom_lines {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
if let Some((style, commands)) = &mc.current_line {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
let shape = DistilledShape {
|
||||
paths,
|
||||
shape_bounds: mc.custom_shape_bounds.clone(),
|
||||
edge_bounds: mc.custom_shape_bounds.clone(),
|
||||
id: mc.id(),
|
||||
};
|
||||
|
||||
if let Some(handle) = mc.custom_shape {
|
||||
context.renderer.replace_shape(shape, handle);
|
||||
} else {
|
||||
mc.custom_shape = Some(context.renderer.register_shape(shape));
|
||||
}
|
||||
}
|
||||
mc.drawing.run_frame(context);
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
context.transform_stack.push(&*self.transform());
|
||||
crate::display_object::render_children(context, &self.0.read().children);
|
||||
if let Some(handle) = self.0.read().custom_shape {
|
||||
context
|
||||
.renderer
|
||||
.render_shape(handle, context.transform_stack.transform());
|
||||
}
|
||||
self.0.read().drawing.render(context);
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
self.0.read().custom_shape_bounds.clone()
|
||||
self.0.read().drawing.self_bounds()
|
||||
}
|
||||
|
||||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
use crate::backend::render::ShapeHandle;
|
||||
use crate::bounding_box::BoundingBox;
|
||||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
use swf::{FillStyle, LineStyle};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Drawing {
|
||||
render_handle: Option<ShapeHandle>,
|
||||
shape_bounds: BoundingBox,
|
||||
edge_bounds: BoundingBox,
|
||||
dirty: bool,
|
||||
fills: Vec<(FillStyle, Vec<DrawCommand>)>,
|
||||
lines: Vec<(LineStyle, Vec<DrawCommand>)>,
|
||||
current_fill: Option<(FillStyle, Vec<DrawCommand>)>,
|
||||
current_line: Option<(LineStyle, Vec<DrawCommand>)>,
|
||||
}
|
||||
|
||||
impl Drawing {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
render_handle: None,
|
||||
shape_bounds: BoundingBox::default(),
|
||||
edge_bounds: BoundingBox::default(),
|
||||
dirty: false,
|
||||
fills: Vec::new(),
|
||||
lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
self.current_fill = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
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();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
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 {
|
||||
self.current_line = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn draw_command(&mut self, command: DrawCommand) {
|
||||
let mut include_last = false;
|
||||
|
||||
match command {
|
||||
DrawCommand::MoveTo { .. } => {}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
self.shape_bounds.encompass(x, y);
|
||||
self.edge_bounds.encompass(x, y);
|
||||
include_last = true;
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
self.shape_bounds.encompass(x1, y1);
|
||||
self.shape_bounds.encompass(x2, y2);
|
||||
self.edge_bounds.encompass(x1, y1);
|
||||
self.edge_bounds.encompass(x2, y2);
|
||||
include_last = true;
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
self.shape_bounds.encompass(x, y);
|
||||
self.edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
self.shape_bounds.encompass(x, y);
|
||||
self.edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
self.shape_bounds.encompass(x1, y1);
|
||||
self.shape_bounds.encompass(x2, y2);
|
||||
self.edge_bounds.encompass(x1, y1);
|
||||
self.edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(command) = self
|
||||
.current_line
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
self.shape_bounds.encompass(x, y);
|
||||
self.edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
self.shape_bounds.encompass(x, y);
|
||||
self.edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
self.shape_bounds.encompass(x1, y1);
|
||||
self.shape_bounds.encompass(x2, y2);
|
||||
self.edge_bounds.encompass(x1, y1);
|
||||
self.edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
pub fn run_frame(&mut self, context: &mut UpdateContext) {
|
||||
if self.dirty {
|
||||
self.dirty = false;
|
||||
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(),
|
||||
edge_bounds: self.shape_bounds.clone(),
|
||||
id: 0,
|
||||
};
|
||||
|
||||
if let Some(handle) = self.render_handle {
|
||||
context.renderer.replace_shape(shape, handle);
|
||||
} else {
|
||||
self.render_handle = Some(context.renderer.register_shape(shape));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(&self, context: &mut RenderContext) {
|
||||
if let Some(handle) = self.render_handle {
|
||||
context
|
||||
.renderer
|
||||
.render_shape(handle, context.transform_stack.transform());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn self_bounds(&self) -> BoundingBox {
|
||||
self.shape_bounds.clone()
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ mod bounding_box;
|
|||
mod character;
|
||||
pub mod color_transform;
|
||||
mod context;
|
||||
mod drawing;
|
||||
pub mod events;
|
||||
mod font;
|
||||
mod library;
|
||||
|
|
Loading…
Reference in New Issue