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