core: Add options to set and force the stage alignment

This commit is contained in:
nosamu 2023-06-12 05:29:05 -05:00 committed by Nathan Adams
parent 0c1cd6aa4f
commit 202fe2f1bb
12 changed files with 197 additions and 59 deletions

View File

@ -95,6 +95,9 @@ pub struct StageData<'gc> {
/// The alignment of the stage.
align: StageAlign,
/// Whether to prevent movies from the changing the stage alignment
forced_align: bool,
/// Whether or not a RENDER event should be dispatched on the next render
invalidated: bool,
@ -168,6 +171,7 @@ impl<'gc> Stage<'gc> {
},
invalidated: false,
align: Default::default(),
forced_align: false,
use_bitmap_downsampling: false,
view_bounds: Default::default(),
window_mode: Default::default(),
@ -386,8 +390,20 @@ impl<'gc> Stage<'gc> {
/// Set the stage alignment.
/// This only has an effect if the scale mode is not `StageScaleMode::ExactFit`.
pub fn set_align(self, context: &mut UpdateContext<'_, 'gc>, align: StageAlign) {
self.0.write(context.gc_context).align = align;
self.build_matrices(context);
if !self.forced_align() {
self.0.write(context.gc_context).align = align;
self.build_matrices(context);
}
}
/// Get whether movies are prevented from changing the stage alignment.
pub fn forced_align(self) -> bool {
self.0.read().forced_align
}
/// Set whether movies are prevented from changing the stage alignment.
pub fn set_forced_align(self, context: &mut UpdateContext<'_, 'gc>, force: bool) {
self.0.write(context.gc_context).forced_align = force;
}
/// Returns whether bitmaps will use high quality downsampling when scaled down.
@ -1034,7 +1050,7 @@ bitflags! {
///
/// This is a bitflags instead of an enum to mimic Flash Player behavior.
/// You can theoretically have both TOP and BOTTOM bits set, for example.
#[derive(Clone, Copy, Default, Collect)]
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Collect)]
#[collect(require_static)]
pub struct StageAlign: u8 {
/// Align to the top of the viewport.

View File

@ -3,7 +3,7 @@
#[macro_use]
mod display_object;
pub use display_object::{StageDisplayState, StageScaleMode};
pub use display_object::{StageAlign, StageDisplayState, StageScaleMode};
#[macro_use]
extern crate smallvec;

View File

@ -840,15 +840,6 @@ impl Player {
})
}
pub fn set_stage_align(&mut self, stage_align: &str) {
self.mutate_with_update_context(|context| {
let stage = context.stage;
if let Ok(stage_align) = StageAlign::from_str(stage_align) {
stage.set_align(context, stage_align);
}
})
}
pub fn set_quality(&mut self, quality: StageQuality) {
self.mutate_with_update_context(|context| {
context.stage.set_quality(context, quality);
@ -2020,6 +2011,8 @@ pub struct PlayerBuilder {
// Misc. player configuration
autoplay: bool,
align: StageAlign,
forced_align: bool,
scale_mode: StageScaleMode,
forced_scale_mode: bool,
fullscreen: bool,
@ -2058,7 +2051,9 @@ impl PlayerBuilder {
video: None,
autoplay: false,
scale_mode: StageScaleMode::ShowAll,
align: StageAlign::default(),
forced_align: false,
scale_mode: StageScaleMode::default(),
forced_scale_mode: false,
fullscreen: false,
// Disable script timeout in debug builds by default.
@ -2139,6 +2134,14 @@ impl PlayerBuilder {
self
}
/// Sets the stage scale mode and optionally prevents movies from changing it.
#[inline]
pub fn with_align(mut self, align: StageAlign, force: bool) -> Self {
self.align = align;
self.forced_align = force;
self
}
/// Sets whether the movie will start playing immediately upon load.
#[inline]
pub fn with_autoplay(mut self, autoplay: bool) -> Self {
@ -2395,6 +2398,8 @@ impl PlayerBuilder {
player_lock.mutate_with_update_context(|context| {
Avm2::load_player_globals(context).expect("Unable to load AVM2 globals");
let stage = context.stage;
stage.set_align(context, self.align);
stage.set_forced_align(context, self.forced_align);
stage.set_scale_mode(context, self.scale_mode);
stage.set_forced_scale_mode(context, self.forced_scale_mode);
stage.post_instantiation(context, None, Instantiator::Movie, false);

View File

@ -34,14 +34,24 @@ quality-high8x8linear = High (8x8) Linear
quality-high16x16 = High (16x16)
quality-high16x16linear = High (16x16) Linear
align = Stage Alignment
align-center = Center
align-left = Left
align-right = Right
align-top = Top
align-bottom = Bottom
align-top-left = Top-Left
align-bottom-left = Bottom-Left
align-top-right = Top-Right
align-bottom-right = Bottom-Right
align-force = Force
scale-mode = Scale Mode
scale-mode-exactfit = Exact Fit
scale-mode-noborder = No Border
scale-mode-noscale = No Scale
scale-mode-showall = Show All
force-scale-mode = Force Scale Mode
force-scale-mode-check = Force
scale-mode-force = Force
warn-if-unsupported = Warn if Unsupported
warn-if-unsupported-check = Warn

View File

@ -3,7 +3,7 @@ use anyhow::Error;
use clap::Parser;
use ruffle_core::backend::navigator::OpenURLMode;
use ruffle_core::config::Letterbox;
use ruffle_core::{LoadBehavior, StageScaleMode};
use ruffle_core::{LoadBehavior, StageAlign, StageScaleMode};
use ruffle_render::quality::StageQuality;
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
use std::path::Path;
@ -56,6 +56,14 @@ pub struct Opt {
#[clap(long, short, default_value = "high")]
pub quality: StageQuality,
/// The alignment of the stage.
#[clap(long, short)]
pub align: Option<StageAlign>,
/// Prevent movies from changing the stage alignment.
#[clap(long, action)]
pub force_align: bool,
/// The scale mode of the stage.
#[clap(long, short, default_value = "show-all")]
pub scale: StageScaleMode,

View File

@ -6,7 +6,7 @@ use egui::{
Align2, Button, Checkbox, ComboBox, DragValue, Grid, Slider, TextEdit, Ui, Widget, Window,
};
use ruffle_core::backend::navigator::OpenURLMode;
use ruffle_core::{LoadBehavior, StageScaleMode};
use ruffle_core::{LoadBehavior, StageAlign, StageScaleMode};
use ruffle_render::quality::StageQuality;
use std::path::Path;
use unic_langid::LanguageIdentifier;
@ -281,43 +281,119 @@ impl OpenDialog {
});
ui.end_row();
ui.label(text(&self.locale, "scale-mode"));
ComboBox::from_id_source("open-file-advanced-options-scale")
.selected_text(match self.options.scale {
StageScaleMode::ExactFit => text(&self.locale, "scale-mode-exactfit"),
StageScaleMode::NoBorder => text(&self.locale, "scale-mode-noborder"),
StageScaleMode::NoScale => text(&self.locale, "scale-mode-noscale"),
StageScaleMode::ShowAll => text(&self.locale, "scale-mode-showall"),
})
.show_ui(ui, |ui| {
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::ExactFit,
text(&self.locale, "scale-mode-exactfit"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::NoBorder,
text(&self.locale, "scale-mode-noborder"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::NoScale,
text(&self.locale, "scale-mode-noscale"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::ShowAll,
text(&self.locale, "scale-mode-showall"),
);
});
ui.label(text(&self.locale, "align"));
ui.horizontal(|ui| {
ComboBox::from_id_source("open-file-advanced-options-align")
.selected_text(match self.options.align {
StageAlign::TOP => text(&self.locale, "align-top"),
StageAlign::BOTTOM => text(&self.locale, "align-bottom"),
StageAlign::LEFT => text(&self.locale, "align-left"),
StageAlign::RIGHT => text(&self.locale, "align-right"),
_ => {
let align = self.options.align;
if align == StageAlign::TOP | StageAlign::LEFT {
text(&self.locale, "align-top-left")
} else if align == StageAlign::TOP | StageAlign::RIGHT {
text(&self.locale, "align-top-right")
} else if align == StageAlign::BOTTOM | StageAlign::LEFT {
text(&self.locale, "align-bottom-left")
} else if align == StageAlign::BOTTOM | StageAlign::RIGHT {
text(&self.locale, "align-bottom-right")
} else {
text(&self.locale, "align-center")
}
}
})
.show_ui(ui, |ui| {
ui.selectable_value(
&mut self.options.align,
StageAlign::default(),
text(&self.locale, "align-center"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::TOP,
text(&self.locale, "align-top"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::BOTTOM,
text(&self.locale, "align-bottom"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::LEFT,
text(&self.locale, "align-left"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::RIGHT,
text(&self.locale, "align-right"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::TOP | StageAlign::LEFT,
text(&self.locale, "align-top-left"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::TOP | StageAlign::RIGHT,
text(&self.locale, "align-top-right"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::BOTTOM | StageAlign::LEFT,
text(&self.locale, "align-bottom-left"),
);
ui.selectable_value(
&mut self.options.align,
StageAlign::BOTTOM | StageAlign::RIGHT,
text(&self.locale, "align-bottom-right"),
);
});
ui.checkbox(
&mut self.options.force_align,
text(&self.locale, "align-force"),
);
});
ui.end_row();
ui.label(text(&self.locale, "force-scale-mode"));
ui.checkbox(
&mut self.options.force_scale,
text(&self.locale, "force-scale-mode-check"),
);
ui.label(text(&self.locale, "scale-mode"));
ui.horizontal(|ui| {
ComboBox::from_id_source("open-file-advanced-options-scale")
.selected_text(match self.options.scale {
StageScaleMode::ExactFit => text(&self.locale, "scale-mode-exactfit"),
StageScaleMode::NoBorder => text(&self.locale, "scale-mode-noborder"),
StageScaleMode::NoScale => text(&self.locale, "scale-mode-noscale"),
StageScaleMode::ShowAll => text(&self.locale, "scale-mode-showall"),
})
.show_ui(ui, |ui| {
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::ExactFit,
text(&self.locale, "scale-mode-exactfit"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::NoBorder,
text(&self.locale, "scale-mode-noborder"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::NoScale,
text(&self.locale, "scale-mode-noscale"),
);
ui.selectable_value(
&mut self.options.scale,
StageScaleMode::ShowAll,
text(&self.locale, "scale-mode-showall"),
);
});
ui.checkbox(
&mut self.options.force_scale,
text(&self.locale, "scale-mode-force"),
);
});
ui.end_row();
ui.label(text(&self.locale, "dummy-external-interface"));

View File

@ -11,7 +11,7 @@ use anyhow::anyhow;
use ruffle_core::backend::audio::AudioBackend;
use ruffle_core::backend::navigator::OpenURLMode;
use ruffle_core::config::Letterbox;
use ruffle_core::{LoadBehavior, Player, PlayerBuilder, PlayerEvent, StageScaleMode};
use ruffle_core::{LoadBehavior, Player, PlayerBuilder, PlayerEvent, StageAlign, StageScaleMode};
use ruffle_render::backend::RenderBackend;
use ruffle_render::quality::StageQuality;
use ruffle_render_wgpu::backend::WgpuRenderBackend;
@ -31,6 +31,8 @@ pub struct PlayerOptions {
pub max_execution_duration: f64,
pub base: Option<Url>,
pub quality: StageQuality,
pub align: StageAlign,
pub force_align: bool,
pub scale: StageScaleMode,
pub volume: f32,
pub force_scale: bool,
@ -54,6 +56,8 @@ impl From<&Opt> for PlayerOptions {
max_execution_duration: value.max_execution_duration,
base: value.base.clone(),
quality: value.quality,
align: value.align.unwrap_or_default(),
force_align: value.force_align,
scale: value.scale,
volume: value.volume,
force_scale: value.force_scale,
@ -138,6 +142,7 @@ impl ActivePlayer {
.with_max_execution_duration(Duration::from_secs_f64(opt.max_execution_duration))
.with_quality(opt.quality)
.with_warn_on_unsupported_content(opt.warn_on_unsupported_content)
.with_align(opt.align, opt.force_align)
.with_scale_mode(opt.scale, opt.force_scale)
.with_fullscreen(opt.fullscreen)
.with_load_behavior(opt.load_behavior)

View File

@ -31,6 +31,7 @@ export const DEFAULT_CONFIG: Required<BaseLoadOptions> = {
base: null,
menu: true,
salign: "",
forceAlign: false,
quality: "high",
scale: "showAll",
forceScale: false,

View File

@ -414,6 +414,13 @@ export interface BaseLoadOptions {
*/
salign?: string;
/**
* If set to true, movies are prevented from changing the stage alignment.
*
* @default false
*/
forceAlign?: boolean;
/**
* This is equivalent to Stage.quality.
*

View File

@ -32,6 +32,7 @@ const defaultConfig = {
letterbox: "on",
logLevel: "warn",
forceScale: true,
forceAlign: true,
};
const swfToFlashVersion = {

View File

@ -33,6 +33,7 @@ window.addEventListener("DOMContentLoaded", async () => {
base: swfUrl.substring(0, swfUrl.lastIndexOf("/") + 1),
// Override some default values when playing in the extension player page.
letterbox: "on" as Letterbox,
forceAlign: true,
forceScale: true,
});
});

View File

@ -19,8 +19,8 @@ use ruffle_core::external::{
};
use ruffle_core::tag_utils::SwfMovie;
use ruffle_core::{
Color, Player, PlayerBuilder, PlayerEvent, SandboxType, StageScaleMode, StaticCallstack,
ViewportDimensions,
Color, Player, PlayerBuilder, PlayerEvent, SandboxType, StageAlign, StageScaleMode,
StaticCallstack, ViewportDimensions,
};
use ruffle_render::quality::StageQuality;
use ruffle_video_software::backend::SoftwareVideoBackend;
@ -261,6 +261,8 @@ struct Config {
salign: Option<String>,
force_align: bool,
quality: Option<String>,
scale: Option<String>,
@ -580,11 +582,18 @@ impl Ruffle {
.and_then(|q| StageQuality::from_str(&q).ok())
.unwrap_or(default_quality),
)
.with_align(
config
.salign
.and_then(|s| StageAlign::from_str(&s).ok())
.unwrap_or_default(),
config.force_align,
)
.with_scale_mode(
config
.scale
.and_then(|s| StageScaleMode::from_str(&s).ok())
.unwrap_or(StageScaleMode::ShowAll),
.unwrap_or_default(),
config.force_scale,
)
.with_frame_rate(config.frame_rate)
@ -597,7 +606,6 @@ impl Ruffle {
// Set config parameters.
core.set_background_color(config.background_color);
core.set_show_menu(config.show_menu);
core.set_stage_align(config.salign.as_deref().unwrap_or(""));
core.set_window_mode(config.wmode.as_deref().unwrap_or("window"));
callstack = Some(core.callstack());
}