diff --git a/Cargo.lock b/Cargo.lock index b82f7408d..2e6a27907 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3569,6 +3569,7 @@ dependencies = [ "jpeg-decoder", "lyon", "png", + "ruffle_wstr", "serde", "smallvec", "swf", diff --git a/core/src/avm1/activation.rs b/core/src/avm1/activation.rs index 8b5e1978a..437e941b6 100644 --- a/core/src/avm1/activation.rs +++ b/core/src/avm1/activation.rs @@ -2045,7 +2045,7 @@ impl<'a, 'gc> Activation<'a, 'gc> { } fn action_toggle_quality(&mut self) -> Result, Error<'gc>> { - use crate::display_object::StageQuality; + use ruffle_render::quality::StageQuality; // Toggle between `Low` and `High`/`Best` quality. // This op remembers whether the stage quality was `Best` or higher, so we have to maintain // the bitmap downsampling flag to ensure we toggle back to the proper quality. diff --git a/core/src/avm1/object/stage_object.rs b/core/src/avm1/object/stage_object.rs index 6e69c43fa..8b2f5142c 100644 --- a/core/src/avm1/object/stage_object.rs +++ b/core/src/avm1/object/stage_object.rs @@ -668,7 +668,7 @@ fn high_quality<'gc>( activation: &mut Activation<'_, 'gc>, _this: DisplayObject<'gc>, ) -> Value<'gc> { - use crate::display_object::StageQuality; + use ruffle_render::quality::StageQuality; let quality = match activation.context.stage.quality() { StageQuality::Best => 2, StageQuality::High => 1, @@ -682,7 +682,7 @@ fn set_high_quality<'gc>( _this: DisplayObject<'gc>, val: Value<'gc>, ) -> Result<(), Error<'gc>> { - use crate::display_object::StageQuality; + use ruffle_render::quality::StageQuality; let val = val.coerce_to_f64(activation)?; if !val.is_nan() { // 0 -> Low, 1 -> High, 2 -> Best, but with some odd rules for non-integers. diff --git a/core/src/avm2/globals/flash/geom/transform.rs b/core/src/avm2/globals/flash/geom/transform.rs index ae64c8287..dc10ae171 100644 --- a/core/src/avm2/globals/flash/geom/transform.rs +++ b/core/src/avm2/globals/flash/geom/transform.rs @@ -3,8 +3,9 @@ use crate::avm2::Multiname; use crate::avm2::{Activation, Error, Namespace, Object, TObject, Value}; use crate::avm2_stub_getter; -use crate::display_object::{StageQuality, TDisplayObject}; +use crate::display_object::TDisplayObject; use crate::prelude::{ColorTransform, DisplayObject, Matrix, Twips}; +use ruffle_render::quality::StageQuality; use swf::Fixed8; fn get_display_object<'gc>( diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 282057f00..b1db53d9e 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -50,7 +50,7 @@ pub use loader_display::LoaderDisplay; pub use morph_shape::{MorphShape, MorphShapeStatic}; pub use movie_clip::{MovieClip, Scene}; use ruffle_render::commands::CommandHandler; -pub use stage::{Stage, StageAlign, StageDisplayState, StageQuality, StageScaleMode, WindowMode}; +pub use stage::{Stage, StageAlign, StageDisplayState, StageScaleMode, WindowMode}; pub use text::Text; pub use video::Video; diff --git a/core/src/display_object/stage.rs b/core/src/display_object/stage.rs index bb538a5bb..f1c5df73c 100644 --- a/core/src/display_object/stage.rs +++ b/core/src/display_object/stage.rs @@ -26,6 +26,7 @@ use bitflags::bitflags; use gc_arena::{Collect, GcCell, MutationContext}; use ruffle_render::backend::ViewportDimensions; use ruffle_render::commands::CommandHandler; +use ruffle_render::quality::StageQuality; use std::cell::{Ref, RefMut}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; @@ -1022,127 +1023,6 @@ impl FromWStr for StageAlign { } } -/// The quality setting of the `Stage`. -/// -/// In the Flash Player, this settings affects anti-aliasing and bitmap smoothing. -/// These settings currently have no effect in Ruffle, but the active setting is still stored. -/// [StageQuality in the AS3 Reference](https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageQuality.html) -#[derive(Default, Clone, Collect, Copy, Debug, Eq, PartialEq)] -#[collect(require_static)] -pub enum StageQuality { - /// No anti-aliasing, and bitmaps are never smoothed. - Low, - - /// 2x anti-aliasing. - Medium, - - /// 4x anti-aliasing. - #[default] - High, - - /// 4x anti-aliasing with high quality downsampling. - /// Bitmaps will use high quality downsampling when scaled down. - /// Despite the name, this is not the best quality setting as 8x8 and 16x16 modes were added to - /// Flash Player 11.3. - Best, - - /// 8x anti-aliasing. - /// Bitmaps will use high quality downsampling when scaled down. - High8x8, - - /// 8x anti-aliasing done in linear sRGB space. - /// Bitmaps will use high quality downsampling when scaled down. - High8x8Linear, - - /// 16x anti-aliasing. - /// Bitmaps will use high quality downsampling when scaled down. - High16x16, - - /// 16x anti-aliasing done in linear sRGB space. - /// Bitmaps will use high quality downsampling when scaled down. - High16x16Linear, -} - -impl StageQuality { - /// Returns the string representing the quality setting as returned by AVM1 `_quality` and - /// AVM2 `Stage.quality`. - pub fn into_avm_str(self) -> &'static str { - // Flash Player always returns quality in uppercase, despite the AVM2 `StageQuality` being - // lowercase. - match self { - StageQuality::Low => "LOW", - StageQuality::Medium => "MEDIUM", - StageQuality::High => "HIGH", - StageQuality::Best => "BEST", - // The linear sRGB quality settings are not returned even if they are active. - StageQuality::High8x8 | StageQuality::High8x8Linear => "8X8", - StageQuality::High16x16 | StageQuality::High16x16Linear => "16X16", - } - } -} - -impl Display for StageQuality { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - // Match string values returned by AS. - let s = match *self { - StageQuality::Low => "low", - StageQuality::Medium => "medium", - StageQuality::High => "high", - StageQuality::Best => "best", - StageQuality::High8x8 => "8x8", - StageQuality::High8x8Linear => "8x8linear", - StageQuality::High16x16 => "16x16", - StageQuality::High16x16Linear => "16x16linear", - }; - f.write_str(s) - } -} - -impl FromStr for StageQuality { - type Err = ParseEnumError; - - fn from_str(s: &str) -> Result { - let quality = match s.to_ascii_lowercase().as_str() { - "low" => StageQuality::Low, - "medium" => StageQuality::Medium, - "high" => StageQuality::High, - "best" => StageQuality::Best, - "8x8" => StageQuality::High8x8, - "8x8linear" => StageQuality::High8x8Linear, - "16x16" => StageQuality::High16x16, - "16x16linear" => StageQuality::High16x16Linear, - _ => return Err(ParseEnumError), - }; - Ok(quality) - } -} - -impl FromWStr for StageQuality { - type Err = ParseEnumError; - - fn from_wstr(s: &WStr) -> Result { - if s.eq_ignore_case(WStr::from_units(b"low")) { - Ok(StageQuality::Low) - } else if s.eq_ignore_case(WStr::from_units(b"medium")) { - Ok(StageQuality::Medium) - } else if s.eq_ignore_case(WStr::from_units(b"high")) { - Ok(StageQuality::High) - } else if s.eq_ignore_case(WStr::from_units(b"best")) { - Ok(StageQuality::Best) - } else if s.eq_ignore_case(WStr::from_units(b"8x8")) { - Ok(StageQuality::High8x8) - } else if s.eq_ignore_case(WStr::from_units(b"8x8linear")) { - Ok(StageQuality::High8x8Linear) - } else if s.eq_ignore_case(WStr::from_units(b"16x16")) { - Ok(StageQuality::High16x16) - } else if s.eq_ignore_case(WStr::from_units(b"16x16linear")) { - Ok(StageQuality::High16x16Linear) - } else { - Err(ParseEnumError) - } - } -} - /// The window mode of the Ruffle player. /// /// This setting controls how the Ruffle container is layered and rendered with other content on diff --git a/core/src/display_object/video.rs b/core/src/display_object/video.rs index 80da2035e..f7942e132 100644 --- a/core/src/display_object/video.rs +++ b/core/src/display_object/video.rs @@ -14,6 +14,7 @@ use gc_arena::{Collect, GcCell, MutationContext}; use ruffle_render::bitmap::BitmapInfo; use ruffle_render::bounding_box::BoundingBox; use ruffle_render::commands::CommandHandler; +use ruffle_render::quality::StageQuality; use ruffle_video::error::Error; use ruffle_video::frame::EncodedFrame; use ruffle_video::VideoStreamHandle; @@ -23,8 +24,6 @@ use std::collections::{BTreeMap, BTreeSet}; use std::sync::Arc; use swf::{CharacterId, DefineVideoStream, VideoFrame}; -use super::StageQuality; - /// A Video display object is a high-level interface to a video player. /// /// Video data may be embedded within a variety of container formats, including diff --git a/core/src/player.rs b/core/src/player.rs index 440f69c1c..0b64d4732 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -22,8 +22,8 @@ use crate::context_menu::{ BuiltInItemFlags, ContextMenuCallback, ContextMenuItem, ContextMenuState, }; use crate::display_object::{ - EditText, InteractiveObject, MovieClip, Stage, StageAlign, StageDisplayState, StageQuality, - StageScaleMode, TInteractiveObject, WindowMode, + EditText, InteractiveObject, MovieClip, Stage, StageAlign, StageDisplayState, StageScaleMode, + TInteractiveObject, WindowMode, }; use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode, MouseButton, PlayerEvent}; use crate::external::Value as ExternalValue; @@ -46,6 +46,7 @@ use instant::Instant; use rand::{rngs::SmallRng, SeedableRng}; use ruffle_render::backend::{null::NullRenderer, RenderBackend, ViewportDimensions}; use ruffle_render::commands::CommandList; +use ruffle_render::quality::StageQuality; use ruffle_render::transform::TransformStack; use ruffle_video::backend::VideoBackend; use std::cell::RefCell; diff --git a/render/Cargo.toml b/render/Cargo.toml index f2a455abe..8cdaa2572 100644 --- a/render/Cargo.toml +++ b/render/Cargo.toml @@ -8,6 +8,7 @@ repository.workspace = true version.workspace = true [dependencies] +ruffle_wstr = { path = "../wstr" } swf = { path = "../swf"} tracing = "0.1.37" gif = "0.12.0" diff --git a/render/src/lib.rs b/render/src/lib.rs index 48ff29659..9430001bc 100644 --- a/render/src/lib.rs +++ b/render/src/lib.rs @@ -12,5 +12,6 @@ pub mod transform; pub mod utils; pub mod commands; +pub mod quality; #[cfg(feature = "tessellator")] pub mod tessellator; diff --git a/render/src/quality.rs b/render/src/quality.rs new file mode 100644 index 000000000..be55ab85f --- /dev/null +++ b/render/src/quality.rs @@ -0,0 +1,128 @@ +use gc_arena::Collect; +use ruffle_wstr::{FromWStr, WStr}; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; + +/// The quality setting of the `Stage`. +/// +/// In the Flash Player, this settings affects anti-aliasing and bitmap smoothing. +/// These settings currently have no effect in Ruffle, but the active setting is still stored. +/// [StageQuality in the AS3 Reference](https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/StageQuality.html) +#[derive(Default, Clone, Collect, Copy, Debug, Eq, PartialEq)] +#[collect(require_static)] +pub enum StageQuality { + /// No anti-aliasing, and bitmaps are never smoothed. + Low, + + /// 2x anti-aliasing. + Medium, + + /// 4x anti-aliasing. + #[default] + High, + + /// 4x anti-aliasing with high quality downsampling. + /// Bitmaps will use high quality downsampling when scaled down. + /// Despite the name, this is not the best quality setting as 8x8 and 16x16 modes were added to + /// Flash Player 11.3. + Best, + + /// 8x anti-aliasing. + /// Bitmaps will use high quality downsampling when scaled down. + High8x8, + + /// 8x anti-aliasing done in linear sRGB space. + /// Bitmaps will use high quality downsampling when scaled down. + High8x8Linear, + + /// 16x anti-aliasing. + /// Bitmaps will use high quality downsampling when scaled down. + High16x16, + + /// 16x anti-aliasing done in linear sRGB space. + /// Bitmaps will use high quality downsampling when scaled down. + High16x16Linear, +} + +impl StageQuality { + /// Returns the string representing the quality setting as returned by AVM1 `_quality` and + /// AVM2 `Stage.quality`. + pub fn into_avm_str(self) -> &'static str { + // Flash Player always returns quality in uppercase, despite the AVM2 `StageQuality` being + // lowercase. + match self { + StageQuality::Low => "LOW", + StageQuality::Medium => "MEDIUM", + StageQuality::High => "HIGH", + StageQuality::Best => "BEST", + // The linear sRGB quality settings are not returned even if they are active. + StageQuality::High8x8 | StageQuality::High8x8Linear => "8X8", + StageQuality::High16x16 | StageQuality::High16x16Linear => "16X16", + } + } +} + +impl Display for StageQuality { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // Match string values returned by AS. + let s = match *self { + StageQuality::Low => "low", + StageQuality::Medium => "medium", + StageQuality::High => "high", + StageQuality::Best => "best", + StageQuality::High8x8 => "8x8", + StageQuality::High8x8Linear => "8x8linear", + StageQuality::High16x16 => "16x16", + StageQuality::High16x16Linear => "16x16linear", + }; + f.write_str(s) + } +} + +pub struct StageQualityError; + +impl FromStr for StageQuality { + type Err = StageQualityError; + + fn from_str(s: &str) -> Result { + let quality = match s.to_ascii_lowercase().as_str() { + "low" => StageQuality::Low, + "medium" => StageQuality::Medium, + "high" => StageQuality::High, + "best" => StageQuality::Best, + "8x8" => StageQuality::High8x8, + "8x8linear" => StageQuality::High8x8Linear, + "16x16" => StageQuality::High16x16, + "16x16linear" => StageQuality::High16x16Linear, + _ => return Err(StageQualityError), + }; + Ok(quality) + } +} + +impl FromWStr for StageQuality { + type Err = StageQualityError; + + fn from_wstr(s: &WStr) -> Result { + if s.eq_ignore_case(WStr::from_units(b"low")) { + Ok(StageQuality::Low) + } else if s.eq_ignore_case(WStr::from_units(b"medium")) { + Ok(StageQuality::Medium) + } else if s.eq_ignore_case(WStr::from_units(b"high")) { + Ok(StageQuality::High) + } else if s.eq_ignore_case(WStr::from_units(b"best")) { + Ok(StageQuality::Best) + } else if s.eq_ignore_case(WStr::from_units(b"8x8")) { + Ok(StageQuality::High8x8) + } else if s.eq_ignore_case(WStr::from_units(b"8x8linear")) { + Ok(StageQuality::High8x8Linear) + } else if s.eq_ignore_case(WStr::from_units(b"16x16")) { + Ok(StageQuality::High16x16) + } else if s.eq_ignore_case(WStr::from_units(b"16x16linear")) { + Ok(StageQuality::High16x16Linear) + } else { + Err(StageQualityError) + } + } +}