@@ -10,7 +10,7 @@ use crate::cache::DiskCache; |
| 10 | 10 | use crate::config::{Config, ScaleMode}; |
| 11 | 11 | use crate::ipc::{Command, GarEvent, GarIpcClient, IpcServer, Response}; |
| 12 | 12 | use crate::ipc::server::IpcClient; |
| 13 | | -use crate::media::{scale_image, AnimatedGif, AnimatedPng, AnimatedWebP, AnimationFrame, ImageLoader}; |
| 13 | +use crate::media::{scale_image, scale_image_fast, AnimatedGif, AnimatedPng, AnimatedWebP, AnimationFrame, ImageLoader}; |
| 14 | 14 | #[cfg(feature = "video")] |
| 15 | 15 | use crate::media::{VideoDecoder, is_video_file}; |
| 16 | 16 | use crate::state::{detect_source_type, PlaylistState}; |
@@ -93,7 +93,7 @@ pub struct ActiveAnimation { |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | 95 | impl ActiveAnimation { |
| 96 | | - /// Create from animation frames |
| 96 | + /// Create from animation frames (scales in parallel with fast filter) |
| 97 | 97 | fn from_frames( |
| 98 | 98 | frames: &[AnimationFrame], |
| 99 | 99 | renderer: AnimationRenderer, |
@@ -103,10 +103,18 @@ impl ActiveAnimation { |
| 103 | 103 | screen_width: u32, |
| 104 | 104 | screen_height: u32, |
| 105 | 105 | ) -> Self { |
| 106 | | - let scaled_frames: Vec<image::RgbaImage> = frames |
| 107 | | - .iter() |
| 108 | | - .map(|frame| scale_image(&frame.image, screen_width, screen_height, scale_mode)) |
| 109 | | - .collect(); |
| 106 | + // Scale frames in parallel using thread::scope for multi-core speedup |
| 107 | + let scaled_frames: Vec<image::RgbaImage> = std::thread::scope(|s| { |
| 108 | + let handles: Vec<_> = frames |
| 109 | + .iter() |
| 110 | + .map(|frame| { |
| 111 | + s.spawn(move || { |
| 112 | + scale_image_fast(&frame.image, screen_width, screen_height, scale_mode) |
| 113 | + }) |
| 114 | + }) |
| 115 | + .collect(); |
| 116 | + handles.into_iter().map(|h| h.join().unwrap()).collect() |
| 117 | + }); |
| 110 | 118 | |
| 111 | 119 | let frame_delays: Vec<Duration> = frames |
| 112 | 120 | .iter() |