made mask exporting multithreaded for speed and greyed out export button during generation

This commit is contained in:
grimsace
2026-04-20 14:33:27 -05:00
parent 33ea3a805a
commit cab349fa52
3 changed files with 145 additions and 75 deletions
+1
View File
@@ -11,6 +11,7 @@ dirs = "6"
rfd = "0.15" rfd = "0.15"
tiny-skia = "0.11" tiny-skia = "0.11"
image = { version = "0.25", default-features = true } image = { version = "0.25", default-features = true }
rayon = "1.10"
[profile.release] [profile.release]
opt-level = "z" opt-level = "z"
+122 -54
View File
@@ -3,6 +3,7 @@ use std::fmt::Write as _;
use std::path::PathBuf; use std::path::PathBuf;
use image::{DynamicImage, ImageFormat, RgbaImage, imageops::FilterType}; use image::{DynamicImage, ImageFormat, RgbaImage, imageops::FilterType};
use rayon::prelude::*;
use rfd::FileDialog; use rfd::FileDialog;
use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Rect, Stroke, StrokeDash, Transform}; use tiny_skia::{FillRule, Paint, PathBuilder, Pixmap, Rect, Stroke, StrokeDash, Transform};
@@ -334,21 +335,59 @@ fn export_composite_masks(
let wall_w = (g.cell / 2.5).max(1.0); let wall_w = (g.cell / 2.5).max(1.0);
let door_w = (g.cell / 10.0).max(1.0); let door_w = (g.cell / 10.0).max(1.0);
let mut floors = Pixmap::new(g.width, g.height) // List of tasks to perform in parallel
.ok_or_else(|| "Failed to allocate floors mask canvas".to_string())?; enum MaskTask {
fill_bg(&mut floors, BLACK); Floors,
for &(x, y) in &scene.corridor_cells { Walls,
fill_rect_cell(&mut floors, &g, x, y, WHITE); Doors,
} Archways,
for room in &layout.rooms { LockedDoors,
fill_rect_room(&mut floors, &g, room, WHITE); SecretDoors,
Windows,
StartMarkers,
EndMarkers,
TrapMarkers,
MonsterMarkers,
Grid,
} }
let mut walls = Pixmap::new(g.width, g.height) let mut tasks = vec![
.ok_or_else(|| "Failed to allocate walls mask canvas".to_string())?; MaskTask::Floors,
fill_bg(&mut walls, BLACK); MaskTask::Walls,
MaskTask::Doors,
MaskTask::Archways,
MaskTask::LockedDoors,
MaskTask::SecretDoors,
MaskTask::Windows,
MaskTask::StartMarkers,
MaskTask::EndMarkers,
MaskTask::TrapMarkers,
MaskTask::MonsterMarkers,
];
if settings.export_show_grid {
tasks.push(MaskTask::Grid);
}
// Process masks in parallel
tasks.into_par_iter().try_for_each(|task| {
let (stem, pixmap) = match task {
MaskTask::Floors => {
let mut pixmap = Pixmap::new(g.width, g.height).unwrap();
fill_bg(&mut pixmap, BLACK);
for &(x, y) in &scene.corridor_cells {
fill_rect_cell(&mut pixmap, &g, x, y, WHITE);
}
for room in &layout.rooms {
fill_rect_room(&mut pixmap, &g, room, WHITE);
}
("floors_mask", pixmap)
}
MaskTask::Walls => {
let mut pixmap = Pixmap::new(g.width, g.height).unwrap();
fill_bg(&mut pixmap, BLACK);
draw_cell_walls( draw_cell_walls(
&mut walls, &mut pixmap,
&g, &g,
&scene.corridor_cells, &scene.corridor_cells,
Some(&scene.corridor_edges), Some(&scene.corridor_edges),
@@ -357,7 +396,7 @@ fn export_composite_masks(
WHITE, WHITE,
); );
draw_cell_walls( draw_cell_walls(
&mut walls, &mut pixmap,
&g, &g,
&scene.room_cells, &scene.room_cells,
None, None,
@@ -365,35 +404,83 @@ fn export_composite_masks(
wall_w, wall_w,
WHITE, WHITE,
); );
("walls_mask", pixmap)
let mut doors = blank_mask(&g, "doors")?; }
let mut archways = blank_mask(&g, "archways")?; MaskTask::Doors => {
let mut locked_doors = blank_mask(&g, "locked doors")?; let mut pixmap = blank_mask(&g, "doors").unwrap();
let mut secret_doors = blank_mask(&g, "secret doors")?; for door in &layout.doors {
let mut windows = blank_mask(&g, "windows")?; if !door.secret && !door.archway && !door.locked {
let mut start_markers = blank_mask(&g, "start markers")?; draw_door(&mut pixmap, &g, door, door_w, WHITE, DoorStyle::Solid);
let mut end_markers = blank_mask(&g, "end markers")?; }
let mut trap_markers = blank_mask(&g, "trap markers")?; }
let mut monster_markers = blank_mask(&g, "monster markers")?; ("doors_mask", pixmap)
}
MaskTask::Archways => {
let mut pixmap = blank_mask(&g, "archways").unwrap();
for door in &layout.doors {
if door.archway {
draw_door(&mut pixmap, &g, door, door_w, WHITE, DoorStyle::Solid);
}
}
("archways_mask", pixmap)
}
MaskTask::LockedDoors => {
let mut pixmap = blank_mask(&g, "locked doors").unwrap();
for door in &layout.doors {
if door.locked {
draw_door(&mut pixmap, &g, door, door_w, WHITE, DoorStyle::Solid);
}
}
("locked_doors_mask", pixmap)
}
MaskTask::SecretDoors => {
let mut pixmap = blank_mask(&g, "secret doors").unwrap();
for door in &layout.doors { for door in &layout.doors {
if door.secret { if door.secret {
draw_door(&mut secret_doors, &g, door, door_w, WHITE, DoorStyle::Solid); draw_door(&mut pixmap, &g, door, door_w, WHITE, DoorStyle::Solid);
} else if door.archway {
draw_door(&mut archways, &g, door, door_w, WHITE, DoorStyle::Solid);
} else if door.locked {
draw_door(&mut locked_doors, &g, door, door_w, WHITE, DoorStyle::Solid);
} else {
draw_door(&mut doors, &g, door, door_w, WHITE, DoorStyle::Solid);
} }
} }
("secret_doors_mask", pixmap)
}
MaskTask::Windows => {
let mut pixmap = blank_mask(&g, "windows").unwrap();
for window in &layout.windows { for window in &layout.windows {
draw_window(&mut windows, &g, window, door_w, WHITE, DoorStyle::Solid); draw_window(&mut pixmap, &g, window, door_w, WHITE, DoorStyle::Solid);
} }
fill_marker_mask(&mut start_markers, &g, &layout.start_markers); ("windows_mask", pixmap)
fill_marker_mask(&mut end_markers, &g, &layout.end_markers); }
fill_marker_mask(&mut trap_markers, &g, &layout.trap_markers); MaskTask::StartMarkers => {
fill_marker_mask(&mut monster_markers, &g, &layout.monster_markers); let mut pixmap = blank_mask(&g, "start markers").unwrap();
fill_marker_mask(&mut pixmap, &g, &layout.start_markers);
("start_markers_mask", pixmap)
}
MaskTask::EndMarkers => {
let mut pixmap = blank_mask(&g, "end markers").unwrap();
fill_marker_mask(&mut pixmap, &g, &layout.end_markers);
("end_markers_mask", pixmap)
}
MaskTask::TrapMarkers => {
let mut pixmap = blank_mask(&g, "trap markers").unwrap();
fill_marker_mask(&mut pixmap, &g, &layout.trap_markers);
("trap_markers_mask", pixmap)
}
MaskTask::MonsterMarkers => {
let mut pixmap = blank_mask(&g, "monster markers").unwrap();
fill_marker_mask(&mut pixmap, &g, &layout.monster_markers);
("monster_markers_mask", pixmap)
}
MaskTask::Grid => {
let mut pixmap = Pixmap::new(g.width, g.height).unwrap();
fill_bg(&mut pixmap, BLACK);
draw_grid_color(&mut pixmap, &g, WHITE);
("grid_mask", pixmap)
}
};
save_mask_image(folder, stem, &pixmap, settings)
})?;
// Also export the composite image
let composite = render_pixmap(layout, settings)?; let composite = render_pixmap(layout, settings)?;
let composite_img = raster_image_from_pixmap(&composite, settings)?; let composite_img = raster_image_from_pixmap(&composite, settings)?;
let composite_ext = settings.export_format.extension(); let composite_ext = settings.export_format.extension();
@@ -402,25 +489,6 @@ fn export_composite_masks(
.save_with_format(&composite_path, export_image_format(settings.export_format)) .save_with_format(&composite_path, export_image_format(settings.export_format))
.map_err(|e| format!("Failed writing {}: {e}", composite_path.display()))?; .map_err(|e| format!("Failed writing {}: {e}", composite_path.display()))?;
save_mask_image(folder, "floors_mask", &floors, settings)?;
save_mask_image(folder, "walls_mask", &walls, settings)?;
save_mask_image(folder, "doors_mask", &doors, settings)?;
save_mask_image(folder, "archways_mask", &archways, settings)?;
save_mask_image(folder, "locked_doors_mask", &locked_doors, settings)?;
save_mask_image(folder, "secret_doors_mask", &secret_doors, settings)?;
save_mask_image(folder, "windows_mask", &windows, settings)?;
save_mask_image(folder, "start_markers_mask", &start_markers, settings)?;
save_mask_image(folder, "end_markers_mask", &end_markers, settings)?;
save_mask_image(folder, "trap_markers_mask", &trap_markers, settings)?;
save_mask_image(folder, "monster_markers_mask", &monster_markers, settings)?;
if settings.export_show_grid {
let mut grid = Pixmap::new(g.width, g.height)
.ok_or_else(|| "Failed to allocate grid mask canvas".to_string())?;
fill_bg(&mut grid, BLACK);
draw_grid_color(&mut grid, &g, WHITE);
save_mask_image(folder, "grid_mask", &grid, settings)?;
}
Ok(()) Ok(())
} }
+2 -1
View File
@@ -471,7 +471,8 @@ fn draw_generate_tab(
} }
ui.add_space(6.0); ui.add_space(6.0);
if ui.button("Export Image").clicked() { let export_button = ui.add_enabled(!export_in_progress, egui::Button::new("Export Image"));
if export_button.clicked() {
result.export_clicked = true; result.export_clicked = true;
} }