Skip to content

Commit 3d0dde1

Browse files
committed
feat: add transparent background support for AVIF animations
- Implemented two-pass encoding process for AVIF format - Fixed alpha channel handling to properly preserve transparency - Added filter complex for separate alpha channel encoding - Optimized encoding parameters based on quality settings
1 parent 823dc49 commit 3d0dde1

4 files changed

Lines changed: 94 additions & 43 deletions

File tree

LottieViewConvert/Helper/Convert/AvifConverter.cs

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
namespace LottieViewConvert.Helper.Convert
88
{
99
/// <summary>
10-
/// AVIF format converter using FFmpeg.
10+
/// AVIF format converter using FFmpeg with proper transparency support.
1111
/// </summary>
1212
public class AvifConverter : IFormatConverter
1313
{
@@ -28,30 +28,101 @@ public async Task<bool> ConvertAsync(
2828
IProgress<TimeSpan>? progress = null,
2929
CancellationToken cancellationToken = default)
3030
{
31+
try
32+
{
33+
// First pass
34+
var firstPassArgs = GetEncodingArgs(options, outputPath, 1);
35+
bool firstPassSuccess = await _commandExecutor.ExecuteAsync(
36+
"ffmpeg",
37+
firstPassArgs,
38+
inputDirectory,
39+
progress,
40+
cancellationToken);
41+
42+
if (!firstPassSuccess)
43+
throw new Exception("First pass AVIF encoding failed");
44+
45+
// Second pass
46+
var secondPassArgs = GetEncodingArgs(options, outputPath, 2);
47+
bool secondPassSuccess = await _commandExecutor.ExecuteAsync(
48+
"ffmpeg",
49+
secondPassArgs,
50+
inputDirectory,
51+
progress,
52+
cancellationToken);
53+
54+
if (!secondPassSuccess)
55+
throw new Exception("Second pass AVIF encoding failed");
56+
57+
return true;
58+
}
59+
catch (Exception ex)
60+
{
61+
throw new Exception("Error while converting AVIF", ex);
62+
}
63+
}
64+
65+
private List<string> GetEncodingArgs(
66+
ConversionOptions options,
67+
string outputPath,
68+
int passNumber)
69+
{
70+
// Get quality parameters
71+
int crf = GetCrfValue(options.Quality);
72+
int crfAlpha = Math.Min(crf + 20, 63); // Higher CRF for alpha (less quality needed for transparency)
73+
int cpuUsed = GetCpuUsed(options.Quality);
74+
3175
var args = new List<string>
3276
{
3377
"-hide_banner",
3478
"-y",
3579
"-r", options.Fps.ToString(),
3680
"-i", "%05d.png",
81+
"-color_range", "tv",
82+
"-pix_fmt:0", "yuv420p",
83+
"-pix_fmt:1", "gray8",
84+
85+
// Key part: proper alpha channel handling
86+
"-filter_complex",
87+
"[0:v]format=pix_fmts=yuva444p[main]; [main]split[main][alpha]; [alpha]alphaextract[alpha]",
88+
89+
// Map the main video and alpha channels separately
90+
"-map", "[main]:v",
91+
"-map", "[alpha]:v",
92+
93+
// No audio
94+
"-an",
95+
96+
// AV1 codec settings
3797
"-c:v", "libaom-av1",
38-
"-crf", GetCrfValue(options.Quality).ToString(),
39-
"-b:v", "0", // Use CRF mode
40-
"-cpu-used", GetCpuUsed(options.Quality).ToString(),
41-
"-row-mt", "1", // Enable row-based multithreading
42-
"-tiles", GetTileConfiguration(options.Quality),
43-
"-pix_fmt", "yuv420p",
44-
"-movflags", "+faststart",
98+
"-cpu-used", cpuUsed.ToString(),
99+
"-crf", crf.ToString(),
100+
"-crf:1", crfAlpha.ToString(),
101+
102+
// Two-pass encoding
103+
"-pass", passNumber.ToString(),
104+
105+
// Add progress reporting for the progress bar
45106
"-progress", "pipe:1",
46107
"-nostats"
47108
};
48-
109+
49110
// Add quality-specific optimizations
50111
AddQualityOptimizations(args, options.Quality);
51-
52-
args.Add(outputPath);
53-
54-
return await _commandExecutor.ExecuteAsync("ffmpeg", args, inputDirectory, progress, cancellationToken);
112+
113+
// For first pass, output to null
114+
if (passNumber == 1)
115+
{
116+
args.Add("-f");
117+
args.Add("null");
118+
args.Add(Environment.OSVersion.Platform == PlatformID.Win32NT ? "NUL" : "/dev/null");
119+
}
120+
else
121+
{
122+
args.Add(outputPath);
123+
}
124+
125+
return args;
55126
}
56127

57128
/// <summary>
@@ -96,23 +167,6 @@ private static int GetCpuUsed(int quality)
96167
};
97168
}
98169

