Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hook up Quality in wgpu and add quality options to context menu & cli #9400

Merged
merged 10 commits into from
Feb 6, 2023
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions core/src/avm1/activation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2045,7 +2045,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
}

fn action_toggle_quality(&mut self) -> Result<FrameControl<'gc>, 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.
Expand All @@ -2057,7 +2057,7 @@ impl<'a, 'gc> Activation<'a, 'gc> {
};
self.context
.stage
.set_quality(self.context.gc_context, new_quality);
.set_quality(&mut self.context, new_quality);
self.context
.stage
.set_use_bitmap_downsampling(self.context.gc_context, use_bitmap_downsamping);
Expand Down
8 changes: 4 additions & 4 deletions core/src/avm1/object/stage_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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.
Expand All @@ -696,7 +696,7 @@ fn set_high_quality<'gc>(
activation
.context
.stage
.set_quality(activation.context.gc_context, quality);
.set_quality(&mut activation.context, quality);
}
Ok(())
}
Expand Down Expand Up @@ -748,7 +748,7 @@ fn set_quality<'gc>(
activation
.context
.stage
.set_quality(activation.context.gc_context, quality);
.set_quality(&mut activation.context, quality);
}
Ok(())
}
Expand Down
2 changes: 1 addition & 1 deletion core/src/avm2/globals/flash/display/stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ pub fn set_quality<'gc>(
activation
.context
.stage
.set_quality(activation.context.gc_context, quality);
.set_quality(&mut activation.context, quality);
}
Ok(Value::Undefined)
}
Expand Down
3 changes: 2 additions & 1 deletion core/src/avm2/globals/flash/geom/transform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>(
Expand Down
35 changes: 34 additions & 1 deletion core/src/context_menu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::avm2;
use crate::display_object::Stage;
use crate::display_object::TDisplayObject;
use gc_arena::Collect;
use ruffle_render::quality::StageQuality;
use serde::Serialize;

#[derive(Collect, Default)]
Expand Down Expand Up @@ -79,6 +80,36 @@ impl<'gc> ContextMenuState<'gc> {
ContextMenuCallback::Back,
);
}
if item_flags.quality {
// TODO: This should be a submenu, but at time of writing those aren't supported
self.push(
ContextMenuItem {
enabled: stage.quality() != StageQuality::Low,
separator_before: true,
checked: stage.quality() == StageQuality::Low,
caption: "Quality: Low".to_string(),
},
ContextMenuCallback::QualityLow,
);
self.push(
ContextMenuItem {
enabled: stage.quality() != StageQuality::Medium,
separator_before: false,
checked: stage.quality() == StageQuality::Medium,
caption: "Quality: Medium".to_string(),
},
ContextMenuCallback::QualityMedium,
);
self.push(
ContextMenuItem {
enabled: stage.quality() != StageQuality::High,
separator_before: false,
checked: stage.quality() == StageQuality::High,
caption: "Quality: High".to_string(),
},
ContextMenuCallback::QualityHigh,
);
}
}
}

Expand All @@ -96,7 +127,9 @@ pub struct ContextMenuItem {
#[collect(no_drop)]
pub enum ContextMenuCallback<'gc> {
Zoom,
Quality,
QualityLow,
QualityMedium,
QualityHigh,
Play,
Loop,
Rewind,
Expand Down
2 changes: 1 addition & 1 deletion core/src/display_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
127 changes: 4 additions & 123 deletions core/src/display_object/stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -223,8 +224,8 @@ impl<'gc> Stage<'gc> {
/// In the Flash Player, the quality setting affects anti-aliasing and smoothing of bitmaps.
/// This setting is currently ignored in Ruffle.
/// Used by AVM1 `stage.quality` and AVM2 `Stage.quality` properties.
pub fn set_quality(self, gc_context: MutationContext<'gc, '_>, quality: StageQuality) {
let mut this = self.0.write(gc_context);
pub fn set_quality(self, context: &mut UpdateContext<'_, 'gc>, quality: StageQuality) {
let mut this = self.0.write(context.gc_context);
this.quality = quality;
this.use_bitmap_downsampling = matches!(
quality,
Expand All @@ -234,6 +235,7 @@ impl<'gc> Stage<'gc> {
| StageQuality::High16x16
| StageQuality::High16x16Linear
);
context.renderer.set_quality(quality);
}

pub fn stage3ds(&self) -> Ref<Vec<Avm2Object<'gc>>> {
Expand Down Expand Up @@ -1022,127 +1024,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<Self, Self::Err> {
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<Self, Self::Err> {
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
Expand Down
3 changes: 1 addition & 2 deletions core/src/display_object/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
Loading