fix: decode AVIF posters via ffmpeg fallback for mosaic slide
Some checks failed
CI / Check / Test (push) Failing after 41s
Some checks failed
CI / Check / Test (push) Failing after 41s
This commit is contained in:
@@ -1,10 +1,34 @@
|
|||||||
use ab_glyph::{FontArc, PxScale};
|
use ab_glyph::{FontArc, PxScale};
|
||||||
use domain::errors::DomainError;
|
use domain::errors::DomainError;
|
||||||
use domain::models::wrapup::WrapUpReport;
|
use domain::models::wrapup::WrapUpReport;
|
||||||
use image::{Rgba, RgbaImage};
|
use image::{DynamicImage, Rgba, RgbaImage};
|
||||||
use imageproc::drawing::{draw_filled_rect_mut, draw_text_mut};
|
use imageproc::drawing::{draw_filled_rect_mut, draw_text_mut};
|
||||||
use imageproc::rect::Rect;
|
use imageproc::rect::Rect;
|
||||||
|
|
||||||
|
fn decode_image(bytes: &[u8]) -> Result<DynamicImage, String> {
|
||||||
|
image::load_from_memory(bytes).or_else(|_| {
|
||||||
|
let dir = tempfile::tempdir().map_err(|e| e.to_string())?;
|
||||||
|
let input = dir.path().join("input");
|
||||||
|
let output = dir.path().join("output.png");
|
||||||
|
std::fs::write(&input, bytes).map_err(|e| e.to_string())?;
|
||||||
|
let status = std::process::Command::new("ffmpeg")
|
||||||
|
.args([
|
||||||
|
"-y", "-i",
|
||||||
|
&input.to_string_lossy(),
|
||||||
|
&output.to_string_lossy(),
|
||||||
|
])
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.status()
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
if !status.success() {
|
||||||
|
return Err("ffmpeg conversion failed".into());
|
||||||
|
}
|
||||||
|
let png_bytes = std::fs::read(&output).map_err(|e| e.to_string())?;
|
||||||
|
image::load_from_memory(&png_bytes).map_err(|e| e.to_string())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const BG: Rgba<u8> = Rgba([26, 26, 36, 255]);
|
const BG: Rgba<u8> = Rgba([26, 26, 36, 255]);
|
||||||
const GOLD: Rgba<u8> = Rgba([229, 192, 52, 255]);
|
const GOLD: Rgba<u8> = Rgba([229, 192, 52, 255]);
|
||||||
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
const WHITE: Rgba<u8> = Rgba([255, 255, 255, 255]);
|
||||||
@@ -364,7 +388,7 @@ impl SlideRenderer {
|
|||||||
let thumb_w = w / cols;
|
let thumb_w = w / cols;
|
||||||
let thumb_h = (thumb_w * 3) / 2;
|
let thumb_h = (thumb_w * 3) / 2;
|
||||||
|
|
||||||
for (i, (_, bytes)) in posters.iter().enumerate() {
|
for (i, (name, bytes)) in posters.iter().enumerate() {
|
||||||
let col = (i as u32) % cols;
|
let col = (i as u32) % cols;
|
||||||
let row = (i as u32) / cols;
|
let row = (i as u32) / cols;
|
||||||
let x = col * thumb_w;
|
let x = col * thumb_w;
|
||||||
@@ -372,10 +396,16 @@ impl SlideRenderer {
|
|||||||
if y + thumb_h > h {
|
if y + thumb_h > h {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if let Ok(poster) = image::load_from_memory(bytes) {
|
match decode_image(bytes) {
|
||||||
let thumb =
|
Ok(poster) => {
|
||||||
poster.resize_exact(thumb_w, thumb_h, image::imageops::FilterType::Triangle);
|
let thumb = poster.resize_exact(
|
||||||
image::imageops::overlay(&mut canvas, &thumb.to_rgba8(), x as i64, y as i64);
|
thumb_w,
|
||||||
|
thumb_h,
|
||||||
|
image::imageops::FilterType::Triangle,
|
||||||
|
);
|
||||||
|
image::imageops::overlay(&mut canvas, &thumb.to_rgba8(), x as i64, y as i64);
|
||||||
|
}
|
||||||
|
Err(e) => tracing::debug!("mosaic: skipped {name}: {e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user