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)?; 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 | ((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}