1use alpha2_gpu::CapabilityTier;
4use alpha2_shaders::{
5 CHROMA_KEY_BODY, EFFECT_HELPERS, EXPANDED_EFFECT, FOCUS_COMBINE, GAUSSIAN_BLUR,
6 GL_TRANSITION_ENTRY, GLOW_COMBINE, GLOW_THRESHOLD, LUT_3D_BODY, SHADOW_COMBINE, SHADOW_OFFSET,
7 TRANSITION_HELPERS, UNSHARP_COMBINE,
8};
9
10use crate::{
11 EffectDef, EffectId, EffectKind, EffectResourceKind, InputSpec, ParamSpec, ParamType,
12 ParamUnit, ParamValue, Renderer, ResourceSpec, ShaderDiagnostics,
13};
14
15mod effects;
16mod transitions;
17
18pub(crate) use effects::blur::{
19 directional_blur_definition, tilt_shift_definition, unsharp_mask_definition,
20};
21pub(crate) use effects::stylize::drop_shadow_definition;
22
23pub const CHROMA_KEY_ID: &str = "alpha2.effect.chroma-key";
25
26pub const GAUSSIAN_BLUR_ID: &str = "alpha2.effect.gaussian-blur";
28
29pub(crate) const GAUSSIAN_BLUR_PASS_ID: &str = "alpha2.internal.gaussian-blur-pass";
31
32pub const GLOW_ID: &str = "alpha2.effect.glow";
34
35pub(crate) const GLOW_THRESHOLD_ID: &str = "alpha2.internal.glow-threshold";
37
38pub(crate) const GLOW_COMBINE_ID: &str = "alpha2.internal.glow-combine";
40
41pub(crate) const DIRECTIONAL_BLUR_ID: &str = "alpha2.effect.directional-blur";
43
44pub(crate) const UNSHARP_MASK_ID: &str = "alpha2.effect.unsharp-mask";
46
47pub(crate) const UNSHARP_COMBINE_ID: &str = "alpha2.internal.unsharp-combine";
49
50pub(crate) const TILT_SHIFT_ID: &str = "alpha2.effect.tilt-shift";
52
53pub(crate) const FOCUS_COMBINE_ID: &str = "alpha2.internal.focus-combine";
55
56pub(crate) const DROP_SHADOW_ID: &str = "alpha2.effect.drop-shadow";
58
59pub(crate) const SHADOW_OFFSET_ID: &str = "alpha2.internal.shadow-offset";
61
62pub(crate) const SHADOW_COMBINE_ID: &str = "alpha2.internal.shadow-combine";
64
65pub(crate) const ALPHA2_EFFECT_LICENSE: &str = "MIT; Copyright Alpha2 contributors";
67
68pub const LUT_3D_ID: &str = "alpha2.effect.lut-3d";
70
71pub(crate) const GL_TRANSITIONS_BASE: &str = "https://github.com/gl-transitions/gl-transitions/blob/902218a1b63773ac0d0d9f491951da3392365bfe/transitions/";
73
74#[must_use]
76pub fn builtin_effect_definitions() -> Vec<EffectDef> {
77 builtin_effect_groups()
78 .into_iter()
79 .flat_map(|(_, definitions)| definitions)
80 .collect()
81}
82
83pub(crate) fn builtin_effect_groups() -> Vec<(&'static str, Vec<EffectDef>)> {
85 vec![
86 ("Color", effects::color::definitions()),
87 ("Keying", effects::keying::definitions()),
88 ("Blur & Sharpen", effects::blur::definitions()),
89 ("Stylize", effects::stylize::definitions()),
90 ("Distort", effects::distort::definitions()),
91 ]
92}
93
94#[must_use]
96pub fn builtin_transition_definitions() -> Vec<EffectDef> {
97 builtin_transition_groups()
98 .into_iter()
99 .flat_map(|(_, definitions)| definitions)
100 .collect()
101}
102
103pub(crate) fn builtin_transition_groups() -> Vec<(&'static str, Vec<EffectDef>)> {
105 vec![
106 ("Classic", transitions::classic::definitions()),
107 ("Wipes", transitions::wipes::definitions()),
108 ("Stylized", transitions::stylized::definitions()),
109 ("Shapes", transitions::shapes::definitions()),
110 ("3D", transitions::three_d::definitions()),
111 ("Zoom & Blur", transitions::zoom_blur::definitions()),
112 ("Glitch", transitions::glitch::definitions()),
113 ]
114}
115
116#[must_use]
123pub fn chroma_key_definition() -> EffectDef {
124 EffectDef {
125 id: CHROMA_KEY_ID.into(),
126 version: 1,
127 kind: EffectKind::Effect,
128 inputs: Vec::new(),
129 params: vec![
130 color("key_color", [0.0, 1.0, 0.0, 1.0]),
131 float("threshold", 0.35, 0.0, 3.0_f32.sqrt()),
132 float("softness", 0.1, f32::EPSILON, 1.0),
133 float("spill_suppression", 0.2, 0.0, 1.0),
134 choice("distance_mode", 0, &["rgb", "chroma"]),
135 ],
136 resources: Vec::new(),
137 wgsl_body: effect_body(CHROMA_KEY_BODY),
138 tier_min: CapabilityTier::TierB,
139 license: Some(ALPHA2_EFFECT_LICENSE.into()),
140 }
141}
142
143#[must_use]
148pub fn gaussian_blur_definition() -> EffectDef {
149 EffectDef {
150 id: GAUSSIAN_BLUR_ID.into(),
151 version: 1,
152 kind: EffectKind::Effect,
153 inputs: Vec::new(),
154 params: vec![float_step("radius", 0.012, 0.0, 1.0, 0.001)],
155 resources: Vec::new(),
156 wgsl_body: effect_body(EXPANDED_EFFECT),
157 tier_min: CapabilityTier::TierB,
158 license: Some(ALPHA2_EFFECT_LICENSE.into()),
159 }
160}
161
162pub(crate) fn gaussian_blur_pass_definition() -> EffectDef {
163 EffectDef {
164 id: GAUSSIAN_BLUR_PASS_ID.into(),
165 version: 1,
166 kind: EffectKind::Effect,
167 inputs: Vec::new(),
168 params: vec![
169 vec2("step", [0.0, 0.0]),
170 float_step(
171 "sigma",
172 1.0,
173 0.0,
174 crate::gaussian_blur::MAX_PASS_SIGMA_TAPS,
175 0.001,
176 ),
177 ],
178 resources: Vec::new(),
179 wgsl_body: effect_body(GAUSSIAN_BLUR),
180 tier_min: CapabilityTier::TierB,
181 license: Some(ALPHA2_EFFECT_LICENSE.into()),
182 }
183}
184
185#[must_use]
190pub fn glow_definition() -> EffectDef {
191 EffectDef {
192 id: GLOW_ID.into(),
193 version: 1,
194 kind: EffectKind::Effect,
195 inputs: Vec::new(),
196 params: vec![
197 float("threshold", 0.6, 0.0, 1.0),
198 float("intensity", 0.8, 0.0, 3.0),
199 float("radius", 0.012, 0.0, 0.06),
200 ],
201 resources: Vec::new(),
202 wgsl_body: effect_body(EXPANDED_EFFECT),
203 tier_min: CapabilityTier::TierB,
204 license: Some(ALPHA2_EFFECT_LICENSE.into()),
205 }
206}
207
208fn glow_threshold_definition() -> EffectDef {
209 EffectDef {
210 id: GLOW_THRESHOLD_ID.into(),
211 ..simple_effect(
212 "glow-threshold",
213 GLOW_THRESHOLD,
214 vec![float("threshold", 0.6, 0.0, 1.0)],
215 )
216 }
217}
218
219fn glow_combine_definition() -> EffectDef {
221 EffectDef {
222 id: GLOW_COMBINE_ID.into(),
223 version: 1,
224 kind: EffectKind::Transition,
225 inputs: Vec::new(),
226 params: vec![float("intensity", 0.8, 0.0, 3.0)],
227 resources: Vec::new(),
228 wgsl_body: effect_body(GLOW_COMBINE),
229 tier_min: CapabilityTier::TierB,
230 license: Some(ALPHA2_EFFECT_LICENSE.into()),
231 }
232}
233
234fn unsharp_combine_definition() -> EffectDef {
235 EffectDef {
236 id: UNSHARP_COMBINE_ID.into(),
237 version: 1,
238 kind: EffectKind::Transition,
239 inputs: Vec::new(),
240 params: vec![
241 float("amount", 1.0, 0.0, 5.0),
242 float("threshold", 0.0, 0.0, 1.0),
243 ],
244 resources: Vec::new(),
245 wgsl_body: effect_body(UNSHARP_COMBINE),
246 tier_min: CapabilityTier::TierB,
247 license: Some(ALPHA2_EFFECT_LICENSE.into()),
248 }
249}
250
251fn focus_combine_definition() -> EffectDef {
252 EffectDef {
253 id: FOCUS_COMBINE_ID.into(),
254 version: 1,
255 kind: EffectKind::Effect,
256 inputs: vec![
257 InputSpec {
258 name: "soft".into(),
259 },
260 InputSpec {
261 name: "blurred".into(),
262 },
263 ],
264 params: vec![
265 float("position", 0.5, 0.0, 1.0),
266 degrees("angle", 0.0, -90.0, 90.0),
267 float("focus", 0.15, 0.0, 1.0),
268 float_step("falloff", 0.25, 0.001, 1.0, 0.005),
269 ],
270 resources: Vec::new(),
271 wgsl_body: effect_body(FOCUS_COMBINE),
272 tier_min: CapabilityTier::TierB,
273 license: Some(ALPHA2_EFFECT_LICENSE.into()),
274 }
275}
276
277fn shadow_offset_definition() -> EffectDef {
278 EffectDef {
279 id: SHADOW_OFFSET_ID.into(),
280 ..simple_effect(
281 "shadow-offset",
282 SHADOW_OFFSET,
283 vec![
284 vec2("offset", [0.0, 0.0]),
285 color("color", [0.0, 0.0, 0.0, 1.0]),
286 float("opacity", 0.6, 0.0, 1.0),
287 ],
288 )
289 }
290}
291
292fn shadow_combine_definition() -> EffectDef {
293 EffectDef {
294 id: SHADOW_COMBINE_ID.into(),
295 version: 1,
296 kind: EffectKind::Transition,
297 inputs: Vec::new(),
298 params: vec![boolean("shadow_only", false)],
299 resources: Vec::new(),
300 wgsl_body: effect_body(SHADOW_COMBINE),
301 tier_min: CapabilityTier::TierB,
302 license: Some(ALPHA2_EFFECT_LICENSE.into()),
303 }
304}
305
306#[must_use]
308pub fn lut_3d_definition() -> EffectDef {
309 EffectDef {
310 id: LUT_3D_ID.into(),
311 version: 1,
312 kind: EffectKind::Effect,
313 inputs: Vec::new(),
314 params: vec![float("strength", 1.0, 0.0, 1.0)],
315 resources: vec![ResourceSpec {
316 name: "lut".into(),
317 kind: EffectResourceKind::Lut3d,
318 }],
319 wgsl_body: effect_body(LUT_3D_BODY),
320 tier_min: CapabilityTier::TierB,
321 license: Some(ALPHA2_EFFECT_LICENSE.into()),
322 }
323}
324
325pub(crate) fn internal_effect_definitions() -> Vec<EffectDef> {
327 let mut definitions = vec![
328 gaussian_blur_pass_definition(),
329 glow_threshold_definition(),
330 glow_combine_definition(),
331 shadow_offset_definition(),
332 shadow_combine_definition(),
333 unsharp_combine_definition(),
334 focus_combine_definition(),
335 crate::masking::track_matte_definition(),
336 crate::generator::generator_definition(),
337 ];
338 definitions.extend(crate::masking::mask_combine_definitions());
339 definitions
340}
341
342impl Renderer {
343 fn register_builtin_definition(
344 &mut self,
345 definition: EffectDef,
346 ) -> Result<EffectId, ShaderDiagnostics> {
347 let id = self.register_lazily(definition)?;
348 self.effects.mark_builtin(id.clone());
349 Ok(id)
350 }
351
352 pub fn register_builtins(&mut self) -> Result<Vec<EffectId>, ShaderDiagnostics> {
358 for definition in internal_effect_definitions() {
359 let internal = self.register_builtin_definition(definition)?;
360 self.effects.mark_internal(internal);
361 }
362 let mut definitions = builtin_effect_definitions();
363 definitions.extend(builtin_transition_definitions());
364 let mut ids = Vec::with_capacity(definitions.len());
365 for definition in definitions {
366 ids.push(self.register_builtin_definition(definition)?);
367 }
368 Ok(ids)
369 }
370
371 pub fn register_builtin_chroma_key(&mut self) -> Result<EffectId, ShaderDiagnostics> {
376 self.register_builtin_definition(chroma_key_definition())
377 }
378
379 pub fn register_builtin_gaussian_blur(&mut self) -> Result<EffectId, ShaderDiagnostics> {
384 let internal = self.register_builtin_definition(gaussian_blur_pass_definition())?;
385 self.effects.mark_internal(internal);
386 self.register_builtin_definition(gaussian_blur_definition())
387 }
388
389 pub fn register_builtin_lut_3d(&mut self) -> Result<EffectId, ShaderDiagnostics> {
394 self.register_builtin_definition(lut_3d_definition())
395 }
396
397 pub fn register_builtin_transitions(&mut self) -> Result<Vec<EffectId>, ShaderDiagnostics> {
402 let definitions = builtin_transition_definitions();
403 let mut ids = Vec::with_capacity(definitions.len());
404 for definition in definitions {
405 ids.push(self.register_builtin_definition(definition)?);
406 }
407 Ok(ids)
408 }
409}
410
411pub(crate) fn effect_body(body: &str) -> String {
412 format!("{EFFECT_HELPERS}\n{body}")
413}
414
415pub(crate) fn simple_effect(name: &str, body: &str, params: Vec<ParamSpec>) -> EffectDef {
417 EffectDef {
418 id: format!("alpha2.effect.{name}"),
419 version: 1,
420 kind: EffectKind::Effect,
421 inputs: Vec::new(),
422 params,
423 resources: Vec::new(),
424 wgsl_body: effect_body(body),
425 tier_min: CapabilityTier::TierB,
426 license: Some(ALPHA2_EFFECT_LICENSE.into()),
427 }
428}
429
430pub(crate) fn transition(
432 name: &str,
433 body: &str,
434 source: &str,
435 author: &str,
436 params: Vec<ParamSpec>,
437) -> EffectDef {
438 EffectDef {
439 id: format!("alpha2.transition.{name}"),
440 version: 1,
441 kind: EffectKind::Transition,
442 inputs: Vec::new(),
443 params,
444 resources: Vec::new(),
445 wgsl_body: format!("{TRANSITION_HELPERS}\n{GL_TRANSITION_ENTRY}\n{body}"),
446 tier_min: CapabilityTier::TierB,
447 license: Some(format!(
448 "MIT; Author: {author}; Source: {GL_TRANSITIONS_BASE}{source}"
449 )),
450 }
451}
452
453pub(crate) fn original_transition(name: &str, body: &str, params: Vec<ParamSpec>) -> EffectDef {
456 EffectDef {
457 id: format!("alpha2.transition.{name}"),
458 version: 1,
459 kind: EffectKind::Transition,
460 inputs: Vec::new(),
461 params,
462 resources: Vec::new(),
463 wgsl_body: format!("{TRANSITION_HELPERS}\n{body}"),
464 tier_min: CapabilityTier::TierB,
465 license: Some(ALPHA2_EFFECT_LICENSE.into()),
466 }
467}
468
469pub(crate) fn float(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
470 let span = max - min;
471 let step = if span > 50.0 {
472 1.0
473 } else if span <= 0.1 {
474 0.001
475 } else {
476 0.01
477 };
478 float_step(name, default, min, max, step)
479}
480
481pub(crate) fn float_step(name: &str, default: f32, min: f32, max: f32, step: f32) -> ParamSpec {
482 ParamSpec {
483 name: name.into(),
484 param_type: ParamType::Float,
485 default: ParamValue::Float(default),
486 min: Some(min),
487 max: Some(max),
488 step: Some(step),
489 unit: ParamUnit::Normalized,
490 enum_values: Vec::new(),
491 }
492}
493
494pub(crate) fn degrees(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
495 ParamSpec {
496 unit: ParamUnit::Degrees,
497 step: Some(1.0),
498 ..float(name, default, min, max)
499 }
500}
501
502pub(crate) fn vec2(name: &str, default: [f32; 2]) -> ParamSpec {
503 ParamSpec {
504 name: name.into(),
505 param_type: ParamType::Vec2,
506 default: ParamValue::Vec2(default),
507 min: None,
508 max: None,
509 step: None,
510 unit: ParamUnit::Normalized,
511 enum_values: Vec::new(),
512 }
513}
514
515pub(crate) fn vec2_range(
516 name: &str,
517 default: [f32; 2],
518 min: f32,
519 max: f32,
520 step: f32,
521) -> ParamSpec {
522 ParamSpec {
523 min: Some(min),
524 max: Some(max),
525 step: Some(step),
526 ..vec2(name, default)
527 }
528}
529
530pub(crate) fn color(name: &str, default: [f32; 4]) -> ParamSpec {
531 ParamSpec {
532 name: name.into(),
533 param_type: ParamType::Color,
534 default: ParamValue::Color(default),
535 min: None,
536 max: None,
537 step: None,
538 unit: ParamUnit::Color,
539 enum_values: Vec::new(),
540 }
541}
542
543pub(crate) fn thresholds(name: &str, default: [f32; 4]) -> ParamSpec {
545 ParamSpec {
546 name: name.into(),
547 param_type: ParamType::Vec4,
548 default: ParamValue::Vec4(default),
549 min: Some(0.0),
550 max: Some(1.0),
551 step: Some(0.01),
552 unit: ParamUnit::Normalized,
553 enum_values: Vec::new(),
554 }
555}
556
557pub(crate) fn choice(name: &str, default: u32, values: &[&str]) -> ParamSpec {
558 ParamSpec {
559 name: name.into(),
560 param_type: ParamType::Enum,
561 default: ParamValue::Enum(default),
562 min: None,
563 max: None,
564 step: None,
565 unit: ParamUnit::Enum,
566 enum_values: values.iter().map(|value| (*value).into()).collect(),
567 }
568}
569
570pub(crate) fn boolean(name: &str, default: bool) -> ParamSpec {
571 ParamSpec {
572 name: name.into(),
573 param_type: ParamType::Bool,
574 default: ParamValue::Bool(default),
575 min: None,
576 max: None,
577 step: None,
578 unit: ParamUnit::Bool,
579 enum_values: Vec::new(),
580 }
581}