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]
154pub fn gaussian_blur_definition() -> EffectDef {
155 EffectDef {
156 id: GAUSSIAN_BLUR_ID.into(),
157 version: 1,
158 kind: EffectKind::Effect,
159 inputs: Vec::new(),
160 params: vec![float_step("radius", 0.012, 0.0, 1.0, 0.001)],
161 resources: Vec::new(),
162 wgsl_body: effect_body(EXPANDED_EFFECT),
163 tier_min: CapabilityTier::TierB,
164 license: Some(ALPHA2_EFFECT_LICENSE.into()),
165 }
166}
167
168pub(crate) fn gaussian_blur_pass_definition() -> EffectDef {
169 EffectDef {
170 id: GAUSSIAN_BLUR_PASS_ID.into(),
171 version: 1,
172 kind: EffectKind::Effect,
173 inputs: Vec::new(),
174 params: vec![
175 vec2("step", [0.0, 0.0]),
176 float_step(
177 "sigma",
178 1.0,
179 0.0,
180 crate::gaussian_blur::MAX_PASS_SIGMA_TAPS,
181 0.001,
182 ),
183 ],
184 resources: Vec::new(),
185 wgsl_body: effect_body(GAUSSIAN_BLUR),
186 tier_min: CapabilityTier::TierB,
187 license: Some(ALPHA2_EFFECT_LICENSE.into()),
188 }
189}
190
191#[must_use]
198pub fn glow_definition() -> EffectDef {
199 EffectDef {
200 id: GLOW_ID.into(),
201 version: 1,
202 kind: EffectKind::Effect,
203 inputs: Vec::new(),
204 params: vec![
205 float("threshold", 0.6, 0.0, 1.0),
206 float("intensity", 0.8, 0.0, 3.0),
207 float("radius", 0.012, 0.0, 0.06),
208 ],
209 resources: Vec::new(),
210 wgsl_body: effect_body(EXPANDED_EFFECT),
211 tier_min: CapabilityTier::TierB,
212 license: Some(ALPHA2_EFFECT_LICENSE.into()),
213 }
214}
215
216fn glow_threshold_definition() -> EffectDef {
217 EffectDef {
218 id: GLOW_THRESHOLD_ID.into(),
219 ..simple_effect(
220 "glow-threshold",
221 GLOW_THRESHOLD,
222 vec![float("threshold", 0.6, 0.0, 1.0)],
223 )
224 }
225}
226
227fn glow_combine_definition() -> EffectDef {
230 EffectDef {
231 id: GLOW_COMBINE_ID.into(),
232 version: 1,
233 kind: EffectKind::Transition,
234 inputs: Vec::new(),
235 params: vec![float("intensity", 0.8, 0.0, 3.0)],
236 resources: Vec::new(),
237 wgsl_body: effect_body(GLOW_COMBINE),
238 tier_min: CapabilityTier::TierB,
239 license: Some(ALPHA2_EFFECT_LICENSE.into()),
240 }
241}
242
243fn unsharp_combine_definition() -> EffectDef {
245 EffectDef {
246 id: UNSHARP_COMBINE_ID.into(),
247 version: 1,
248 kind: EffectKind::Transition,
249 inputs: Vec::new(),
250 params: vec![
251 float("amount", 1.0, 0.0, 5.0),
252 float("threshold", 0.0, 0.0, 1.0),
253 ],
254 resources: Vec::new(),
255 wgsl_body: effect_body(UNSHARP_COMBINE),
256 tier_min: CapabilityTier::TierB,
257 license: Some(ALPHA2_EFFECT_LICENSE.into()),
258 }
259}
260
261fn focus_combine_definition() -> EffectDef {
263 EffectDef {
264 id: FOCUS_COMBINE_ID.into(),
265 version: 1,
266 kind: EffectKind::Effect,
267 inputs: vec![
268 InputSpec {
269 name: "soft".into(),
270 },
271 InputSpec {
272 name: "blurred".into(),
273 },
274 ],
275 params: vec![
276 float("position", 0.5, 0.0, 1.0),
277 degrees("angle", 0.0, -90.0, 90.0),
278 float("focus", 0.15, 0.0, 1.0),
279 float_step("falloff", 0.25, 0.001, 1.0, 0.005),
280 ],
281 resources: Vec::new(),
282 wgsl_body: effect_body(FOCUS_COMBINE),
283 tier_min: CapabilityTier::TierB,
284 license: Some(ALPHA2_EFFECT_LICENSE.into()),
285 }
286}
287
288fn shadow_offset_definition() -> EffectDef {
289 EffectDef {
290 id: SHADOW_OFFSET_ID.into(),
291 ..simple_effect(
292 "shadow-offset",
293 SHADOW_OFFSET,
294 vec![
295 vec2("offset", [0.0, 0.0]),
296 color("color", [0.0, 0.0, 0.0, 1.0]),
297 float("opacity", 0.6, 0.0, 1.0),
298 ],
299 )
300 }
301}
302
303fn shadow_combine_definition() -> EffectDef {
305 EffectDef {
306 id: SHADOW_COMBINE_ID.into(),
307 version: 1,
308 kind: EffectKind::Transition,
309 inputs: Vec::new(),
310 params: vec![boolean("shadow_only", false)],
311 resources: Vec::new(),
312 wgsl_body: effect_body(SHADOW_COMBINE),
313 tier_min: CapabilityTier::TierB,
314 license: Some(ALPHA2_EFFECT_LICENSE.into()),
315 }
316}
317
318#[must_use]
320pub fn lut_3d_definition() -> EffectDef {
321 EffectDef {
322 id: LUT_3D_ID.into(),
323 version: 1,
324 kind: EffectKind::Effect,
325 inputs: Vec::new(),
326 params: vec![float("strength", 1.0, 0.0, 1.0)],
327 resources: vec![ResourceSpec {
328 name: "lut".into(),
329 kind: EffectResourceKind::Lut3d,
330 }],
331 wgsl_body: effect_body(LUT_3D_BODY),
332 tier_min: CapabilityTier::TierB,
333 license: Some(ALPHA2_EFFECT_LICENSE.into()),
334 }
335}
336
337pub(crate) fn internal_effect_definitions() -> Vec<EffectDef> {
339 let mut definitions = vec![
340 gaussian_blur_pass_definition(),
341 glow_threshold_definition(),
342 glow_combine_definition(),
343 shadow_offset_definition(),
344 shadow_combine_definition(),
345 unsharp_combine_definition(),
346 focus_combine_definition(),
347 crate::masking::track_matte_definition(),
348 crate::generator::generator_definition(),
349 ];
350 definitions.extend(crate::masking::mask_combine_definitions());
351 definitions
352}
353
354impl Renderer {
355 fn register_builtin_definition(
356 &mut self,
357 definition: EffectDef,
358 ) -> Result<EffectId, ShaderDiagnostics> {
359 let id = self.register_lazily(definition)?;
360 self.effects.mark_builtin(id.clone());
361 Ok(id)
362 }
363
364 pub fn register_builtins(&mut self) -> Result<Vec<EffectId>, ShaderDiagnostics> {
374 for definition in internal_effect_definitions() {
375 let internal = self.register_builtin_definition(definition)?;
376 self.effects.mark_internal(internal);
377 }
378 let mut definitions = builtin_effect_definitions();
379 definitions.extend(builtin_transition_definitions());
380 let mut ids = Vec::with_capacity(definitions.len());
381 for definition in definitions {
382 ids.push(self.register_builtin_definition(definition)?);
383 }
384 Ok(ids)
385 }
386
387 pub fn register_builtin_chroma_key(&mut self) -> Result<EffectId, ShaderDiagnostics> {
392 self.register_builtin_definition(chroma_key_definition())
393 }
394
395 pub fn register_builtin_gaussian_blur(&mut self) -> Result<EffectId, ShaderDiagnostics> {
400 let internal = self.register_builtin_definition(gaussian_blur_pass_definition())?;
401 self.effects.mark_internal(internal);
402 self.register_builtin_definition(gaussian_blur_definition())
403 }
404
405 pub fn register_builtin_lut_3d(&mut self) -> Result<EffectId, ShaderDiagnostics> {
410 self.register_builtin_definition(lut_3d_definition())
411 }
412
413 pub fn register_builtin_transitions(&mut self) -> Result<Vec<EffectId>, ShaderDiagnostics> {
419 let definitions = builtin_transition_definitions();
420 let mut ids = Vec::with_capacity(definitions.len());
421 for definition in definitions {
422 ids.push(self.register_builtin_definition(definition)?);
423 }
424 Ok(ids)
425 }
426}
427
428pub(crate) fn effect_body(body: &str) -> String {
430 format!("{EFFECT_HELPERS}\n{body}")
431}
432
433pub(crate) fn simple_effect(name: &str, body: &str, params: Vec<ParamSpec>) -> EffectDef {
435 EffectDef {
436 id: format!("alpha2.effect.{name}"),
437 version: 1,
438 kind: EffectKind::Effect,
439 inputs: Vec::new(),
440 params,
441 resources: Vec::new(),
442 wgsl_body: effect_body(body),
443 tier_min: CapabilityTier::TierB,
444 license: Some(ALPHA2_EFFECT_LICENSE.into()),
445 }
446}
447
448pub(crate) fn transition(
451 name: &str,
452 body: &str,
453 source: &str,
454 author: &str,
455 params: Vec<ParamSpec>,
456) -> EffectDef {
457 EffectDef {
458 id: format!("alpha2.transition.{name}"),
459 version: 1,
460 kind: EffectKind::Transition,
461 inputs: Vec::new(),
462 params,
463 resources: Vec::new(),
464 wgsl_body: format!("{TRANSITION_HELPERS}\n{GL_TRANSITION_ENTRY}\n{body}"),
465 tier_min: CapabilityTier::TierB,
466 license: Some(format!(
467 "MIT; Author: {author}; Source: {GL_TRANSITIONS_BASE}{source}"
468 )),
469 }
470}
471
472pub(crate) fn original_transition(name: &str, body: &str, params: Vec<ParamSpec>) -> EffectDef {
475 EffectDef {
476 id: format!("alpha2.transition.{name}"),
477 version: 1,
478 kind: EffectKind::Transition,
479 inputs: Vec::new(),
480 params,
481 resources: Vec::new(),
482 wgsl_body: format!("{TRANSITION_HELPERS}\n{body}"),
483 tier_min: CapabilityTier::TierB,
484 license: Some(ALPHA2_EFFECT_LICENSE.into()),
485 }
486}
487
488pub(crate) fn float(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
489 let span = max - min;
490 let step = if span > 50.0 {
491 1.0
492 } else if span <= 0.1 {
493 0.001
494 } else {
495 0.01
496 };
497 float_step(name, default, min, max, step)
498}
499
500pub(crate) fn float_step(name: &str, default: f32, min: f32, max: f32, step: f32) -> ParamSpec {
501 ParamSpec {
502 name: name.into(),
503 param_type: ParamType::Float,
504 default: ParamValue::Float(default),
505 min: Some(min),
506 max: Some(max),
507 step: Some(step),
508 unit: ParamUnit::Normalized,
509 enum_values: Vec::new(),
510 }
511}
512
513pub(crate) fn degrees(name: &str, default: f32, min: f32, max: f32) -> ParamSpec {
514 ParamSpec {
515 unit: ParamUnit::Degrees,
516 step: Some(1.0),
517 ..float(name, default, min, max)
518 }
519}
520
521pub(crate) fn vec2(name: &str, default: [f32; 2]) -> ParamSpec {
522 ParamSpec {
523 name: name.into(),
524 param_type: ParamType::Vec2,
525 default: ParamValue::Vec2(default),
526 min: None,
527 max: None,
528 step: None,
529 unit: ParamUnit::Normalized,
530 enum_values: Vec::new(),
531 }
532}
533
534pub(crate) fn vec2_range(
535 name: &str,
536 default: [f32; 2],
537 min: f32,
538 max: f32,
539 step: f32,
540) -> ParamSpec {
541 ParamSpec {
542 min: Some(min),
543 max: Some(max),
544 step: Some(step),
545 ..vec2(name, default)
546 }
547}
548
549pub(crate) fn color(name: &str, default: [f32; 4]) -> ParamSpec {
550 ParamSpec {
551 name: name.into(),
552 param_type: ParamType::Color,
553 default: ParamValue::Color(default),
554 min: None,
555 max: None,
556 step: None,
557 unit: ParamUnit::Color,
558 enum_values: Vec::new(),
559 }
560}
561
562pub(crate) fn thresholds(name: &str, default: [f32; 4]) -> ParamSpec {
564 ParamSpec {
565 name: name.into(),
566 param_type: ParamType::Vec4,
567 default: ParamValue::Vec4(default),
568 min: Some(0.0),
569 max: Some(1.0),
570 step: Some(0.01),
571 unit: ParamUnit::Normalized,
572 enum_values: Vec::new(),
573 }
574}
575
576pub(crate) fn choice(name: &str, default: u32, values: &[&str]) -> ParamSpec {
577 ParamSpec {
578 name: name.into(),
579 param_type: ParamType::Enum,
580 default: ParamValue::Enum(default),
581 min: None,
582 max: None,
583 step: None,
584 unit: ParamUnit::Enum,
585 enum_values: values.iter().map(|value| (*value).into()).collect(),
586 }
587}
588
589pub(crate) fn boolean(name: &str, default: bool) -> ParamSpec {
590 ParamSpec {
591 name: name.into(),
592 param_type: ParamType::Bool,
593 default: ParamValue::Bool(default),
594 min: None,
595 max: None,
596 step: None,
597 unit: ParamUnit::Bool,
598 enum_values: Vec::new(),
599 }
600}