diff --git a/Cargo.lock b/Cargo.lock
index 1625debd..c6939aa1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2998,6 +2998,7 @@ dependencies = [
"fragile",
"fuzzy-matcher",
"glam",
+ "image",
"indextree",
"itertools 0.11.0",
"lexical-sort",
@@ -3009,6 +3010,7 @@ dependencies = [
"luminol-filesystem",
"luminol-graphics",
"murmur3",
+ "oneshot",
"parking_lot",
"qp-trie",
"strum",
diff --git a/crates/components/Cargo.toml b/crates/components/Cargo.toml
index c8caf78c..b05541e1 100644
--- a/crates/components/Cargo.toml
+++ b/crates/components/Cargo.toml
@@ -46,6 +46,9 @@ lexical-sort = "0.3.1"
fragile.workspace = true
parking_lot.workspace = true
+oneshot.workspace = true
fuzzy-matcher = "0.3.7"
murmur3.workspace = true
+
+image.workspace = true
diff --git a/crates/components/src/map_view.rs b/crates/components/src/map_view.rs
index f9754376..e237efd7 100644
--- a/crates/components/src/map_view.rs
+++ b/crates/components/src/map_view.rs
@@ -15,7 +15,9 @@
// You should have received a copy of the GNU General Public License
// along with Luminol. If not, see .
+use color_eyre::eyre::{ContextCompat, WrapErr};
use itertools::Itertools;
+use std::io::Write;
pub struct MapView {
/// Toggle to display the visible region in-game.
@@ -697,4 +699,284 @@ impl MapView {
response
}
+
+ /// Saves the current state of the map to an image file of the user's choice (will prompt the
+ /// user with a file picker).
+ /// This function returns a future that you need to `.await` to finish saving the image, but
+ /// the future doesn't borrow anything so you don't need to worry about lifetime-related issues.
+ pub fn save_as_image(
+ &self,
+ graphics_state: &std::sync::Arc,
+ map: &luminol_data::rpg::Map,
+ ) -> impl std::future::Future