swf_emitter/
display.rs

1use std::convert::TryInto;
2use std::io;
3
4use swf_types as ast;
5
6use crate::basic_data_types::emit_straight_s_rgba8;
7use crate::primitives::{emit_le_f32, emit_le_i16, emit_le_i32, emit_le_u16, emit_le_u32, emit_u8};
8
9pub fn emit_blend_mode<W: io::Write>(writer: &mut W, value: ast::BlendMode) -> io::Result<()> {
10  let code: u8 = match value {
11    ast::BlendMode::Normal => 0,
12    ast::BlendMode::Layer => 2,
13    ast::BlendMode::Multiply => 3,
14    ast::BlendMode::Screen => 4,
15    ast::BlendMode::Lighten => 5,
16    ast::BlendMode::Darken => 6,
17    ast::BlendMode::Difference => 7,
18    ast::BlendMode::Add => 8,
19    ast::BlendMode::Subtract => 9,
20    ast::BlendMode::Invert => 10,
21    ast::BlendMode::Alpha => 11,
22    ast::BlendMode::Erase => 12,
23    ast::BlendMode::Overlay => 13,
24    ast::BlendMode::Hardlight => 14,
25  };
26  emit_u8(writer, code)
27}
28
29pub fn emit_clip_actions_string<W: io::Write>(
30  writer: &mut W,
31  value: &[ast::ClipAction],
32  extended_events: bool,
33) -> io::Result<()> {
34  emit_le_u16(writer, 0)?; // Reserved
35
36  let mut event_union: ast::ClipEventFlags = ast::ClipEventFlags {
37    load: false,
38    enter_frame: false,
39    unload: false,
40    mouse_move: false,
41    mouse_down: false,
42    mouse_up: false,
43    key_down: false,
44    key_up: false,
45    data: false,
46    initialize: false,
47    press: false,
48    release: false,
49    release_outside: false,
50    roll_over: false,
51    roll_out: false,
52    drag_over: false,
53    drag_out: false,
54    key_press: false,
55    construct: false,
56  };
57
58  for clip_action in value {
59    event_union.load = event_union.load || clip_action.events.load;
60    event_union.enter_frame = event_union.enter_frame || clip_action.events.enter_frame;
61    event_union.unload = event_union.unload || clip_action.events.unload;
62    event_union.mouse_move = event_union.mouse_move || clip_action.events.mouse_move;
63    event_union.mouse_down = event_union.mouse_down || clip_action.events.mouse_down;
64    event_union.mouse_up = event_union.mouse_up || clip_action.events.mouse_up;
65    event_union.key_down = event_union.key_down || clip_action.events.key_down;
66    event_union.key_up = event_union.key_up || clip_action.events.key_up;
67    event_union.data = event_union.data || clip_action.events.data;
68    event_union.initialize = event_union.initialize || clip_action.events.initialize;
69    event_union.press = event_union.press || clip_action.events.press;
70    event_union.release = event_union.release || clip_action.events.release;
71    event_union.release_outside = event_union.release_outside || clip_action.events.release_outside;
72    event_union.roll_over = event_union.roll_over || clip_action.events.roll_over;
73    event_union.roll_out = event_union.roll_out || clip_action.events.roll_out;
74    event_union.drag_over = event_union.drag_over || clip_action.events.drag_over;
75    event_union.drag_out = event_union.drag_out || clip_action.events.drag_out;
76    event_union.key_press = event_union.key_press || clip_action.events.key_press;
77    event_union.construct = event_union.construct || clip_action.events.construct;
78  }
79
80  emit_clip_event_flags(writer, event_union, extended_events)?;
81  for clip_action in value {
82    emit_clip_actions(writer, clip_action, extended_events)?;
83  }
84  if extended_events {
85    emit_le_u32(writer, 0)
86  } else {
87    emit_le_u16(writer, 0)
88  }
89}
90
91pub fn emit_clip_event_flags<W: io::Write>(
92  writer: &mut W,
93  value: ast::ClipEventFlags,
94  extended_events: bool,
95) -> io::Result<()> {
96  #[allow(clippy::identity_op)]
97  let flags: u16 = 0
98    | (if value.load { 1 << 0 } else { 0 })
99    | (if value.enter_frame { 1 << 1 } else { 0 })
100    | (if value.unload { 1 << 2 } else { 0 })
101    | (if value.mouse_move { 1 << 3 } else { 0 })
102    | (if value.mouse_down { 1 << 4 } else { 0 })
103    | (if value.mouse_up { 1 << 5 } else { 0 })
104    | (if value.key_down { 1 << 6 } else { 0 })
105    | (if value.key_up { 1 << 7 } else { 0 })
106    | (if value.data { 1 << 8 } else { 0 })
107    | (if value.initialize { 1 << 9 } else { 0 })
108    | (if value.press { 1 << 10 } else { 0 })
109    | (if value.release { 1 << 11 } else { 0 })
110    | (if value.release_outside { 1 << 12 } else { 0 })
111    | (if value.roll_over { 1 << 13 } else { 0 })
112    | (if value.roll_out { 1 << 14 } else { 0 })
113    | (if value.drag_over { 1 << 15 } else { 0 });
114
115  if !extended_events {
116    return emit_le_u16(writer, flags);
117  }
118
119  let extended_flags: u32 = u32::from(flags)
120    | (if value.drag_out { 1 << 16 } else { 0 })
121    | (if value.key_press { 1 << 17 } else { 0 })
122    | (if value.construct { 1 << 18 } else { 0 });
123
124  emit_le_u32(writer, extended_flags)
125}
126
127pub fn emit_clip_actions<W: io::Write>(
128  writer: &mut W,
129  value: &ast::ClipAction,
130  extended_events: bool,
131) -> io::Result<()> {
132  use std::io::Write;
133
134  emit_clip_event_flags(writer, value.events, extended_events)?;
135
136  let mut action_writer = Vec::new();
137  if value.events.key_press {
138    match value.key_code {
139      Some(key_code) => emit_u8(&mut action_writer, key_code)?,
140      None => panic!("Expected key_code to be defined"),
141    }
142  }
143  action_writer.write_all(&value.actions)?;
144  emit_le_u32(writer, action_writer.len().try_into().unwrap())?;
145  writer.write_all(&action_writer)
146}
147
148pub fn emit_filter_list<W: io::Write>(writer: &mut W, value: &[ast::Filter]) -> io::Result<()> {
149  emit_u8(writer, value.len().try_into().unwrap())?;
150  for filter in value {
151    emit_filter(writer, filter)?;
152  }
153  Ok(())
154}
155
156pub fn emit_filter<W: io::Write>(writer: &mut W, value: &ast::Filter) -> io::Result<()> {
157  match value {
158    ast::Filter::Bevel(filter) => {
159      emit_u8(writer, 3)?;
160      emit_bevel_filter(writer, filter)
161    }
162    ast::Filter::Blur(filter) => {
163      emit_u8(writer, 1)?;
164      emit_blur_filter(writer, filter)
165    }
166    ast::Filter::Convolution(filter) => {
167      emit_u8(writer, 5)?;
168      emit_convolution_filter(writer, filter)
169    }
170    ast::Filter::ColorMatrix(filter) => {
171      emit_u8(writer, 6)?;
172      emit_color_matrix_filter(writer, filter)
173    }
174    ast::Filter::DropShadow(filter) => {
175      emit_u8(writer, 0)?;
176      emit_drop_shadow_filter(writer, filter)
177    }
178    ast::Filter::Glow(filter) => {
179      emit_u8(writer, 2)?;
180      emit_glow_filter(writer, filter)
181    }
182    ast::Filter::GradientBevel(filter) => {
183      emit_u8(writer, 7)?;
184      emit_gradient_bevel_filter(writer, filter)
185    }
186    ast::Filter::GradientGlow(filter) => {
187      emit_u8(writer, 4)?;
188      emit_gradient_glow_filter(writer, filter)
189    }
190  }
191}
192
193pub fn emit_bevel_filter<W: io::Write>(writer: &mut W, value: &ast::filters::Bevel) -> io::Result<()> {
194  assert!(value.passes < 0x10);
195
196  emit_straight_s_rgba8(writer, value.shadow_color)?;
197  emit_straight_s_rgba8(writer, value.highlight_color)?;
198  emit_le_i32(writer, value.blur_x.epsilons)?;
199  emit_le_i32(writer, value.blur_y.epsilons)?;
200  emit_le_i32(writer, value.angle.epsilons)?;
201  emit_le_i32(writer, value.distance.epsilons)?;
202  emit_le_i16(writer, value.strength.epsilons)?;
203
204  #[allow(clippy::identity_op)]
205  let flags: u8 = 0
206    | ((value.passes & 0x0f) << 0)
207    | (if value.on_top { 1 << 4 } else { 0 })
208    | (if value.composite_source { 1 << 5 } else { 0 })
209    | (if value.knockout { 1 << 6 } else { 0 })
210    | (if value.inner { 1 << 7 } else { 0 });
211  emit_u8(writer, flags)
212}
213
214pub fn emit_blur_filter<W: io::Write>(writer: &mut W, value: &ast::filters::Blur) -> io::Result<()> {
215  assert!(value.passes < 0x20);
216
217  emit_le_i32(writer, value.blur_x.epsilons)?;
218  emit_le_i32(writer, value.blur_y.epsilons)?;
219
220  #[allow(clippy::identity_op)]
221  let flags: u8 = 0
222    // Skip bits [0, 2]
223    | ((value.passes & 0x1f) << 3);
224  emit_u8(writer, flags)
225}
226
227pub fn emit_color_matrix_filter<W: io::Write>(writer: &mut W, value: &ast::filters::ColorMatrix) -> io::Result<()> {
228  assert_eq!(value.matrix.len(), 20);
229  for coefficient in &value.matrix {
230    emit_le_f32(writer, *coefficient)?;
231  }
232  Ok(())
233}
234
235pub fn emit_convolution_filter<W: io::Write>(writer: &mut W, value: &ast::filters::Convolution) -> io::Result<()> {
236  assert!(value.matrix_width < 256);
237  assert!(value.matrix_height < 256);
238  assert_eq!(value.matrix.len(), value.matrix_width * value.matrix_height);
239
240  emit_u8(writer, value.matrix_width.try_into().unwrap())?;
241  emit_u8(writer, value.matrix_height.try_into().unwrap())?;
242  emit_le_f32(writer, value.divisor)?;
243  emit_le_f32(writer, value.bias)?;
244  for coefficient in &value.matrix {
245    emit_le_f32(writer, *coefficient)?;
246  }
247  emit_straight_s_rgba8(writer, value.default_color)?;
248
249  #[allow(clippy::identity_op)]
250  let flags: u8 = 0
251    | (if value.preserve_alpha { 1 << 0 } else { 0 })
252    | (if value.clamp { 1 << 1 } else { 0 });
253  emit_u8(writer, flags)
254}
255
256pub fn emit_drop_shadow_filter<W: io::Write>(writer: &mut W, value: &ast::filters::DropShadow) -> io::Result<()> {
257  assert!(value.passes < 0x20);
258
259  emit_straight_s_rgba8(writer, value.color)?;
260  emit_le_i32(writer, value.blur_x.epsilons)?;
261  emit_le_i32(writer, value.blur_y.epsilons)?;
262  emit_le_i32(writer, value.angle.epsilons)?;
263  emit_le_i32(writer, value.distance.epsilons)?;
264  emit_le_i16(writer, value.strength.epsilons)?;
265
266  #[allow(clippy::identity_op)]
267  let flags: u8 = 0
268    | ((value.passes & 0x1f) << 0)
269    | (if value.composite_source { 1 << 5 } else { 0 })
270    | (if value.knockout { 1 << 6 } else { 0 })
271    | (if value.inner { 1 << 7 } else { 0 });
272  emit_u8(writer, flags)
273}
274
275pub fn emit_glow_filter<W: io::Write>(writer: &mut W, value: &ast::filters::Glow) -> io::Result<()> {
276  assert!(value.passes < 0x20);
277
278  emit_straight_s_rgba8(writer, value.color)?;
279  emit_le_i32(writer, value.blur_x.epsilons)?;
280  emit_le_i32(writer, value.blur_y.epsilons)?;
281  emit_le_i16(writer, value.strength.epsilons)?;
282
283  #[allow(clippy::identity_op)]
284  let flags: u8 = 0
285    | ((value.passes & 0x1f) << 0)
286    | (if value.composite_source { 1 << 5 } else { 0 })
287    | (if value.knockout { 1 << 6 } else { 0 })
288    | (if value.inner { 1 << 7 } else { 0 });
289  emit_u8(writer, flags)
290}
291
292pub fn emit_gradient_bevel_filter<W: io::Write>(writer: &mut W, value: &ast::filters::GradientBevel) -> io::Result<()> {
293  assert!(value.passes < 0x10);
294  assert!(value.gradient.len() < 256);
295
296  emit_u8(writer, value.gradient.len().try_into().unwrap())?;
297  for color_stop in &value.gradient {
298    emit_straight_s_rgba8(writer, color_stop.color)?;
299  }
300  for color_stop in &value.gradient {
301    emit_u8(writer, color_stop.ratio)?;
302  }
303  emit_le_i32(writer, value.blur_x.epsilons)?;
304  emit_le_i32(writer, value.blur_y.epsilons)?;
305  emit_le_i32(writer, value.angle.epsilons)?;
306  emit_le_i32(writer, value.distance.epsilons)?;
307  emit_le_i16(writer, value.strength.epsilons)?;
308
309  #[allow(clippy::identity_op)]
310  let flags: u8 = 0
311    | ((value.passes & 0x0f) << 0)
312    | (if value.on_top { 1 << 4 } else { 0 })
313    | (if value.composite_source { 1 << 5 } else { 0 })
314    | (if value.knockout { 1 << 6 } else { 0 })
315    | (if value.inner { 1 << 7 } else { 0 });
316  emit_u8(writer, flags)
317}
318
319pub fn emit_gradient_glow_filter<W: io::Write>(writer: &mut W, value: &ast::filters::GradientGlow) -> io::Result<()> {
320  assert!(value.passes < 0x10);
321  assert!(value.gradient.len() < 256);
322
323  emit_u8(writer, value.gradient.len().try_into().unwrap())?;
324  for color_stop in &value.gradient {
325    emit_straight_s_rgba8(writer, color_stop.color)?;
326  }
327  for color_stop in &value.gradient {
328    emit_u8(writer, color_stop.ratio)?;
329  }
330  emit_le_i32(writer, value.blur_x.epsilons)?;
331  emit_le_i32(writer, value.blur_y.epsilons)?;
332  emit_le_i32(writer, value.angle.epsilons)?;
333  emit_le_i32(writer, value.distance.epsilons)?;
334  emit_le_i16(writer, value.strength.epsilons)?;
335
336  #[allow(clippy::identity_op)]
337  let flags: u8 = 0
338    | ((value.passes & 0x0f) << 0)
339    | (if value.on_top { 1 << 4 } else { 0 })
340    | (if value.composite_source { 1 << 5 } else { 0 })
341    | (if value.knockout { 1 << 6 } else { 0 })
342    | (if value.inner { 1 << 7 } else { 0 });
343  emit_u8(writer, flags)
344}