Shaders: Anime4K presets in-session and in Settings

Vendored Anime4K v4.0.1 GLSL (MIT), extracted to app support once per
version. Presets A/B/C/A+A/B+B/C+A in HQ and Fast per Anime4K's mpv
templates, plus custom files. Shader button in the video controls
(works in fullscreen), Ctrl+1..6 / Ctrl+0, live apply with read-back,
failures fall back to Off with a toast. Render mode detected from
mpv's GL log; warns when on the software path. Docs include the
Jellyfin client section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-07 01:45:54 +03:00
1 parent 4814bfb561
commit fb2927c094
28 files changed
+6516 -53

No files matched your search

@@ -0,0 +1,87 @@
import 'dart:io';
// Anime4K v4.0.1 presets, file for file as in Anime4K's mpv templates
// (md/Template/GLSL_Mac_Linux_{High,Low}-end/input.conf): HQ = the
// high-end GPU set, Fast = the low-end set.
/// Anime4K release the shipped files come from (assets/shaders/anime4k).
const anime4kVersion = '4.0.1';
enum ShaderPreset {
off('Off'),
a('Mode A'),
b('Mode B'),
c('Mode C'),
aa('Mode A+A'),
bb('Mode B+B'),
ca('Mode C+A'),
custom('Custom shaders');
final String label;
const ShaderPreset(this.label);
/// The six Anime4K modes, in Ctrl+1..Ctrl+6 order.
static const modes = [a, b, c, aa, bb, ca];
bool get isAnime4k => modes.contains(this);
}
enum ShaderQuality {
hq('HQ'),
fast('Fast');
final String label;
const ShaderQuality(this.label);
}
/// "Mode A (HQ)", "Off", "Custom shaders".
String shaderLabel(ShaderPreset preset, ShaderQuality quality) =>
preset.isAnime4k ? '${preset.label} (${quality.label})' : preset.label;
const _clamp = 'Anime4K_Clamp_Highlights.glsl';
const _down = [
'Anime4K_AutoDownscalePre_x2.glsl',
'Anime4K_AutoDownscalePre_x4.glsl',
];
/// Anime4K file names of [preset] in mpv order; empty for off/custom.
List<String> anime4kFiles(ShaderPreset preset, ShaderQuality quality) {
final hq = quality == ShaderQuality.hq;
// Big/small network sizes: HQ uses VL then M, Fast uses M then S.
final big = hq ? 'VL' : 'M';
final small = hq ? 'M' : 'S';
String restore(String size) => 'Anime4K_Restore_CNN_$size.glsl';
String soft(String size) => 'Anime4K_Restore_CNN_Soft_$size.glsl';
String up(String size) => 'Anime4K_Upscale_CNN_x2_$size.glsl';
String denoise(String size) => 'Anime4K_Upscale_Denoise_CNN_x2_$size.glsl';
return switch (preset) {
ShaderPreset.off || ShaderPreset.custom => const [],
ShaderPreset.a => [_clamp, restore(big), up(big), ..._down, up(small)],
ShaderPreset.b => [_clamp, soft(big), up(big), ..._down, up(small)],
ShaderPreset.c => [_clamp, denoise(big), ..._down, up(small)],
ShaderPreset.aa => [
_clamp, restore(big), up(big), restore(small), ..._down, up(small)
],
ShaderPreset.bb => [
_clamp, soft(big), up(big), ..._down, soft(small), up(small)
],
ShaderPreset.ca => [
_clamp, denoise(big), ..._down, restore(small), up(small)
],
};
}
/// Every file some preset needs: exactly what assets/shaders/anime4k holds.
List<String> get anime4kAllFiles => {
for (final p in ShaderPreset.modes)
for (final q in ShaderQuality.values) ...anime4kFiles(p, q),
}.toList()
..sort();
/// mpv's path-list separator: ';' on Windows, ':' elsewhere.
String shaderListSeparator({bool? windows}) =>
(windows ?? Platform.isWindows) ? ';' : ':';
/// The `glsl-shaders` value for [paths].
String joinShaderPaths(List<String> paths, {bool? windows}) =>
paths.join(shaderListSeparator(windows: windows));