固定抽帧像素格式并收敛动作帧图像处理重复代码

ffmpeg 抽帧显式指定 -pix_fmt rgb24,消除 to_rgb8 截断 alpha 的隐患
抽出 compute_contain_dimensions 与 image_format_from_mime 供两条帧路径共用
尺寸已匹配时跳过无效的 Triangle 重采样

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 13:37:55 +00:00
parent 41f578f0ac
commit 75184d0771
@@ -4287,6 +4287,8 @@ fn extract_video_frame_to_png(
input_path.to_string_lossy().as_ref(),
"-frames:v",
"1",
"-pix_fmt",
"rgb24",
"-f",
"image2",
output_path.to_string_lossy().as_ref(),
@@ -4386,13 +4388,7 @@ fn prepare_editor_character_animation_bgfilter_input(
target_width: u32,
target_height: u32,
) -> Result<FinalizedAnimationFrame, AppError> {
let image_format = match mime_type {
"image/png" => Some(ImageFormat::Png),
"image/jpeg" | "image/jpg" => Some(ImageFormat::Jpeg),
"image/webp" => Some(ImageFormat::WebP),
_ => None,
};
let image = match image_format {
let image = match image_format_from_mime(mime_type) {
Some(format) => image::load_from_memory_with_format(source, format),
None => image::load_from_memory(source),
}
@@ -4404,19 +4400,13 @@ fn prepare_editor_character_animation_bgfilter_input(
})?
.to_rgb8();
let target_width = target_width.max(1);
let target_height = target_height.max(1);
let source_width = image.width().max(1);
let source_height = image.height().max(1);
let scale = (target_width as f32 / source_width as f32)
.min(target_height as f32 / source_height as f32);
let draw_width = ((source_width as f32 * scale).round() as u32)
.max(1)
.min(target_width);
let draw_height = ((source_height as f32 * scale).round() as u32)
.max(1)
.min(target_height);
let resized = image::imageops::resize(&image, draw_width, draw_height, FilterType::Triangle);
let (draw_width, draw_height) =
compute_contain_dimensions(image.width(), image.height(), target_width, target_height);
let resized = if (draw_width, draw_height) == (image.width(), image.height()) {
image
} else {
image::imageops::resize(&image, draw_width, draw_height, FilterType::Triangle)
};
let mut encoded = Vec::new();
let encoder = PngEncoder::new(&mut encoded);
@@ -4448,13 +4438,7 @@ fn finalize_animation_frame_payload(
frame_height: u32,
apply_chroma_key: bool,
) -> Result<FinalizedAnimationFrame, AppError> {
let image_format = match mime_type {
"image/png" => Some(ImageFormat::Png),
"image/jpeg" | "image/jpg" => Some(ImageFormat::Jpeg),
"image/webp" => Some(ImageFormat::WebP),
_ => None,
};
let mut image = match image_format {
let mut image = match image_format_from_mime(mime_type) {
Some(format) => image::load_from_memory_with_format(source, format),
None => image::load_from_memory(source),
}
@@ -4501,8 +4485,39 @@ fn finalize_animation_frame_payload(
fn contain_rgba_image(source: &RgbaImage, target_width: u32, target_height: u32) -> RgbaImage {
let mut canvas = RgbaImage::from_pixel(target_width, target_height, Rgba([0, 0, 0, 0]));
let source_width = source.width().max(1);
let source_height = source.height().max(1);
let (draw_width, draw_height) =
compute_contain_dimensions(source.width(), source.height(), target_width, target_height);
let offset_x = ((target_width - draw_width) / 2) as i64;
let offset_y = ((target_height - draw_height) / 2) as i64;
if (draw_width, draw_height) == (source.width(), source.height()) {
image::imageops::overlay(&mut canvas, source, offset_x, offset_y);
} else {
let resized =
image::imageops::resize(source, draw_width, draw_height, FilterType::Triangle);
image::imageops::overlay(&mut canvas, &resized, offset_x, offset_y);
}
canvas
}
fn image_format_from_mime(mime_type: &str) -> Option<ImageFormat> {
match mime_type {
"image/png" => Some(ImageFormat::Png),
"image/jpeg" | "image/jpg" => Some(ImageFormat::Jpeg),
"image/webp" => Some(ImageFormat::WebP),
_ => None,
}
}
fn compute_contain_dimensions(
source_width: u32,
source_height: u32,
target_width: u32,
target_height: u32,
) -> (u32, u32) {
let target_width = target_width.max(1);
let target_height = target_height.max(1);
let source_width = source_width.max(1);
let source_height = source_height.max(1);
let scale = (target_width as f32 / source_width as f32)
.min(target_height as f32 / source_height as f32);
let draw_width = ((source_width as f32 * scale).round() as u32)
@@ -4511,11 +4526,7 @@ fn contain_rgba_image(source: &RgbaImage, target_width: u32, target_height: u32)
let draw_height = ((source_height as f32 * scale).round() as u32)
.max(1)
.min(target_height);
let resized = image::imageops::resize(source, draw_width, draw_height, FilterType::Triangle);
let offset_x = ((target_width - draw_width) / 2) as i64;
let offset_y = ((target_height - draw_height) / 2) as i64;
image::imageops::overlay(&mut canvas, &resized, offset_x, offset_y);
canvas
(draw_width, draw_height)
}
async fn load_media_source_payload(