export: add Fit mode to image export too
ImageExportSettings gains `fit: ExportFitMode` (default Letterbox) + a "Fit" dropdown in advanced image settings; the image render path uses it instead of the hardcoded Letterbox. Image export supports a resolution override, so the mode matters when the override aspect differs from the document.
This commit is contained in:
parent
fffcf0679c
commit
5bc117c518
|
|
@ -524,11 +524,14 @@ pub struct ImageExportSettings {
|
||||||
/// When false, the image is composited onto an opaque background before encoding.
|
/// When false, the image is composited onto an opaque background before encoding.
|
||||||
/// Only meaningful for formats that support alpha (PNG, WebP).
|
/// Only meaningful for formats that support alpha (PNG, WebP).
|
||||||
pub allow_transparency: bool,
|
pub allow_transparency: bool,
|
||||||
|
/// How the document is fit into the output frame when aspect ratios differ (default Letterbox).
|
||||||
|
#[serde(default)]
|
||||||
|
pub fit: ExportFitMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ImageExportSettings {
|
impl Default for ImageExportSettings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self { format: ImageFormat::Png, time: 0.0, width: None, height: None, quality: 90, allow_transparency: false }
|
Self { format: ImageFormat::Png, time: 0.0, width: None, height: None, quality: 90, allow_transparency: false, fit: ExportFitMode::Letterbox }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,6 +378,19 @@ impl ExportDialog {
|
||||||
if changed_h { self.image_settings.height = if h == 0 { None } else { Some(h) }; }
|
if changed_h { self.image_settings.height = if h == 0 { None } else { Some(h) }; }
|
||||||
ui.weak("(0 = document size)");
|
ui.weak("(0 = document size)");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fit mode — how the document maps into the output frame when aspect ratios differ.
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
use lightningbeam_core::export::ExportFitMode;
|
||||||
|
ui.label("Fit:");
|
||||||
|
egui::ComboBox::from_id_salt("image_fit_mode")
|
||||||
|
.selected_text(self.image_settings.fit.name())
|
||||||
|
.show_ui(ui, |ui| {
|
||||||
|
ui.selectable_value(&mut self.image_settings.fit, ExportFitMode::Letterbox, ExportFitMode::Letterbox.name());
|
||||||
|
ui.selectable_value(&mut self.image_settings.fit, ExportFitMode::Crop, ExportFitMode::Crop.name());
|
||||||
|
ui.selectable_value(&mut self.image_settings.fit, ExportFitMode::Stretch, ExportFitMode::Stretch.name());
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render advanced audio settings (sample rate, channels, bit depth, bitrate, time range)
|
/// Render advanced audio settings (sample rate, channels, bit depth, bitrate, time range)
|
||||||
|
|
|
||||||
|
|
@ -598,6 +598,7 @@ impl ExportOrchestrator {
|
||||||
// ── First call: render the frame to the GPU output texture ────────
|
// ── First call: render the frame to the GPU output texture ────────
|
||||||
let w = state.width;
|
let w = state.width;
|
||||||
let h = state.height;
|
let h = state.height;
|
||||||
|
let fit = state.settings.fit;
|
||||||
|
|
||||||
if state.gpu_resources.is_none() {
|
if state.gpu_resources.is_none() {
|
||||||
state.gpu_resources = Some(video_exporter::ExportGpuResources::new(device, w, h));
|
state.gpu_resources = Some(video_exporter::ExportGpuResources::new(device, w, h));
|
||||||
|
|
@ -632,8 +633,7 @@ impl ExportOrchestrator {
|
||||||
state.settings.allow_transparency,
|
state.settings.allow_transparency,
|
||||||
raster_store,
|
raster_store,
|
||||||
true, // image export composites on the shared device
|
true, // image export composites on the shared device
|
||||||
// Image export renders at the document's own size, so the fit transform is identity.
|
fit,
|
||||||
lightningbeam_core::export::ExportFitMode::Letterbox,
|
|
||||||
)?;
|
)?;
|
||||||
queue.submit(Some(encoder.finish()));
|
queue.submit(Some(encoder.finish()));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue