fix(save): decouple native saves from screenshots (#956)

This commit is contained in:
Hakan Seven committed 2026-09-03 21:06:19 +03:00
1 parent a581636aea
commit edd3faeeae
6 files changed
+62 -206

No files matched your search

Generated
+12 -12
View File
@@ -2324,7 +2324,7 @@ dependencies = [
[[package]] [[package]]
name = "iced" name = "iced"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"iced_core", "iced_core",
"iced_debug", "iced_debug",
@@ -2351,7 +2351,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_core" name = "iced_core"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"bitflags 2.13.1", "bitflags 2.13.1",
"bytes", "bytes",
@@ -2369,7 +2369,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_debug" name = "iced_debug"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"iced_core", "iced_core",
"iced_futures", "iced_futures",
@@ -2391,7 +2391,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_futures" name = "iced_futures"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"futures", "futures",
"iced_core", "iced_core",
@@ -2405,7 +2405,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_graphics" name = "iced_graphics"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"bitflags 2.13.1", "bitflags 2.13.1",
"bytemuck", "bytemuck",
@@ -2426,7 +2426,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_program" name = "iced_program"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"iced_graphics", "iced_graphics",
"iced_runtime", "iced_runtime",
@@ -2435,7 +2435,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_renderer" name = "iced_renderer"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"iced_graphics", "iced_graphics",
"iced_tiny_skia", "iced_tiny_skia",
@@ -2447,7 +2447,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_runtime" name = "iced_runtime"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"bytes", "bytes",
"iced_core", "iced_core",
@@ -2458,7 +2458,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_tiny_skia" name = "iced_tiny_skia"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"cosmic-text 0.19.0", "cosmic-text 0.19.0",
@@ -2475,7 +2475,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_wgpu" name = "iced_wgpu"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"bitflags 2.13.1", "bitflags 2.13.1",
"bytemuck", "bytemuck",
@@ -2495,7 +2495,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_widget" name = "iced_widget"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"iced_renderer", "iced_renderer",
"iced_runtime", "iced_runtime",
@@ -2510,7 +2510,7 @@ dependencies = [
[[package]] [[package]]
name = "iced_winit" name = "iced_winit"
version = "0.15.0-dev" version = "0.15.0-dev"
source = "git+https://github.com/HakanSeven12/iced.git?rev=1d1a17f146a3dc01597883ca524633c14646934a#1d1a17f146a3dc01597883ca524633c14646934a" source = "git+https://github.com/iced-rs/iced.git?rev=23604ff22ab0aad9e00b9327cb7b8546ed84db39#23604ff22ab0aad9e00b9327cb7b8546ed84db39"
dependencies = [ dependencies = [
"arboard", "arboard",
"iced_debug", "iced_debug",
+5 -5
View File
@@ -19,8 +19,8 @@ winresource = "0.1"
serde_json = "1" serde_json = "1"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
semver = "1" semver = "1"
iced = { git = "https://github.com/HakanSeven12/iced.git", rev = "1d1a17f146a3dc01597883ca524633c14646934a", features = ["image", "svg", "advanced", "canvas", "smol", "markdown"] } iced = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39", features = ["image", "svg", "advanced", "canvas", "smol", "markdown"] }
iced_core = { git = "https://github.com/HakanSeven12/iced.git", rev = "1d1a17f146a3dc01597883ca524633c14646934a" } iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
iced_aw = { git = "https://github.com/HakanSeven12/iced_aw.git", branch = "agent/fix-iced-fonts", features = ["menu", "context_menu", "color_picker", "drop_down"] } iced_aw = { git = "https://github.com/HakanSeven12/iced_aw.git", branch = "agent/fix-iced-fonts", features = ["menu", "context_menu", "color_picker", "drop_down"] }
bytemuck = { version = "1.25", features = ["derive"] } bytemuck = { version = "1.25", features = ["derive"] }
glam = { version = "0.33", features = ["bytemuck"] } glam = { version = "0.33", features = ["bytemuck"] }
@@ -57,8 +57,8 @@ windows-sys = { version = "0.61", features = ["Win32_UI_Shell", "Win32_UI_Window
ashpd = { version = "0.13.13", default-features = false, features = ["async-io", "wayland"] } ashpd = { version = "0.13.13", default-features = false, features = ["async-io", "wayland"] }
[patch.crates-io] [patch.crates-io]
iced_core = { git = "https://github.com/HakanSeven12/iced.git", rev = "1d1a17f146a3dc01597883ca524633c14646934a" } iced_core = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
iced_widget = { git = "https://github.com/HakanSeven12/iced.git", rev = "1d1a17f146a3dc01597883ca524633c14646934a" } iced_widget = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39" }
[patch."https://github.com/HakanSeven12/cadcodec.git"] [patch."https://github.com/HakanSeven12/cadcodec.git"]
acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "393d6c0857447f9ca678bed97a612707f0a842c6" } acadrust = { git = "https://git@github.com/HakanSeven12/cadcodec.git", rev = "393d6c0857447f9ca678bed97a612707f0a842c6" }
@@ -117,7 +117,7 @@ web-sys = { version = "=0.3.85", features = [
"Node", "Node",
"HtmlElement", "HtmlElement",
] } ] }
iced = { git = "https://github.com/HakanSeven12/iced.git", rev = "1d1a17f146a3dc01597883ca524633c14646934a", features = ["webgl", "fira-sans"] } iced = { git = "https://github.com/iced-rs/iced.git", rev = "23604ff22ab0aad9e00b9327cb7b8546ed84db39", features = ["webgl", "fira-sans"] }
[profile.release] [profile.release]
strip = true strip = true
-18
View File
@@ -671,8 +671,6 @@ pub(super) struct OpenCADStudio {
main_window: Option<window::Id>, main_window: Option<window::Id>,
/// Hides drawing UI overlays for one thumbnail capture frame. /// Hides drawing UI overlays for one thumbnail capture frame.
thumbnail_capture_clean: bool, thumbnail_capture_clean: bool,
#[cfg(not(target_arch = "wasm32"))]
pending_native_thumbnail_save: Option<PendingNativeThumbnailSave>,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pending_web_thumbnail_save: Option<PendingWebThumbnailSave>, pending_web_thumbnail_save: Option<PendingWebThumbnailSave>,
// ── Floating panel windows ──────────────────────────────────────────── // ── Floating panel windows ────────────────────────────────────────────
@@ -1229,18 +1227,6 @@ pub(super) enum SaveContinuation {
Quit, Quit,
} }
#[derive(Debug, Clone)]
#[cfg(not(target_arch = "wasm32"))]
pub(super) struct PendingNativeThumbnailSave {
tab_id: u64,
path: PathBuf,
version: acadrust::DxfVersion,
purpose: SavePurpose,
continuation: SaveContinuation,
set_current_path: bool,
check_external_change: bool,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pub(super) struct PendingWebThumbnailSave { pub(super) struct PendingWebThumbnailSave {
@@ -2063,8 +2049,6 @@ pub enum Message {
AutoSave, AutoSave,
/// A clean viewport frame is ready for thumbnail capture. /// A clean viewport frame is ready for thumbnail capture.
ThumbnailCaptureFrame, ThumbnailCaptureFrame,
/// Restore drawing UI after the compositor screenshot is captured.
ThumbnailCaptureFinished,
/// Native background save/autosave completed. /// Native background save/autosave completed.
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
SaveFinished(SaveOutcome), SaveFinished(SaveOutcome),
@@ -3389,8 +3373,6 @@ impl OpenCADStudio {
last_point: None, last_point: None,
main_window: None, main_window: None,
thumbnail_capture_clean: false, thumbnail_capture_clean: false,
#[cfg(not(target_arch = "wasm32"))]
pending_native_thumbnail_save: None,
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
pending_web_thumbnail_save: None, pending_web_thumbnail_save: None,
color_pick_target: None, color_pick_target: None,
+34 -165
View File
@@ -1709,22 +1709,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub(super) fn on_thumbnail_capture_frame(&mut self) -> Task<Message> { pub(super) fn on_thumbnail_capture_frame(&mut self) -> Task<Message> {
let Some(pending) = self.pending_native_thumbnail_save.take() else { Task::none()
return Task::none();
};
let Some(i) = self.tabs.iter().position(|tab| tab.id == pending.tab_id) else {
self.thumbnail_capture_clean = false;
return Task::none();
};
self.queue_native_save(
i,
pending.path,
pending.version,
pending.purpose,
pending.continuation,
pending.set_current_path,
pending.check_external_change,
)
} }
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
@@ -1816,27 +1801,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
check_external_change: bool, check_external_change: bool,
) -> Task<Message> { ) -> Task<Message> {
let tab_id = self.tabs[i].id; let tab_id = self.tabs[i].id;
if self
.pending_native_thumbnail_save
.as_ref()
.is_some_and(|pending| pending.tab_id == tab_id)
{
if purpose != crate::app::SavePurpose::Autosave {
self.command_line
.push_info(crate::t!("Save already running for this drawing.").as_ref());
}
return Task::none();
}
let capture_ready =
self.thumbnail_capture_clean && self.pending_native_thumbnail_save.is_none();
if self.active_save_jobs.contains_key(&tab_id) { if self.active_save_jobs.contains_key(&tab_id) {
if purpose != crate::app::SavePurpose::Autosave { if purpose != crate::app::SavePurpose::Autosave {
self.command_line self.command_line
.push_info(crate::t!("Save already running for this drawing.").as_ref()); .push_info(crate::t!("Save already running for this drawing.").as_ref());
} }
if capture_ready {
self.thumbnail_capture_clean = false;
}
return Task::none(); return Task::none();
} }
let destination_is_current = self.tabs[i] let destination_is_current = self.tabs[i]
@@ -1854,9 +1823,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
self.active_tab = i; self.active_tab = i;
self.save_dialog_for_unsaved = self.save_dialog_for_unsaved =
continuation != crate::app::SaveContinuation::None; continuation != crate::app::SaveContinuation::None;
if capture_ready {
self.thumbnail_capture_clean = false;
}
return self.open_save_dialog_window(i); return self.open_save_dialog_window(i);
} }
if purpose != crate::app::SavePurpose::Autosave if purpose != crate::app::SavePurpose::Autosave
@@ -1881,33 +1847,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
}); });
self.restore_failed_save_continuation(continuation, i); self.restore_failed_save_continuation(continuation, i);
self.active_modal = Some(crate::app::ModalKind::FileInUse); self.active_modal = Some(crate::app::ModalKind::FileInUse);
if capture_ready {
self.thumbnail_capture_clean = false;
}
return Task::none();
}
if purpose != crate::app::SavePurpose::Autosave
&& i == self.active_tab
&& !self.thumbnail_capture_clean
&& self.main_window.is_some()
&& crate::ui::wrap_bar::dropdown_bounds(
crate::app::view::VIEWPORT_CAPTURE_BOUNDS_ID,
)
.is_some()
{
self.pending_native_thumbnail_save = Some(
crate::app::PendingNativeThumbnailSave {
tab_id,
path,
version,
purpose,
continuation,
set_current_path,
check_external_change,
},
);
self.thumbnail_capture_clean = true;
return Task::none(); return Task::none();
} }
@@ -1934,9 +1873,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
}); });
self.restore_failed_save_continuation(continuation, i); self.restore_failed_save_continuation(continuation, i);
self.active_modal = Some(crate::app::ModalKind::FileInUse); self.active_modal = Some(crate::app::ModalKind::FileInUse);
if capture_ready {
self.thumbnail_capture_clean = false;
}
return Task::none(); return Task::none();
} }
Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => { Err(crate::io::edit_lock::EditLeaseError::Unavailable(error)) => {
@@ -1950,13 +1886,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let epoch = self.tabs[i].scene.geometry_epoch; let epoch = self.tabs[i].scene.geometry_epoch;
let revision = self.tabs[i].edit_revision; let revision = self.tabs[i].edit_revision;
let camera_generation = self.tabs[i].scene.camera_generation; let camera_generation = self.tabs[i].scene.camera_generation;
let thumbnail = (purpose != crate::app::SavePurpose::Autosave && i == self.active_tab)
.then_some(version >= acadrust::DxfVersion::AC1027);
let capture_bounds = thumbnail.and_then(|_| {
crate::ui::wrap_bar::dropdown_bounds(
crate::app::view::VIEWPORT_CAPTURE_BOUNDS_ID,
)
});
let clone_started = iced::time::Instant::now(); let clone_started = iced::time::Instant::now();
let snapshot = self.tabs[i].scene.document.clone(); let snapshot = self.tabs[i].scene.document.clone();
let clone_ms = clone_started.elapsed().as_secs_f64() * 1000.0; let clone_ms = clone_started.elapsed().as_secs_f64() * 1000.0;
@@ -2004,9 +1933,6 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
let (expected_fingerprint, verify_reader) = match verification { let (expected_fingerprint, verify_reader) = match verification {
Ok(verification) => verification, Ok(verification) => verification,
Err(error) => { Err(error) => {
if capture_ready {
self.thumbnail_capture_clean = false;
}
return Task::perform( return Task::perform(
async move { async move {
crate::app::SaveOutcome { crate::app::SaveOutcome {
@@ -2030,97 +1956,40 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
} }
}; };
let worker_path = path.clone(); let worker_path = path.clone();
let capture_window = thumbnail.and(capture_bounds).and(self.main_window); Task::perform(
let mut work = Some(( async move {
snapshot, let result = std::thread::spawn(move || {
thumbnail, crate::io::save_owned_as_version_atomic(
capture_bounds, snapshot,
worker_path, &worker_path,
expected_fingerprint,
verify_reader,
path,
previous_autosave,
));
let mut run_save = move |screenshot: Option<iced::window::Screenshot>| {
let (
mut snapshot,
thumbnail,
capture_bounds,
worker_path,
expected_fingerprint,
verify_reader,
path,
previous_autosave,
) = work.take().expect("save capture produced more than one result");
Task::perform(
async move {
let (result, refreshed_preview) = std::thread::spawn(move || {
let mut refreshed_preview = None;
if let Some(png) = thumbnail {
let started = iced::time::Instant::now();
if let Some(preview) = screenshot.as_ref().and_then(|screenshot| {
capture_bounds.and_then(|bounds| {
crate::io::thumbnail::from_screenshot(screenshot, bounds, png)
})
}) {
snapshot.preview = Some(preview);
refreshed_preview = Some(snapshot.preview.clone());
}
if crate::perf::enabled() {
crate::perf_record!(
"[perf] save-thumbnail {:.1}ms",
started.elapsed().as_secs_f64() * 1000.0,
);
}
}
let result = crate::io::save_owned_as_version_atomic(
snapshot,
&worker_path,
version,
backup,
expected_fingerprint,
verify_reader,
);
(result, refreshed_preview)
})
.join()
.unwrap_or_else(|_| {
(
Err(crate::io::SaveFailure::other("save worker panicked")),
None,
)
});
crate::app::SaveOutcome {
job_id,
tab_id,
epoch,
revision,
camera_generation,
path,
version, version,
previous_autosave, backup,
set_current_path, expected_fingerprint,
purpose, verify_reader,
continuation, )
refreshed_preview, })
result, .join()
} .unwrap_or_else(|_| {
}, Err(crate::io::SaveFailure::other("save worker panicked"))
Message::SaveFinished, });
) crate::app::SaveOutcome {
}; job_id,
match capture_window { tab_id,
Some(window) => iced::window::screenshot(window).map(Some).then(move |screenshot| { epoch,
Task::batch([ revision,
Task::done(Message::ThumbnailCaptureFinished), camera_generation,
run_save(screenshot), path,
]) version,
}), previous_autosave,
None => { set_current_path,
self.thumbnail_capture_clean = false; purpose,
run_save(None) continuation,
} refreshed_preview: None,
} result,
}
},
Message::SaveFinished,
)
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
-5
View File
@@ -6994,11 +6994,6 @@ impl OpenCADStudio {
Message::ThumbnailCaptureFrame => self.on_thumbnail_capture_frame(), Message::ThumbnailCaptureFrame => self.on_thumbnail_capture_frame(),
Message::ThumbnailCaptureFinished => {
self.thumbnail_capture_clean = false;
Task::none()
}
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
Message::SaveFinished(outcome) => self.on_save_finished(outcome), Message::SaveFinished(outcome) => self.on_save_finished(outcome),
+11 -1
View File
@@ -7,12 +7,18 @@
//! the shared [`dwg_thumbnailer`] core crate (also used by the Windows/macOS //! the shared [`dwg_thumbnailer`] core crate (also used by the Windows/macOS
//! thumbnail handlers). //! thumbnail handlers).
#[cfg(any(target_arch = "wasm32", test))]
use acadrust::{Preview, PreviewFormat}; use acadrust::{Preview, PreviewFormat};
#[cfg(target_arch = "wasm32")]
use iced::Rectangle; use iced::Rectangle;
use image::{ImageFormat, RgbImage}; use image::ImageFormat;
#[cfg(any(target_arch = "wasm32", test))]
use image::RgbImage;
#[cfg(any(target_arch = "wasm32", test))]
use std::io::Cursor; use std::io::Cursor;
/// Build a preview from the visible drawing area of an Iced window screenshot. /// Build a preview from the visible drawing area of an Iced window screenshot.
#[cfg(target_arch = "wasm32")]
pub fn from_screenshot( pub fn from_screenshot(
screenshot: &iced::window::Screenshot, screenshot: &iced::window::Screenshot,
bounds: Rectangle, bounds: Rectangle,
@@ -59,6 +65,7 @@ pub fn from_screenshot(
const MAX_DIM: u32 = 256; const MAX_DIM: u32 = 256;
/// Canvas dimensions for an aspect ratio, longest edge = [`MAX_DIM`]. /// Canvas dimensions for an aspect ratio, longest edge = [`MAX_DIM`].
#[cfg(any(target_arch = "wasm32", test))]
fn canvas_dims(aspect: f64) -> (u32, u32) { fn canvas_dims(aspect: f64) -> (u32, u32) {
if aspect >= 1.0 { if aspect >= 1.0 {
(MAX_DIM, ((MAX_DIM as f64 / aspect).round() as u32).clamp(16, MAX_DIM)) (MAX_DIM, ((MAX_DIM as f64 / aspect).round() as u32).clamp(16, MAX_DIM))
@@ -68,6 +75,7 @@ fn canvas_dims(aspect: f64) -> (u32, u32) {
} }
/// Encode PNG for R2013+; older targets receive a BMP/DIB. /// Encode PNG for R2013+; older targets receive a BMP/DIB.
#[cfg(any(target_arch = "wasm32", test))]
fn encode(img: RgbImage, png: bool) -> Option<Preview> { fn encode(img: RgbImage, png: bool) -> Option<Preview> {
if png { if png {
let mut buf = Cursor::new(Vec::new()); let mut buf = Cursor::new(Vec::new());
@@ -81,6 +89,7 @@ fn encode(img: RgbImage, png: bool) -> Option<Preview> {
/// Build an 8-bit palettised, `BI_RLE8`-compressed DIB. `None` when the image /// Build an 8-bit palettised, `BI_RLE8`-compressed DIB. `None` when the image
/// holds more than 256 distinct colours (the caller then uses 24-bit). /// holds more than 256 distinct colours (the caller then uses 24-bit).
#[cfg(any(target_arch = "wasm32", test))]
fn rle8_dib(img: &RgbImage) -> Option<Vec<u8>> { fn rle8_dib(img: &RgbImage) -> Option<Vec<u8>> {
let (w, h) = (img.width(), img.height()); let (w, h) = (img.width(), img.height());
// Exact palette + per-pixel index (top-to-bottom, left-to-right). // Exact palette + per-pixel index (top-to-bottom, left-to-right).
@@ -149,6 +158,7 @@ fn rle8_dib(img: &RgbImage) -> Option<Vec<u8>> {
} }
/// 24-bit uncompressed DIB (fallback): the `image` BMP minus its file header. /// 24-bit uncompressed DIB (fallback): the `image` BMP minus its file header.
#[cfg(any(target_arch = "wasm32", test))]
fn bmp24_dib(img: &RgbImage) -> Option<Vec<u8>> { fn bmp24_dib(img: &RgbImage) -> Option<Vec<u8>> {
let mut buf = Cursor::new(Vec::new()); let mut buf = Cursor::new(Vec::new());
img.write_to(&mut buf, ImageFormat::Bmp).ok()?; img.write_to(&mut buf, ImageFormat::Bmp).ok()?;