99-
/// <summary>
100-
/// Gets the tile configuration for parallel encoding.
101-
/// More tiles = faster encoding but slightly reduced efficiency.
102-
/// </summary>
103-
/// <param name="quality">Quality percentage (0-100)</param>
104-
/// <returns>Tile configuration string</returns>
105-
private static string GetTileConfiguration(int quality)
106-
{
107-
return quality switch
108-
{
109-
>= 80 => "2x1", // Fewer tiles for better compression
110-
>= 60 => "2x2", // Balanced
111-
>= 40 => "3x2", // More tiles for faster encoding
112-
_ => "4x2" // Maximum tiles for speed
113-
};
114-
}
115-
116170
/// <summary>
117171
/// Adds quality-specific optimization parameters.
118172
/// </summary>
@@ -123,32 +177,29 @@ private static void AddQualityOptimizations(List<string> args, int quality)
123177
if (quality >= 80)
124178
{
125179
// High quality optimizations
126-
args.AddRange(new[]
127-
{
180+
args.AddRange([
128181
"-aom-params", "enable-chroma-deltaq=1:enable-qm=1:qm-min=0:qm-max=15"
129-
});
182+
]);
130183
}
131184
else if (quality >= 60)
132185
{
133186
// Medium quality optimizations
134-
args.AddRange(new[]
135-
{
187+
args.AddRange([
136188
"-aom-params", "enable-chroma-deltaq=1"
137-
});
189+
]);
138190
}
139191
else if (quality < 40)
140192
{
141193
// Low quality - prioritize speed
142-
args.AddRange(new[]
143-
{
194+
args.AddRange([
144195
"-aom-params", "enable-cdef=0:enable-restoration=0"
145-
});
196+
]);
146197
}
147198

148199
// Animation-specific tuning
149200
if (quality >= 70)
150201
{
151-
args.AddRange(new[] { "-tune", "psnr" });
202+
args.AddRange(["-tune", "psnr"]);
152203
}
153204
}
154205
}

LottieViewConvert/Views/AboutView.axaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676

7777
<TextBlock Text="{x:Static lang:Resources.AppName}" Classes="AppTitle"/>
7878
<TextBlock Text="{x:Static lang:Resources.AppSubTitle}" Classes="Subtitle"/>
79-
<TextBlock Text="Version 1.2.5" Classes="VersionText"
79+
<TextBlock Text="Version 1.2.6" Classes="VersionText"
8080
HorizontalAlignment="Center"/>
8181
</StackPanel>
8282

readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44
<p align="center">
55
<a href="readme_cn.md"><img src="https://img.shields.io/badge/Lang-简体中文-red"></a>
6-
<img src="https://img.shields.io/badge/version-1.2.5-yellow">
6+
<img src="https://img.shields.io/badge/version-1.2.6-yellow">
77
<a href="//github.com/SwaggyMacro/LottieViewConvert"><img src="https://img.shields.io/badge/Repo-LottieViewConvert-green"></a>
88
</p>
99
<p align="center">

readme_cn.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
</p>
44
<p align="center">
55
<a href="readme.md"><img src="https://img.shields.io/badge/Lang-English-blue"></a>
6-
<img src="https://img.shields.io/badge/version-1.2.5-yellow">
6+
<img src="https://img.shields.io/badge/version-1.2.6-yellow">
77
<a href="//github.com/SwaggyMacro/LottieViewConvert"><img src="https://img.shields.io/badge/Repo-LottieViewConvert-green"></a>
88
</p>
99
<p align="center">

0 commit comments

Comments
 (0)